Behaviours - INTERACT_COIN Hitbox Not Working?

Spring E. Thing

Member
Modder
Apr 8, 2024
41
5
230
TL;DR
I've done a lot of digging into how coins work in the SM64 Decomp source and largely deduced how the interaction between Mario and coins works. I've made a behaviour that sets a hitbox to replicate this, and I can prove the hitbox works already with obj_check_hitbox_overlap(), however for some reason the Object using the coin behaviour replica I made doesn't act like a coin.


So I need to make a behaviour for my mod that shifts the coin value to something other than 1, 2, or 5 when Mario collides with the object representing the behaviour. I spent like two hours digging deep into the Co-Op DX source to investigate this and have found pretty much all I could need, but clearly I'm not understanding something even deeper than everything I've already found because my Lua code simply doesn't work. Off on a tangent a bit, I first considered just trying to network the coins directly to the server counter, but not only is the method for that not exposed via Lua, but it also doesn't perform the 100-coin star check as that's done as a sibling call in all the contexts in which the network coin method is called in the Co-Op DX source.

So here's the behaviour I wrote (and yes I'm absolutely certain the behaviour is properly hooked as this isn't the first behaviour I've had to make this far into my Co-Op DX modding career):
Code:
local function bhv_gonefishin_coin_init(o)
    local hitbox = get_temp_object_hitbox()
    hitbox.interactType = INTERACT_COIN
    hitbox.downOffset = 0
    hitbox.damageOrCoinValue = 1
    hitbox.health = 0
    hitbox.numLootCoins = 0
    hitbox.radius=100
    hitbox.height = 64
    hitbox.hurtboxRadius = 0
    hitbox.hurtboxHeight = 0
    obj_set_hitbox(o,hitbox)
end

