Post here for help and support regarding LunaLua and SMBX2's libraries and features.
Moderator: Userbase Moderators
|
|
|
|
-
Cambroski
- Shy Guy

- Posts: 8
- Joined: Tue Nov 01, 2022 7:29 pm
- Flair: Awake by day, asleep by night.
Postby Cambroski » Tue Nov 01, 2022 9:58 pm
Hello. I am currently making a world that uses custom powerups from the Super Mario Land 1 & 2 NPC pack by MegaDood. When I test the level the powerups work like they should, however when I test a world file, as if I were playing the episode/game, I get an error from anotherpowerup.lua.
I have also tried to test this with the latest anotherpowerup.lua from the forums and I still receive the same error. I don't really know what to look at when examining the debug window and I haven't seen any other errors like this on the forums. I was also wondering if it could be the version of SMBX2 that I am using. It is Beta 4 Patch 4 if that can narrow anything down. Thanks in advance!
|
|
|
|
|
|
|
|
|
-
Hoeloe
- Phanto

- Posts: 1465
- Joined: Sat Oct 03, 2015 6:18 pm
- Flair: The Codehaus Girl
- Pronouns: she/her
Postby Hoeloe » Thu Nov 03, 2022 11:20 am
.wld files can't be tested from the editor in SMBX2. Attempting to do so will instead boot up the Moondust engine, which is not complete and does not support SMBX2 features.
|
|
|
|
|
|
|
|
|
-
Emral
- Cute Yoshi Egg

- Posts: 9891
- Joined: Mon Jan 20, 2014 12:58 pm
- Flair: Phoenix
Postby Emral » Thu Nov 03, 2022 11:38 am
This is a bug in anotherpowerup. Having never tested it in worlds (>_>), it loads testmodemenu regardless of whether or not it's in the editor.
The solution is to ctrl + f for all lines that use "testmodemenu" in the anotherpowerup.lua script, and encasing them in an if statement like so
Code: Select all if Misc.inEditor() then
-- the code that loads or uses Testmodemenu
end
|
|
|
|
|
|
|
|
|
-
Cambroski
- Shy Guy

- Posts: 8
- Joined: Tue Nov 01, 2022 7:29 pm
- Flair: Awake by day, asleep by night.
Postby Cambroski » Thu Nov 03, 2022 6:51 pm
Enjl wrote: ↑Thu Nov 03, 2022 11:38 am
This is a bug in anotherpowerup. Having never tested it in worlds (>_>), it loads testmodemenu regardless of whether or not it's in the editor.
The solution is to ctrl + f for all lines that use "testmodemenu" in the anotherpowerup.lua script, and encasing them in an if statement like so
Code: Select all if Misc.inEditor() then
-- the code that loads or uses Testmodemenu
end
That didn't seem to work. It's giving me the exact same error that it usually does.
Code: Select all ===> scripts/base/engine/testmodemenu.lua:4 attempt to index global 'ffi' (a nil value)
===========================================================================
stack traceback:
scripts/base/engine/require.lua:150: in function 'require'
worlds/WIP Game/libs/anotherpowerup.lua:13: in function 'func'
scripts/base/engine/require.lua:150: in function 'require'
worlds/WIP Game/luna.lua:5: in function 'codeFile'
main.lua:743: in function 'loadCodeFile'
main.lua.889: in function <main.lua:793
[C]: in function '__xpcall'
main.lua:793: in function <main.lua:792>
|
|
|
|
|
|
|
|
|
-
Emral
- Cute Yoshi Egg

- Posts: 9891
- Joined: Mon Jan 20, 2014 12:58 pm
- Flair: Phoenix
Postby Emral » Thu Nov 03, 2022 9:23 pm
can you post the modified script?
|
|
|
|
|
|
|
|
|
-
Cambroski
- Shy Guy

