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

- Posts: 3
- Joined: Tue May 27, 2025 4:49 am
- Pronouns: he/him
Postby Ruslix » Fri Jun 20, 2025 4:57 pm
I want to make a " Bullet Bill Bro" (with bullet bill gun) In SMBX2, which is originally from Mario Forever. I've used Megadood's "Ice Bro" as " lua" script. But I've got a problem, whenever BB Bro's direction is "left" he shoots Bullet Bill from gun PERFECTLY,

BUT whenever BB Bro's direction is "right", he shoot Bullet Bill from exact same position
(After shooting Bullet Bill from right, it has 1 frame before it rotates)
BB Bro's hitbox is also in the middle, which I can't change (changing his " gfxoffsetx" will make him kinda teleport)
Here is a video of him as Npc (with sound):
https://drive.google.com/file/d/1b8eBIQ ... drive_link
Here is script:
Code: Select all local npcManager = require("npcManager")
local rng = require("rng")
local npcID = NPC_ID
local firebros = {}
npcManager.setNpcSettings{
id = npcID,
gfxoffsety = 2,
gfxwidth = 54,
gfxheight = 48,
width = 54,
height = 48,
frames = 3,
framespeed = 8,
framestyle = 1,
nogravity=0,
speed = 1,
projectileid = 17,
friendlyProjectileid = 17,
lowjumpheight = 4,
highjumpheight = 6,
shotcount = 2,
walktime = 60,
jumptimemin = 65,
jumptimemax = 85,
shoottimemin = 135,
shoottimemax = 180,
shotspeedx = 3.5,
shotsound = 22,
npcheldfirerate = 0.75
}
npcManager.registerHarmTypes(
npcID,
{
HARM_TYPE_JUMP,
HARM_TYPE_FROMBELOW,
HARM_TYPE_NPC,
HARM_TYPE_HELD,
HARM_TYPE_TAIL,
HARM_TYPE_SPINJUMP,
HARM_TYPE_SWORD,
HARM_TYPE_LAVA
},
{
[HARM_TYPE_JUMP]={id=npcID, speedX=0, speedY=0},
[HARM_TYPE_FROMBELOW]=npcID,
[HARM_TYPE_NPC]=npcID,
[HARM_TYPE_HELD]=npcID,
[HARM_TYPE_TAIL]=npcID,
[HARM_TYPE_SPINJUMP]=10,
[HARM_TYPE_LAVA]={id=13, xoffset=0.5, xoffsetBack = 0, yoffset=1, yoffsetBack = 1.5}
}
)
local function spawnFireballs(v, data, heldPlayer)
local id = NPC.config[v.id].projectileID
if v.friendly or heldPlayer then
id = NPC.config[v.id].friendlyProjectileID
end
local spawn = NPC.spawn(id,v.x - 0.5 * v.width,v.y + 12,v:mem(0x146,FIELD_WORD), false, true)
spawn.direction = data.lockDirection
spawn.speedX = NPC.config[v.id].shotspeedx * spawn.direction
spawn.layerName = "Spawned NPCs"
SFX.play(NPC.config[v.id].shotsound)
if heldPlayer and heldPlayer.keys.up then
spawn.speedY = -8
end
end
function firebros.onTickEndNPC(v)
if Defines.levelFreeze then return end
local data = v.data._basegame
if v.despawnTimer <= 0 or (v.forcedState > 0 and (v.forcedState ~= 208 or v.forcedCounter1 > 0)) or v.isProjectile then
data.timer = nil
data.forceFrame = 0
data.lockDirection = v.direction
if data.wasHeld and data.wasHeld > 0 then
v:kill(3)
end
return
end
local cfg = NPC.config[v.id]
local walkframes = cfg.frames - 1
local held = v:mem(0x12C, FIELD_WORD)
local skipShooting = false
if held < 0 then
data.wasHeldByNPC = true
skipShooting = true
elseif held > 0 then
data.wasHeldByNPC = false
end
if data.wasHeldByNPC and v.forcedState == 208 then
held = -1
data.lockDirection = v.direction
end
if data.timer == nil then
data.timer = cfg.walktime * 0.5
data.jumptimer = RNG.randomInt(cfg.jumptimemin, cfg.jumptimemax)
data.walk = v.direction
data.shottimer = RNG.randomInt(cfg.shoottimemin, cfg.shoottimemax)
data.wasHeld = held
if held ~= 0 then
data.shottimer = 120
end
data.forceFrame = 0
data.lockDirection = v.direction
end
local heldPlayer
if held > 0 then
heldPlayer = Player(held)
data.lockDirection = heldPlayer.direction
end
if data.walk == 0 then
data.walk = RNG.irandomEntry{-1, 1}
end
data.jumptimer = data.jumptimer - 1
data.wasHeld = held
if skipShooting then
return
end
if held == 0 then
if v.collidesBlockBottom then
v.speedX = 0
if data.timer % 8 == 0 then
data.forceFrame = (data.forceFrame + 1) % walkframes
end
if v:mem(0x12E,FIELD_WORD) == 0 then
if Player.getNearest(v.x, v.y).x < v.x then
data.lockDirection = -1
else
data.lockDirection = 1
end
end
v.speedX = data.walk * 1.2 * NPC.config[v.id].speed
if data.shottimer > 20 then
data.timer = data.timer + 1
end
if data.jumptimer <= 0 then
if data.shottimer > 30 then
v.speedY = -math.abs(NPC.config[v.id].lowjumpheight)
v.speedX = 0
end
data.jumptimer = RNG.randomInt(cfg.jumptimemin, cfg.jumptimemax)
else
data.shottimer = data.shottimer - 1
end
else
if data.jumptimer <= 0 then
data.jumptimer = RNG.randomInt(cfg.jumptimemin, cfg.jumptimemax)
elseif data.shottimer <= 20 then
data.shottimer = data.shottimer - 1
end
end
else
if held < 0 then
data.shottimer = data.shottimer - 1 * NPC.config[v.id].npcheldfirerate
else
data.shottimer = data.shottimer - 1
end
if data.shottimer <= 100 then
spawnFireballs(v, data, heldPlayer)
data.shottimer = 140
end
if data.shottimer <= 108 or data.shottimer >= 132 then
data.forceFrame = walkframes
else
data.forceFrame = walkframes - 1
end
end
if data.timer % cfg.walktime == 0 then
data.walk = -data.walk
end
if data.shottimer <= 20 then
v.speedX = 0
data.forceFrame = walkframes
if data.shottimer % 40 == 0 then
spawnFireballs(v, data)
end
if data.shottimer == -40 * (cfg.shotcount - 1) + 20 and RNG.randomInt(0, 1) == 1 then
v.speedY = -math.abs(NPC.config[v.id].highjumpheight)
end
if data.shottimer < -40 * (cfg.shotcount - 1) - 10 then
data.shottimer = RNG.randomInt(cfg.shoottimemin, cfg.shoottimemax)
data.jumptimer = data.jumptimer + 60
end
end
end
function firebros.onDrawNPC(v)
if v:mem(0x12A, FIELD_WORD) <= 0 then return end
local data = v.data._basegame
if data.forceFrame then
v.animationTimer = 500
v.animationFrame = data.forceFrame
if data.lockDirection == 1 then v.animationFrame = v.animationFrame + NPC.config[v.id].frames end
end
end
function firebros.onInitAPI()
npcManager.registerEvent(npcID, firebros, "onTickEndNPC")
npcManager.registerEvent(npcID, firebros, "onDrawNPC")
end
return firebros
And here is level itself with him:
https://drive.google.com/file/d/1WUz0b8 ... drive_link
Help me please, I would appreciate it so much
Update 1: Files are not restricted anymore
Last edited by Ruslix on Fri Jun 27, 2025 5:35 am, edited 1 time in total.
|
|
|
|
|
|
|
|
|
-
MegaDood
- Charged Spiny