(Object deletion is not yet implemented so currently the coin just lingers in the level, but that's fine for now until I get the intended coin behaviour working in the first place)

From my time investigating the source, I know that interactions are processed in the mario_process_interactions() method of src/game/interaction.c. From that method, I know that as long as an Object spawned via spawn_sync_object() has an .oInteractType that is a bit set as part of the value for m.collidedObjInteractTypes and the hitbox of the Object is overlapping the hitbox of m.marioObj, then a collision and more importantly an interaction should occur. In the snippet above, the ObjectHitbox I define is one-for-one the yellow coin hitbox from the SM64 decomp source code included within Co-Op DX's source. Additionally, I know from further source poking that obj_set_hitbox() is just a method to transfer the data of an ObjectHitbox to various members of the Object type such as hitbox.interactType to Object.oInteractType. Yet, despite all this, the Object(s) that this behaviour is attached to do not act like coins when Mario touches them.

I had a few thoughts about what the issue could be but all of the thoughts have only further convinced me that this SHOULD be working.
First of all, I decided to manually test the hitbox collision myself with obj_check_hitbox_overlap() as seen in the snippet below
Code:
id_bhvGoneFishinCoin = hook_behavior(nil,OBJ_LIST_DEFAULT,true,function(o) bhv_generic_init(o) bhv_gonefishin_coin_init(o) end,function(o)

local hittingMario = obj_check_hitbox_overlap(o,gMarioStates[0].marioObj)

log_to_console("mario hit: "..tostring(hittingMario))


end,"bhvGoneFishinCoin")

This snippet, exactly as I'd expect with a fully-working coin, prints mario hit: true whenever Mario has his hitbox overlapping with that of the coin Object and mario hti: false at all other times.

Second of all, I tried to poll the state of m.collidedObjInteractTypes every frame making sure that the INTERACT_COIN bit was set and printing a message if so to the console, like so:
Code:
hook_event(HOOK_MARIO_UPDATE,function(m)

    if(m.playerIndex==0) then
        if(m.collidedObjInteractTypes & INTERACT_COIN ~=0) then
            log_to_console(string.format("touching a coin | %.1f",get_global_timer()))
        end

    end
end)

This one didn't work even when touching vanilla coins present already in the many levels of SM64, so we can probably count out this being of any reliable advice since it didn't print anything when Mario was touching my custom coin behaviour either.

Third of all, and this was my last ditch effort to understand the issue before this post, I noticed that hitboxes are scaled by the same scale as the graphical representation of an Object in-game. Assuming that maybe somehow the graphical scale was off-kilter by default, I decided to print the scale channel values like so:
Code:
djui_chat_message_create(string.format("gfx scale: %.1f,%.1f,%.1f",o.header.gfx.scale.x,o.header.gfx.scale.y,o.header.gfx.scale.z))

This proved fruitless as this always just printed that the scale of the graphics for my custom Object were, as-expected, (1,1,1), meaning the scale of the graphics ultimately had no effect on the hitbox dimensions.

So that's it, I'm out of ideas, what am I doing wrong here? Many thanks for reading this far!
 
Last edited:
Solution
Code:
local function bhv_gonefishin_coin_init(o)
    o.oFlags = OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE
    local hitbox = get_temp_object_hitbox()
    hitbox.interactType = INTERACT_COIN
    hitbox.downOffset = 0
    hitbox.damageOrCoinValue = 1
    hitbox.health = 0
    hitbox.numLootCoins = 0
    hitbox.radius = 100
    hitbox.height = 64
    hitbox.hurtboxRadius = 0
    hitbox.hurtboxHeight = 0
    obj_set_hitbox(o,hitbox)
    obj_set_billboard(o)
end
local function level_init()
    local pos = gMarioStates[0].pos
    spawn_non_sync_object(id_bhvFishCoin, E_MODEL_YELLOW_COIN, pos.x, pos.y, pos.z, nil)
end
hook_event(HOOK_ON_LEVEL_INIT, level_init)
id_bhvFishCoin = hook_behavior(nil, OBJ_LIST_LEVEL, false, bhv_gonefishin_coin_init, nil...
You should set the object flag to contain OBJ_UPDATE_GFX_POS_AND_ANGLE, like this:

o.oFlags = OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE

To add more flags, simply do the same in an action:

o.oFlags = OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE } OBJ_FLAG_ACTIVE_FROM_AFAR
 
Code:
local function bhv_gonefishin_coin_init(o)
    o.oFlags = OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE
    local hitbox = get_temp_object_hitbox()
    hitbox.interactType = INTERACT_COIN
    hitbox.downOffset = 0
    hitbox.damageOrCoinValue = 1
    hitbox.health = 0
    hitbox.numLootCoins = 0
    hitbox.radius = 100
    hitbox.height = 64
    hitbox.hurtboxRadius = 0
    hitbox.hurtboxHeight = 0
    obj_set_hitbox(o,hitbox)
    obj_set_billboard(o)
end
local function level_init()
    local pos = gMarioStates[0].pos
    spawn_non_sync_object(id_bhvFishCoin, E_MODEL_YELLOW_COIN, pos.x, pos.y, pos.z, nil)
end
hook_event(HOOK_ON_LEVEL_INIT, level_init)
id_bhvFishCoin = hook_behavior(nil, OBJ_LIST_LEVEL, false, bhv_gonefishin_coin_init, nil, "bhvFishCoin")

Notable changes made:

1. Use OBJ_LIST_LEVEL instead of OBJ_LIST_DEFAULT
2. Update gfx pos and angle to obj pos and angle
3. If you're using the coin model, set object to billboard
 
  • Like
Reactions: Spring E. Thing
Solution
Code:
local function bhv_gonefishin_coin_init(o)
    o.oFlags = OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE
    local hitbox = get_temp_object_hitbox()
    hitbox.interactType = INTERACT_COIN
    hitbox.downOffset = 0
    hitbox.damageOrCoinValue = 1
    hitbox.health = 0
    hitbox.numLootCoins = 0
    hitbox.radius = 100
    hitbox.height = 64
    hitbox.hurtboxRadius = 0
    hitbox.hurtboxHeight = 0
    obj_set_hitbox(o,hitbox)
    obj_set_billboard(o)
end
local function level_init()
    local pos = gMarioStates[0].pos
    spawn_non_sync_object(id_bhvFishCoin, E_MODEL_YELLOW_COIN, pos.x, pos.y, pos.z, nil)
end
hook_event(HOOK_ON_LEVEL_INIT, level_init)
id_bhvFishCoin = hook_behavior(nil, OBJ_LIST_LEVEL, false, bhv_gonefishin_coin_init, nil, "bhvFishCoin")

Notable changes made:

1. Use OBJ_LIST_LEVEL instead of OBJ_LIST_DEFAULT
2. Update gfx pos and angle to obj pos and angle
3. If you're using the coin model, set object to billboard
TY for the consise and simple help, worked like a charm! I actually already had the OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE flag set in the bhv_generic_init() method so that wasn't a problem, but changing the object list to OBJ_LIST_LEVEL was all I needed for functionality. I won't even need a model for the coin since the coin gets immediately collected and is really just a way for me to network a coin collection with the methods to do so not exposed to Lua, and to that end I've tested it and it worked very well : )
I don't even need to set the OBJ_FLAG_UPDATE_GFX_POS_AND_ANGLE flag considering the Object representing the behaviour has no model, so I'll be considering to remove that from the generic method sometime soon.
 

Users who are viewing this thread