- Posts: 8
- Joined: Tue Nov 01, 2022 7:29 pm
- Flair: Awake by day, asleep by night.
Postby Cambroski » Thu Nov 03, 2022 11:37 pm
Enjl wrote: ↑Thu Nov 03, 2022 9:23 pm
can you post the modified script?
Yup here it is. I think that I edited like you said but it didn't do anything different.
Code: Select all local playerManager = require("playerManager")
local testModeMenu = require("engine/testmodemenu")
local npcManager = require("npcManager")
local ap = {}
SaveData._ap = SaveData._ap or {}
local powerups = {}
local currentPowerup
local itemMap = {}
local tiers = {}
local isPoweringUp = false
powerups[2] = {9, 184, 185}
powerups[3] = {14, 182, 183}
powerups[4] = {34}
powerups[5] = {169}
powerups[6] = {170}
powerups[7] = {264, 277}
local defaultNPCPowerupMap = {
[9] = PLAYER_BIG,
[14] = PLAYER_FIREFLOWER,
[34] = PLAYER_LEAF,
[169] = PLAYER_TANOOKIE,
[170] = PLAYER_HAMMER,
[182] = PLAYER_FIREFLOWER,
[183] = PLAYER_FIREFLOWER,
[184] = PLAYER_BIG,
[185] = PLAYER_BIG,
[249] = PLAYER_BIG,
[264] = PLAYER_ICE,
[277] = PLAYER_ICE,
}
local players = {
"mario", "luigi", "peach", "toad", "link"
}
local function registerItemsInternal(powerup, ids)
if type(ids) == "number" then
ids = {}
elseif type(ids) ~= "table" then
powerup.items = {}
return
end
for k,v in ipairs(powerup.items) do
itemMap[v] = nil
end
powerup.items = ids
for k,v in ipairs(ids) do
if itemMap[v] ~= nil then
Misc.warn("Item " .. v .. " could not be registered to Powerup " .. libraryName .. ", because it was already registered to Powerup " .. itemMap[v] .. ".")
return
else
itemMap[v] = libraryName
end
end
end
local function loadPowerupAssets(thisPlayer,basePowerup)
if currentPowerup == nil then
return
end
local iniFile = Misc.resolveFile(players[thisPlayer.character] .. "-" .. currentPowerup.name .. ".ini")
if (iniFile == nil) then
iniFile = playerManager.getHitboxPath(thisPlayer.character, basePowerup);
end
Misc.loadCharacterHitBoxes(thisPlayer.character, basePowerup, iniFile)
Graphics.sprites[players[thisPlayer.character]][basePowerup].img = currentPowerup.spritesheets[thisPlayer.character]
end
-- registerPowerup registers a new powerup to the system
--- libraryName: string - The name of the library containing powerup information. One library per powerup. Think of it as a require(libraryName) call
-- Returns the library table for the powerup
function ap.registerPowerup(libraryName)
local entry = require(libraryName)
entry.registerItems = registerItemsInternal
entry.name = libraryName
entry.items = entry.items or {}
powerups[libraryName] = entry
for k,v in ipairs(entry.items) do
if itemMap[v] ~= nil then
Misc.warn("Item " .. v .. " could not be registered to Powerup " .. libraryName .. ", because it was already registered to Powerup " .. itemMap[v] .. ".")
return
else
itemMap[v] = libraryName
end
end
return entry
end
-- Replacement powerups
ap.powerReplacements = {}
ap.powerReplacements[3] = ap.registerPowerup("ap_fireflower")
ap.powerReplacements[7] = ap.registerPowerup("ap_iceflower")
-- registerItemTier registers a new tier chain for items.
-- spawnedItem: number - The item that spawns from a block.
-- chain: table - The list of states ordered by significance. Numbers correspond to vanilla states, strings correspond to the names of anotherpowerup powerups. See demo for more info.
-- if you provide chain as "true", it will accept everything except for small mario
function ap.registerItemTier(spawnedItem, chain)
if chain ~= true then
for k,v in ipairs(chain) do
if ap.powerReplacements[v] then
chain[k] = ap.powerReplacements[v].name
end
end
end
tiers[spawnedItem] = chain
end
-- Returns the player's current powerup, accounting for anotherpowerup powerups
function ap.getPowerup()
if player.powerup == 3 or player.powerup == 7 then
return currentPowerup.name
else
return player.powerup
end
end
function ap.onInitAPI()
registerEvent(ap, "onPostNPCKill")
registerEvent(ap, "onPlayerHarm")
registerEvent(ap, "onTickEnd")
registerEvent(ap, "onTick")
registerEvent(ap, "onExit")
registerEvent(ap, "onDraw")
registerEvent(ap, "onStart")
end
function ap.onTick()
if currentPowerup and player.forcedState == 0 and ap.getPowerup() ~= currentPowerup.name then
currentPowerup.onDisable()
currentPowerup = nil
SaveData._ap.cp = nil
end
if currentPowerup then
if player.mount < 2 then
if player.character ~= CHARACTER_LINK then
player:mem(0x160, FIELD_WORD, 3) -- Disable the nomral projectile timer. Powerups need to implement their own.
else
player:mem(0x162, FIELD_WORD, 29) -- Disable the link projectile timer. Powerups need to implement their own.
end
end
currentPowerup.onTick()
end
end
local testMenuWasActive = false
if Misc.inEditor () then
function ap.onDraw()
if currentPowerup and player.forcedState == 0 and ap.getPowerup() ~= currentPowerup.name then
currentPowerup.onDisable()
currentPowerup = nil
SaveData._ap.cp = nil
end
if currentPowerup then
currentPowerup.onDraw()
end
-- Handle test mode menu, and reload hitboxes after a bit
if (not testModeMenu.active and testMenuWasActive) or lunatime.tick() == 1 then
loadPowerupAssets(player,player.powerup)
end
testMenuWasActive = testModeMenu.active
end
end
function ap.onTickEnd()
for k,v in ipairs(NPC.get(table.unmap(itemMap))) do -- Thankfully this is more performant these days I guess!
if not v.data._anotherpowerup and v.despawnTimer > 0 then
v.data._anotherpowerup = true
local spawn = v:mem(0x138, FIELD_WORD)
if spawn == 1 or spawn == 3 or spawn == 4 then
local currentTier
local list
if tiers[v.id] ~= nil and tiers[v.id] ~= true then
if type(ap.getPowerup()) == "number" then
list = powerups[ap.getPowerup()]
else
list = currentPowerup.items
end
if list ~= nil then
for k,v in ipairs(tiers[v.id]) do
currentTier = 0
for _,n in ipairs(list) do
if n == v then
currentTier = k
break
end
end
if currentTier ~= 0 then break end
end
else
if tiers[v.id] then
currentTier = 0
end
end
if currentTier and currentTier < #tiers[v.id] then
local nextTier = tiers[v.id][currentTier + 1]
v.id = powerups[itemMap[nextTier]].items[1]
end
else
if player.powerup == 1 then
v.id = 9
end
return
end
end
end
end
if currentPowerup and player.forcedState == 0 and ap.getPowerup() ~= currentPowerup.name then
currentPowerup.onDisable()
currentPowerup = nil
SaveData._ap.cp = nil
end
if currentPowerup then
currentPowerup.onTickEnd()
end
if isPoweringUp then
if not (player.forcedState ~= 0 or (currentPowerup and ap.getPowerup() ~= currentPowerup.name)) then
player.powerup = isPoweringUp
isPoweringUp = false
end
end
end
function ap.setPlayerPowerup(appower, silent, reservePowerup, thisPlayer)
thisPlayer = thisPlayer or player
local nextPower = 7
if thisPlayer.powerup == 3 then
nextPower = 3
end
if currentPowerup and appower.name ~= currentPowerup.name then
currentPowerup.onDisable()
end
if not silent then
if currentPowerup and appower.name == currentPowerup.name then
if appower.apSounds ~= nil and appower.apSounds.reserve ~= nil then
SFX.play(appower.apSounds.reserve)
end
if player.forcedState == 3 or player.forcedState == 41 then
thisPlayer:mem(0x122, FIELD_WORD, 0)
thisPlayer:mem(0x124, FIELD_WORD, 0)
end
return
else
if appower.apSounds ~= nil and appower.apSounds.upgrade ~= nil then
SFX.play(appower.apSounds.upgrade)
end
if nextPower == 3 then
thisPlayer:mem(0x122, FIELD_WORD, 4)
else
thisPlayer:mem(0x122, FIELD_WORD, 41)
end
thisPlayer:mem(0x124, FIELD_WORD, 100)
--Audio.sounds[7].sfx = nil
end
isPoweringUp = nextPower
else
thisPlayer.powerup = nextPower
end
currentPowerup = appower
SaveData._ap.cp = appower.name
--thisPlayer.forcedState = 0
if thisPlayer.character > 5 then
error("Character IDs above 5 are unsupported.")
return
end
loadPowerupAssets(thisPlayer,nextPower)
currentPowerup.onEnable()
end
function ap.onStart()
if Misc.inEditor() then
SaveData._ap.cp = nil
SaveData._ap.lastPowerupItem = nil
currentPowerup = nil
return
end
if SaveData._ap.cp then
ap.setPlayerPowerup(powerups[SaveData._ap.cp], true)
end
end
function ap.onPostNPCKill(v, r)
if isPoweringUp then return end
local p = npcManager.collected(v, r)
if not p then
return
end
local defaultPowerupID = defaultNPCPowerupMap[v.id]
local apPowerupName = itemMap[v.id]
if defaultPowerupID or apPowerupName then
if p.character >= 3 and p.character <= 5 and not defaultPowerupID then
p:mem(0x16, FIELD_WORD, p:mem(0x16, FIELD_WORD) + 1)
end
if SaveData._ap.lastPowerupItem then
player.reservePowerup = SaveData._ap.lastPowerupItem
end
SaveData._ap.lastPowerupItem = v.id
end
if apPowerupName then
ap.setPlayerPowerup(powerups[apPowerupName], false, v.id, p)
v:kill(9)
end
end
function ap.onPlayerHarm(event, p)
if p.powerup > 1 and p.mount == MOUNT_NONE and not p:mem(0x0C, FIELD_BOOL) and not p.hasStarman then
SaveData._ap.lastPowerupItem = nil
end
end
return ap
|
|
|
|
|
|
|
|
|
-
Emral
- Cute Yoshi Egg

