Share and discuss custom LunaLua code and content packs for SMBX2.
Moderator: Userbase Moderators
|
|
|
|
-
Lithobraker
- Fighter Fly

- Posts: 31
- Joined: Sat Mar 09, 2019 1:41 am
Postby Lithobraker » Mon Jul 06, 2020 3:27 pm
Hey, I'm currently repurposing the Flurret code into a Ruff Puff, and I've got a 4-frame moving animation for it. Everything seems to work fine when it's left-facing with this code, but when it's facing right, the animation plays extremely rapidly for some reason.
I might be able to fix it if I knew how modulus operators (%) worked...
Code: Select all v.animationFrame = v.animationFrame % 2
Changing the 2 here to 3 gets me an NPC which animates with only the first 3 frames of its moving animation- but it doesn't have a fit when facing right.
If it's set to 4 though, the fit occurs.
I feel like this should be obvious, but my understanding of modulus operators is that they give the remainder of the first number divided by the second. If this was true, shouldn't it not animate properly, since a remainder is only returned with an odd number? Clearly I'm missing something.
Sprite:
NPC.lua:
Code: Select all local npcManager = require("npcManager")
local ruffPuffNPC = {}
local npcID = NPC_ID
local deathEffectID = npcID
-- settings
local config = {
id = npcID,
gfxoffsetx = 0,
gfxoffsety = 0,
width = 30,
height = 30,
gfxwidth = 46,
gfxheight = 38,
frames = 6,
framestyle = 1,
playerblocktop = false,
npcblocktop = false,
playerblock = false,
npcblock = false,
nofireball = true,
noiceball = false,
nohurt = true,
nogravity = true,
noblockcollision = true,
jumphurt = true,
spinjumpsafe = true,
projectile = 935,
iswalker = true
}
npcManager.setNpcSettings(config)
npcManager.registerHarmTypes(npcID,
{
--HARM_TYPE_JUMP,
--HARM_TYPE_FROMBELOW,
HARM_TYPE_NPC,
--HARM_TYPE_PROJECTILE_USED,
HARM_TYPE_LAVA,
HARM_TYPE_HELD,
--HARM_TYPE_TAIL,
--HARM_TYPE_SPINJUMP,
--HARM_TYPE_OFFSCREEN,
HARM_TYPE_SWORD
},
{
--[HARM_TYPE_JUMP]=10,
[HARM_TYPE_FROMBELOW]= deathEffectID,
[HARM_TYPE_NPC]= deathEffectID,
--[HARM_TYPE_PROJECTILE_USED]= 10,
[HARM_TYPE_LAVA]={id=13, xoffset=0.5, xoffsetBack = 0, yoffset=1, yoffsetBack = 1.5},
[HARM_TYPE_HELD]= deathEffectID,
--[HARM_TYPE_TAIL]=10,
--[HARM_TYPE_SPINJUMP]=10,
--[HARM_TYPE_OFFSCREEN]=10,
[HARM_TYPE_SWORD]= deathEffectID,
[HARM_TYPE_EXT_ICE] = 10,
}
);
local ST_IDLE = 0
local ST_WAIT = 1
local ST_SHOOT = 2
local turnTimer = 0
function ruffPuffNPC.onInitAPI()
npcManager.registerEvent(npcID, ruffPuffNPC, "onTickEndNPC")
end
function ruffPuffNPC.onTickEndNPC(v)
if Defines.levelFreeze then return end
if v.isHidden or v:mem(0x12A, FIELD_WORD) <= 0 or v:mem(0x138, FIELD_WORD) > 0 then
v.data.state = 0
v.data.timer = 0
turnTimer = 0
return
end
local data = v.data
local shootframe = NPC.config[v.id].frames - 2
if data.state == nil then
data.state = 0
data.timer = 0
turnTimer = 0
end
data.timer = data.timer + 1
turnTimer = turnTimer + 1
v.animationFrame = v.animationFrame % 4
if data.state ~= ST_SHOOT then
if turnTimer >= 128 then
v.speedX = v.speedX * -1
turnTimer = 0
end
end
if data.state == ST_IDLE then
if data.timer == 64 and not v.friendly then
data.timer = 0
data.state = ST_WAIT
end
elseif data.state == ST_WAIT then
if data.timer == 16 then
local n = NPC.spawn(NPC.config[v.id].projectile, v.x + 0.5 * v.width, v.y + 0.5 * v.height, player.section, false, true)
n.speedX = 2 * v.direction
data.timer = 0
data.state = ST_SHOOT
SFX.play("puffspit.ogg")
end
v.animationFrame = shootframe
elseif data.state == ST_SHOOT then
if data.timer == 16 then
data.timer = 0
data.state = ST_IDLE
end
v.animationFrame = shootframe + 1
end
if v.direction == 1 and NPC.config[v.id].framestyle == 1 then
v.animationFrame = v.animationFrame + NPC.config[v.id].frames
end
end
return ruffPuffNPC
|
|
|
|
|
|
|
|
|
-
Emral
- Cute Yoshi Egg

