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

- Posts: 51
- Joined: Sat Feb 06, 2016 11:48 am
Postby BrokenAce » Mon Aug 15, 2022 8:38 pm
As I am very new to LunaLua, trying to figure out how it works in relation to the existing pre-coded aspects of SMBX can be a real pain. Specifically, I'm trying to modify the SMB3 goal roulette so that the mushroom and flower give out coins instead of points. This sounds like it should be simple to do, but I've had no luck with even getting started, since the goal roulette does not exist in lua form to edit or study. I'm aware of Defines.smb3RouletteScore, but the values for this only allow for alternative scores.
Any ideas on how to implement this? I am open to unconventional methods, such as modifying the 4000 and 8000 points values to give coins as well as or instead of points (though I'm not sure how to do this either).
|
|
|
|
|
|
|
|
|
-
Emral
- Cute Yoshi Egg

- Posts: 9891
- Joined: Mon Jan 20, 2014 12:58 pm
- Flair: Phoenix
Postby Emral » Mon Aug 15, 2022 8:56 pm
Easiest way would be to make a new goal roulette npc from scratch.
- copy the npc-n stuff from the templates folder into your level or episode folder and rename n to an id in the custom range (example: npc-751.lua)
- change the npc-751.png to look like what you want
- change the npc-751.ini to point to your image file instead of Dummy.png
- in the lua file, make sure to set the following configs in the npc settings section: isinteractable = true, iscollectablegoal = true, nogravity = true, score = 0
- put this line at the top of the lua file: "local npcManager=require("npcManager")
- in onInitAPI, register the handler for an onPostNPCKill event. There is a commented out one for onNPCKill which you can alter and comment back in
- Get rid of onTickNPC and the registration for it
- add a function onPostNPCKill(killedNPC, killReason)
- in that function, the logic looks like this:
-- filter for collecting the NPC: if killedNPC.id == NPC_ID and npcManager.collected(killedNPC, killReason) then
-- when that filter passes, you can do a second if-else over the NPC's killedNPC.animationFrame variable. If it's 0, it's a mushroom. if it's 1, it's a flower. if it's 2, it's a a star. In the if-else you can then put whatever you want.
-- I haven't used iscollectablegoal before but you might have to put Level.winState(LEVEL_END_STATE_ROULETTE) into the filter-if statement as well to make sure the game ends with the roulette goal type.
|
|
|
|
|
|
|
|
|
-
BrokenAce
- Bot

- Posts: 51
- Joined: Sat Feb 06, 2016 11:48 am
Postby BrokenAce » Tue Aug 16, 2022 7:00 am
Enjl wrote: ↑Mon Aug 15, 2022 8:56 pm
Easiest way would be to make a new goal roulette npc from scratch.
- copy the npc-n stuff from the templates folder into your level or episode folder and rename n to an id in the custom range (example: npc-751.lua)
- change the npc-751.png to look like what you want
- change the npc-751.ini to point to your image file instead of Dummy.png
- in the lua file, make sure to set the following configs in the npc settings section: isinteractable = true, iscollectablegoal = true, nogravity = true, score = 0
- put this line at the top of the lua file: "local npcManager=require("npcManager")
- in onInitAPI, register the handler for an onPostNPCKill event. There is a commented out one for onNPCKill which you can alter and comment back in
- Get rid of onTickNPC and the registration for it
- add a function onPostNPCKill(killedNPC, killReason)
- in that function, the logic looks like this:
-- filter for collecting the NPC: if killedNPC.id == NPC_ID and npcManager.collected(killedNPC, killReason) then
-- when that filter passes, you can do a second if-else over the NPC's killedNPC.animationFrame variable. If it's 0, it's a mushroom. if it's 1, it's a flower. if it's 2, it's a a star. In the if-else you can then put whatever you want.
-- I haven't used iscollectablegoal before but you might have to put Level.winState(LEVEL_END_STATE_ROULETTE) into the filter-if statement as well to make sure the game ends with the roulette goal type.
Thanks for the help, but I'm having absolutely no luck getting this set up. It just spits out multiple error messages and refuses to run the code. Here's what I've got for my Register events, in case it helps (the NPC:toCoin is just a placeholder for now):
Code: Select all --Register events
function sampleNPC.onInitAPI()
registerEvent(sampleNPC, "onNPCKill")
onPostNPCKill(killedNPC, killReason)
if killedNPC.id == NPC_ID and npcManager.collected(killedNPC, killReason) then
if-else killedNPC.animationFrame = 0, then NPC:toCoin(),
if-else killedNPC.animationFrame = 1, then NPC:toCoin(),
if-else killedNPC.animationFrame = 2, then NPC:toCoin(),
end
|
|
|
|
|
|
|
|
|
-
Emral
- Cute Yoshi Egg

- Posts: 9891
- Joined: Mon Jan 20, 2014 12:58 pm
- Flair: Phoenix
Postby Emral » Tue Aug 16, 2022 8:08 am
BrokenAce wrote: ↑Tue Aug 16, 2022 7:00 am
Thanks for the help, but I'm having absolutely no luck getting this set up. It just spits out multiple error messages and refuses to run the code. Here's what I've got for my Register events, in case it helps (the NPC:toCoin is just a placeholder for now):
Code: Select all --Register events
function sampleNPC.onInitAPI()
registerEvent(sampleNPC, "onNPCKill")
onPostNPCKill(killedNPC, killReason)
if killedNPC.id == NPC_ID and npcManager.collected(killedNPC, killReason) then
if-else killedNPC.animationFrame = 0, then NPC:toCoin(),
if-else killedNPC.animationFrame = 1, then NPC:toCoin(),
if-else killedNPC.animationFrame = 2, then NPC:toCoin(),
end
right, nah, the bullet points dont translate to literal lines of code. they were more just a guide assuming some familiarity with programming. i underestimated "very new", sorry
Code: Select all local npcManager = require("npcManager")
function sampleNPC.onInitAPI()
registerEvent(sampleNPC, "onPostNPCKill")
end
function sampleNPC.onPostNPCKill(killedNPC, killReason)
if killedNPC.id == NPC_ID and npcManager.collected(killedNPC, killReason) then
if killedNPC.animationFrame == 0 then
Misc.coins(1) -- gives 1 coin to the player
elseif killedNPC.animationFrame == 1 then
Misc.coins(2) -- gives 2 coins to the player
elseif killedNPC.animationFrame == 2 then
Misc.coins(3) -- gives 3 coins to the player
end
Misc.npcToCoins() -- turn all onscreen npcs into coins
Level.winState(LEVEL_END_STATE_ROULETTE) --win the level
end
end
Lot to unpack, but the main differences are:
- onPostNPCKill is a function https://docs.codehaus.moe/#/types/function https://docs.codehaus.moe/#/reference/lunalua-events
- The registerEvent call to register lunalua event functions needs to be given the name of the function
- General syntax, like elseif, and not using commas
- = is assignment, == is comparison
|
|
|
|
|
|
|
|
|
-
BrokenAce
- Bot

- Posts: 51
- Joined: Sat Feb 06, 2016 11:48 am
Postby BrokenAce » Tue Aug 16, 2022 10:09 am
Enjl wrote: ↑Tue Aug 16, 2022 8:08 am
Code: Select all local npcManager = require("npcManager")
function sampleNPC.onInitAPI()
registerEvent(sampleNPC, "onPostNPCKill")
end
function sampleNPC.onPostNPCKill(killedNPC, killReason)
if killedNPC.id == NPC_ID and npcManager.collected(killedNPC, killReason) then
if killedNPC.animationFrame == 0 then
Misc.coins(1) -- gives 1 coin to the player
elseif killedNPC.animationFrame == 1 then
Misc.coins(2) -- gives 2 coins to the player
elseif killedNPC.animationFrame == 2 then
Misc.coins(3) -- gives 3 coins to the player
end
Misc.npcToCoins() -- turn all onscreen npcs into coins
Level.winState(LEVEL_END_STATE_ROULETTE) --win the level
end
end
Lot to unpack, but the main differences are:
- onPostNPCKill is a function https://docs.codehaus.moe/#/types/function https://docs.codehaus.moe/#/reference/lunalua-events
- The registerEvent call to register lunalua event functions needs to be given the name of the function
- General syntax, like elseif, and not using commas
- = is assignment, == is comparison
Thanks, this is very informative and I'll keep it in mind next time I do some tinkering. I've only dipped my toes in coding in the past and wasn't aware of just how different the syntax can be between languages.
As for the actual code though, even after applying it, it still does not work as it should. The NPC does not trigger a goal (despite having followed the steps on listing it as a goal), and can be jumped on like a Goomba. I'm getting the feeling custom goals are somewhat of a blind spot in SMBX2's documentation, so I think I'll just stick with one of the vanilla goal types for the time being.
|
|
|
|
|
|
|
|
|
-
Emral
- Cute Yoshi Egg

- Posts: 9891
- Joined: Mon Jan 20, 2014 12:58 pm
- Flair: Phoenix
Postby Emral » Tue Aug 16, 2022 10:12 am
nah chances are i just forgot stuff because i never made a goal before. but other people have
in the registerHarmTypes section, make sure HARM_TYPE_OFFSCREEN is the only harm type that is registered. I think it's
Code: Select all npcManager.registerHarmTypes(NPC_ID, {HARM_TYPE_OFFSCREEN}, {})
in the npc config section, make sure spinjumpsafe = false, nohurt = true and jumphurt = true are all set as described
if that still doesnt work, it may be the case that the code doesnt run. is there an error? what happens if you put Misc.dialog("hi") into the onPostNPCKill or the if statements? Is there a case where it doesnt open a debug displaying hi?
|
|
|
|
|
|
|
|
|
-
BrokenAce
- Bot

- Posts: 51
- Joined: Sat Feb 06, 2016 11:48 am
Postby BrokenAce » Tue Aug 16, 2022 10:46 am
Ah, never mind. I figured out the issue; I'd forgotten to remove the -- from the identity flags.
As for being able to be jumped on, setting spinjumpsafe to false does the trick. It now all works as intended, and should be good to go once I hook up the audio cues, which I should (hopefully) be able to do myself with a little help from the wiki.
Edit: Okay actually maybe not. The wiki seems to be incredibly outdated as it doesn't even have Misc.coins() listed. Is there better documentation anywhere?
|
|
|
|
|
|
|
|
|
-
Emral
- Cute Yoshi Egg

- Posts: 9891
- Joined: Mon Jan 20, 2014 12:58 pm
- Flair: Phoenix
Postby Emral » Tue Aug 16, 2022 11:15 am
BrokenAce wrote: ↑Tue Aug 16, 2022 10:46 am
Ah, never mind. I figured out the issue; I'd forgotten to remove the -- from the identity flags. :oops:
As for being able to be jumped on, setting spinjumpsafe to false does the trick. It now all works as intended, and should be good to go once I hook up the audio cues, which I should (hopefully) be able to do myself with a little help from the wiki.
Edit: Okay actually maybe not. The wiki seems to be incredibly outdated as it doesn't even have Misc.coins() listed. Is there better documentation anywhere?
https://docs.codehaus.moe/#/
For audio stuff, https://docs.codehaus.moe/#/reference/SFX
|
|
|
|
|
|
|
|
|
-
BrokenAce
- Bot

- Posts: 51
- Joined: Sat Feb 06, 2016 11:48 am
Postby BrokenAce » Tue Aug 16, 2022 1:00 pm
Some incredibly useful stuff here. I managed to get the sound effects working fine, and I also managed to get one of the goal frames to give a 1up (though I haven't managed to get the 1up icon on-screen, but that's more of a nitpick).
One problem still remains though: music. The custom goal does not mute music by default, and I can't figure out how to code it in. I found this: https://docs.codehaus.moe/#/handbook/lu ... -and-music, but it's very different from anything I've seen before, and I honestly have no idea what I'm even looking at here.
Edit: I've just found a major problem: the SMB3 goal roulette AI does not account for multiplayer. Is there any way to destroy Player 2 upon triggering the NPC, like how the SMB3 goal roulette does?
|
|
|
|
|
|
|
|
|
-
Emral
- Cute Yoshi Egg

- Posts: 9891
- Joined: Mon Jan 20, 2014 12:58 pm
- Flair: Phoenix
Postby Emral » Tue Aug 16, 2022 7:50 pm
For music: Audio.MusicFadeOut(-1, 0) should do I thiiink?
For multiplayer, I don't actually know what SMBX usually does. You can permanently disable player controls and make them invincible like this though (if it's in the npc-n.lua, it needs to be sampleNPC.onTick and needs to have a registerEvent(sampleNPC, "onTick") in onInitAPI:
Code: Select all function onTick()
if Level.winState() > 0 then
for k,p in ipairs(Player.get()) do
for key, _ in ipairs(p.keys) do
p.keys[key] = false -- disable all input keys
end
p:mem(0x140, FIELD_WORD, 3) -- Set powerup invulnerability to 3 frames every frame
end
end
end
If it also does some camera stuff you might find a solution on the camera docs page.
|
|
|
|
|
Return to “LunaLua Help”
Users browsing this forum: No registered users and 1 guest
|