- Posts: 9891
- Joined: Mon Jan 20, 2014 12:58 pm
- Flair: Phoenix
Postby Emral » Fri Nov 04, 2022 3:40 am
Code: Select all local playerManager = require("playerManager")
if Misc.inEditor() then
local testModeMenu = require("engine/testmodemenu")
end
local npcManager = require("npcManager")
local ap = {}
SaveData._ap = SaveData._ap or {}
local powerups = {}
local currentPowerup
local itemMap = {}
local tiers = {}
local isPoweringUp = false
powerups[2] = {9, 184, 185}
powerups[3] = {14, 182, 183}
powerups[4] = {34}
powerups[5] = {169}
powerups[6] = {170}
powerups[7] = {264, 277}
local defaultNPCPowerupMap = {
[9] = PLAYER_BIG,
[14] = PLAYER_FIREFLOWER,
[34] = PLAYER_LEAF,
[169] = PLAYER_TANOOKIE,
[170] = PLAYER_HAMMER,
[182] = PLAYER_FIREFLOWER,
[183] = PLAYER_FIREFLOWER,
[184] = PLAYER_BIG,
[185] = PLAYER_BIG,
[249] = PLAYER_BIG,
[264] = PLAYER_ICE,
[277] = PLAYER_ICE,
}
local players = {
"mario", "luigi", "peach", "toad", "link"
}
local function registerItemsInternal(powerup, ids)
if type(ids) == "number" then
ids = {}
elseif type(ids) ~= "table" then
powerup.items = {}
return
end
for k,v in ipairs(powerup.items) do
itemMap[v] = nil
end
powerup.items = ids
for k,v in ipairs(ids) do
if itemMap[v] ~= nil then
Misc.warn("Item " .. v .. " could not be registered to Powerup " .. libraryName .. ", because it was already registered to Powerup " .. itemMap[v] .. ".")
return
else
itemMap[v] = libraryName
end
end
end
local function loadPowerupAssets(thisPlayer,basePowerup)
if currentPowerup == nil then
return
end
local iniFile = Misc.resolveFile(players[thisPlayer.character] .. "-" .. currentPowerup.name .. ".ini")
if (iniFile == nil) then
iniFile = playerManager.getHitboxPath(thisPlayer.character, basePowerup);
end
Misc.loadCharacterHitBoxes(thisPlayer.character, basePowerup, iniFile)
Graphics.sprites[players[thisPlayer.character]][basePowerup].img = currentPowerup.spritesheets[thisPlayer.character]
end
-- registerPowerup registers a new powerup to the system
--- libraryName: string - The name of the library containing powerup information. One library per powerup. Think of it as a require(libraryName) call
-- Returns the library table for the powerup
function ap.registerPowerup(libraryName)
local entry = require(libraryName)
entry.registerItems = registerItemsInternal
entry.name = libraryName
entry.items = entry.items or {}
powerups[libraryName] = entry
for k,v in ipairs(entry.items) do
if itemMap[v] ~= nil then
Misc.warn("Item " .. v .. " could not be registered to Powerup " .. libraryName .. ", because it was already registered to Powerup " .. itemMap[v] .. ".")
return
else
itemMap[v] = libraryName
end
end
return entry
end
-- Replacement powerups
ap.powerReplacements = {}
ap.powerReplacements[3] = ap.registerPowerup("ap_fireflower")
ap.powerReplacements[7] = ap.registerPowerup("ap_iceflower")
-- registerItemTier registers a new tier chain for items.
-- spawnedItem: number - The item that spawns from a block.
-- chain: table - The list of states ordered by significance. Numbers correspond to vanilla states, strings correspond to the names of anotherpowerup powerups. See demo for more info.
-- if you provide chain as "true", it will accept everything except for small mario
function ap.registerItemTier(spawnedItem, chain)
if chain ~= true then
for k,v in ipairs(chain) do
if ap.powerReplacements[v] then
chain[k] = ap.powerReplacements[v].name
end
end
end
tiers[spawnedItem] = chain
end
-- Returns the player's current powerup, accounting for anotherpowerup powerups
function ap.getPowerup()
if player.powerup == 3 or player.powerup == 7 then
return currentPowerup.name
else
return player.powerup
end
end
function ap.onInitAPI()
registerEvent(ap, "onPostNPCKill")
registerEvent(ap, "onPlayerHarm")
registerEvent(ap, "onTickEnd")
registerEvent(ap, "onTick")
registerEvent(ap, "onExit")
registerEvent(ap, "onDraw")
registerEvent(ap, "onStart")
end
function ap.onTick()
if currentPowerup and player.forcedState == 0 and ap.getPowerup() ~= currentPowerup.name then
currentPowerup.onDisable()
currentPowerup = nil
SaveData._ap.cp = nil
end
if currentPowerup then
if player.mount < 2 then
if player.character ~= CHARACTER_LINK then
player:mem(0x160, FIELD_WORD, 3) -- Disable the nomral projectile timer. Powerups need to implement their own.
else
player:mem(0x162, FIELD_WORD, 29) -- Disable the link projectile timer. Powerups need to implement their own.
end
end
currentPowerup.onTick()
end
end
local testMenuWasActive = false
function ap.onDraw()
if currentPowerup and player.forcedState == 0 and ap.getPowerup() ~= currentPowerup.name then
currentPowerup.onDisable()
currentPowerup = nil
SaveData._ap.cp = nil
end
if currentPowerup then
currentPowerup.onDraw()
end
-- Handle test mode menu, and reload hitboxes after a bit
if Misc.inEditor() then
if (not testModeMenu.active and testMenuWasActive) or lunatime.tick() == 1 then
loadPowerupAssets(player,player.powerup)
end
testMenuWasActive = testModeMenu.active
end
end
function ap.onTickEnd()
for k,v in ipairs(NPC.get(table.unmap(itemMap))) do -- Thankfully this is more performant these days I guess!
if not v.data._anotherpowerup and v.despawnTimer > 0 then
v.data._anotherpowerup = true
local spawn = v:mem(0x138, FIELD_WORD)
if spawn == 1 or spawn == 3 or spawn == 4 then
local currentTier
local list
if tiers[v.id] ~= nil and tiers[v.id] ~= true then
if type(ap.getPowerup()) == "number" then
list = powerups[ap.getPowerup()]
else
list = currentPowerup.items
end
if list ~= nil then
for k,v in ipairs(tiers[v.id]) do
currentTier = 0
for _,n in ipairs(list) do
if n == v then
currentTier = k
break
end
end
if currentTier ~= 0 then break end
end
else
if tiers[v.id] then
currentTier = 0
end
end
if currentTier and currentTier < #tiers[v.id] then
local nextTier = tiers[v.id][currentTier + 1]
v.id = powerups[itemMap[nextTier]].items[1]
end
else
if player.powerup == 1 then
v.id = 9
end
return
end
end
end
end
if currentPowerup and player.forcedState == 0 and ap.getPowerup() ~= currentPowerup.name then
currentPowerup.onDisable()
currentPowerup = nil
SaveData._ap.cp = nil
end
if currentPowerup then
currentPowerup.onTickEnd()
end
if isPoweringUp then
if not (player.forcedState ~= 0 or (currentPowerup and ap.getPowerup() ~= currentPowerup.name)) then
player.powerup = isPoweringUp
isPoweringUp = false
end
end
end
function ap.setPlayerPowerup(appower, silent, reservePowerup, thisPlayer)
thisPlayer = thisPlayer or player
local nextPower = 7
if thisPlayer.powerup == 3 then
nextPower = 3
end
if currentPowerup and appower.name ~= currentPowerup.name then
currentPowerup.onDisable()
end
if not silent then
if currentPowerup and appower.name == currentPowerup.name then
if appower.apSounds ~= nil and appower.apSounds.reserve ~= nil then
SFX.play(appower.apSounds.reserve)
end
if player.forcedState == 3 or player.forcedState == 41 then
thisPlayer:mem(0x122, FIELD_WORD, 0)
thisPlayer:mem(0x124, FIELD_WORD, 0)
end
return
else
if appower.apSounds ~= nil and appower.apSounds.upgrade ~= nil then
SFX.play(appower.apSounds.upgrade)
end
if nextPower == 3 then
thisPlayer:mem(0x122, FIELD_WORD, 4)
else
thisPlayer:mem(0x122, FIELD_WORD, 41)
end
thisPlayer:mem(0x124, FIELD_WORD, 100)
--Audio.sounds[7].sfx = nil
end
isPoweringUp = nextPower
else
thisPlayer.powerup = nextPower
end
currentPowerup = appower
SaveData._ap.cp = appower.name
--thisPlayer.forcedState = 0
if thisPlayer.character > 5 then
error("Character IDs above 5 are unsupported.")
return
end
loadPowerupAssets(thisPlayer,nextPower)
currentPowerup.onEnable()
end
function ap.onStart()
if Misc.inEditor() then
SaveData._ap.cp = nil
SaveData._ap.lastPowerupItem = nil
currentPowerup = nil
return
end
if SaveData._ap.cp then
ap.setPlayerPowerup(powerups[SaveData._ap.cp], true)
end
end
function ap.onPostNPCKill(v, r)
if isPoweringUp then return end
local p = npcManager.collected(v, r)
if not p then
return
end
local defaultPowerupID = defaultNPCPowerupMap[v.id]
local apPowerupName = itemMap[v.id]
if defaultPowerupID or apPowerupName then
if p.character >= 3 and p.character <= 5 and not defaultPowerupID then
p:mem(0x16, FIELD_WORD, p:mem(0x16, FIELD_WORD) + 1)
end
if SaveData._ap.lastPowerupItem then
player.reservePowerup = SaveData._ap.lastPowerupItem
end
SaveData._ap.lastPowerupItem = v.id
end
if apPowerupName then
ap.setPlayerPowerup(powerups[apPowerupName], false, v.id, p)
v:kill(9)
end
end
function ap.onPlayerHarm(event, p)
if p.powerup > 1 and p.mount == MOUNT_NONE and not p:mem(0x0C, FIELD_BOOL) and not p.hasStarman then
SaveData._ap.lastPowerupItem = nil
end
end
return ap
try this
|
|
|
|
|
|
|
|
|
-
Cambroski
- Shy Guy