- Posts: 1823
- Joined: Wed Nov 01, 2017 2:43 am
Postby MegaDood » Thu Jun 26, 2025 7:25 am
Havent tested it but something like this should work
Code: Select all local npcManager = require("npcManager")
local rng = require("rng")
local npcID = NPC_ID
local firebros = {}
npcManager.setNpcSettings{
id = npcID,
gfxoffsety = 2,
gfxwidth = 54,
gfxheight = 48,
width = 54,
height = 48,
frames = 3,
framespeed = 8,
framestyle = 1,
nogravity=0,
speed = 1,
projectileid = 17,
friendlyProjectileid = 17,
lowjumpheight = 4,
highjumpheight = 6,
shotcount = 2,
walktime = 60,
jumptimemin = 65,
jumptimemax = 85,
shoottimemin = 135,
shoottimemax = 180,
shotspeedx = 3.5,
shotsound = 22,
npcheldfirerate = 0.75
}
npcManager.registerHarmTypes(
npcID,
{
HARM_TYPE_JUMP,
HARM_TYPE_FROMBELOW,
HARM_TYPE_NPC,
HARM_TYPE_HELD,
HARM_TYPE_TAIL,
HARM_TYPE_SPINJUMP,
HARM_TYPE_SWORD,
HARM_TYPE_LAVA
},
{
[HARM_TYPE_JUMP]={id=npcID, speedX=0, speedY=0},
[HARM_TYPE_FROMBELOW]=npcID,
[HARM_TYPE_NPC]=npcID,
[HARM_TYPE_HELD]=npcID,
[HARM_TYPE_TAIL]=npcID,
[HARM_TYPE_SPINJUMP]=10,
[HARM_TYPE_LAVA]={id=13, xoffset=0.5, xoffsetBack = 0, yoffset=1, yoffsetBack = 1.5}
}
)
local function spawnFireballs(v, data, heldPlayer)
local id = NPC.config[v.id].projectileID
if v.friendly or heldPlayer then
id = NPC.config[v.id].friendlyProjectileID
end
local spawn = NPC.spawn(id,v.x - 0.5 * v.width + ((v.direction + 1) * (54 / 2)),v.y + 12,v:mem(0x146,FIELD_WORD), false, true)
spawn.direction = data.lockDirection
spawn.speedX = NPC.config[v.id].shotspeedx * spawn.direction
spawn.layerName = "Spawned NPCs"
SFX.play(NPC.config[v.id].shotsound)
if heldPlayer and heldPlayer.keys.up then
spawn.speedY = -8
end
end
function firebros.onTickEndNPC(v)
if Defines.levelFreeze then return end
local data = v.data._basegame
if v.despawnTimer <= 0 or (v.forcedState > 0 and (v.forcedState ~= 208 or v.forcedCounter1 > 0)) or v.isProjectile then
data.timer = nil
data.forceFrame = 0
data.lockDirection = v.direction
if data.wasHeld and data.wasHeld > 0 then
v:kill(3)
end
return
end
local cfg = NPC.config[v.id]
local walkframes = cfg.frames - 1
local held = v:mem(0x12C, FIELD_WORD)
local skipShooting = false
if held < 0 then
data.wasHeldByNPC = true
skipShooting = true
elseif held > 0 then
data.wasHeldByNPC = false
end
if data.wasHeldByNPC and v.forcedState == 208 then
held = -1
data.lockDirection = v.direction
end
if data.timer == nil then
data.timer = cfg.walktime * 0.5
data.jumptimer = RNG.randomInt(cfg.jumptimemin, cfg.jumptimemax)
data.walk = v.direction
data.shottimer = RNG.randomInt(cfg.shoottimemin, cfg.shoottimemax)
data.wasHeld = held
if held ~= 0 then
data.shottimer = 120
end
data.forceFrame = 0
data.lockDirection = v.direction
end
local heldPlayer
if held > 0 then
heldPlayer = Player(held)
data.lockDirection = heldPlayer.direction
end
if data.walk == 0 then
data.walk = RNG.irandomEntry{-1, 1}
end
data.jumptimer = data.jumptimer - 1
data.wasHeld = held
if skipShooting then
return
end
if held == 0 then
if v.collidesBlockBottom then
v.speedX = 0
if data.timer % 8 == 0 then
data.forceFrame = (data.forceFrame + 1) % walkframes
end
if v:mem(0x12E,FIELD_WORD) == 0 then
if Player.getNearest(v.x, v.y).x < v.x then
data.lockDirection = -1
else
data.lockDirection = 1
end
end
v.speedX = data.walk * 1.2 * NPC.config[v.id].speed
if data.shottimer > 20 then
data.timer = data.timer + 1
end
if data.jumptimer <= 0 then
if data.shottimer > 30 then
v.speedY = -math.abs(NPC.config[v.id].lowjumpheight)
v.speedX = 0
end
data.jumptimer = RNG.randomInt(cfg.jumptimemin, cfg.jumptimemax)
else
data.shottimer = data.shottimer - 1
end
else
if data.jumptimer <= 0 then
data.jumptimer = RNG.randomInt(cfg.jumptimemin, cfg.jumptimemax)
elseif data.shottimer <= 20 then
data.shottimer = data.shottimer - 1
end
end
else
if held < 0 then
data.shottimer = data.shottimer - 1 * NPC.config[v.id].npcheldfirerate
else
data.shottimer = data.shottimer - 1
end
if data.shottimer <= 100 then
spawnFireballs(v, data, heldPlayer)
data.shottimer = 140
end
if data.shottimer <= 108 or data.shottimer >= 132 then
data.forceFrame = walkframes
else
data.forceFrame = walkframes - 1
end
end
if data.timer % cfg.walktime == 0 then
data.walk = -data.walk
end
if data.shottimer <= 20 then
v.speedX = 0
data.forceFrame = walkframes
if data.shottimer % 40 == 0 then
spawnFireballs(v, data)
end
if data.shottimer == -40 * (cfg.shotcount - 1) + 20 and RNG.randomInt(0, 1) == 1 then
v.speedY = -math.abs(NPC.config[v.id].highjumpheight)
end
if data.shottimer < -40 * (cfg.shotcount - 1) - 10 then
data.shottimer = RNG.randomInt(cfg.shoottimemin, cfg.shoottimemax)
data.jumptimer = data.jumptimer + 60
end
end
end
function firebros.onDrawNPC(v)
if v:mem(0x12A, FIELD_WORD) <= 0 then return end
local data = v.data._basegame
if data.forceFrame then
v.animationTimer = 500
v.animationFrame = data.forceFrame
if data.lockDirection == 1 then v.animationFrame = v.animationFrame + NPC.config[v.id].frames end
end
end
function firebros.onInitAPI()
npcManager.registerEvent(npcID, firebros, "onTickEndNPC")
npcManager.registerEvent(npcID, firebros, "onDrawNPC")
end
return firebros
Essentially I put ((v.direction + 1) * (54 / 2)) in the line that spawns the NPC. This takes the spawned NPC, which is currently perfect facing left, and offsets it by 54 pixels to the right when facing the other way. The reason I did 54/2 is because it multiplies this number by the other direction + 1, which becomes 2.
|
|
|
|
|
|
|
|
|
-
Ruslix
- Goomba

- Posts: 3
- Joined: Tue May 27, 2025 4:49 am
- Pronouns: he/him
Postby Ruslix » Fri Jun 27, 2025 5:38 am
I appreciate that you helped me, but now BB Bro has likely a chance system whenever he can shoot from his Gun or behind his back in both directions
|
|
|
|
|
Return to “LunaLua Help”
Users browsing this forum: Petal [Bot] and 3 guests
|