mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2024-10-20 12:12:39 +00:00
d2a2a80d56
Synchronized currentRoom per-player Synchronized haunted bookshelf, and the bookshelf manager Synchronized haunted chairs Synchronized mad piano Synchronized BBH's tilting trap, and made the physics multiple-player-aware Synchronized scuttlebugs Synchronized every variety of Boo Synchronized elevators Synchronized flamethrowers Synchronized the various types of enemy books Synchronized the book switches Synchronized jumping box Made coffins multiple-player-aware Fixed everything that used gMarioState as an array instead of gMarioStates Prevented some NPC-dialog softlocks Prevented the remote player from messing up the local's camera settings Possibly fixed the relatively rare chain chomp softlock Possibly fixed the relatively rare chain hoot softlock Fixed the first-person-camera softlock Forced camera code to use the correct mario struct
591 lines
21 KiB
C
591 lines
21 KiB
C
|
|
/**
|
|
* Behavior for bhvChainChomp, bhvChainChompChainPart, bhvWoodenPost, and bhvChainChompGate.
|
|
* bhvChainChomp spawns its bhvWoodenPost in its behavior script. It spawns 5 chain
|
|
* parts. Part 0 is the "pivot", which is positioned at the wooden post while
|
|
* the chomp is chained up. Parts 1-4 are the other parts, starting from the
|
|
* chain chomp and moving toward the pivot.
|
|
* Processing order is bhvWoodenPost, bhvChainChompGate, bhvChainChomp, bhvChainChompChainPart.
|
|
* The chain parts are processed starting at the post and ending at the chomp.
|
|
*/
|
|
|
|
/**
|
|
* Hitbox for chain chomp.
|
|
*/
|
|
static struct ObjectHitbox sChainChompHitbox = {
|
|
/* interactType: */ INTERACT_MR_BLIZZARD,
|
|
/* downOffset: */ 0,
|
|
/* damageOrCoinValue: */ 3,
|
|
/* health: */ 1,
|
|
/* numLootCoins: */ 0,
|
|
/* radius: */ 80,
|
|
/* height: */ 160,
|
|
/* hurtboxRadius: */ 80,
|
|
/* hurtboxHeight: */ 160,
|
|
};
|
|
|
|
/**
|
|
* Update function for chain chomp part / pivot.
|
|
*/
|
|
void bhv_chain_chomp_chain_part_update(void) {
|
|
struct ChainSegment *segment;
|
|
|
|
if (o->parentObj->behavior != &bhvChainChomp || o->parentObj->oAction == CHAIN_CHOMP_ACT_UNLOAD_CHAIN) {
|
|
obj_mark_for_deletion(o);
|
|
} else if (o->oBehParams2ndByte != CHAIN_CHOMP_CHAIN_PART_BP_PIVOT) {
|
|
segment = &o->parentObj->oChainChompSegments[o->oBehParams2ndByte];
|
|
|
|
// Set position relative to the pivot
|
|
o->oPosX = o->parentObj->parentObj->oPosX + segment->posX;
|
|
o->oPosY = o->parentObj->parentObj->oPosY + segment->posY;
|
|
o->oPosZ = o->parentObj->parentObj->oPosZ + segment->posZ;
|
|
;
|
|
} else if (o->parentObj->oChainChompReleaseStatus != CHAIN_CHOMP_NOT_RELEASED) {
|
|
cur_obj_update_floor_and_walls();
|
|
cur_obj_move_standard(78);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* When mario gets close enough, allocate chain segments and spawn their objects.
|
|
*/
|
|
static void chain_chomp_act_uninitialized(void) {
|
|
struct ChainSegment *segments;
|
|
s32 i;
|
|
|
|
#ifndef NODRAWINGDISTANCE
|
|
struct Object* player = nearest_player_to_object(o);
|
|
int distanceToPlayer = dist_between_objects(o, player);
|
|
if (distanceToPlayer < 3000.0f) {
|
|
#endif
|
|
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++) {
|
|
chain_segment_init(&segments[i]);
|
|
}
|
|
|
|
cur_obj_set_pos_to_home();
|
|
|
|
// Spawn the pivot and set to parent
|
|
if ((o->parentObj =
|
|
spawn_object(o, CHAIN_CHOMP_CHAIN_PART_BP_PIVOT, bhvChainChompChainPart))
|
|
!= NULL) {
|
|
// Spawn the non-pivot chain parts, starting from the chain
|
|
// chomp and moving toward the pivot
|
|
for (i = 1; i <= 4; i++) {
|
|
spawn_object_relative(i, 0, 0, 0, o, MODEL_METALLIC_BALL, bhvChainChompChainPart);
|
|
}
|
|
|
|
o->oAction = CHAIN_CHOMP_ACT_MOVE;
|
|
cur_obj_unhide();
|
|
}
|
|
}
|
|
#ifndef NODRAWINGDISTANCE
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Apply gravity to each chain part, and cap its distance to the previous
|
|
* 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;
|
|
} else {
|
|
segmentVelY = -20.0f;
|
|
}
|
|
|
|
// 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];
|
|
|
|
// Apply gravity
|
|
|
|
if ((segment->posY += segmentVelY) < 0.0f) {
|
|
segment->posY = 0.0f;
|
|
}
|
|
|
|
// 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);
|
|
|
|
if (offset > o->oChainChompMaxDistBetweenChainParts) {
|
|
offset = o->oChainChompMaxDistBetweenChainParts / offset;
|
|
offsetX *= offset;
|
|
offsetY *= offset;
|
|
offsetZ *= offset;
|
|
}
|
|
|
|
// Cap distance to pivot (so that it stretches when the chomp moves far
|
|
// from the wooden post)
|
|
|
|
offsetX += prevSegment->posX;
|
|
offsetY += prevSegment->posY;
|
|
offsetZ += prevSegment->posZ;
|
|
offset = sqrtf(offsetX * offsetX + offsetY * offsetY + offsetZ * offsetZ);
|
|
|
|
maxTotalOffset = o->oChainChompMaxDistFromPivotPerChainPart * (5 - i);
|
|
if (offset > maxTotalOffset) {
|
|
offset = maxTotalOffset / offset;
|
|
offsetX *= offset;
|
|
offsetY *= offset;
|
|
offsetZ *= offset;
|
|
}
|
|
|
|
segment->posX = offsetX;
|
|
segment->posY = offsetY;
|
|
segment->posZ = offsetZ;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lunging increases the maximum distance from the pivot and changes the maximum
|
|
* distance between chain parts. Restore these values to normal.
|
|
*/
|
|
static void chain_chomp_restore_normal_chain_lengths(void) {
|
|
approach_f32_ptr(&o->oChainChompMaxDistFromPivotPerChainPart, 750.0f / 5, 4.0f);
|
|
o->oChainChompMaxDistBetweenChainParts = o->oChainChompMaxDistFromPivotPerChainPart;
|
|
}
|
|
|
|
/**
|
|
* Turn toward mario. Wait a bit and then enter the lunging sub-action.
|
|
*/
|
|
static void chain_chomp_sub_act_turn(void) {
|
|
o->oGravity = -4.0f;
|
|
|
|
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);
|
|
|
|
if (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) {
|
|
cur_obj_rotate_yaw_toward(angleToPlayer, 0x400);
|
|
if (abs_angle_diff(angleToPlayer, o->oMoveAngleYaw) < 0x800 && distanceToPlayer < 3000) {
|
|
if (o->oTimer > 30) {
|
|
if (cur_obj_check_anim_frame(0)) {
|
|
cur_obj_reverse_animation();
|
|
if (o->oTimer > 40) {
|
|
// Increase the maximum distance from the pivot and enter
|
|
// the lunging sub-action.
|
|
cur_obj_play_sound_2(SOUND_GENERAL_CHAIN_CHOMP2);
|
|
|
|
o->oSubAction = CHAIN_CHOMP_SUB_ACT_LUNGE;
|
|
o->oChainChompMaxDistFromPivotPerChainPart = 900.0f / 5;
|
|
|
|
o->oForwardVel = 140.0f;
|
|
o->oVelY = 20.0f;
|
|
o->oGravity = 0.0f;
|
|
o->oChainChompTargetPitch = obj_get_pitch_from_vel();
|
|
}
|
|
} else {
|
|
o->oTimer -= 1;
|
|
}
|
|
} else {
|
|
o->oForwardVel = 0.0f;
|
|
}
|
|
} else {
|
|
cur_obj_play_sound_2(SOUND_GENERAL_CHAIN_CHOMP1);
|
|
o->oForwardVel = 10.0f;
|
|
o->oVelY = 20.0f;
|
|
}
|
|
} else {
|
|
cur_obj_rotate_yaw_toward(angleToPlayer, 0x190);
|
|
o->oTimer = 0;
|
|
}
|
|
}
|
|
|
|
static void chain_chomp_sub_act_lunge(void) {
|
|
f32 val04;
|
|
|
|
obj_face_pitch_approach(o->oChainChompTargetPitch, 0x400);
|
|
|
|
if (o->oForwardVel != 0.0f) {
|
|
if (o->oChainChompRestrictedByChain == TRUE) {
|
|
o->oForwardVel = o->oVelY = 0.0f;
|
|
o->oChainChompUnk104 = 30.0f;
|
|
}
|
|
|
|
// TODO: What is this
|
|
if ((val04 = 900.0f - o->oChainChompDistToPivot) > 220.0f) {
|
|
val04 = 220.0f;
|
|
}
|
|
|
|
o->oChainChompMaxDistBetweenChainParts =
|
|
val04 / 220.0f * o->oChainChompMaxDistFromPivotPerChainPart;
|
|
o->oTimer = 0;
|
|
;
|
|
} else {
|
|
// Turn toward pivot
|
|
cur_obj_rotate_yaw_toward(atan2s(o->oChainChompSegments[0].posZ, o->oChainChompSegments[0].posX),
|
|
0x1000);
|
|
|
|
if (o->oChainChompUnk104 != 0.0f) {
|
|
approach_f32_ptr(&o->oChainChompUnk104, 0.0f, 0.8f);
|
|
} else {
|
|
o->oSubAction = CHAIN_CHOMP_SUB_ACT_TURN;
|
|
}
|
|
|
|
o->oChainChompMaxDistBetweenChainParts = o->oChainChompUnk104;
|
|
if (gGlobalTimer % 2 != 0) {
|
|
o->oChainChompMaxDistBetweenChainParts = -o->oChainChompUnk104;
|
|
}
|
|
}
|
|
|
|
if (o->oTimer < 30) {
|
|
cur_obj_reverse_animation();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fall to the ground and interrupt mario into a cutscene action.
|
|
*/
|
|
static void chain_chomp_released_trigger_cutscene(void) {
|
|
o->oForwardVel = 0.0f;
|
|
o->oGravity = -4.0f;
|
|
|
|
//! Can delay this if we get into a cutscene-unfriendly action after the
|
|
// last post ground pound and before this
|
|
if (set_mario_npc_dialog(&gMarioStates[0], 2) == 2 && (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND)
|
|
&& cutscene_object(CUTSCENE_STAR_SPAWN, o) == 1) {
|
|
o->oChainChompReleaseStatus = CHAIN_CHOMP_RELEASED_LUNGE_AROUND;
|
|
o->oTimer = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Lunge 4 times, each time moving toward mario +/- 0x2000 angular units.
|
|
* Finally, begin a lunge toward x=1450, z=562 (near the gate).
|
|
*/
|
|
static void chain_chomp_released_lunge_around(void) {
|
|
chain_chomp_restore_normal_chain_lengths();
|
|
|
|
// Finish bounce
|
|
if (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) {
|
|
struct Object* player = nearest_player_to_object(o);
|
|
int angleToPlayer = obj_angle_to_object(o, player);
|
|
|
|
// Before first bounce, turn toward mario and wait 2 seconds
|
|
if (o->oChainChompNumLunges == 0) {
|
|
if (cur_obj_rotate_yaw_toward(angleToPlayer, 0x320)) {
|
|
if (o->oTimer > 60) {
|
|
o->oChainChompNumLunges += 1;
|
|
// enable wall collision
|
|
o->oWallHitboxRadius = 200.0f;
|
|
}
|
|
} else {
|
|
o->oTimer = 0;
|
|
}
|
|
} else {
|
|
if (++o->oChainChompNumLunges <= 5) {
|
|
cur_obj_play_sound_2(SOUND_GENERAL_CHAIN_CHOMP1);
|
|
o->oMoveAngleYaw = angleToPlayer + random_sign() * 0x2000;
|
|
o->oForwardVel = 30.0f;
|
|
o->oVelY = 50.0f;
|
|
} else {
|
|
o->oChainChompReleaseStatus = CHAIN_CHOMP_RELEASED_BREAK_GATE;
|
|
o->oHomeX = 1450.0f;
|
|
o->oHomeZ = 562.0f;
|
|
o->oMoveAngleYaw = cur_obj_angle_to_home();
|
|
o->oForwardVel = cur_obj_lateral_dist_to_home() / 8;
|
|
o->oVelY = 50.0f;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Continue lunging until a wall collision occurs. Mark the gate as destroyed,
|
|
* wait for the chain chomp to land, and then begin a jump toward the final
|
|
* target, x=3288, z=-1770.
|
|
*/
|
|
static void chain_chomp_released_break_gate(void) {
|
|
if (!o->oChainChompHitGate) {
|
|
// If hit wall, assume it's the gate and bounce off of it
|
|
//! The wall may not be the gate
|
|
//! If the chain chomp gets stuck, it may never hit a wall, resulting
|
|
// in a softlock
|
|
if (o->oMoveFlags & OBJ_MOVE_HIT_WALL) {
|
|
o->oChainChompHitGate = TRUE;
|
|
o->oMoveAngleYaw = cur_obj_reflect_move_angle_off_wall();
|
|
o->oForwardVel *= 0.4f;
|
|
}
|
|
} else if (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) {
|
|
o->oChainChompReleaseStatus = CHAIN_CHOMP_RELEASED_JUMP_AWAY;
|
|
o->oHomeX = 3288.0f;
|
|
o->oHomeZ = -1770.0f;
|
|
o->oMoveAngleYaw = cur_obj_angle_to_home();
|
|
o->oForwardVel = cur_obj_lateral_dist_to_home() / 50.0f;
|
|
o->oVelY = 120.0f;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wait until the chain chomp lands.
|
|
*/
|
|
static void chain_chomp_released_jump_away(void) {
|
|
if (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) {
|
|
gObjCutsceneDone = TRUE;
|
|
o->oChainChompReleaseStatus = CHAIN_CHOMP_RELEASED_END_CUTSCENE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Release mario and transition to the unload chain action.
|
|
*/
|
|
static void chain_chomp_released_end_cutscene(void) {
|
|
if (cutscene_object(CUTSCENE_STAR_SPAWN, o) == -1) {
|
|
set_mario_npc_dialog(&gMarioStates[0], 0);
|
|
o->oAction = CHAIN_CHOMP_ACT_UNLOAD_CHAIN;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* All chain chomp movement behavior, including the cutscene after being
|
|
* released.
|
|
*/
|
|
static void chain_chomp_act_move(void) {
|
|
f32 maxDistToPivot;
|
|
|
|
// Unload chain if mario is far enough
|
|
#ifndef NODRAWINGDISTANCE
|
|
struct Object* player = nearest_player_to_object(o);
|
|
int distanceToPlayer = dist_between_objects(o, player);
|
|
if (o->oChainChompReleaseStatus == CHAIN_CHOMP_NOT_RELEASED && distanceToPlayer > 4000.0f) {
|
|
o->oAction = CHAIN_CHOMP_ACT_UNLOAD_CHAIN;
|
|
o->oForwardVel = o->oVelY = 0.0f;
|
|
} else {
|
|
#endif
|
|
cur_obj_update_floor_and_walls();
|
|
|
|
switch (o->oChainChompReleaseStatus) {
|
|
case CHAIN_CHOMP_NOT_RELEASED:
|
|
switch (o->oSubAction) {
|
|
case CHAIN_CHOMP_SUB_ACT_TURN:
|
|
chain_chomp_sub_act_turn();
|
|
break;
|
|
case CHAIN_CHOMP_SUB_ACT_LUNGE:
|
|
chain_chomp_sub_act_lunge();
|
|
break;
|
|
}
|
|
break;
|
|
case CHAIN_CHOMP_RELEASED_TRIGGER_CUTSCENE:
|
|
chain_chomp_released_trigger_cutscene();
|
|
break;
|
|
case CHAIN_CHOMP_RELEASED_LUNGE_AROUND:
|
|
chain_chomp_released_lunge_around();
|
|
break;
|
|
case CHAIN_CHOMP_RELEASED_BREAK_GATE:
|
|
chain_chomp_released_break_gate();
|
|
break;
|
|
case CHAIN_CHOMP_RELEASED_JUMP_AWAY:
|
|
chain_chomp_released_jump_away();
|
|
break;
|
|
case CHAIN_CHOMP_RELEASED_END_CUTSCENE:
|
|
chain_chomp_released_end_cutscene();
|
|
break;
|
|
}
|
|
|
|
cur_obj_move_standard(78);
|
|
|
|
// Segment 0 connects the pivot to the chain chomp itself
|
|
o->oChainChompSegments[0].posX = o->oPosX - o->parentObj->oPosX;
|
|
o->oChainChompSegments[0].posY = o->oPosY - o->parentObj->oPosY;
|
|
o->oChainChompSegments[0].posZ = o->oPosZ - o->parentObj->oPosZ;
|
|
|
|
o->oChainChompDistToPivot =
|
|
sqrtf(o->oChainChompSegments[0].posX * o->oChainChompSegments[0].posX
|
|
+ o->oChainChompSegments[0].posY * o->oChainChompSegments[0].posY
|
|
+ o->oChainChompSegments[0].posZ * o->oChainChompSegments[0].posZ);
|
|
|
|
// If the chain is fully stretched
|
|
maxDistToPivot = o->oChainChompMaxDistFromPivotPerChainPart * 5;
|
|
if (o->oChainChompDistToPivot > maxDistToPivot) {
|
|
f32 ratio = maxDistToPivot / o->oChainChompDistToPivot;
|
|
o->oChainChompDistToPivot = maxDistToPivot;
|
|
|
|
o->oChainChompSegments[0].posX *= ratio;
|
|
o->oChainChompSegments[0].posY *= ratio;
|
|
o->oChainChompSegments[0].posZ *= ratio;
|
|
|
|
if (o->oChainChompReleaseStatus == CHAIN_CHOMP_NOT_RELEASED) {
|
|
// Restrict chain chomp position
|
|
o->oPosX = o->parentObj->oPosX + o->oChainChompSegments[0].posX;
|
|
o->oPosY = o->parentObj->oPosY + o->oChainChompSegments[0].posY;
|
|
o->oPosZ = o->parentObj->oPosZ + o->oChainChompSegments[0].posZ;
|
|
|
|
o->oChainChompRestrictedByChain = TRUE;
|
|
} else {
|
|
// Move pivot like the chain chomp is pulling it along
|
|
f32 oldPivotY = o->parentObj->oPosY;
|
|
|
|
o->parentObj->oPosX = o->oPosX - o->oChainChompSegments[0].posX;
|
|
o->parentObj->oPosY = o->oPosY - o->oChainChompSegments[0].posY;
|
|
o->parentObj->oVelY = o->parentObj->oPosY - oldPivotY;
|
|
o->parentObj->oPosZ = o->oPosZ - o->oChainChompSegments[0].posZ;
|
|
}
|
|
} else {
|
|
o->oChainChompRestrictedByChain = FALSE;
|
|
}
|
|
|
|
chain_chomp_update_chain_segments();
|
|
|
|
// Begin a lunge if mario tries to attack
|
|
if (obj_check_attacks(&sChainChompHitbox, o->oAction)) {
|
|
o->oSubAction = CHAIN_CHOMP_SUB_ACT_LUNGE;
|
|
o->oChainChompMaxDistFromPivotPerChainPart = 900.0f / 5;
|
|
o->oForwardVel = 0.0f;
|
|
o->oVelY = 300.0f;
|
|
o->oGravity = -4.0f;
|
|
o->oChainChompTargetPitch = -0x3000;
|
|
}
|
|
#ifndef NODRAWINGDISTANCE
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* Hide and free the chain chomp segments. The chain objects will unload
|
|
* themselves when they see that the chain chomp is in this action.
|
|
*/
|
|
static void chain_chomp_act_unload_chain(void) {
|
|
cur_obj_hide();
|
|
mem_pool_free(gObjectMemoryPool, o->oChainChompSegments);
|
|
|
|
o->oAction = CHAIN_CHOMP_ACT_UNINITIALIZED;
|
|
|
|
if (o->oChainChompReleaseStatus != CHAIN_CHOMP_NOT_RELEASED) {
|
|
for (u8 i = 0; i < 5; i++) {
|
|
obj_mark_for_deletion(&o->oChainChompSegments[i]);
|
|
}
|
|
obj_mark_for_deletion(o);
|
|
obj_mark_for_deletion(o->parentObj);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update function for chain chomp.
|
|
*/
|
|
void bhv_chain_chomp_update(void) {
|
|
if (!network_sync_object_initialized(o)) {
|
|
struct SyncObject* so = network_init_object(o, 1000.0f);
|
|
so->syncDeathEvent = FALSE;
|
|
network_init_object_field(o, &o->oChainChompUnk104);
|
|
network_init_object_field(o, &o->header.gfx.unk38.animFrame);
|
|
}
|
|
|
|
switch (o->oAction) {
|
|
case CHAIN_CHOMP_ACT_UNINITIALIZED:
|
|
chain_chomp_act_uninitialized();
|
|
break;
|
|
case CHAIN_CHOMP_ACT_MOVE:
|
|
chain_chomp_act_move();
|
|
break;
|
|
case CHAIN_CHOMP_ACT_UNLOAD_CHAIN:
|
|
chain_chomp_act_unload_chain();
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update function for wooden post.
|
|
*/
|
|
void bhv_wooden_post_update(void) {
|
|
if (!network_sync_object_initialized(o)) {
|
|
network_init_object(o, SYNC_DISTANCE_ONLY_EVENTS);
|
|
network_init_object_field(o, &o->oWoodenPostMarioPounding);
|
|
network_init_object_field(o, &o->oWoodenPostOffsetY);
|
|
network_init_object_field(o, &o->oWoodenPostSpeedY);
|
|
network_init_object_field(o, &o->oWoodenPostTotalMarioAngle);
|
|
network_init_object_field(o, &o->oTimer);
|
|
}
|
|
|
|
// When ground pounded by mario, drop by -45 + -20
|
|
if (!o->oWoodenPostMarioPounding) {
|
|
if ((o->oWoodenPostMarioPounding = cur_obj_is_mario_ground_pounding_platform())) {
|
|
cur_obj_play_sound_2(SOUND_GENERAL_POUND_WOOD_POST);
|
|
o->oWoodenPostSpeedY = -70.0f;
|
|
network_send_object(o);
|
|
}
|
|
} else if (approach_f32_ptr(&o->oWoodenPostSpeedY, 0.0f, 25.0f)) {
|
|
// Stay still until mario is done ground pounding
|
|
o->oWoodenPostMarioPounding = cur_obj_is_mario_ground_pounding_platform();
|
|
} else if ((o->oWoodenPostOffsetY += o->oWoodenPostSpeedY) < -190.0f) {
|
|
// Once pounded, if this is the chain chomp's post, release the chain
|
|
// chomp
|
|
o->oWoodenPostOffsetY = -190.0f;
|
|
if (o->parentObj != o) {
|
|
play_puzzle_jingle();
|
|
o->parentObj->oChainChompReleaseStatus = CHAIN_CHOMP_RELEASED_TRIGGER_CUTSCENE;
|
|
o->parentObj = o;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
// Reset the timer once mario is far enough
|
|
if (distanceToPlayer > 400.0f) {
|
|
o->oTimer = o->oWoodenPostTotalMarioAngle = 0;
|
|
} else {
|
|
// When mario runs around the post 3 times within 200 frames, spawn
|
|
// 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);
|
|
}
|
|
}
|
|
|
|
o->oWoodenPostPrevAngleToMario = angleToPlayer;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Init function for chain chomp gate.
|
|
*/
|
|
void bhv_chain_chomp_gate_init(void) {
|
|
o->parentObj = cur_obj_nearest_object_with_behavior(bhvChainChomp);
|
|
}
|
|
|
|
/**
|
|
* Update function for chain chomp gate
|
|
*/
|
|
void bhv_chain_chomp_gate_update(void) {
|
|
if (o->parentObj->oChainChompHitGate) {
|
|
spawn_mist_particles_with_sound(SOUND_GENERAL_WALL_EXPLOSION);
|
|
set_camera_shake_from_point(SHAKE_POS_SMALL, o->oPosX, o->oPosY, o->oPosZ);
|
|
spawn_mist_particles_variable(0, 0x7F, 200.0f);
|
|
spawn_triangle_break_particles(30, 0x8A, 3.0f, 4);
|
|
obj_mark_for_deletion(o);
|
|
}
|
|
}
|