- Posts: 8
- Joined: Tue Nov 01, 2022 7:29 pm
- Flair: Awake by day, asleep by night.
Postby Cambroski » Fri Nov 04, 2022 9:27 am
Enjl wrote: ↑Fri Nov 04, 2022 3:40 am
Code: Select all local playerManager = require("playerManager")
if Misc.inEditor() then
local testModeMenu = require("engine/testmodemenu")
end
local npcManager = require("npcManager")
local ap = {}
SaveData._ap = SaveData._ap or {}
local powerups = {}
local currentPowerup
local itemMap = {}
local tiers = {}
local isPoweringUp = false
powerups[2] = {9, 184, 185}
powerups[3] = {14, 182, 183}
powerups[4] = {34}
powerups[5] = {169}
powerups[6] = {170}
powerups[7] = {264, 277}
local defaultNPCPowerupMap = {
[9] = PLAYER_BIG,
[14] = PLAYER_FIREFLOWER,
[34] = PLAYER_LEAF,
[169] = PLAYER_TANOOKIE,
[170] = PLAYER_HAMMER,
[182] = PLAYER_FIREFLOWER,
[183] = PLAYER_FIREFLOWER,
[184] = PLAYER_BIG,
[185] = PLAYER_BIG,
[249] = PLAYER_BIG,
[264] = PLAYER_ICE,
[277] = PLAYER_ICE,
}
local players = {
"mario", "luigi", "peach", "toad", "link"
}
local function registerItemsInternal(powerup, ids)
if type(ids) == "number" then
ids = {}
elseif type(ids) ~= "table" then
powerup.items = {}
return
end
for k,v in ipairs(powerup.items) do
itemMap[v] = nil
end
powerup.items = ids
for k,v in ipairs(ids) do
if itemMap[v] ~= nil then
Misc.warn("Item " .. v .. " could not be registered to Powerup " .. libraryName .. ", because it was already registered to Powerup " .. itemMap[v] .. ".")
return
else
itemMap[v] = libraryName
end
end
end
local function loadPowerupAssets(thisPlayer,basePowerup)
if currentPowerup == nil then
return
end
local iniFile = Misc.resolveFile(players[thisPlayer.character] .. "-" .. currentPowerup.name .. ".ini")
if (iniFile == nil) then
iniFile = playerManager.getHitboxPath(thisPlayer.character, basePowerup);
end
Misc.loadCharacterHitBoxes(thisPlayer.character, basePowerup, iniFile)
Graphics.sprites[players[thisPlayer.character]][basePowerup].img = currentPowerup.spritesheets[thisPlayer.character]
end
-- registerPowerup registers a new powerup to the system
--- libraryName: string - The name of the library containing powerup information. One library per powerup. Think of it as a require(libraryName) call
-- Returns the library table for the powerup
function ap.registerPowerup(libraryName)
local entry = require(libraryName)
entry.registerItems = registerItemsInternal
entry.name = libraryName
entry.items = entry.items or {}
powerups[libraryName] = entry
for k,v in ipairs(entry.items) do
if itemMap[v] ~= nil then
Misc.warn("Item " .. v .. " could not be registered to Powerup " .. libraryName .. ", because it was already registered to Powerup " .. itemMap[v] .. ".")
return
else
itemMap[v] = libraryName
end
end
return entry
end
-- Replacement powerups
ap.powerReplacements = {}
ap.powerReplacements[3] = ap.registerPowerup("ap_fireflower")
ap.powerReplacements[7] = ap.registerPowerup("ap_iceflower")
-- registerItemTier registers a new tier chain for items.
-- spawnedItem: number - The item that spawns from a block.
-- chain: table - The list of states ordered by significance. Numbers correspond to vanilla states, strings correspond to the names of anotherpowerup powerups. See demo for more info.
-- if you provide chain as "true", it will accept everything except for small mario
function ap.registerItemTier(spawnedItem, chain)
if chain ~= true then
for k,v in ipairs(chain) do
if ap.powerReplacements[v] then
chain[k] = ap.powerReplacements[v].name
end
end
end
tiers[spawnedItem] = chain
end
-- Returns the player's current powerup, accounting for anotherpowerup powerups
function ap.getPowerup()
if player.powerup == 3 or player.powerup == 7 then
return currentPowerup.name
else
return player.powerup
end
end
function ap.onInitAPI()
registerEvent(ap, "onPostNPCKill")
registerEvent(ap, "onPlayerHarm")
registerEvent(ap, "onTickEnd")
registerEvent(ap, "onTick")
registerEvent(ap, "onExit")
registerEvent(ap, "onDraw")
registerEvent(ap, "onStart")
end
function ap.onTick()
if currentPowerup and player.forcedState == 0 and ap.getPowerup() ~= currentPowerup.name then
currentPowerup.onDisable()
currentPowerup = nil
SaveData._ap.cp = nil
end
if currentPowerup then
if player.mount < 2 then
if player.character ~= CHARACTER_LINK then
player:mem(0x160, FIELD_WORD, 3) -- Disable the nomral projectile timer. Powerups need to implement their own.
else
player:mem(0x162, FIELD_WORD, 29) -- Disable the link projectile timer. Powerups need to implement their own.
end
end
currentPowerup.onTick()
end
end
local testMenuWasActive = false
function ap.onDraw()
if currentPowerup and player.forcedState == 0 and ap.getPowerup() ~= currentPowerup.name then
currentPowerup.onDisable()
currentPowerup = nil
SaveData._ap.cp = nil
end
if currentPowerup then
currentPowerup.onDraw()
end
-- Handle test mode menu, and reload hitboxes after a bit
if Misc.inEditor() then
if (not testModeMenu.active and testMenuWasActive) or lunatime.tick() == 1 then
loadPowerupAssets(player,player.powerup)
end
testMenuWasActive = testModeMenu.active
end
end
function ap.onTickEnd()
for k,v in ipairs(NPC.get(table.unmap(itemMap))) do -- Thankfully this is more performant these days I guess!
if not v.data._anotherpowerup and v.despawnTimer > 0 then
v.data._anotherpowerup = true
local spawn = v:mem(0x138, FIELD_WORD)
if spawn == 1 or spawn == 3 or spawn == 4 then
local currentTier
local list
if tiers[v.id] ~= nil and tiers[v.id] ~= true then
if type(ap.getPowerup()) == "number" then
list = powerups[ap.getPowerup()]
else
list = currentPowerup.items
end
if list ~= nil then
for k,v in ipairs(tiers[v.id]) do
currentTier = 0
for _,n in ipairs(list) do
if n == v then
currentTier = k
break
end
end
if currentTier ~= 0 then break end
end
else
if tiers[v.id] then
currentTier = 0
end
end
if currentTier and currentTier < #tiers[v.id] then
local nextTier = tiers[v.id][currentTier + 1]
v.id = powerups[itemMap[nextTier]].items[1]
end
else
if player.powerup == 1 then
v.id = 9
end
return
end
end
end
end
if currentPowerup and player.forcedState == 0 and ap.getPowerup() ~= currentPowerup.name then
currentPowerup.onDisable()
currentPowerup = nil
SaveData._ap.cp = nil
end
if currentPowerup then
currentPowerup.onTickEnd()
end
if isPoweringUp then
if not (player.forcedState ~= 0 or (currentPowerup and ap.getPowerup() ~= currentPowerup.name)) then
player.powerup = isPoweringUp
isPoweringUp = false
end
end
end
function ap.setPlayerPowerup(appower, silent, reservePowerup, thisPlayer)
thisPlayer = thisPlayer or player
local nextPower = 7
if thisPlayer.powerup == 3 then
nextPower = 3
end
if currentPowerup and appower.name ~= currentPowerup.name then
currentPowerup.onDisable()
end
if not silent then
if currentPowerup and appower.name == currentPowerup.name then
if appower.apSounds ~= nil and appower.apSounds.reserve ~= nil then
SFX.play(appower.apSounds.reserve)
end
if player.forcedState == 3 or player.forcedState == 41 then
thisPlayer:mem(0x122, FIELD_WORD, 0)
thisPlayer:mem(0x124, FIELD_WORD, 0)
end
return
else
if appower.apSounds ~= nil and appower.apSounds.upgrade ~= nil then
SFX.play(appower.apSounds.upgrade)
end
if nextPower == 3 then
thisPlayer:mem(0x122, FIELD_WORD, 4)
else
thisPlayer:mem(0x122, FIELD_WORD, 41)
end
thisPlayer:mem(0x124, FIELD_WORD, 100)
--Audio.sounds[7].sfx = nil
end
isPoweringUp = nextPower
else
thisPlayer.powerup = nextPower
end
currentPowerup = appower
SaveData._ap.cp = appower.name
--thisPlayer.forcedState = 0
if thisPlayer.character > 5 then
error("Character IDs above 5 are unsupported.")
return
end
loadPowerupAssets(thisPlayer,nextPower)
currentPowerup.onEnable()
end
function ap.onStart()
if Misc.inEditor() then
SaveData._ap.cp = nil
SaveData._ap.lastPowerupItem = nil
currentPowerup = nil
return
end
if SaveData._ap.cp then
ap.setPlayerPowerup(powerups[SaveData._ap.cp], true)
end
end
function ap.onPostNPCKill(v, r)
if isPoweringUp then return end
local p = npcManager.collected(v, r)
if not p then
return
end
local defaultPowerupID = defaultNPCPowerupMap[v.id]
local apPowerupName = itemMap[v.id]
if defaultPowerupID or apPowerupName then
if p.character >= 3 and p.character <= 5 and not defaultPowerupID then
p:mem(0x16, FIELD_WORD, p:mem(0x16, FIELD_WORD) + 1)
end
if SaveData._ap.lastPowerupItem then
player.reservePowerup = SaveData._ap.lastPowerupItem
end
SaveData._ap.lastPowerupItem = v.id
end
if apPowerupName then
ap.setPlayerPowerup(powerups[apPowerupName], false, v.id, p)
v:kill(9)
end
end
function ap.onPlayerHarm(event, p)
if p.powerup > 1 and p.mount == MOUNT_NONE and not p:mem(0x0C, FIELD_BOOL) and not p.hasStarman then
SaveData._ap.lastPowerupItem = nil
end
end
return ap
try this
Yes this worked. I was worried it might be a problem with my install of SMBX2 since it seemed like nobody else had this problem. I'm curious to know why the code was causing the error in the first place. Thank you for your help!
|
|
|
|
|
|
|
|
|
-
Emral
- Cute Yoshi Egg