- Posts: 9865
- Joined: Mon Jan 20, 2014 12:58 pm
- Flair: Phoenix
Postby Emral » Mon Jul 06, 2020 4:09 pm
Ah, yeah. Might've taken an animation shortcut there. Modulo can get a bit wacky due to the nature of the spritesheet and how the right-facing frames are just higher numbers. Your intuition is correct. IDK what that line 71 is doing but you miiight be able to fix it by removing it and putting the following just after line 77 instead:
v.animationFrame = math.floor(data.timer / NPC.config[v.id].framespeed) % frame
That'll basically overwrite the basegame frame timer completely. Fairly confident that'll work.
|
|
|
|
|
|
|
|
|
-
Lithobraker
- Fighter Fly

- Posts: 31
- Joined: Sat Mar 09, 2019 1:41 am
Postby Lithobraker » Mon Jul 06, 2020 4:12 pm
Enjl wrote: ↑Mon Jul 06, 2020 4:09 pm
Ah, yeah. Might've taken an animation shortcut there. Modulo can get a bit wacky due to the nature of the spritesheet and how the right-facing frames are just higher numbers. Your intuition is correct. IDK what that line 71 is doing but you miiight be able to fix it by removing it and putting the following just after line 77 instead:
v.animationFrame = math.floor(data.timer / NPC.config[v.id].framespeed) % frame
That'll basically overwrite the basegame frame timer completely. Fairly confident that'll work.
Um... line 71? In my file, line 71 is this:
Code: Select all npcManager.registerEvent(npcID, ruffPuffNPC, "onTickEndNPC")
I think that one's important. Could you just copy+paste which line you're talking about?
|
|
|
|
|
|
|
|
|
-
Emral
- Cute Yoshi Egg

- Posts: 9865
- Joined: Mon Jan 20, 2014 12:58 pm
- Flair: Phoenix
Postby Emral » Mon Jul 06, 2020 4:16 pm
Lithobraker wrote: ↑Mon Jul 06, 2020 4:12 pm
Enjl wrote: ↑Mon Jul 06, 2020 4:09 pm
Ah, yeah. Might've taken an animation shortcut there. Modulo can get a bit wacky due to the nature of the spritesheet and how the right-facing frames are just higher numbers. Your intuition is correct. IDK what that line 71 is doing but you miiight be able to fix it by removing it and putting the following just after line 77 instead:
v.animationFrame = math.floor(data.timer / NPC.config[v.id].framespeed) % frame
That'll basically overwrite the basegame frame timer completely. Fairly confident that'll work.
Um... line 71? In my file, line 71 is this:
Code: Select all npcManager.registerEvent(npcID, ruffPuffNPC, "onTickEndNPC")
I think that one's important. Could you just copy+paste which line you're talking about?
Oh, you must've modified some earlier lines. Got it.
Line 71:
v.animationFrame = v.animationFrame % 2
"Just after line 77" is between these two:
end
-- right here
elseif data.state == ST_WAIT then
|
|
|
|
|
|
|
|
|
-
Lithobraker
- Fighter Fly

