diff --git a/.gitignore b/.gitignore index f2c4db8f..3a9283d9 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,9 @@ # Stackdumps *.stackdump +# Backup files +*.bak + # datadump /tools/ddump/* diff --git a/autogen/lua_definitions/structs.lua b/autogen/lua_definitions/structs.lua index 03dc6805..8fc07201 100644 --- a/autogen/lua_definitions/structs.lua +++ b/autogen/lua_definitions/structs.lua @@ -1218,10 +1218,11 @@ --- @field public oToadMessageRecentlyTalked integer --- @field public oToadMessageState integer --- @field public oToxBoxMovementStep integer +--- @field public oTreasureChestCurrentAnswer integer +--- @field public oTreasureChestIsAboveWater integer +--- @field public oTreasureChestIsLastInteractionIncorrect integer +--- @field public oTreasureChestLastNetworkPlayerIndex integer --- @field public oTreasureChestSound integer ---- @field public oTreasureChestUnkF4 integer ---- @field public oTreasureChestUnkF8 integer ---- @field public oTreasureChestUnkFC integer --- @field public oTreeSnowOrLeafUnkF4 integer --- @field public oTreeSnowOrLeafUnkF8 integer --- @field public oTreeSnowOrLeafUnkFC integer diff --git a/docs/lua/structs.md b/docs/lua/structs.md index 41577a10..128d05eb 100644 --- a/docs/lua/structs.md +++ b/docs/lua/structs.md @@ -1526,10 +1526,11 @@ | oTTCSpinnerDir | `integer` | | | oTTCChangeDirTimer | `integer` | | | oBetaTrampolineMarioOnTrampoline | `integer` | | -| oTreasureChestUnkF4 | `integer` | | -| oTreasureChestUnkF8 | `integer` | | -| oTreasureChestUnkFC | `integer` | | +| oTreasureChestCurrentAnswer | `integer` | | +| oTreasureChestIsLastInteractionIncorrect | `integer` | | +| oTreasureChestIsAboveWater | `integer` | | | oTreasureChestSound | `integer` | | +| oTreasureChestLastNetworkPlayerIndex | `integer` | | | oTreeSnowOrLeafUnkF4 | `integer` | | | oTreeSnowOrLeafUnkF8 | `integer` | | | oTreeSnowOrLeafUnkFC | `integer` | | diff --git a/include/object_fields.h b/include/object_fields.h index 841ffec6..0f2b7e3a 100644 --- a/include/object_fields.h +++ b/include/object_fields.h @@ -928,7 +928,8 @@ /* Hidden Star */ // Secrets/Red Coins -#define /*0x0F4*/ oHiddenStarTriggerCounter OBJECT_FIELD_S32(0x1B) +#define /*0x0F4*/ oHiddenStarTriggerCounter OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oHiddenStarLastInteractedObject OBJECT_FIELD_VPTR(0x1D) // Overall very difficult to determine usage, mostly stubbed code. /* Sparkle Spawn Star */ @@ -1037,10 +1038,11 @@ #define /*0x110*/ oBetaTrampolineMarioOnTrampoline OBJECT_FIELD_S32(0x22) /* Treasure Chest */ -#define /*0x0F4*/ oTreasureChestUnkF4 OBJECT_FIELD_S32(0x1B) -#define /*0x0F8*/ oTreasureChestUnkF8 OBJECT_FIELD_S32(0x1C) -#define /*0x0FC*/ oTreasureChestUnkFC OBJECT_FIELD_S32(0x1D) -#define /*0x100*/ oTreasureChestSound OBJECT_FIELD_S32(0x1E) +#define /*0x0F4*/ oTreasureChestCurrentAnswer OBJECT_FIELD_S32(0x1B) +#define /*0x0F8*/ oTreasureChestIsLastInteractionIncorrect OBJECT_FIELD_S32(0x1C) +#define /*0x0FC*/ oTreasureChestIsAboveWater OBJECT_FIELD_S32(0x1D) +#define /*0x100*/ oTreasureChestSound OBJECT_FIELD_S32(0x1E) +#define /*0x104*/ oTreasureChestLastNetworkPlayerIndex OBJECT_FIELD_S16(0x21, 0) /* Tree Snow Or Leaf */ #define /*0x0F4*/ oTreeSnowOrLeafUnkF4 OBJECT_FIELD_S32(0x1B) diff --git a/src/game/behaviors/amp.inc.c b/src/game/behaviors/amp.inc.c index c975518e..2f31cd98 100644 --- a/src/game/behaviors/amp.inc.c +++ b/src/game/behaviors/amp.inc.c @@ -97,7 +97,7 @@ static void homing_amp_appear_loop(void) { */ static void homing_amp_chase_loop(void) { struct Object* player = nearest_player_to_object(o); - int angleToPlayer = obj_angle_to_object(o, player); + s32 angleToPlayer = obj_angle_to_object(o, player); // Lock on to Mario if he ever goes within 11.25 degrees of the amp's line of sight if ((angleToPlayer - 0x400 < o->oMoveAngleYaw) @@ -199,15 +199,17 @@ static void amp_attack_cooldown_loop(void) { */ void bhv_homing_amp_loop(void) { if (!network_sync_object_initialized(o)) { - network_init_object(o, 4000.0f); - network_init_object_field(o, &o->oAmpYPhase); - network_init_object_field(o, &o->oAnimState); - network_init_object_field(o, &o->oFaceAnglePitch); - network_init_object_field(o, &o->oFaceAngleYaw); - network_init_object_field(o, &o->oForwardVel); - network_init_object_field(o, &o->oFriction); - network_init_object_field(o, &o->oHomingAmpAvgY); - network_init_object_field(o, &o->oHomingAmpLockedOn); + struct SyncObject *so = network_init_object(o, 4000.0f); + if (so) { + network_init_object_field(o, &o->oAmpYPhase); + network_init_object_field(o, &o->oAnimState); + network_init_object_field(o, &o->oFaceAnglePitch); + network_init_object_field(o, &o->oFaceAngleYaw); + network_init_object_field(o, &o->oForwardVel); + network_init_object_field(o, &o->oFriction); + network_init_object_field(o, &o->oHomingAmpAvgY); + network_init_object_field(o, &o->oHomingAmpLockedOn); + } } switch (o->oAction) { @@ -342,13 +344,15 @@ static void circling_amp_idle_loop(void) { */ void bhv_circling_amp_loop(void) { if (!network_sync_object_initialized(o)) { - network_init_object(o, 4000.0f); - network_init_object_field(o, &o->oAmpYPhase); - network_init_object_field(o, &o->oAnimState); - network_init_object_field(o, &o->oFaceAnglePitch); - network_init_object_field(o, &o->oFaceAngleYaw); - network_init_object_field(o, &o->oForwardVel); - network_init_object_field(o, &o->oFriction); + struct SyncObject *so = network_init_object(o, 4000.0f); + if (so) { + network_init_object_field(o, &o->oAmpYPhase); + network_init_object_field(o, &o->oAnimState); + network_init_object_field(o, &o->oFaceAnglePitch); + network_init_object_field(o, &o->oFaceAngleYaw); + network_init_object_field(o, &o->oForwardVel); + network_init_object_field(o, &o->oFriction); + } } switch (o->oAction) { diff --git a/src/game/behaviors/arrow_lift.inc.c b/src/game/behaviors/arrow_lift.inc.c index 8e1bd2bd..e815b4f2 100644 --- a/src/game/behaviors/arrow_lift.inc.c +++ b/src/game/behaviors/arrow_lift.inc.c @@ -60,10 +60,12 @@ static s8 arrow_lift_move_back(void) { */ void bhv_arrow_lift_loop(void) { if (!network_sync_object_initialized(o)) { - network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS); - network_init_object_field(o, &o->oTimer); - network_init_object_field(o, &o->oPrevAction); - network_init_object_field(o, &o->oAction); + struct SyncObject *so = network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS); + if (so) { + network_init_object_field(o, &o->oTimer); + network_init_object_field(o, &o->oPrevAction); + network_init_object_field(o, &o->oAction); + } } switch (o->oAction) { diff --git a/src/game/behaviors/bbh_merry_go_round.inc.c b/src/game/behaviors/bbh_merry_go_round.inc.c index 45b2051f..5cf3f0f6 100644 --- a/src/game/behaviors/bbh_merry_go_round.inc.c +++ b/src/game/behaviors/bbh_merry_go_round.inc.c @@ -21,14 +21,13 @@ static void handle_merry_go_round_music(void) { } } else { // Get Mario's floor and floor surface type - struct Surface *marioFloor; - u16 marioFloorType; + struct Surface *marioFloor = NULL; + struct Object *marioObject = gMarioObjects[0]; + u16 marioFloorType = 0; - find_floor(gMarioObject->oPosX, gMarioObject->oPosY, gMarioObject->oPosZ, &marioFloor); + find_floor(marioObject->oPosX, marioObject->oPosY, marioObject->oPosZ, &marioFloor); - if (marioFloor == NULL) { - marioFloorType = 0; - } else { + if (marioFloor != NULL) { marioFloorType = marioFloor->type; } diff --git a/src/game/behaviors/boo.inc.c b/src/game/behaviors/boo.inc.c index 0d20125a..a9ea6f7d 100644 --- a/src/game/behaviors/boo.inc.c +++ b/src/game/behaviors/boo.inc.c @@ -24,7 +24,7 @@ static u8 boo_ignore_update(void) { } struct SyncObject* boo_network_init_object(void) { - struct SyncObject* so = network_init_object(o, 4000.0f); + struct SyncObject *so = network_init_object(o, 4000.0f); if (so == NULL) { return NULL; } so->ignore_if_true = boo_ignore_update; network_init_object_field(o, &o->oBooBaseScale); diff --git a/src/game/behaviors/bub.inc.c b/src/game/behaviors/bub.inc.c index aad0a34d..66eb50c9 100644 --- a/src/game/behaviors/bub.inc.c +++ b/src/game/behaviors/bub.inc.c @@ -48,8 +48,8 @@ void bub_act_0(void) { void bub_act_1(void) { struct Object* player = nearest_player_to_object(o); - int distanceToPlayer = dist_between_objects(o, player); - int angleToPlayer = obj_angle_to_object(o, player); + s32 distanceToPlayer = dist_between_objects(o, player); + s32 angleToPlayer = obj_angle_to_object(o, player); f32 dy; if (o->oTimer == 0) { o->oForwardVel = random_float() * 2 + 2; @@ -80,8 +80,8 @@ void bub_act_1(void) { void bub_act_2(void) { struct Object* player = nearest_player_to_object(o); - int distanceToPlayer = dist_between_objects(o, player); - int angleToPlayer = obj_angle_to_object(o, player); + s32 distanceToPlayer = dist_between_objects(o, player); + s32 angleToPlayer = obj_angle_to_object(o, player); f32 dy; if (o->oTimer < 20) { if (o->oInteractStatus & INT_STATUS_INTERACTED) @@ -116,12 +116,14 @@ void (*sCheepCheepActions[])(void) = { bub_act_0, bub_act_1, bub_act_2 }; void bhv_bub_loop(void) { if (!network_sync_object_initialized(o)) { - network_init_object(o, 4000.0f); - network_init_object_field(o, &o->oCheepCheepUnkF4); - network_init_object_field(o, &o->oCheepCheepUnkF8); - network_init_object_field(o, &o->oCheepCheepUnkFC); - network_init_object_field(o, &o->oCheepCheepUnk104); - network_init_object_field(o, &o->oCheepCheepUnk108); + struct SyncObject *so = network_init_object(o, 4000.0f); + if (so) { + network_init_object_field(o, &o->oCheepCheepUnkF4); + network_init_object_field(o, &o->oCheepCheepUnkF8); + network_init_object_field(o, &o->oCheepCheepUnkFC); + network_init_object_field(o, &o->oCheepCheepUnk104); + network_init_object_field(o, &o->oCheepCheepUnk108); + } } struct Object* player = nearest_player_to_object(o); diff --git a/src/game/behaviors/camera_lakitu.inc.c b/src/game/behaviors/camera_lakitu.inc.c index 8073afe7..4645e5c0 100644 --- a/src/game/behaviors/camera_lakitu.inc.c +++ b/src/game/behaviors/camera_lakitu.inc.c @@ -38,20 +38,22 @@ void bhv_camera_lakitu_init(void) { spawn_object_relative_with_scale(CLOUD_BP_LAKITU_CLOUD, 0, 0, 0, 2.0f, o, MODEL_MIST, bhvCloud); } lakituTargetLocalIndex = UNKNOWN_LOCAL_INDEX; - - struct SyncObject* so = network_init_object(o, 4000.0f); - if (so) { - so->ignore_if_true = bhv_camera_lakitu_ignore_if_true; - so->override_ownership = bhv_camera_lakitu_override_ownership; - so->on_received_post = bhv_camera_lakitu_on_received_post; - network_init_object_field(o, &o->oAngleVelPitch); - network_init_object_field(o, &o->oFaceAnglePitch); - network_init_object_field(o, &o->oCameraLakituBlinkTimer); - network_init_object_field(o, &o->oCameraLakituSpeed); - network_init_object_field(o, &o->oCameraLakituCircleRadius); - network_init_object_field(o, &o->oCameraLakituFinishedDialog); - network_init_object_field(o, &o->oCameraLakituUnk104); - network_init_object_field(o, &o->oCameraLakituPitchVel); + + if (!network_sync_object_initialized(o)) { + struct SyncObject *so = network_init_object(o, 4000.0f); + if (so) { + so->ignore_if_true = bhv_camera_lakitu_ignore_if_true; + so->override_ownership = bhv_camera_lakitu_override_ownership; + so->on_received_post = bhv_camera_lakitu_on_received_post; + network_init_object_field(o, &o->oAngleVelPitch); + network_init_object_field(o, &o->oFaceAnglePitch); + network_init_object_field(o, &o->oCameraLakituBlinkTimer); + network_init_object_field(o, &o->oCameraLakituSpeed); + network_init_object_field(o, &o->oCameraLakituCircleRadius); + network_init_object_field(o, &o->oCameraLakituFinishedDialog); + network_init_object_field(o, &o->oCameraLakituUnk104); + network_init_object_field(o, &o->oCameraLakituPitchVel); + } } } @@ -112,8 +114,8 @@ static void camera_lakitu_intro_act_show_dialog(void) { marioState = &gMarioStates[lakituTargetLocalIndex]; } struct Object* player = marioState->marioObj; - int distanceToPlayer = dist_between_objects(o, player); - int angleToPlayer = obj_angle_to_object(o, player); + s32 distanceToPlayer = dist_between_objects(o, player); + s32 angleToPlayer = obj_angle_to_object(o, player); s16 targetMovePitch = 0; s16 targetMoveYaw = 0; diff --git a/src/game/behaviors/cannon.inc.c b/src/game/behaviors/cannon.inc.c index 19f146fd..12bfc5e0 100644 --- a/src/game/behaviors/cannon.inc.c +++ b/src/game/behaviors/cannon.inc.c @@ -19,7 +19,7 @@ void opened_cannon_act_0(void) { cur_obj_enable_rendering(); struct Object* player = nearest_player_to_object(o); - int distanceToPlayer = dist_between_objects(o, player); + s32 distanceToPlayer = dist_between_objects(o, player); if (distanceToPlayer < 500.0f) { //cur_obj_become_tangible(); diff --git a/src/game/behaviors/chain_chomp.inc.c b/src/game/behaviors/chain_chomp.inc.c index 6034d8c8..e7167144 100644 --- a/src/game/behaviors/chain_chomp.inc.c +++ b/src/game/behaviors/chain_chomp.inc.c @@ -28,8 +28,13 @@ static struct ObjectHitbox sChainChompHitbox = { * Update function for chain chomp part / pivot. */ void bhv_chain_chomp_chain_part_update(void) { - if (o->parentObj->behavior != (BehaviorScript*)&bhvChainChomp || o->parentObj->oAction == CHAIN_CHOMP_ACT_UNLOAD_CHAIN) { + if (!network_sync_object_initialized(o)) { + network_init_object(o, SYNC_DISTANCE_ONLY_DEATH); + } + + if (o->parentObj->behavior != (BehaviorScript *)&bhvChainChomp || o->parentObj->oAction == CHAIN_CHOMP_ACT_UNLOAD_CHAIN) { obj_mark_for_deletion(o); + network_send_object(o); } else if (o->oBehParams2ndByte != CHAIN_CHOMP_CHAIN_PART_BP_PIVOT) { struct ChainSegment *segment = &o->parentObj->oChainChompSegments[o->oBehParams2ndByte]; @@ -47,17 +52,14 @@ void bhv_chain_chomp_chain_part_update(void) { * When mario gets close enough, allocate chain segments and spawn their objects. */ static void chain_chomp_act_uninitialized(void) { - struct ChainSegment *segments; - s32 i; - - segments = mem_pool_alloc(gObjectMemoryPool, 5 * sizeof(struct ChainSegment)); + struct ChainSegment *segments = mem_pool_alloc(gObjectMemoryPool, 5 * sizeof(struct ChainSegment)); if (segments != NULL) { // Each segment represents the offset of a chain part to the pivot. // Segment 0 connects the pivot to the chain chomp itself. Segment // 1 connects the pivot to the chain part next to the chain chomp // (chain part 1), etc. o->oChainChompSegments = segments; - for (i = 0; i <= 4; i++) { + for (s32 i = 0; i <= 4; i++) { chain_segment_init(&segments[i]); } @@ -69,7 +71,7 @@ static void chain_chomp_act_uninitialized(void) { != NULL) { // Spawn the non-pivot chain parts, starting from the chain // chomp and moving toward the pivot - for (i = 1; i <= 4; i++) { + for (s32 i = 1; i <= 4; i++) { spawn_object_relative(i, 0, 0, 0, o, MODEL_METALLIC_BALL, bhvChainChompChainPart); } @@ -84,15 +86,7 @@ static void chain_chomp_act_uninitialized(void) { * part as well as from the pivot. */ static void chain_chomp_update_chain_segments(void) { - struct ChainSegment *prevSegment; - struct ChainSegment *segment; - f32 offsetX; - f32 offsetY; - f32 offsetZ; - f32 offset; f32 segmentVelY; - f32 maxTotalOffset; - s32 i; if (o->oVelY < 0.0f) { segmentVelY = o->oVelY; @@ -103,9 +97,9 @@ static void chain_chomp_update_chain_segments(void) { // Segment 0 connects the pivot to the chain chomp itself, and segment i>0 // connects the pivot to chain part i (1 is closest to the chain chomp). - for (i = 1; i <= 4; i++) { - prevSegment = &o->oChainChompSegments[i - 1]; - segment = &o->oChainChompSegments[i]; + for (s32 i = 1; i <= 4; i++) { + struct ChainSegment *prevSegment = &o->oChainChompSegments[i - 1]; + struct ChainSegment *segment = &o->oChainChompSegments[i]; // Apply gravity @@ -116,10 +110,10 @@ static void chain_chomp_update_chain_segments(void) { // Cap distance to previous chain part (so that the tail follows the // chomp) - offsetX = segment->posX - prevSegment->posX; - offsetY = segment->posY - prevSegment->posY; - offsetZ = segment->posZ - prevSegment->posZ; - offset = sqrtf(offsetX * offsetX + offsetY * offsetY + offsetZ * offsetZ); + f32 offsetX = segment->posX - prevSegment->posX; + f32 offsetY = segment->posY - prevSegment->posY; + f32 offsetZ = segment->posZ - prevSegment->posZ; + f32 offset = sqrtf(offsetX * offsetX + offsetY * offsetY + offsetZ * offsetZ); if (offset > o->oChainChompMaxDistBetweenChainParts) { offset = o->oChainChompMaxDistBetweenChainParts / offset; @@ -136,7 +130,7 @@ static void chain_chomp_update_chain_segments(void) { offsetZ += prevSegment->posZ; offset = sqrtf(offsetX * offsetX + offsetY * offsetY + offsetZ * offsetZ); - maxTotalOffset = o->oChainChompMaxDistFromPivotPerChainPart * (5 - i); + f32 maxTotalOffset = o->oChainChompMaxDistFromPivotPerChainPart * (5 - i); if (offset > maxTotalOffset) { offset = maxTotalOffset / offset; offsetX *= offset; @@ -168,9 +162,9 @@ static void chain_chomp_sub_act_turn(void) { chain_chomp_restore_normal_chain_lengths(); obj_move_pitch_approach(0, 0x100); - struct Object* player = nearest_player_to_object(o); - int distanceToPlayer = dist_between_objects(o, player); - int angleToPlayer = obj_angle_to_object(o, player); + struct Object *player = nearest_player_to_object(o); + s32 distanceToPlayer = dist_between_objects(o, player); + s32 angleToPlayer = obj_angle_to_object(o, player); if (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) { cur_obj_rotate_yaw_toward(angleToPlayer, 0x400); @@ -343,8 +337,6 @@ static void chain_chomp_released_end_cutscene(void) { * released. */ static void chain_chomp_act_move(void) { - f32 maxDistToPivot; - // Unload chain if mario is far enough cur_obj_update_floor_and_walls(); @@ -389,7 +381,7 @@ static void chain_chomp_act_move(void) { + o->oChainChompSegments[0].posZ * o->oChainChompSegments[0].posZ); // If the chain is fully stretched - maxDistToPivot = o->oChainChompMaxDistFromPivotPerChainPart * 5; + f32 maxDistToPivot = o->oChainChompMaxDistFromPivotPerChainPart * 5; if (o->oChainChompDistToPivot > maxDistToPivot) { f32 ratio = maxDistToPivot / o->oChainChompDistToPivot; o->oChainChompDistToPivot = maxDistToPivot; @@ -524,9 +516,9 @@ void bhv_wooden_post_update(void) { if (o->oWoodenPostOffsetY != 0.0f) { o->oPosY = o->oHomeY + o->oWoodenPostOffsetY; } else if (!(o->oBehParams & WOODEN_POST_BP_NO_COINS_MASK)) { - struct Object* player = nearest_player_to_object(o); - int distanceToPlayer = dist_between_objects(o, player); - int angleToPlayer = obj_angle_to_object(o, player); + struct Object *player = nearest_player_to_object(o); + s32 distanceToPlayer = dist_between_objects(o, player); + s32 angleToPlayer = obj_angle_to_object(o, player); // Reset the timer once mario is far enough if (distanceToPlayer > 400.0f) { @@ -536,9 +528,9 @@ void bhv_wooden_post_update(void) { // coins o->oWoodenPostTotalMarioAngle += (s16)(angleToPlayer - o->oWoodenPostPrevAngleToMario); if (absi(o->oWoodenPostTotalMarioAngle) > 0x30000 && o->oTimer < 200) { - network_send_object(o); obj_spawn_loot_yellow_coins(o, 5, 20.0f); set_object_respawn_info_bits(o, 1); + network_send_object(o); } } diff --git a/src/game/behaviors/hidden_star.inc.c b/src/game/behaviors/hidden_star.inc.c index b15eb3df..0642e050 100644 --- a/src/game/behaviors/hidden_star.inc.c +++ b/src/game/behaviors/hidden_star.inc.c @@ -1,31 +1,55 @@ // hidden_star.c.inc void bhv_hidden_star_init(void) { - s16 sp36; - struct Object *sp30; - - sp36 = count_objects_with_behavior(bhvHiddenStarTrigger); - if (sp36 == 0) { - sp30 = - spawn_object_abs_with_rot(o, 0, MODEL_STAR, bhvStar, o->oPosX, o->oPosY, o->oPosZ, 0, 0, 0); - if (sp30 != NULL) { sp30->oBehParams = o->oBehParams; } + s16 count = count_objects_with_behavior(bhvHiddenStarTrigger); + if (count == 0) { + struct Object *obj = spawn_object_abs_with_rot(o, 0, MODEL_STAR, bhvStar, o->oPosX, o->oPosY, o->oPosZ, 0, 0, 0); + if (obj != NULL) { obj->oBehParams = o->oBehParams; } o->activeFlags = ACTIVE_FLAG_DEACTIVATED; } - o->oHiddenStarTriggerCounter = 5 - sp36; + o->oHiddenStarTriggerCounter = 5 - count; + + // We haven't interacted with a player yet. + // We also don't sync this as not only is it not required + // but it also is only set for an interaction. + // Therefore this object must already be loaded for it to be set + // and if it wasn't. You couldn't of possibly been the one + // who last interacted to begin with. + o->oHiddenStarLastInteractedObject = NULL; + + if (!network_sync_object_initialized(o)) { + struct SyncObject *so = network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS); + if (so) { + network_init_object_field(o, &o->oAction); + network_init_object_field(o, &o->oHiddenStarTriggerCounter); + network_init_object_field(o, &o->oPosX); + network_init_object_field(o, &o->oPosY); + network_init_object_field(o, &o->oPosZ); + network_init_object_field(o, &o->oTimer); + } + } } void bhv_hidden_star_loop(void) { switch (o->oAction) { case 0: - if (o->oHiddenStarTriggerCounter == 5) + if (o->oHiddenStarTriggerCounter == 5) { o->oAction = 1; + } break; case 1: if (o->oTimer > 2) { - spawn_red_coin_cutscene_star(o->oPosX, o->oPosY, o->oPosZ); - spawn_mist_particles(); + struct Object *obj = spawn_red_coin_cutscene_star(o->oPosX, o->oPosY, o->oPosZ); + if (obj != NULL) { + if (o->oHiddenStarLastInteractedObject == &gMarioStates[0]) { + obj->oStarSpawnExtCutsceneFlags = 1; + } else { + obj->oStarSpawnExtCutsceneFlags = 0; + } + spawn_mist_particles(); + } o->activeFlags = ACTIVE_FLAG_DEACTIVATED; } break; @@ -34,14 +58,20 @@ void bhv_hidden_star_loop(void) { /* TODO: this is likely not a checkpoint but a Secret */ void bhv_hidden_star_trigger_loop(void) { - struct Object *hiddenStar; - if ((o->oInteractStatus & INT_STATUS_INTERACTED) || obj_check_if_collided_with_object(o, gMarioObject) == 1) { - hiddenStar = cur_obj_nearest_object_with_behavior(bhvHiddenStar); + if ((o->oInteractStatus & INT_STATUS_INTERACTED) || obj_check_if_collided_with_object(o, gMarioObjects[0]) == 1) { + struct Object *hiddenStar = cur_obj_nearest_object_with_behavior(bhvHiddenStar); if (hiddenStar != NULL) { hiddenStar->oHiddenStarTriggerCounter++; if (hiddenStar->oHiddenStarTriggerCounter != 5) { spawn_orange_number(hiddenStar->oHiddenStarTriggerCounter, 0, 0, 0); } + + // Set the last person who interacted with a secret to the + // parent so only they get the star cutscene. + struct MarioState *player = nearest_mario_state_to_object(o); + if (player) { + hiddenStar->oHiddenStarLastInteractedObject = player; + } #ifdef VERSION_JP play_sound(SOUND_MENU_STAR_SOUND, gGlobalSoundSource); @@ -63,14 +93,22 @@ void bhv_bowser_course_red_coin_star_loop(void) { gRedCoinsCollected = o->oHiddenStarTriggerCounter; switch (o->oAction) { case 0: - if (o->oHiddenStarTriggerCounter == 8) + if (o->oHiddenStarTriggerCounter == 8) { o->oAction = 1; + } break; case 1: if (o->oTimer > 2) { - spawn_no_exit_star(o->oPosX, o->oPosY, o->oPosZ); - spawn_mist_particles(); + struct Object *obj = spawn_no_exit_star(o->oPosX, o->oPosY, o->oPosZ); + if (obj != NULL) { + if (o->oHiddenStarLastInteractedObject == &gMarioStates[0]) { + obj->oStarSpawnExtCutsceneFlags = 1; + } else { + obj->oStarSpawnExtCutsceneFlags = 0; + } + spawn_mist_particles(); + } o->activeFlags = ACTIVE_FLAG_DEACTIVATED; } break; diff --git a/src/game/behaviors/red_coin.inc.c b/src/game/behaviors/red_coin.inc.c index 40d7d6da..2797e539 100644 --- a/src/game/behaviors/red_coin.inc.c +++ b/src/game/behaviors/red_coin.inc.c @@ -23,23 +23,18 @@ static struct ObjectHitbox sRedCoinHitbox = { * Red coin initialization function. Sets the coin's hitbox and parent object. */ void bhv_red_coin_init(void) { - // This floor and floor height are unused. Perhaps for orange number spawns originally? - struct Surface *dummyFloor; - UNUSED f32 floorHeight = find_floor(o->oPosX, o->oPosY, o->oPosZ, &dummyFloor); - - struct Object *hiddenRedCoinStar; - // Set the red coins to have a parent of the closest red coin star. - hiddenRedCoinStar = cur_obj_nearest_object_with_behavior(bhvHiddenRedCoinStar); - if (hiddenRedCoinStar != NULL) - o->parentObj = hiddenRedCoinStar; - else { + struct Object *hiddenRedCoinStar = cur_obj_nearest_object_with_behavior(bhvHiddenRedCoinStar); + // If it's not a typical red coin star, it's a Bowser one. + if (hiddenRedCoinStar == NULL) { hiddenRedCoinStar = cur_obj_nearest_object_with_behavior(bhvBowserCourseRedCoinStar); - if (hiddenRedCoinStar != NULL) { - o->parentObj = hiddenRedCoinStar; - } else { - o->parentObj = NULL; - } + } + + // If we found a red coin star, It's our parent. + if (hiddenRedCoinStar != NULL) { + o->parentObj = hiddenRedCoinStar; + } else { + o->parentObj = NULL; } obj_set_hitbox(o, &sRedCoinHitbox); @@ -56,6 +51,13 @@ void bhv_red_coin_loop(void) { if (o->parentObj != NULL) { // ...increment the star's counter. o->parentObj->oHiddenStarTriggerCounter++; + + // Set the last person who interacted with a red coin to the + // parent so only they get the star cutscene. + struct MarioState *player = nearest_mario_state_to_object(o); + if (player) { + o->parentObj->oHiddenStarLastInteractedObject = player; + } // For JP version, play an identical sound for all coins. #ifdef VERSION_JP diff --git a/src/game/behaviors/rolling_log.inc.c b/src/game/behaviors/rolling_log.inc.c index 5458578f..121b7982 100644 --- a/src/game/behaviors/rolling_log.inc.c +++ b/src/game/behaviors/rolling_log.inc.c @@ -7,14 +7,18 @@ // a rolling log of another variation. static void bhv_rolling_log_network_init(void) { - network_init_object(o, 4000.0f); - network_init_object_field(o, &o->oAngleVelPitch); - network_init_object_field(o, &o->oFaceAnglePitch); - network_init_object_field(o, &o->oMoveAnglePitch); - network_init_object_field(o, &o->oPitouneUnkF4); - network_init_object_field(o, &o->oPitouneUnkF8); - network_init_object_field(o, &o->oPitouneUnkFC); - network_init_object_field(o, &o->oForwardVel); + if (!network_sync_object_initialized(o)) { + struct SyncObject *so = network_init_object(o, 4000.0f); + if (so) { + network_init_object_field(o, &o->oAngleVelPitch); + network_init_object_field(o, &o->oFaceAnglePitch); + network_init_object_field(o, &o->oMoveAnglePitch); + network_init_object_field(o, &o->oPitouneUnkF4); + network_init_object_field(o, &o->oPitouneUnkF8); + network_init_object_field(o, &o->oPitouneUnkFC); + network_init_object_field(o, &o->oForwardVel); + } + } } void bhv_ttm_rolling_log_init(void) { @@ -143,10 +147,12 @@ void volcano_act_3(void) { void bhv_volcano_trap_loop(void) { if (!network_sync_object_initialized(o)) { - network_init_object(o, 2000.0f); - network_init_object_field(o, &o->oRollingLogUnkF4); - network_init_object_field(o, &o->oAngleVelPitch); - network_init_object_field(o, &o->oFaceAnglePitch); + struct SyncObject *so = network_init_object(o, 2000.0f); + if (so) { + network_init_object_field(o, &o->oRollingLogUnkF4); + network_init_object_field(o, &o->oAngleVelPitch); + network_init_object_field(o, &o->oFaceAnglePitch); + } } switch (o->oAction) { diff --git a/src/game/behaviors/scuttlebug.inc.c b/src/game/behaviors/scuttlebug.inc.c index 7718d49f..318a173a 100644 --- a/src/game/behaviors/scuttlebug.inc.c +++ b/src/game/behaviors/scuttlebug.inc.c @@ -25,21 +25,21 @@ s32 update_angle_from_move_flags(s32 *angle) { void bhv_scuttlebug_loop(void) { if (!network_sync_object_initialized(o)) { - network_init_object(o, 4000.0f); - network_init_object_field(o, &o->oFlags); - network_init_object_field(o, &o->oForwardVel); - network_init_object_field(o, &o->oHomeX); - network_init_object_field(o, &o->oHomeY); - network_init_object_field(o, &o->oHomeZ); - network_init_object_field(o, &o->oInteractStatus); - network_init_object_field(o, &o->oScuttlebugUnkF4); + struct SyncObject *so = network_init_object(o, 4000.0f); + if (so) { + network_init_object_field(o, &o->oFlags); + network_init_object_field(o, &o->oForwardVel); + network_init_object_field(o, &o->oHomeX); + network_init_object_field(o, &o->oHomeY); + network_init_object_field(o, &o->oHomeZ); + network_init_object_field(o, &o->oInteractStatus); + network_init_object_field(o, &o->oScuttlebugUnkF4); + } } - struct Object* player = nearest_player_to_object(o); - int angleToPlayer = obj_angle_to_object(o, player); + struct Object *player = nearest_player_to_object(o); + s32 angleToPlayer = obj_angle_to_object(o, player); - UNUSED s32 unused; - f32 sp18; cur_obj_update_floor_and_walls(); if (o->oSubAction != 0 && cur_obj_set_hitbox_and_die_if_attacked(&sScuttlebugHitbox, SOUND_OBJ_DYING_ENEMY1, @@ -114,13 +114,16 @@ void bhv_scuttlebug_loop(void) { o->oSubAction = 0; break; } - if (o->oForwardVel < 10.0f) + f32 sp18; + if (o->oForwardVel < 10.0f) { sp18 = 1.0f; - else + } else { sp18 = 3.0f; + } cur_obj_init_animation_with_accel_and_sound(0, sp18); - if (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) + if (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) { set_obj_anim_with_accel_and_sound(1, 23, SOUND_OBJ2_SCUTTLEBUG_WALK); + } if (o->parentObj != o) { if (obj_is_hidden(o)) obj_mark_for_deletion(o); @@ -134,31 +137,32 @@ void bhv_scuttlebug_loop(void) { void bhv_scuttlebug_spawn_loop(void) { if (!network_sync_object_initialized(o)) { - network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS); - network_init_object_field(o, &o->oAction); - network_init_object_field(o, &o->oTimer); - network_init_object_field(o, &o->oScuttlebugSpawnerUnkF4); - network_init_object_field(o, &o->oScuttlebugSpawnerUnk88); + struct SyncObject *so = network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS); + if (so) { + network_init_object_field(o, &o->oAction); + network_init_object_field(o, &o->oTimer); + network_init_object_field(o, &o->oScuttlebugSpawnerUnkF4); + network_init_object_field(o, &o->oScuttlebugSpawnerUnk88); + } } struct MarioState* marioState = nearest_mario_state_to_object(o); if (marioState->playerIndex != 0) { return; } struct Object* player = marioState->marioObj; - int distanceToPlayer = dist_between_objects(o, player); + s32 distanceToPlayer = dist_between_objects(o, player); - struct Object *scuttlebug; if (o->oAction == 0) { if (o->oTimer > 30 && 500.0f < distanceToPlayer && distanceToPlayer < 1500.0f) { cur_obj_play_sound_2(SOUND_OBJ2_SCUTTLEBUG_ALERT); - scuttlebug = spawn_object(o, MODEL_SCUTTLEBUG, bhvScuttlebug); + struct Object *scuttlebug = spawn_object(o, MODEL_SCUTTLEBUG, bhvScuttlebug); if (scuttlebug != NULL) { scuttlebug->oScuttlebugUnkF4 = o->oScuttlebugSpawnerUnkF4; scuttlebug->oForwardVel = 30.0f; scuttlebug->oVelY = 80.0f; network_set_sync_id(scuttlebug); - struct Object* spawn_objects[] = { scuttlebug }; + struct Object *spawn_objects[] = { scuttlebug }; u32 models[] = { MODEL_SCUTTLEBUG }; network_send_spawn_objects(spawn_objects, models, 1); } diff --git a/src/game/behaviors/seesaw_platform.inc.c b/src/game/behaviors/seesaw_platform.inc.c index 56d851b2..978b6464 100644 --- a/src/game/behaviors/seesaw_platform.inc.c +++ b/src/game/behaviors/seesaw_platform.inc.c @@ -24,16 +24,19 @@ void bhv_seesaw_platform_init(void) { o->oCollisionDistance = 2000.0f; } - network_init_object(o, 1000.0f); - network_init_object_field(o, &o->oSeesawPlatformPitchVel); - network_init_object_field(o, &o->oFaceAnglePitch); + if (!network_sync_object_initialized(o)) { + struct SyncObject *so = network_init_object(o, 1000.0f); + if (so) { + network_init_object_field(o, &o->oSeesawPlatformPitchVel); + network_init_object_field(o, &o->oFaceAnglePitch); + } + } } /** * Update function for bhvSeesawPlatform. */ void bhv_seesaw_platform_update(void) { - UNUSED s32 startPitch = o->oFaceAnglePitch; o->oFaceAnglePitch += (s32) o->oSeesawPlatformPitchVel; if (absf(o->oSeesawPlatformPitchVel) > 10.0f) { @@ -44,7 +47,7 @@ void bhv_seesaw_platform_update(void) { f32 y = 0; f32 z = 0; u8 playersTouched = 0; - for (int i = 0; i < MAX_PLAYERS; i++) { + for (s32 i = 0; i < MAX_PLAYERS; i++) { if (!is_player_active(&gMarioStates[i])) { continue; } if (gMarioStates[i].marioObj->platform == o) { x += gMarioStates[i].marioObj->oPosX; @@ -59,12 +62,11 @@ void bhv_seesaw_platform_update(void) { y /= (f32)playersTouched; z /= (f32)playersTouched; - int distanceToPlayer = dist_between_object_and_point(o, x, y, z); - int angleToPlayer = obj_angle_to_point(o, x, z); + s32 distanceToPlayer = dist_between_object_and_point(o, x, y, z); + s32 angleToPlayer = obj_angle_to_point(o, x, z); // Rotate toward mario f32 rotation = distanceToPlayer * coss(angleToPlayer - o->oMoveAngleYaw); - UNUSED s32 unused; // Deceleration is faster than acceleration if (o->oSeesawPlatformPitchVel * rotation < 0) { diff --git a/src/game/behaviors/sl_snowman_wind.inc.c b/src/game/behaviors/sl_snowman_wind.inc.c index 6341cf3f..b388d797 100644 --- a/src/game/behaviors/sl_snowman_wind.inc.c +++ b/src/game/behaviors/sl_snowman_wind.inc.c @@ -4,18 +4,19 @@ u8 bhv_sl_snowman_wind_loop_continue_dialog(void) { return o->oSubAction == SL_S void bhv_sl_snowman_wind_loop(void) { if (!network_sync_object_initialized(o)) { - network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS); - network_init_object_field(o, &o->oAction); - network_init_object_field(o, &o->oPrevAction); - network_init_object_field(o, &o->oTimer); - network_init_object_field(o, &o->oSubAction); + struct SyncObject *so = network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS); + if (so) { + network_init_object_field(o, &o->oAction); + network_init_object_field(o, &o->oPrevAction); + network_init_object_field(o, &o->oTimer); + network_init_object_field(o, &o->oSubAction); + } } - struct Object* player = nearest_player_to_object(o); - int distanceToPlayer = dist_between_objects(o, player); - int angleToPlayer = obj_angle_to_object(o, player); + struct Object *player = nearest_player_to_object(o); + s32 distanceToPlayer = dist_between_objects(o, player); + s32 angleToPlayer = obj_angle_to_object(o, player); - UNUSED s32 unusedVar = 0; s16 marioAngleFromWindSource; Vec3f tempPos; diff --git a/src/game/behaviors/sl_walking_penguin.inc.c b/src/game/behaviors/sl_walking_penguin.inc.c index c08bd6f5..2610b41f 100644 --- a/src/game/behaviors/sl_walking_penguin.inc.c +++ b/src/game/behaviors/sl_walking_penguin.inc.c @@ -38,14 +38,16 @@ void bhv_sl_walking_penguin_loop(void) { f32 perpendicularOffset = 100.0f; if (!network_sync_object_initialized(o)) { - network_init_object(o, 4000.0f); - network_init_object_field(o, &o->oTimer); - network_init_object_field(o, &o->oAction); - network_init_object_field(o, &o->oPrevAction); - network_init_object_field(o, &o->oSLWalkingPenguinCurStep); - network_init_object_field(o, &o->oSLWalkingPenguinCurStepTimer); - network_init_object_field(o, &o->oSLWalkingPenguinWindCollisionXPos); - network_init_object_field(o, &o->oSLWalkingPenguinWindCollisionZPos); + struct SyncObject *so = network_init_object(o, 4000.0f); + if (so) { + network_init_object_field(o, &o->oTimer); + network_init_object_field(o, &o->oAction); + network_init_object_field(o, &o->oPrevAction); + network_init_object_field(o, &o->oSLWalkingPenguinCurStep); + network_init_object_field(o, &o->oSLWalkingPenguinCurStepTimer); + network_init_object_field(o, &o->oSLWalkingPenguinWindCollisionXPos); + network_init_object_field(o, &o->oSLWalkingPenguinWindCollisionZPos); + } } o->oAngleVelYaw = 0; diff --git a/src/game/behaviors/sliding_platform_2.inc.c b/src/game/behaviors/sliding_platform_2.inc.c index ab7f2bd1..32414f79 100644 --- a/src/game/behaviors/sliding_platform_2.inc.c +++ b/src/game/behaviors/sliding_platform_2.inc.c @@ -36,7 +36,6 @@ void bhv_sliding_plat_2_init(void) { void bhv_sliding_plat_2_loop(void) { if (!network_sync_object_initialized(o)) { struct SyncObject* so = network_init_object(o, 4000.0f); - if (so) { so->minUpdateRate = 5.0f; network_init_object_field(o, &o->oBackAndForthPlatformDirection); diff --git a/src/game/behaviors/spawn_star.inc.c b/src/game/behaviors/spawn_star.inc.c index e15e48be..fee946aa 100644 --- a/src/game/behaviors/spawn_star.inc.c +++ b/src/game/behaviors/spawn_star.inc.c @@ -140,7 +140,7 @@ static u8 spawn_star_deduplication(u32* array, u8* count, u32 behParams) { return TRUE; } -struct Object* spawn_default_star(f32 x, f32 y, f32 z) { +struct Object *spawn_default_star(f32 x, f32 y, f32 z) { if (sCurrPlayMode != PLAY_MODE_NORMAL && sCurrPlayMode != PLAY_MODE_PAUSED) { return NULL; } if (o == NULL) { return NULL; } u32 behParams = o->oBehParams; @@ -154,12 +154,12 @@ struct Object* spawn_default_star(f32 x, f32 y, f32 z) { star = spawn_star(star, x, y, z); if (star != NULL) { star->oBehParams2ndByte = 0; - network_send_spawn_star(star, 0, x, y, z, behParams); + network_send_spawn_star(star, 0, x, y, z, behParams, UNKNOWN_GLOBAL_INDEX); } return star; } -struct Object* spawn_red_coin_cutscene_star(f32 x, f32 y, f32 z) { +struct Object *spawn_red_coin_cutscene_star(f32 x, f32 y, f32 z) { u32 behParams = o->oBehParams; // de-duplication checking @@ -171,12 +171,12 @@ struct Object* spawn_red_coin_cutscene_star(f32 x, f32 y, f32 z) { star = spawn_star(star, x, y, z); if (star != NULL) { star->oBehParams2ndByte = 1; - network_send_spawn_star(star, 1, x, y, z, behParams); + network_send_spawn_star(star, 1, x, y, z, behParams, UNKNOWN_GLOBAL_INDEX); } return star; } -struct Object* spawn_no_exit_star(f32 x, f32 y, f32 z) { +struct Object *spawn_no_exit_star(f32 x, f32 y, f32 z) { u32 behParams = o->oBehParams; // de-duplication checking @@ -189,14 +189,45 @@ struct Object* spawn_no_exit_star(f32 x, f32 y, f32 z) { if (star != NULL) { star->oBehParams2ndByte = 1; star->oInteractionSubtype |= INT_SUBTYPE_NO_EXIT; - network_send_spawn_star(star, 2, x, y, z, behParams); + network_send_spawn_star(star, 2, x, y, z, behParams, UNKNOWN_GLOBAL_INDEX); + } + return star; +} + +/** + * A special star spawning routine just for a networked stars. + * These stars require the global index for a network player for proper + * cutscene functionality. + */ +struct Object *spawn_networked_default_star(f32 x, f32 y, f32 z, u8 networkPlayerIndex) { + if (sCurrPlayMode != PLAY_MODE_NORMAL && sCurrPlayMode != PLAY_MODE_PAUSED) { return NULL; } + if (o == NULL) { return NULL; } + u32 behParams = o->oBehParams; + + // de-duplication checking + if (spawn_star_deduplication(gSpawnedStarDefault, &gSpawnedStarDefaultCount, behParams)) { + return NULL; + } + + struct Object *star = NULL; + star = spawn_star(star, x, y, z); + if (star != NULL) { + star->oBehParams2ndByte = 0; + //printf("spawn_networked_default_star: Network Player Index is %i, Our Global Index is %i.\n", networkPlayerIndex, gNetworkPlayers[0].globalIndex); + if (networkPlayerIndex == gNetworkPlayers[0].globalIndex) { + star->oStarSpawnExtCutsceneFlags = 1; + } else { + star->oStarSpawnExtCutsceneFlags = 0; + } + network_send_spawn_star(star, 0, x, y, z, behParams, networkPlayerIndex); } return star; } void bhv_hidden_red_coin_star_init(void) { - if (gCurrCourseNum != COURSE_JRB) + if (gCurrCourseNum != COURSE_JRB) { spawn_object(o, MODEL_TRANSPARENT_STAR, bhvRedCoinStarMarker); + } s16 redCoins = count_objects_with_behavior(bhvRedCoin); if (redCoins == 0) { @@ -206,20 +237,48 @@ void bhv_hidden_red_coin_star_init(void) { } o->oHiddenStarTriggerCounter = 8 - redCoins; + + // We haven't interacted with a player yet. + // We also don't sync this as not only is it not required + // but it also is only set for an interaction. + // Therefore this object must already be loaded for it to be set + // and if it wasn't. You couldn't of possibly been the one + // who last interacted to begin with. + o->oHiddenStarLastInteractedObject = NULL; + + if (!network_sync_object_initialized(o)) { + struct SyncObject *so = network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS); + if (so) { + network_init_object_field(o, &o->oAction); + network_init_object_field(o, &o->oHiddenStarTriggerCounter); + network_init_object_field(o, &o->oPosX); + network_init_object_field(o, &o->oPosY); + network_init_object_field(o, &o->oPosZ); + network_init_object_field(o, &o->oTimer); + } + } } void bhv_hidden_red_coin_star_loop(void) { gRedCoinsCollected = o->oHiddenStarTriggerCounter; switch (o->oAction) { case 0: - if (o->oHiddenStarTriggerCounter == 8) + if (o->oHiddenStarTriggerCounter == 8) { o->oAction = 1; + } break; case 1: if (o->oTimer > 2) { - spawn_red_coin_cutscene_star(o->oPosX, o->oPosY, o->oPosZ); - spawn_mist_particles(); + struct Object *obj = spawn_red_coin_cutscene_star(o->oPosX, o->oPosY, o->oPosZ); + if (obj != NULL) { + if (o->oHiddenStarLastInteractedObject == &gMarioStates[0]) { + obj->oStarSpawnExtCutsceneFlags = 1; + } else { + obj->oStarSpawnExtCutsceneFlags = 0; + } + spawn_mist_particles(); + } o->activeFlags = ACTIVE_FLAG_DEACTIVATED; } break; diff --git a/src/game/behaviors/treasure_chest.inc.c b/src/game/behaviors/treasure_chest.inc.c index 18dfae41..227a2838 100644 --- a/src/game/behaviors/treasure_chest.inc.c +++ b/src/game/behaviors/treasure_chest.inc.c @@ -1,5 +1,7 @@ // treasure_chest.c.inc +#include "pc/network/network_player.h" + /** * Hitbox for treasure chest bottom. */ @@ -15,8 +17,9 @@ static struct ObjectHitbox sTreasureChestBottomHitbox = { /* hurtboxHeight: */ 310, }; + void bhv_treasure_chest_top_loop(void) { - struct Object* sp34 = o->parentObj->parentObj; + struct Object *rootParent = o->parentObj->parentObj; switch (o->oAction) { case 0: @@ -27,7 +30,7 @@ void bhv_treasure_chest_top_loop(void) { case 1: if (o->oTimer == 0) { - if (sp34->oTreasureChestUnkFC == 0) { + if (rootParent->oTreasureChestIsAboveWater == 0) { spawn_object_relative(0, 0, -80, 120, o, MODEL_BUBBLE, bhvWaterAirBubble); play_sound(SOUND_GENERAL_CLAM_SHELL1, o->header.gfx.cameraToObject); } else { @@ -39,8 +42,9 @@ void bhv_treasure_chest_top_loop(void) { if (o->oFaceAnglePitch < -0x4000) { o->oFaceAnglePitch = -0x4000; o->oAction++; - if (o->parentObj->oBehParams2ndByte != 4) + if (o->parentObj->oBehParams2ndByte != 4) { spawn_orange_number(o->parentObj->oBehParams2ndByte, 0, -40, 0); + } } break; @@ -69,41 +73,40 @@ void bhv_treasure_chest_bottom_loop(void) { switch (o->parentObj->oTreasureChestSound) { case 1: play_sound(SOUND_GENERAL2_RIGHT_ANSWER, gGlobalSoundSource); break; case 2: play_sound(SOUND_MENU_CAMERA_BUZZ, gGlobalSoundSource); break; - case 3: play_puzzle_jingle(); fade_volume_scale(0, 127, 1000); break; - case 4: play_puzzle_jingle(); break; + case 3: play_puzzle_jingle(); fade_volume_scale(0, 127, 1000); break; + case 4: play_puzzle_jingle(); break; } o->parentObj->oTreasureChestSound = 0; } - struct Object* player = nearest_player_to_object(o); + struct MarioState *player = nearest_mario_state_to_object(o); switch (o->oAction) { case 0: - if (network_owns_object(o->parentObj) && obj_check_if_facing_toward_angle(o->oMoveAngleYaw, player->header.gfx.angle[1] + 0x8000, 0x3000)) { + if (player && network_owns_object(o->parentObj) && obj_check_if_facing_toward_angle(o->oMoveAngleYaw, player->marioObj->header.gfx.angle[1] + 0x8000, 0x3000)) { if (is_point_within_radius_of_mario(o->oPosX, o->oPosY, o->oPosZ, 150)) { - if (!o->parentObj->oTreasureChestUnkF8) { - if (o->parentObj->oTreasureChestUnkF4 == o->oBehParams2ndByte) { + if (!o->parentObj->oTreasureChestIsLastInteractionIncorrect) { + if (o->parentObj->oTreasureChestCurrentAnswer == o->oBehParams2ndByte) { play_sound(SOUND_GENERAL2_RIGHT_ANSWER, gGlobalSoundSource); - o->parentObj->oTreasureChestUnkF4++; + o->parentObj->oTreasureChestCurrentAnswer++; o->oAction = 1; o->parentObj->oTreasureChestSound = 1; - network_send_object(o->parentObj); - o->parentObj->oTreasureChestSound = 0; } else { - o->parentObj->oTreasureChestUnkF4 = 1; - o->parentObj->oTreasureChestUnkF8 = 1; + o->parentObj->oTreasureChestCurrentAnswer = 1; + o->parentObj->oTreasureChestIsLastInteractionIncorrect = 1; o->oAction = 2; cur_obj_become_tangible(); play_sound(SOUND_MENU_CAMERA_BUZZ, gGlobalSoundSource); o->parentObj->oTreasureChestSound = 2; - network_send_object(o->parentObj); - o->parentObj->oTreasureChestSound = 0; } + o->parentObj->oTreasureChestLastNetworkPlayerIndex = gNetworkPlayers[player->playerIndex].globalIndex; + network_send_object(o->parentObj); + o->parentObj->oTreasureChestSound = 0; } } } break; case 1: - if (o->parentObj->oTreasureChestUnkF8 == 1) { + if (o->parentObj->oTreasureChestIsLastInteractionIncorrect == 1) { o->oAction = 0; } break; @@ -111,7 +114,7 @@ void bhv_treasure_chest_bottom_loop(void) { case 2: cur_obj_become_intangible(); if (!is_point_within_radius_of_mario(o->oPosX, o->oPosY, o->oPosZ, 500)) { - o->parentObj->oTreasureChestUnkF8 = 0; + o->parentObj->oTreasureChestIsLastInteractionIncorrect = 0; o->oAction = 0; } } @@ -120,44 +123,56 @@ void bhv_treasure_chest_bottom_loop(void) { o->oInteractStatus = 0; } -struct Object* spawn_treasure_chest(s8 sp3B, s32 sp3C, s32 sp40, s32 sp44, s16 sp4A) { - struct Object* sp34; - sp34 = spawn_object_abs_with_rot(o, 0, MODEL_TREASURE_CHEST_BASE, bhvTreasureChestBottom, sp3C, - sp40, sp44, 0, sp4A, 0); - if (sp34 != NULL) { sp34->oBehParams2ndByte = sp3B; } - return sp34; +struct Object* spawn_treasure_chest(s8 index, s32 x, s32 y, s32 z, s16 r) { + struct Object *obj = spawn_object_abs_with_rot(o, 0, MODEL_TREASURE_CHEST_BASE, bhvTreasureChestBottom, x, y, z, 0, r, 0); + if (obj != NULL) { obj->oBehParams2ndByte = index; } + return obj; } void bhv_treasure_chest_ship_init(void) { - struct Object* chests[4] = { 0 }; + struct Object *chests[4] = { 0 }; chests[0] = spawn_treasure_chest(1, 400, -350, -2700, 0); chests[1] = spawn_treasure_chest(2, 650, -350, -940, -0x6001); chests[2] = spawn_treasure_chest(3, -550, -350, -770, 0x5FFF); chests[3] = spawn_treasure_chest(4, 100, -350, -1700, 0); - o->oTreasureChestUnkF4 = 1; - o->oTreasureChestUnkFC = 0; + + o->oTreasureChestCurrentAnswer = 1; + o->oTreasureChestIsAboveWater = 0; + + // We haven't interacted with a player yet. + // We also don't sync this as not only is it not required + // but it also is only set for an interaction. + // Therefore this object must already be loaded for it to be set + // and if it wasn't. You couldn't of possibly been the one + // who last interacted to begin with. + o->oTreasureChestLastNetworkPlayerIndex = UNKNOWN_GLOBAL_INDEX; - network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS); - network_init_object_field(o, &o->oAction); - network_init_object_field(o, &o->oPrevAction); - network_init_object_field(o, &o->oTimer); - network_init_object_field(o, &o->oTreasureChestUnkF4); - network_init_object_field(o, &o->oTreasureChestUnkF8); - network_init_object_field(o, &o->oTreasureChestUnkFC); - network_init_object_field(o, &o->oTreasureChestSound); - for (int i = 0; i < 4; i++) { - struct Object* chest = chests[i]; - network_init_object_field(o, &chest->oAction); - network_init_object_field(o, &chest->oPrevAction); - network_init_object_field(o, &chest->oTimer); - network_init_object_field(o, &chest->oIntangibleTimer); + if (!network_sync_object_initialized(o)) { + struct SyncObject *so = network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS); + if (so) { + network_init_object_field(o, &o->oAction); + network_init_object_field(o, &o->oPrevAction); + network_init_object_field(o, &o->oTimer); + network_init_object_field(o, &o->oTreasureChestCurrentAnswer); + network_init_object_field(o, &o->oTreasureChestIsLastInteractionIncorrect); + network_init_object_field(o, &o->oTreasureChestIsAboveWater); + network_init_object_field(o, &o->oTreasureChestSound); + network_init_object_field(o, &o->oTreasureChestLastNetworkPlayerIndex); + for (s32 i = 0; i < 4; i++) { + struct Object *chest = chests[i]; + network_init_object_field(o, &chest->oAction); + network_init_object_field(o, &chest->oPrevAction); + network_init_object_field(o, &chest->oTimer); + network_init_object_field(o, &chest->oIntangibleTimer); + } + } } } void bhv_treasure_chest_ship_loop(void) { switch (o->oAction) { case 0: - if (network_owns_object(o) && o->oTreasureChestUnkF4 == 5) { + if (network_owns_object(o) && o->oTreasureChestCurrentAnswer == 5) { play_puzzle_jingle(); fade_volume_scale(0, 127, 1000); o->oAction = 1; @@ -185,35 +200,49 @@ void bhv_treasure_chest_ship_loop(void) { } void bhv_treasure_chest_jrb_init(void) { - struct Object* chests[4] = { 0 }; + struct Object *chests[4] = { 0 }; chests[0] = spawn_treasure_chest(1, -1700, -2812, -1150, 0x7FFF); chests[1] = spawn_treasure_chest(2, -1150, -2812, -1550, 0x7FFF); chests[2] = spawn_treasure_chest(3, -2400, -2812, -1800, 0x7FFF); chests[3] = spawn_treasure_chest(4, -1800, -2812, -2100, 0x7FFF); - o->oTreasureChestUnkF4 = 1; - o->oTreasureChestUnkFC = 1; + + o->oTreasureChestCurrentAnswer = 1; + o->oTreasureChestIsAboveWater = 1; + + // We haven't interacted with a player yet. + // We also don't sync this as not only is it not required + // but it also is only set for an interaction. + // Therefore this object must already be loaded for it to be set + // and if it wasn't. You couldn't of possibly been the one + // who last interacted to begin with. + o->oTreasureChestLastNetworkPlayerIndex = UNKNOWN_GLOBAL_INDEX; - network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS); - network_init_object_field(o, &o->oAction); - network_init_object_field(o, &o->oPrevAction); - network_init_object_field(o, &o->oTimer); - network_init_object_field(o, &o->oTreasureChestUnkF4); - network_init_object_field(o, &o->oTreasureChestUnkF8); - network_init_object_field(o, &o->oTreasureChestUnkFC); - network_init_object_field(o, &o->oTreasureChestSound); - for (int i = 0; i < 4; i++) { - struct Object* chest = chests[i]; - network_init_object_field(o, &chest->oAction); - network_init_object_field(o, &chest->oPrevAction); - network_init_object_field(o, &chest->oTimer); - network_init_object_field(o, &chest->oIntangibleTimer); + if (!network_sync_object_initialized(o)) { + struct SyncObject *so = network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS); + if (so) { + network_init_object_field(o, &o->oAction); + network_init_object_field(o, &o->oPrevAction); + network_init_object_field(o, &o->oTimer); + network_init_object_field(o, &o->oTreasureChestCurrentAnswer); + network_init_object_field(o, &o->oTreasureChestIsLastInteractionIncorrect); + network_init_object_field(o, &o->oTreasureChestIsAboveWater); + network_init_object_field(o, &o->oTreasureChestSound); + network_init_object_field(o, &o->oTreasureChestLastNetworkPlayerIndex); + for (s32 i = 0; i < 4; i++) { + struct Object *chest = chests[i]; + network_init_object_field(o, &chest->oAction); + network_init_object_field(o, &chest->oPrevAction); + network_init_object_field(o, &chest->oTimer); + network_init_object_field(o, &chest->oIntangibleTimer); + } + } } } void bhv_treasure_chest_jrb_loop(void) { switch (o->oAction) { case 0: - if (network_owns_object(o) && o->oTreasureChestUnkF4 == 5) { + if (network_owns_object(o) && o->oTreasureChestCurrentAnswer == 5) { play_puzzle_jingle(); o->oAction = 1; o->oTreasureChestSound = 4; @@ -225,7 +254,7 @@ void bhv_treasure_chest_jrb_loop(void) { case 1: if (o->oTimer == 60) { spawn_mist_particles(); - spawn_default_star(-1800.0f, -2500.0f, -1700.0f); + spawn_networked_default_star(-1800.0f, -2500.0f, -1700.0f, o->oTreasureChestLastNetworkPlayerIndex); o->oAction = 2; } break; @@ -236,36 +265,49 @@ void bhv_treasure_chest_jrb_loop(void) { } void bhv_treasure_chest_init(void) { - struct Object* chests[4] = { 0 }; + struct Object *chests[4] = { 0 }; chests[0] = spawn_treasure_chest(1, -4500, -5119, 1300, -0x6001); chests[1] = spawn_treasure_chest(2, -1800, -5119, 1050, 0x1FFF); chests[2] = spawn_treasure_chest(3, -4500, -5119, -1100, 9102); chests[3] = spawn_treasure_chest(4, -2400, -4607, 125, 16019); - o->oTreasureChestUnkF4 = 1; - o->oTreasureChestUnkFC = 0; + o->oTreasureChestCurrentAnswer = 1; + o->oTreasureChestIsAboveWater = 0; + + // We haven't interacted with a player yet. + // We also don't sync this as not only is it not required + // but it also is only set for an interaction. + // Therefore this object must already be loaded for it to be set + // and if it wasn't. You couldn't of possibly been the one + // who last interacted to begin with. + o->oTreasureChestLastNetworkPlayerIndex = UNKNOWN_GLOBAL_INDEX; - network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS); - network_init_object_field(o, &o->oAction); - network_init_object_field(o, &o->oPrevAction); - network_init_object_field(o, &o->oTimer); - network_init_object_field(o, &o->oTreasureChestUnkF4); - network_init_object_field(o, &o->oTreasureChestUnkF8); - network_init_object_field(o, &o->oTreasureChestUnkFC); - network_init_object_field(o, &o->oTreasureChestSound); - for (int i = 0; i < 4; i++) { - struct Object* chest = chests[i]; - network_init_object_field(o, &chest->oAction); - network_init_object_field(o, &chest->oPrevAction); - network_init_object_field(o, &chest->oTimer); - network_init_object_field(o, &chest->oIntangibleTimer); + if (!network_sync_object_initialized(o)) { + struct SyncObject *so = network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS); + if (so) { + network_init_object_field(o, &o->oAction); + network_init_object_field(o, &o->oPrevAction); + network_init_object_field(o, &o->oTimer); + network_init_object_field(o, &o->oTreasureChestCurrentAnswer); + network_init_object_field(o, &o->oTreasureChestIsLastInteractionIncorrect); + network_init_object_field(o, &o->oTreasureChestIsAboveWater); + network_init_object_field(o, &o->oTreasureChestSound); + network_init_object_field(o, &o->oTreasureChestLastNetworkPlayerIndex); + for (s32 i = 0; i < 4; i++) { + struct Object *chest = chests[i]; + network_init_object_field(o, &chest->oAction); + network_init_object_field(o, &chest->oPrevAction); + network_init_object_field(o, &chest->oTimer); + network_init_object_field(o, &chest->oIntangibleTimer); + } + } } } void bhv_treasure_chest_loop(void) { switch (o->oAction) { case 0: - if (network_owns_object(o) && o->oTreasureChestUnkF4 == 5) { + if (network_owns_object(o) && o->oTreasureChestCurrentAnswer == 5) { play_puzzle_jingle(); o->oAction = 1; o->oTreasureChestSound = 4; @@ -277,7 +319,7 @@ void bhv_treasure_chest_loop(void) { case 1: if (o->oTimer == 60) { spawn_mist_particles(); - spawn_default_star(-1900.0f, -4000.0f, -1400.0f); + spawn_networked_default_star(-1900.0f, -4000.0f, -1400.0f, o->oTreasureChestLastNetworkPlayerIndex); o->oAction = 2; } break; diff --git a/src/pc/lua/smlua_cobject.c b/src/pc/lua/smlua_cobject.c index c3e87ae2..25620d0b 100644 --- a/src/pc/lua/smlua_cobject.c +++ b/src/pc/lua/smlua_cobject.c @@ -380,6 +380,11 @@ static bool smlua_is_valid_object_field(struct Object* obj, struct LuaObjectFiel case id_bhvHauntedChair: if (!smlua_field_valid(data, LOT_NONE, offsetof(struct Object, oHauntedChairUnk100))) { return false; } break; + case id_bhvHiddenStar: + case id_bhvHiddenRedCoinStar: + case id_bhvBowserCourseRedCoinStar: + if (!smlua_field_valid(data, LOT_NONE, offsetof(struct Object, oHiddenStarLastInteractedObject ))) { return false; } + break; case id_bhvBreakableBox: case id_bhvHiddenObject: if (!smlua_field_valid(data, LOT_OBJECT, offsetof(struct Object, oHiddenObjectUnkF4))) { return false; } diff --git a/src/pc/lua/smlua_cobject_autogen.c b/src/pc/lua/smlua_cobject_autogen.c index ba4cea18..f396510b 100644 --- a/src/pc/lua/smlua_cobject_autogen.c +++ b/src/pc/lua/smlua_cobject_autogen.c @@ -678,7 +678,7 @@ static struct LuaObjectField sNetworkPlayerFields[LUA_NETWORK_PLAYER_FIELD_COUNT { "type", LVT_U8, offsetof(struct NetworkPlayer, type), true, LOT_NONE }, }; -#define LUA_OBJECT_FIELD_COUNT 750 +#define LUA_OBJECT_FIELD_COUNT 751 static struct LuaObjectField sObjectFields[LUA_OBJECT_FIELD_COUNT] = { { "activeFlags", LVT_S16, offsetof(struct Object, activeFlags), false, LOT_NONE }, { "areaTimer", LVT_U32, offsetof(struct Object, areaTimer), false, LOT_NONE }, @@ -999,6 +999,7 @@ static struct LuaObjectField sObjectFields[LUA_OBJECT_FIELD_COUNT] = { { "oHeldState", LVT_U32, offsetof(struct Object, oHeldState), false, LOT_NONE }, { "oHiddenBlueCoinSwitch", LVT_COBJECT_P, offsetof(struct Object, oHiddenBlueCoinSwitch), false, LOT_OBJECT }, { "oHiddenObjectUnkF4", LVT_COBJECT_P, offsetof(struct Object, oHiddenObjectUnkF4), false, LOT_OBJECT }, +// { "oHiddenStarLastInteractedObject", LVT_???, offsetof(struct Object, oHiddenStarLastInteractedObject), false, LOT_??? }, <--- UNIMPLEMENTED { "oHiddenStarTriggerCounter", LVT_S32, offsetof(struct Object, oHiddenStarTriggerCounter), false, LOT_NONE }, { "oHomeX", LVT_F32, offsetof(struct Object, oHomeX), false, LOT_NONE }, { "oHomeY", LVT_F32, offsetof(struct Object, oHomeY), false, LOT_NONE }, @@ -1332,10 +1333,11 @@ static struct LuaObjectField sObjectFields[LUA_OBJECT_FIELD_COUNT] = { { "oToadMessageState", LVT_S32, offsetof(struct Object, oToadMessageState), false, LOT_NONE }, // { "oToxBoxMovementPattern", LVT_???, offsetof(struct Object, oToxBoxMovementPattern), false, LOT_??? }, <--- UNIMPLEMENTED { "oToxBoxMovementStep", LVT_S32, offsetof(struct Object, oToxBoxMovementStep), false, LOT_NONE }, + { "oTreasureChestCurrentAnswer", LVT_S32, offsetof(struct Object, oTreasureChestCurrentAnswer), false, LOT_NONE }, + { "oTreasureChestIsAboveWater", LVT_S32, offsetof(struct Object, oTreasureChestIsAboveWater), false, LOT_NONE }, + { "oTreasureChestIsLastInteractionIncorrect", LVT_S32, offsetof(struct Object, oTreasureChestIsLastInteractionIncorrect), false, LOT_NONE }, + { "oTreasureChestLastNetworkPlayerIndex", LVT_S16, offsetof(struct Object, oTreasureChestLastNetworkPlayerIndex), false, LOT_NONE }, { "oTreasureChestSound", LVT_S32, offsetof(struct Object, oTreasureChestSound), false, LOT_NONE }, - { "oTreasureChestUnkF4", LVT_S32, offsetof(struct Object, oTreasureChestUnkF4), false, LOT_NONE }, - { "oTreasureChestUnkF8", LVT_S32, offsetof(struct Object, oTreasureChestUnkF8), false, LOT_NONE }, - { "oTreasureChestUnkFC", LVT_S32, offsetof(struct Object, oTreasureChestUnkFC), false, LOT_NONE }, { "oTreeSnowOrLeafUnkF4", LVT_S32, offsetof(struct Object, oTreeSnowOrLeafUnkF4), false, LOT_NONE }, { "oTreeSnowOrLeafUnkF8", LVT_S32, offsetof(struct Object, oTreeSnowOrLeafUnkF8), false, LOT_NONE }, { "oTreeSnowOrLeafUnkFC", LVT_S32, offsetof(struct Object, oTreeSnowOrLeafUnkFC), false, LOT_NONE }, diff --git a/src/pc/network/packets/packet.h b/src/pc/network/packets/packet.h index ae9ee474..dd7afb69 100644 --- a/src/pc/network/packets/packet.h +++ b/src/pc/network/packets/packet.h @@ -187,7 +187,7 @@ void network_send_spawn_objects_to(u8 sendToLocalIndex, struct Object* objects[] void network_receive_spawn_objects(struct Packet* p); // packet_spawn_star.c -void network_send_spawn_star(struct Object* o, u8 starType, f32 x, f32 y, f32 z, u32 behParams); +void network_send_spawn_star(struct Object* o, u8 starType, f32 x, f32 y, f32 z, u32 behParams, u8 networkPlayerIndex); void network_receive_spawn_star(struct Packet* p); void network_send_spawn_star_nle(struct Object* o, u32 params); void network_receive_spawn_star_nle(struct Packet* p); diff --git a/src/pc/network/packets/packet_spawn_star.c b/src/pc/network/packets/packet_spawn_star.c index c8b82c3f..1c25fdc2 100644 --- a/src/pc/network/packets/packet_spawn_star.c +++ b/src/pc/network/packets/packet_spawn_star.c @@ -7,7 +7,7 @@ extern struct Object* gCurrentObject; -void network_send_spawn_star(struct Object* o, u8 starType, f32 x, f32 y, f32 z, u32 behParams) { +void network_send_spawn_star(struct Object* o, u8 starType, f32 x, f32 y, f32 z, u32 behParams, u8 networkPlayerIndex) { struct Packet p = { 0 }; packet_init(&p, PACKET_SPAWN_STAR, true, PLMT_AREA); packet_write(&p, &starType, sizeof(u8)); @@ -15,6 +15,7 @@ void network_send_spawn_star(struct Object* o, u8 starType, f32 x, f32 y, f32 z, packet_write(&p, &y, sizeof(f32)); packet_write(&p, &z, sizeof(f32)); packet_write(&p, &behParams, sizeof(u32)); + packet_write(&p, &networkPlayerIndex, sizeof(u8)); packet_write(&p, &o->oPosX, sizeof(u32) * 3); packet_write(&p, &o->oHomeX, sizeof(u32) * 3); @@ -26,12 +27,14 @@ void network_receive_spawn_star(struct Packet* p) { u8 starType; f32 x, y, z; u32 behParams; + u8 networkPlayerIndex = UNKNOWN_GLOBAL_INDEX; packet_read(p, &starType, sizeof(u8)); packet_read(p, &x, sizeof(f32)); packet_read(p, &y, sizeof(f32)); packet_read(p, &z, sizeof(f32)); packet_read(p, &behParams, sizeof(u32)); + packet_read(p, &networkPlayerIndex, sizeof(u8)); u32 oldBehParams = gCurrentObject->oBehParams; gCurrentObject->oBehParams = behParams; @@ -47,7 +50,19 @@ void network_receive_spawn_star(struct Packet* p) { if (o != NULL) { packet_read(p, &o->oPosX, sizeof(u32) * 3); packet_read(p, &o->oHomeX, sizeof(u32) * 3); - o->oStarSpawnExtCutsceneFlags = 0; + + // Here we check if we're supposed to play the cutscene or not depending on if + // the global player index sent matches us. + // If the network player index is -1, Then the cutscene will always be skipped. + // This check is vital for objects which are network owned specfically. + // Leaving this the only way to properly set the cutscene flags + // for those who don't own the object. + //printf("network_receive_spawn_star: Network Player Index is %i, Our Global Index is %i.\n", networkPlayerIndex, gNetworkPlayers[0].globalIndex); + if (networkPlayerIndex == gNetworkPlayers[0].globalIndex) { + o->oStarSpawnExtCutsceneFlags = 1; + } else { + o->oStarSpawnExtCutsceneFlags = 0; + } } }