- Posts: 9891
- Joined: Mon Jan 20, 2014 12:58 pm
- Flair: Phoenix
Postby Emral » Fri Nov 04, 2022 9:52 pm
Cambroski wrote: ↑Fri Nov 04, 2022 9:27 am
Yes this worked. I was worried it might be a problem with my install of SMBX2 since it seemed like nobody else had this problem. I'm curious to know why the code was causing the error in the first place. Thank you for your help!
Nobody else had the problem because you appear to be the first person to make an episode using this script and I didn't test thoroughly enough when writing it, hah.
I'll commit the fix to the download in the coming days, then it should be taken care of for other people in the future, too.
|
|
|
|
|
|
|
|
|
-
Cambroski
- Shy Guy

- Posts: 8
- Joined: Tue Nov 01, 2022 7:29 pm
- Flair: Awake by day, asleep by night.
Postby Cambroski » Sat Nov 05, 2022 12:19 am
Enjl wrote: ↑Fri Nov 04, 2022 9:52 pm
Cambroski wrote: ↑Fri Nov 04, 2022 9:27 am
Yes this worked. I was worried it might be a problem with my install of SMBX2 since it seemed like nobody else had this problem. I'm curious to know why the code was causing the error in the first place. Thank you for your help!
Nobody else had the problem because you appear to be the first person to make an episode using this script and I didn't test thoroughly enough when writing it, hah.
I'll commit the fix to the download in the coming days, then it should be taken care of for other people in the future, too.
Another problem that arises with the fixed code that you provided is that it no longer works on level files. This time it has two different errors.
Here is the first.
Code: Select all ==> worlds/WIP Game/libs/anotherpowerup.lua:188: attempt to index global 'testModeMenu' (a nil value)
===========================================================================================
stack traceback:
scripts/base/engine/main_events.lua:63: in function 'callApiListeners'
scripts/base/engine/main_events.lua:169: in function 'callEventInternal'
scripts/base/engine/main_events.lua:240: in function <scripts/base/engine/main_events.lua:218>
[C]: in function '__xpcall'
main.lua:781: in function <main.lua:780>
Here is the second.
Code: Select all ==> scripts/base/HUDOverride.lua:773: attemt to index upvalue 'activeCameras' (a nil value)
================================================================================
stack traceback:
scripts/base/engine/main_events.lua:63: in function 'callApiListeners'
scripts/base/engine/main_events.lua:184: in function 'callEventInternal'
scripts/base/engine/main_events.lua:240: in function <scripts/base/engine/main_events.lua:218>
[C]: in function '__xpcall'
main.lua:781: in function <main.lua:780>
Thanks for your commitment to the SMBX community bro.
|
|
|
|
|
|
|
|
|
-
Emral
- Cute Yoshi Egg

