A load of fixes and cleanup.. (#39)

Red Coins will now properly play the cutscene for who collected the last red coin.
Secrets will now properly play the cutscene for who collected the last secret.
Treasure Chests will now only play the star cutscene for the person who opened the last chest.
Properly named some unknown Treasure Chest fields.
The Chain Chomp Chain should now properly be despawned for late joiners.
Improved the Big Boo's Haunt Merry-Go-Round checks for Mario.
This commit is contained in:
Prince Frizzy 2022-03-22 22:50:13 -04:00 committed by GitHub
parent 2cf7a87ab4
commit a89aa6ced8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
26 changed files with 476 additions and 291 deletions

3
.gitignore vendored
View file

@ -42,6 +42,9 @@
# Stackdumps
*.stackdump
# Backup files
*.bak
# datadump
/tools/ddump/*

View file

@ -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

View file

@ -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` | |

View file

@ -929,6 +929,7 @@
/* Hidden Star */
// Secrets/Red Coins
#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 /*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)

View file

@ -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,7 +199,8 @@ 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);
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);
@ -209,6 +210,7 @@ void bhv_homing_amp_loop(void) {
network_init_object_field(o, &o->oHomingAmpAvgY);
network_init_object_field(o, &o->oHomingAmpLockedOn);
}
}
switch (o->oAction) {
case HOMING_AMP_ACT_INACTIVE:
@ -342,7 +344,8 @@ 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);
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);
@ -350,6 +353,7 @@ void bhv_circling_amp_loop(void) {
network_init_object_field(o, &o->oForwardVel);
network_init_object_field(o, &o->oFriction);
}
}
switch (o->oAction) {
case AMP_ACT_IDLE:

View file

@ -60,11 +60,13 @@ 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);
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) {
case ARROW_LIFT_ACT_IDLE:

View file

@ -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;
}

View file

@ -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,13 +116,15 @@ 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);
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);

View file

@ -39,6 +39,7 @@ void bhv_camera_lakitu_init(void) {
}
lakituTargetLocalIndex = UNKNOWN_LOCAL_INDEX;
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;
@ -54,6 +55,7 @@ void bhv_camera_lakitu_init(void) {
network_init_object_field(o, &o->oCameraLakituPitchVel);
}
}
}
static u8 camera_lakitu_intro_act_trigger_cutscene_continue_dialog(void) {
return (o->oAction == CAMERA_LAKITU_INTRO_ACT_TRIGGER_CUTSCENE);
@ -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;

View file

@ -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();

View file

@ -28,8 +28,13 @@ static struct ObjectHitbox sChainChompHitbox = {
* Update function for chain chomp part / pivot.
*/
void bhv_chain_chomp_chain_part_update(void) {
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;
@ -169,8 +163,8 @@ static void chain_chomp_sub_act_turn(void) {
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);
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;
@ -525,8 +517,8 @@ void bhv_wooden_post_update(void) {
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);
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);
}
}

View file

@ -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);
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,15 +58,21 @@ 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);
#else
@ -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);
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;

View file

@ -23,24 +23,19 @@ 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 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);
}
@ -57,6 +52,13 @@ void bhv_red_coin_loop(void) {
// ...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
create_sound_spawner(SOUND_GENERAL_RED_COIN);

View file

@ -7,7 +7,9 @@
// a rolling log of another variation.
static void bhv_rolling_log_network_init(void) {
network_init_object(o, 4000.0f);
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);
@ -16,6 +18,8 @@ static void bhv_rolling_log_network_init(void) {
network_init_object_field(o, &o->oPitouneUnkFC);
network_init_object_field(o, &o->oForwardVel);
}
}
}
void bhv_ttm_rolling_log_init(void) {
o->oPitouneUnkF8 = 3970.0f;
@ -143,11 +147,13 @@ void volcano_act_3(void) {
void bhv_volcano_trap_loop(void) {
if (!network_sync_object_initialized(o)) {
network_init_object(o, 2000.0f);
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) {
case 0:

View file

@ -25,7 +25,8 @@ 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);
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);
@ -34,12 +35,11 @@ void bhv_scuttlebug_loop(void) {
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);
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,24 +137,25 @@ 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);
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;

View file

@ -24,16 +24,19 @@ void bhv_seesaw_platform_init(void) {
o->oCollisionDistance = 2000.0f;
}
network_init_object(o, 1000.0f);
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) {

View file

@ -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);
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);
s32 distanceToPlayer = dist_between_objects(o, player);
s32 angleToPlayer = obj_angle_to_object(o, player);
UNUSED s32 unusedVar = 0;
s16 marioAngleFromWindSource;
Vec3f tempPos;

View file

@ -38,7 +38,8 @@ void bhv_sl_walking_penguin_loop(void) {
f32 perpendicularOffset = 100.0f;
if (!network_sync_object_initialized(o)) {
network_init_object(o, 4000.0f);
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);
@ -47,6 +48,7 @@ void bhv_sl_walking_penguin_loop(void) {
network_init_object_field(o, &o->oSLWalkingPenguinWindCollisionXPos);
network_init_object_field(o, &o->oSLWalkingPenguinWindCollisionZPos);
}
}
o->oAngleVelYaw = 0;
cur_obj_update_floor_and_walls();

View file

@ -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);

View file

@ -154,7 +154,7 @@ 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;
}
@ -171,7 +171,7 @@ 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;
}
@ -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);
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;

View file

@ -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,9 +42,10 @@ 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;
case 2:
@ -74,36 +78,35 @@ void bhv_treasure_chest_bottom_loop(void) {
}
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;
}
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,12 +123,10 @@ 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) {
@ -134,18 +135,30 @@ void bhv_treasure_chest_ship_init(void) {
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;
network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS);
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;
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->oTreasureChestUnkF4);
network_init_object_field(o, &o->oTreasureChestUnkF8);
network_init_object_field(o, &o->oTreasureChestUnkFC);
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);
for (int i = 0; i < 4; i++) {
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);
@ -153,11 +166,13 @@ void bhv_treasure_chest_ship_init(void) {
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;
@ -190,18 +205,30 @@ void bhv_treasure_chest_jrb_init(void) {
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;
network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS);
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;
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->oTreasureChestUnkF4);
network_init_object_field(o, &o->oTreasureChestUnkF8);
network_init_object_field(o, &o->oTreasureChestUnkFC);
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);
for (int i = 0; i < 4; i++) {
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);
@ -209,11 +236,13 @@ void bhv_treasure_chest_jrb_init(void) {
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;
@ -242,18 +271,29 @@ void bhv_treasure_chest_init(void) {
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;
network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS);
// 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;
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->oTreasureChestUnkF4);
network_init_object_field(o, &o->oTreasureChestUnkF8);
network_init_object_field(o, &o->oTreasureChestUnkFC);
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);
for (int i = 0; i < 4; i++) {
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);
@ -261,11 +301,13 @@ void bhv_treasure_chest_init(void) {
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;

View file

@ -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; }

View file

@ -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 },

View file

@ -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);

View file

@ -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,9 +50,21 @@ 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);
// 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;
}
}
}
void network_send_spawn_star_nle(struct Object* o, u32 params) {
u8 globalIndex = UNKNOWN_GLOBAL_INDEX;