- Posts: 31
- Joined: Sat Mar 09, 2019 1:41 am
Postby Lithobraker » Mon Jul 06, 2020 4:20 pm
Enjl wrote: ↑Mon Jul 06, 2020 4:16 pm
Lithobraker wrote: ↑Mon Jul 06, 2020 4:12 pm
Enjl wrote: ↑Mon Jul 06, 2020 4:09 pm
Ah, yeah. Might've taken an animation shortcut there. Modulo can get a bit wacky due to the nature of the spritesheet and how the right-facing frames are just higher numbers. Your intuition is correct. IDK what that line 71 is doing but you miiight be able to fix it by removing it and putting the following just after line 77 instead:
v.animationFrame = math.floor(data.timer / NPC.config[v.id].framespeed) % frame
That'll basically overwrite the basegame frame timer completely. Fairly confident that'll work.
Um... line 71? In my file, line 71 is this:
Code: Select all npcManager.registerEvent(npcID, ruffPuffNPC, "onTickEndNPC")
I think that one's important. Could you just copy+paste which line you're talking about?
Oh, you must've modified some earlier lines. Got it.
Line 71:
v.animationFrame = v.animationFrame % 2
"Just after line 77" is between these two:
end
-- right here
elseif data.state == ST_WAIT then
Well, I had to change your line a bit, because "% frame" is calling a global that doesn't exist. You meant "% NPC.config[v.id].frames", right?
Code: Select all v.animationFrame = math.floor(data.timer / NPC.config[v.id].framespeed) % NPC.config[v.id].frames
With the changes you suggested made, though, while the idle animations seem to work mostly fine, the NPC plays the shooting animation repeatedly. I can't tell if it's doing this because it's cycling through all its frames, or if it actually thinks it's shooting a bit before it does.
I'm inclined to believe the former.
|
|
|
|
|
|
|
|
|
-
Emral
- Cute Yoshi Egg

- Posts: 9865
- Joined: Mon Jan 20, 2014 12:58 pm
- Flair: Phoenix
Postby Emral » Mon Jul 06, 2020 4:23 pm
Lithobraker wrote: ↑Mon Jul 06, 2020 4:20 pm
Well, I had to change your line a bit, because "% frame" is calling a global that doesn't exist. You meant "% NPC.config[v.id].frames", right?
Code: Select all v.animationFrame = math.floor(data.timer / NPC.config[v.id].framespeed) % NPC.config[v.id].frames
With the changes you suggested made, though, while the idle animations seem to work mostly fine, the NPC plays the shooting animation repeatedly. I can't tell if it's doing this because it's cycling through all its frames, or if it actually thinks it's shooting a bit before it does.
I'm inclined to believe the former.
Wait... huh? We were both looking at the flurret ai file, right?
It is the former, hold on.. Here's what my file I just downloaded looked like:
https://hastebin.com/oleduqugog.rb
Notice the definition of "frame" in line 62. It's the number of the first shooting frame, so I was taking modulo by that to take the animation loop of the frames before it.
|
|
|
|
|
|
|
|
|
-
Lithobraker
- Fighter Fly

- Posts: 31
- Joined: Sat Mar 09, 2019 1:41 am
Postby Lithobraker » Mon Jul 06, 2020 4:27 pm
Enjl wrote: ↑Mon Jul 06, 2020 4:23 pm
Lithobraker wrote: ↑Mon Jul 06, 2020 4:20 pm
Well, I had to change your line a bit, because "% frame" is calling a global that doesn't exist. You meant "% NPC.config[v.id].frames", right?
Code: Select all v.animationFrame = math.floor(data.timer / NPC.config[v.id].framespeed) % NPC.config[v.id].frames
With the changes you suggested made, though, while the idle animations seem to work mostly fine, the NPC plays the shooting animation repeatedly. I can't tell if it's doing this because it's cycling through all its frames, or if it actually thinks it's shooting a bit before it does.
I'm inclined to believe the former.
Wait... huh? We were both looking at the flurret ai file, right?
It is the former, hold on.. Here's what my file I just downloaded looked like:
https://hastebin.com/oleduqugog.rb
Notice the definition of "frame" in line 62. It's the number of the first shooting frame, so I was taking modulo by that to take the animation loop of the frames before it.
Ah, right. I renamed that to "shootframe" since that's what it appears to mean, so I would be able to read the file better. That fixed it! Thanks for the help.
You've been a huge help in general so far! I hope you don't mind all these questions I've been having.
|
|
|
|
|
Return to “LunaLua”
Users browsing this forum: No registered users and 3 guests
|