- Posts: 9891
- Joined: Mon Jan 20, 2014 12:58 pm
- Flair: Phoenix
Postby Emral » Sat Nov 05, 2022 5:31 am
oops
try this
Code: Select all -- Amotherpowerup serves as a wrapper for an endless powerup system.
-- Features:
--- Infinite Powerups!
--- Custom collision for every individual powerup!
--- Unique images for every individual powerup!
--- Events that happen on powerup switch!
--- Define custom powerup tiers!
-- Limitations:
--- Every powerup will behave as if the player is 'big'
--- Only applicable to Mario, Peach, Luigi, Toad. Not Link. Not SMBX2 Characters.
local playerManager = require("playerManager")
if Misc.inEditor() then
local testModeMenu = require("engine/testmodemenu")
end
local npcManager = require("npcManager")
local ap = {}
SaveData._ap = SaveData._ap or {}
local powerups = {}
local currentPowerup
local itemMap = {}
local tiers = {}
local isPoweringUp = false
powerups[2] = {9, 184, 185}
powerups[3] = {14, 182, 183}
powerups[4] = {34}
powerups[5] = {169}
powerups[6] = {170}
powerups[7] = {264, 277}
local defaultNPCPowerupMap = {
[9] = PLAYER_BIG,
[14] = PLAYER_FIREFLOWER,
[34] = PLAYER_LEAF,
[169] = PLAYER_TANOOKIE,
[170] = PLAYER_HAMMER,
[182] = PLAYER_FIREFLOWER,
[183] = PLAYER_FIREFLOWER,
[184] = PLAYER_BIG,
[185] = PLAYER_BIG,
[249] = PLAYER_BIG,
[264] = PLAYER_ICE,
[277] = PLAYER_ICE,
}
local players = {
"mario", "luigi", "peach", "toad", "link"
}
local function registerItemsInternal(powerup, ids)
if type(ids) == "number" then
ids = {}
elseif type(ids) ~= "table" then
powerup.items = {}
return
end
for k,v in ipairs(powerup.items) do
itemMap[v] = nil
end
powerup.items = ids
for k,v in ipairs(ids) do
if itemMap[v] ~= nil then
Misc.warn("Item " .. v .. " could not be registered to Powerup " .. libraryName .. ", because it was already registered to Powerup " .. itemMap[v] .. ".")
return
else
itemMap[v] = libraryName
end
end
end
local function loadPowerupAssets(thisPlayer,basePowerup)
if currentPowerup == nil then
return
end
local iniFile = Misc.resolveFile(players[thisPlayer.character] .. "-" .. currentPowerup.name .. ".ini")
if (iniFile == nil) then
iniFile = playerManager.getHitboxPath(thisPlayer.character, basePowerup);
end
Misc.loadCharacterHitBoxes(thisPlayer.character, basePowerup, iniFile)
Graphics.sprites[players[thisPlayer.character]][basePowerup].img = currentPowerup.spritesheets[thisPlayer.character]
end
-- registerPowerup registers a new powerup to the system
--- libraryName: string - The name of the library containing powerup information. One library per powerup. Think of it as a require(libraryName) call
-- Returns the library table for the powerup
function ap.registerPowerup(libraryName)
local entry = require(libraryName)
entry.registerItems = registerItemsInternal
entry.name = libraryName
entry.items = entry.items or {}
powerups[libraryName] = entry
for k,v in ipairs(entry.items) do
if itemMap[v] ~= nil then
Misc.warn("Item " .. v .. " could not be registered to Powerup " .. libraryName .. ", because it was already registered to Powerup " .. itemMap[v] .. ".")
return
else
itemMap[v] = libraryName
end
end
return entry
end
-- Replacement powerups
ap.powerReplacements = {}
ap.powerReplacements[3] = ap.registerPowerup("ap_fireflower")
ap.powerReplacements[7] = ap.registerPowerup("ap_iceflower")
-- registerItemTier registers a new tier chain for items.
-- spawnedItem: number - The item that spawns from a block.
-- chain: table - The list of states ordered by significance. Numbers correspond to vanilla states, strings correspond to the names of anotherpowerup powerups. See demo for more info.
-- if you provide chain as "true", it will accept everything except for small mario
function ap.registerItemTier(spawnedItem, chain)
if chain ~= true then
for k,v in ipairs(chain) do
if ap.powerReplacements[v] then
chain[k] = ap.powerReplacements[v].name
end
end
end
tiers[spawnedItem] = chain
end
-- Returns the player's current powerup, accounting for anotherpowerup powerups
function ap.getPowerup()
if player.powerup == 3 or player.powerup == 7 then
return currentPowerup.name
else
return player.powerup
end
end
function ap.onInitAPI()
registerEvent(ap, "onPostNPCKill")
registerEvent(ap, "onTickEnd")
registerEvent(ap, "onTick")
registerEvent(ap, "onExit")
registerEvent(ap, "onDraw")
registerEvent(ap, "onStart")
end
function ap.onTick()
if currentPowerup and player.forcedState == 0 and ap.getPowerup() ~= currentPowerup.name then
currentPowerup.onDisable()
currentPowerup = nil
SaveData._ap.cp = nil
end
if currentPowerup then
if player.mount < 2 then
if player.character ~= CHARACTER_LINK then
player:mem(0x160, FIELD_WORD, 3) -- Disable the nomral projectile timer. Powerups need to implement their own.
else
player:mem(0x162, FIELD_WORD, 29) -- Disable the link projectile timer. Powerups need to implement their own.
end
end
currentPowerup.onTick()
end
end
local testMenuWasActive = false
function ap.onDraw()
if currentPowerup and player.forcedState == 0 and ap.getPowerup() ~= currentPowerup.name then
currentPowerup.onDisable()
currentPowerup = nil
SaveData._ap.cp = nil
end
if currentPowerup then
currentPowerup.onDraw()
end
-- Handle test mode menu, and reload hitboxes after a bit
if (Misc.inEditor() and (not testModeMenu.active) and testMenuWasActive) or lunatime.tick() == 1 then
loadPowerupAssets(player,player.powerup)
end
if Misc.inEditor() then
testMenuWasActive = testModeMenu.active
end
end
function ap.onTickEnd()
for k,v in ipairs(NPC.get(table.unmap(itemMap))) do -- Thankfully this is more performant these days I guess!
if not v.data._anotherpowerup and v.despawnTimer > 0 then
v.data._anotherpowerup = true
local spawn = v:mem(0x138, FIELD_WORD)
if spawn == 1 or spawn == 3 or spawn == 4 then
local currentTier
local list
if tiers[v.id] ~= nil and tiers[v.id] ~= true then
if type(ap.getPowerup()) == "number" then
list = powerups[ap.getPowerup()]
else
list = currentPowerup.items
end
if list ~= nil then
for k,v in ipairs(tiers[v.id]) do
currentTier = 0
for _,n in ipairs(list) do
if n == v then
currentTier = k
break
end
end
if currentTier ~= 0 then break end
end
else
if tiers[v.id] then
currentTier = 0
end
end
if currentTier and currentTier < #tiers[v.id] then
local nextTier = tiers[v.id][currentTier + 1]
v.id = powerups[itemMap[nextTier]].items[1]
end
else
if player.powerup == 1 then
v.id = 9
end
return
end
end
end
end
if currentPowerup and player.forcedState == 0 and ap.getPowerup() ~= currentPowerup.name then
currentPowerup.onDisable()
currentPowerup = nil
SaveData._ap.cp = nil
end
if currentPowerup then
currentPowerup.onTickEnd()
end
if isPoweringUp then
if not (player.forcedState ~= 0 or (currentPowerup and ap.getPowerup() ~= currentPowerup.name)) then
player.powerup = isPoweringUp
isPoweringUp = false
end
end
end
function ap.setPlayerPowerup(appower, silent, reservePowerup, thisPlayer)
thisPlayer = thisPlayer or player
local nextPower = 7
if thisPlayer.powerup == 3 then
nextPower = 3
end
if currentPowerup and appower.name ~= currentPowerup.name then
currentPowerup.onDisable()
end
if not silent then
if currentPowerup and appower.name == currentPowerup.name then
if appower.apSounds ~= nil and appower.apSounds.reserve ~= nil then
SFX.play(appower.apSounds.reserve)
end
if player.forcedState == 3 or player.forcedState == 41 then
thisPlayer:mem(0x122, FIELD_WORD, 0)
thisPlayer:mem(0x124, FIELD_WORD, 0)
end
return
else
if appower.apSounds ~= nil and appower.apSounds.upgrade ~= nil then
SFX.play(appower.apSounds.upgrade)
end
if nextPower == 3 then
thisPlayer:mem(0x122, FIELD_WORD, 4)
else
thisPlayer:mem(0x122, FIELD_WORD, 41)
end
thisPlayer:mem(0x124, FIELD_WORD, 100)
--Audio.sounds[7].sfx = nil
end
isPoweringUp = nextPower
else
thisPlayer.powerup = nextPower
end
currentPowerup = appower
SaveData._ap.cp = appower.name
--thisPlayer.forcedState = 0
if thisPlayer.character > 5 then
error("Character IDs above 5 are unsupported.")
return
end
loadPowerupAssets(thisPlayer,nextPower)
currentPowerup.onEnable()
end
function ap.onStart()
if Misc.inEditor() then
SaveData._ap.cp = nil
SaveData._ap.lastPowerupItem = nil
currentPowerup = nil
return
end
if SaveData._ap.cp then
ap.setPlayerPowerup(powerups[SaveData._ap.cp], true)
end
end
function ap.onPostNPCKill(v, r)
if isPoweringUp then return end
local p = npcManager.collected(v, r)
if not p then
return
end
local defaultPowerupID = defaultNPCPowerupMap[v.id]
local apPowerupName = itemMap[v.id]
if defaultPowerupID or apPowerupName then
if p.character >= 3 and p.character <= 5 and not defaultPowerupID then
p:mem(0x16, FIELD_WORD, p:mem(0x16, FIELD_WORD) + 1)
end
if SaveData._ap.lastPowerupItem then
player.reservePowerup = SaveData._ap.lastPowerupItem
end
SaveData._ap.lastPowerupItem = v.id
end
if apPowerupName then
ap.setPlayerPowerup(powerups[apPowerupName], false, v.id, p)
v:kill(9)
end
end
return ap
|
|
|
|
|
|
|
|
|
-
Cambroski
- Shy Guy

- Posts: 8
- Joined: Tue Nov 01, 2022 7:29 pm
- Flair: Awake by day, asleep by night.
Postby Cambroski » Sat Nov 05, 2022 11:07 am
Enjl wrote: ↑Sat Nov 05, 2022 5:31 am
oops
try this
Code: Select all -- Amotherpowerup serves as a wrapper for an endless powerup system.
-- Features:
--- Infinite Powerups!
--- Custom collision for every individual powerup!
--- Unique images for every individual powerup!
--- Events that happen on powerup switch!
--- Define custom powerup tiers!
-- Limitations:
--- Every powerup will behave as if the player is 'big'
--- Only applicable to Mario, Peach, Luigi, Toad. Not Link. Not SMBX2 Characters.
local playerManager = require("playerManager")
if Misc.inEditor() then
local testModeMenu = require("engine/testmodemenu")
end
local npcManager = require("npcManager")
local ap = {}
SaveData._ap = SaveData._ap or {}
local powerups = {}
local currentPowerup
local itemMap = {}
local tiers = {}
local isPoweringUp = false
powerups[2] = {9, 184, 185}
powerups[3] = {14, 182, 183}
powerups[4] = {34}
powerups[5] = {169}
powerups[6] = {170}
powerups[7] = {264, 277}
local defaultNPCPowerupMap = {
[9] = PLAYER_BIG,
[14] = PLAYER_FIREFLOWER,
[34] = PLAYER_LEAF,
[169] = PLAYER_TANOOKIE,
[170] = PLAYER_HAMMER,
[182] = PLAYER_FIREFLOWER,
[183] = PLAYER_FIREFLOWER,
[184] = PLAYER_BIG,
[185] = PLAYER_BIG,
[249] = PLAYER_BIG,
[264] = PLAYER_ICE,
[277] = PLAYER_ICE,
}
local players = {
"mario", "luigi", "peach", "toad", "link"
}
local function registerItemsInternal(powerup, ids)
if type(ids) == "number" then
ids = {}
elseif type(ids) ~= "table" then
powerup.items = {}
return
end
for k,v in ipairs(powerup.items) do
itemMap[v] = nil
end
powerup.items = ids
for k,v in ipairs(ids) do
if itemMap[v] ~= nil then
Misc.warn("Item " .. v .. " could not be registered to Powerup " .. libraryName .. ", because it was already registered to Powerup " .. itemMap[v] .. ".")
return
else
itemMap[v] = libraryName
end
end
end
local function loadPowerupAssets(thisPlayer,basePowerup)
if currentPowerup == nil then
return
end
local iniFile = Misc.resolveFile(players[thisPlayer.character] .. "-" .. currentPowerup.name .. ".ini")
if (iniFile == nil) then
iniFile = playerManager.getHitboxPath(thisPlayer.character, basePowerup);
end
Misc.loadCharacterHitBoxes(thisPlayer.character, basePowerup, iniFile)
Graphics.sprites[players[thisPlayer.character]][basePowerup].img = currentPowerup.spritesheets[thisPlayer.character]
end
-- registerPowerup registers a new powerup to the system
--- libraryName: string - The name of the library containing powerup information. One library per powerup. Think of it as a require(libraryName) call
-- Returns the library table for the powerup
function ap.registerPowerup(libraryName)
local entry = require(libraryName)
entry.registerItems = registerItemsInternal
entry.name = libraryName
entry.items = entry.items or {}
powerups[libraryName] = entry
for k,v in ipairs(entry.items) do
if itemMap[v] ~= nil then
Misc.warn("Item " .. v .. " could not be registered to Powerup " .. libraryName .. ", because it was already registered to Powerup " .. itemMap[v] .. ".")
return
else
itemMap[v] = libraryName
end
end
return entry
end
-- Replacement powerups
ap.powerReplacements = {}
ap.powerReplacements[3] = ap.registerPowerup("ap_fireflower")
ap.powerReplacements[7] = ap.registerPowerup("ap_iceflower")
-- registerItemTier registers a new tier chain for items.
-- spawnedItem: number - The item that spawns from a block.
-- chain: table - The list of states ordered by significance. Numbers correspond to vanilla states, strings correspond to the names of anotherpowerup powerups. See demo for more info.
-- if you provide chain as "true", it will accept everything except for small mario
function ap.registerItemTier(spawnedItem, chain)
if chain ~= true then
for k,v in ipairs(chain) do
if ap.powerReplacements[v] then
chain[k] = ap.powerReplacements[v].name
end
end
end
tiers[spawnedItem] = chain
end
-- Returns the player's current powerup, accounting for anotherpowerup powerups
function ap.getPowerup()
if player.powerup == 3 or player.powerup == 7 then
return currentPowerup.name
else
return player.powerup
end
end
function ap.onInitAPI()
registerEvent(ap, "onPostNPCKill")
registerEvent(ap, "onTickEnd")
registerEvent(ap, "onTick")
registerEvent(ap, "onExit")
registerEvent(ap, "onDraw")
registerEvent(ap, "onStart")
end
function ap.onTick()
if currentPowerup and player.forcedState == 0 and ap.getPowerup() ~= currentPowerup.name then
currentPowerup.onDisable()
currentPowerup = nil
SaveData._ap.cp = nil
end
if currentPowerup then
if player.mount < 2 then
if player.character ~= CHARACTER_LINK then
player:mem(0x160, FIELD_WORD, 3) -- Disable the nomral projectile timer. Powerups need to implement their own.
else
player:mem(0x162, FIELD_WORD, 29) -- Disable the link projectile timer. Powerups need to implement their own.
end
end
currentPowerup.onTick()
end
end
local testMenuWasActive = false
function ap.onDraw()
if currentPowerup and player.forcedState == 0 and ap.getPowerup() ~= currentPowerup.name then
currentPowerup.onDisable()
currentPowerup = nil
SaveData._ap.cp = nil
end
if currentPowerup then
currentPowerup.onDraw()
end
-- Handle test mode menu, and reload hitboxes after a bit
if (Misc.inEditor() and (not testModeMenu.active) and testMenuWasActive) or lunatime.tick() == 1 then
loadPowerupAssets(player,player.powerup)
end
if Misc.inEditor() then
testMenuWasActive = testModeMenu.active
end
end
function ap.onTickEnd()
for k,v in ipairs(NPC.get(table.unmap(itemMap))) do -- Thankfully this is more performant these days I guess!
if not v.data._anotherpowerup and v.despawnTimer > 0 then
v.data._anotherpowerup = true
local spawn = v:mem(0x138, FIELD_WORD)
if spawn == 1 or spawn == 3 or spawn == 4 then
local currentTier
local list
if tiers[v.id] ~= nil and tiers[v.id] ~= true then
if type(ap.getPowerup()) == "number" then
list = powerups[ap.getPowerup()]
else
list = currentPowerup.items
end
if list ~= nil then
for k,v in ipairs(tiers[v.id]) do
currentTier = 0
for _,n in ipairs(list) do
if n == v then
currentTier = k
break
end
end
if currentTier ~= 0 then break end
end
else
if tiers[v.id] then
currentTier = 0
end
end
if currentTier and currentTier < #tiers[v.id] then
local nextTier = tiers[v.id][currentTier + 1]
v.id = powerups[itemMap[nextTier]].items[1]
end
else
if player.powerup == 1 then
v.id = 9
end
return
end
end
end
end
if currentPowerup and player.forcedState == 0 and ap.getPowerup() ~= currentPowerup.name then
currentPowerup.onDisable()
currentPowerup = nil
SaveData._ap.cp = nil
end
if currentPowerup then
currentPowerup.onTickEnd()
end
if isPoweringUp then
if not (player.forcedState ~= 0 or (currentPowerup and ap.getPowerup() ~= currentPowerup.name)) then
player.powerup = isPoweringUp
isPoweringUp = false
end
end
end
function ap.setPlayerPowerup(appower, silent, reservePowerup, thisPlayer)
thisPlayer = thisPlayer or player
local nextPower = 7
if thisPlayer.powerup == 3 then
nextPower = 3
end
if currentPowerup and appower.name ~= currentPowerup.name then
currentPowerup.onDisable()
end
if not silent then
if currentPowerup and appower.name == currentPowerup.name then
if appower.apSounds ~= nil and appower.apSounds.reserve ~= nil then
SFX.play(appower.apSounds.reserve)
end
if player.forcedState == 3 or player.forcedState == 41 then
thisPlayer:mem(0x122, FIELD_WORD, 0)
thisPlayer:mem(0x124, FIELD_WORD, 0)
end
return
else
if appower.apSounds ~= nil and appower.apSounds.upgrade ~= nil then
SFX.play(appower.apSounds.upgrade)
end
if nextPower == 3 then
thisPlayer:mem(0x122, FIELD_WORD, 4)
else
thisPlayer:mem(0x122, FIELD_WORD, 41)
end
thisPlayer:mem(0x124, FIELD_WORD, 100)
--Audio.sounds[7].sfx = nil
end
isPoweringUp = nextPower
else
thisPlayer.powerup = nextPower
end
currentPowerup = appower
SaveData._ap.cp = appower.name
--thisPlayer.forcedState = 0
if thisPlayer.character > 5 then
error("Character IDs above 5 are unsupported.")
return
end
loadPowerupAssets(thisPlayer,nextPower)
currentPowerup.onEnable()
end
function ap.onStart()
if Misc.inEditor() then
SaveData._ap.cp = nil
SaveData._ap.lastPowerupItem = nil
currentPowerup = nil
return
end
if SaveData._ap.cp then
ap.setPlayerPowerup(powerups[SaveData._ap.cp], true)
end
end
function ap.onPostNPCKill(v, r)
if isPoweringUp then return end
local p = npcManager.collected(v, r)
if not p then
return
end
local defaultPowerupID = defaultNPCPowerupMap[v.id]
local apPowerupName = itemMap[v.id]
if defaultPowerupID or apPowerupName then
if p.character >= 3 and p.character <= 5 and not defaultPowerupID then
p:mem(0x16, FIELD_WORD, p:mem(0x16, FIELD_WORD) + 1)
end
if SaveData._ap.lastPowerupItem then
player.reservePowerup = SaveData._ap.lastPowerupItem
end
SaveData._ap.lastPowerupItem = v.id
end
if apPowerupName then
ap.setPlayerPowerup(powerups[apPowerupName], false, v.id, p)
v:kill(9)
end
end
return ap
Sadly that did not work. The errors are still the same two.
|
|
|
|
|
|
|
|
|
-
Emral
- Cute Yoshi Egg

- Posts: 9891
- Joined: Mon Jan 20, 2014 12:58 pm
- Flair: Phoenix
Postby Emral » Sat Nov 05, 2022 11:38 am
that... doesn't really make sense. the line numbers should be different, at least, and i didn't get any errors when i tested the new version.
(also when multiple errors are thrown, usually the first one causes the second due to some hiccup in the code execution, so the second is rarely ever necessary to be known because fixing the first fixes the second, too. just so you know)
can you confirm that the version of anotherpowerup running in the crashing level is the one i just posted and not an older one?
|
|
|
|
|
|
|
|
|
-
Cambroski
- Shy Guy

- Posts: 8
- Joined: Tue Nov 01, 2022 7:29 pm
- Flair: Awake by day, asleep by night.
Postby Cambroski » Sat Nov 05, 2022 11:44 am
Enjl wrote: ↑Sat Nov 05, 2022 11:38 am
that... doesn't really make sense. the line numbers should be different, at least, and i didn't get any errors when i tested the new version.
(also when multiple errors are thrown, usually the first one causes the second due to some hiccup in the code execution, so the second is rarely ever necessary to be known because fixing the first fixes the second, too. just so you know)
can you confirm that the version of anotherpowerup running in the crashing level is the one i just posted and not an older one?
Yes I can. The one that causes an error is the latest code for it. Odd that I'm getting an error an you're not.
|
|
|
|
|
|
|
|
|
-
Cambroski
- Shy Guy

- Posts: 8
- Joined: Tue Nov 01, 2022 7:29 pm
- Flair: Awake by day, asleep by night.
Postby Cambroski » Sat Nov 05, 2022 11:53 am
That did it! Both levels and worlds can now be played with custom powerups. Thanks for your help, bro.
|
|
|
|
|
Return to “LunaLua Help”
Users browsing this forum: No registered users and 1 guest
|