Path: blob/master/src/game/behaviors/chain_chomp.inc.c
7861 views
1/**2* Behavior for bhvChainChomp, bhvChainChompChainPart, bhvWoodenPost, and bhvChainChompGate.3* bhvChainChomp spawns its bhvWoodenPost in its behavior script. It spawns 5 chain4* parts. Part 0 is the "pivot", which is positioned at the wooden post while5* the chomp is chained up. Parts 1-4 are the other parts, starting from the6* chain chomp and moving toward the pivot.7* Processing order is bhvWoodenPost, bhvChainChompGate, bhvChainChomp, bhvChainChompChainPart.8* The chain parts are processed starting at the post and ending at the chomp.9*/10#include "../settings.h"1112/**13* Hitbox for chain chomp.14*/15static struct ObjectHitbox sChainChompHitbox = {16/* interactType: */ INTERACT_MR_BLIZZARD,17/* downOffset: */ 0,18/* damageOrCoinValue: */ 3,19/* health: */ 1,20/* numLootCoins: */ 0,21/* radius: */ 80,22/* height: */ 160,23/* hurtboxRadius: */ 80,24/* hurtboxHeight: */ 160,25};2627/**28* Update function for chain chomp part / pivot.29*/30void bhv_chain_chomp_chain_part_update(void) {31if (o->parentObj->oAction == CHAIN_CHOMP_ACT_UNLOAD_CHAIN) {32obj_mark_for_deletion(o);33} else if (o->oBehParams2ndByte != CHAIN_CHOMP_CHAIN_PART_BP_PIVOT) {34struct ChainSegment *segment = &o->parentObj->oChainChompSegments[o->oBehParams2ndByte];3536// Set position relative to the pivot37o->oPosX = o->parentObj->parentObj->oPosX + segment->posX;38o->oPosY = o->parentObj->parentObj->oPosY + segment->posY;39o->oPosZ = o->parentObj->parentObj->oPosZ + segment->posZ;40} else if (o->parentObj->oChainChompReleaseStatus != CHAIN_CHOMP_NOT_RELEASED) {41cur_obj_update_floor_and_walls();42cur_obj_move_standard(78);43}44}4546/**47* When mario gets close enough, allocate chain segments and spawn their objects.48*/49static void chain_chomp_act_uninitialized(void) {50struct ChainSegment *segments;51s32 i;5253if (o->oDistanceToMario < 3000.0f * configDrawDistanceMultiplier || configDrawDistanceMultiplier <= 0.0f) {54segments = mem_pool_alloc(gObjectMemoryPool, 5 * sizeof(struct ChainSegment));55if (segments != NULL) {56// Each segment represents the offset of a chain part to the pivot.57// Segment 0 connects the pivot to the chain chomp itself. Segment58// 1 connects the pivot to the chain part next to the chain chomp59// (chain part 1), etc.60o->oChainChompSegments = segments;61for (i = 0; i <= 4; i++) {62chain_segment_init(&segments[i]);63}6465cur_obj_set_pos_to_home();6667// Spawn the pivot and set to parent68if ((o->parentObj =69spawn_object(o, CHAIN_CHOMP_CHAIN_PART_BP_PIVOT, bhvChainChompChainPart))70!= NULL) {71// Spawn the non-pivot chain parts, starting from the chain72// chomp and moving toward the pivot73for (i = 1; i <= 4; i++) {74spawn_object_relative(i, 0, 0, 0, o, MODEL_METALLIC_BALL, bhvChainChompChainPart);75}7677o->oAction = CHAIN_CHOMP_ACT_MOVE;78cur_obj_unhide();79}80}81}82}8384/**85* Apply gravity to each chain part, and cap its distance to the previous86* part as well as from the pivot.87*/88static void chain_chomp_update_chain_segments(void) {89struct ChainSegment *prevSegment;90struct ChainSegment *segment;91f32 offsetX;92f32 offsetY;93f32 offsetZ;94f32 offset;95f32 segmentVelY;96f32 maxTotalOffset;97s32 i;9899if (o->oVelY < 0.0f) {100segmentVelY = o->oVelY;101} else {102segmentVelY = -20.0f;103}104105// Segment 0 connects the pivot to the chain chomp itself, and segment i>0106// connects the pivot to chain part i (1 is closest to the chain chomp).107108for (i = 1; i <= 4; i++) {109prevSegment = &o->oChainChompSegments[i - 1];110segment = &o->oChainChompSegments[i];111112// Apply gravity113114if ((segment->posY += segmentVelY) < 0.0f) {115segment->posY = 0.0f;116}117118// Cap distance to previous chain part (so that the tail follows the119// chomp)120121offsetX = segment->posX - prevSegment->posX;122offsetY = segment->posY - prevSegment->posY;123offsetZ = segment->posZ - prevSegment->posZ;124offset = sqrtf(offsetX * offsetX + offsetY * offsetY + offsetZ * offsetZ);125126if (offset > o->oChainChompMaxDistBetweenChainParts) {127offset = o->oChainChompMaxDistBetweenChainParts / offset;128offsetX *= offset;129offsetY *= offset;130offsetZ *= offset;131}132133// Cap distance to pivot (so that it stretches when the chomp moves far134// from the wooden post)135136offsetX += prevSegment->posX;137offsetY += prevSegment->posY;138offsetZ += prevSegment->posZ;139offset = sqrtf(offsetX * offsetX + offsetY * offsetY + offsetZ * offsetZ);140141maxTotalOffset = o->oChainChompMaxDistFromPivotPerChainPart * (5 - i);142if (offset > maxTotalOffset) {143offset = maxTotalOffset / offset;144offsetX *= offset;145offsetY *= offset;146offsetZ *= offset;147}148149segment->posX = offsetX;150segment->posY = offsetY;151segment->posZ = offsetZ;152}153}154155/**156* Lunging increases the maximum distance from the pivot and changes the maximum157* distance between chain parts. Restore these values to normal.158*/159static void chain_chomp_restore_normal_chain_lengths(void) {160approach_f32_ptr(&o->oChainChompMaxDistFromPivotPerChainPart, 750.0f / 5, 4.0f);161o->oChainChompMaxDistBetweenChainParts = o->oChainChompMaxDistFromPivotPerChainPart;162}163164/**165* Turn toward mario. Wait a bit and then enter the lunging sub-action.166*/167static void chain_chomp_sub_act_turn(void) {168o->oGravity = -4.0f;169170chain_chomp_restore_normal_chain_lengths();171obj_move_pitch_approach(0, 0x100);172173if (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) {174cur_obj_rotate_yaw_toward(o->oAngleToMario, 0x400);175if (abs_angle_diff(o->oAngleToMario, o->oMoveAngleYaw) < 0x800) {176if (o->oTimer > 30) {177if (cur_obj_check_anim_frame(0)) {178cur_obj_reverse_animation();179if (o->oTimer > 40) {180// Increase the maximum distance from the pivot and enter181// the lunging sub-action.182cur_obj_play_sound_2(SOUND_GENERAL_CHAIN_CHOMP2);183184o->oSubAction = CHAIN_CHOMP_SUB_ACT_LUNGE;185o->oChainChompMaxDistFromPivotPerChainPart = 900.0f / 5;186187o->oForwardVel = 140.0f;188o->oVelY = 20.0f;189o->oGravity = 0.0f;190o->oChainChompTargetPitch = obj_get_pitch_from_vel();191}192} else {193o->oTimer -= 1;194}195} else {196o->oForwardVel = 0.0f;197}198} else {199cur_obj_play_sound_2(SOUND_GENERAL_CHAIN_CHOMP1);200o->oForwardVel = 10.0f;201o->oVelY = 20.0f;202}203} else {204cur_obj_rotate_yaw_toward(o->oAngleToMario, 0x190);205o->oTimer = 0;206}207}208209static void chain_chomp_sub_act_lunge(void) {210obj_face_pitch_approach(o->oChainChompTargetPitch, 0x400);211212if (o->oForwardVel != 0.0f) {213f32 val04;214215if (o->oChainChompRestrictedByChain == TRUE) {216o->oForwardVel = o->oVelY = 0.0f;217o->oChainChompUnk104 = 30.0f;218}219220// TODO: What is this221if ((val04 = 900.0f - o->oChainChompDistToPivot) > 220.0f) {222val04 = 220.0f;223}224225o->oChainChompMaxDistBetweenChainParts =226val04 / 220.0f * o->oChainChompMaxDistFromPivotPerChainPart;227o->oTimer = 0;228} else {229// Turn toward pivot230cur_obj_rotate_yaw_toward(atan2s(o->oChainChompSegments[0].posZ, o->oChainChompSegments[0].posX),2310x1000);232233if (o->oChainChompUnk104 != 0.0f) {234approach_f32_ptr(&o->oChainChompUnk104, 0.0f, 0.8f);235} else {236o->oSubAction = CHAIN_CHOMP_SUB_ACT_TURN;237}238239o->oChainChompMaxDistBetweenChainParts = o->oChainChompUnk104;240if (gGlobalTimer % 2 != 0) {241o->oChainChompMaxDistBetweenChainParts = -o->oChainChompUnk104;242}243}244245if (o->oTimer < 30) {246cur_obj_reverse_animation();247}248}249250/**251* Fall to the ground and interrupt mario into a cutscene action.252*/253static void chain_chomp_released_trigger_cutscene(void) {254o->oForwardVel = 0.0f;255o->oGravity = -4.0f;256257//! Can delay this if we get into a cutscene-unfriendly action after the258// last post ground pound and before this259if (set_mario_npc_dialog(MARIO_DIALOG_LOOK_UP) == MARIO_DIALOG_STATUS_SPEAK260&& (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) && cutscene_object(CUTSCENE_STAR_SPAWN, o) == 1) {261o->oChainChompReleaseStatus = CHAIN_CHOMP_RELEASED_LUNGE_AROUND;262o->oTimer = 0;263}264}265266/**267* Lunge 4 times, each time moving toward mario +/- 0x2000 angular units.268* Finally, begin a lunge toward x=1450, z=562 (near the gate).269*/270static void chain_chomp_released_lunge_around(void) {271chain_chomp_restore_normal_chain_lengths();272273// Finish bounce274if (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) {275// Before first bounce, turn toward mario and wait 2 seconds276if (o->oChainChompNumLunges == 0) {277if (cur_obj_rotate_yaw_toward(o->oAngleToMario, 0x320)) {278if (o->oTimer > 60) {279o->oChainChompNumLunges += 1;280// enable wall collision281o->oWallHitboxRadius = 200.0f;282}283} else {284o->oTimer = 0;285}286} else {287if (++o->oChainChompNumLunges <= 5) {288cur_obj_play_sound_2(SOUND_GENERAL_CHAIN_CHOMP1);289o->oMoveAngleYaw = o->oAngleToMario + random_sign() * 0x2000;290o->oForwardVel = 30.0f;291o->oVelY = 50.0f;292} else {293o->oChainChompReleaseStatus = CHAIN_CHOMP_RELEASED_BREAK_GATE;294o->oHomeX = 1450.0f;295o->oHomeZ = 562.0f;296o->oMoveAngleYaw = cur_obj_angle_to_home();297o->oForwardVel = cur_obj_lateral_dist_to_home() / 8;298o->oVelY = 50.0f;299}300}301}302}303304/**305* Continue lunging until a wall collision occurs. Mark the gate as destroyed,306* wait for the chain chomp to land, and then begin a jump toward the final307* target, x=3288, z=-1770.308*/309static void chain_chomp_released_break_gate(void) {310if (!o->oChainChompHitGate) {311// If hit wall, assume it's the gate and bounce off of it312//! The wall may not be the gate313//! If the chain chomp gets stuck, it may never hit a wall, resulting314// in a softlock315if (o->oMoveFlags & OBJ_MOVE_HIT_WALL) {316o->oChainChompHitGate = TRUE;317o->oMoveAngleYaw = cur_obj_reflect_move_angle_off_wall();318o->oForwardVel *= 0.4f;319}320} else if (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) {321o->oChainChompReleaseStatus = CHAIN_CHOMP_RELEASED_JUMP_AWAY;322o->oHomeX = 3288.0f;323o->oHomeZ = -1770.0f;324o->oMoveAngleYaw = cur_obj_angle_to_home();325o->oForwardVel = cur_obj_lateral_dist_to_home() / 50.0f;326o->oVelY = 120.0f;327}328}329330/**331* Wait until the chain chomp lands.332*/333static void chain_chomp_released_jump_away(void) {334if (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) {335gObjCutsceneDone = TRUE;336o->oChainChompReleaseStatus = CHAIN_CHOMP_RELEASED_END_CUTSCENE;337}338}339340/**341* Release mario and transition to the unload chain action.342*/343static void chain_chomp_released_end_cutscene(void) {344if (cutscene_object(CUTSCENE_STAR_SPAWN, o) == -1) {345set_mario_npc_dialog(MARIO_DIALOG_STOP);346o->oAction = CHAIN_CHOMP_ACT_UNLOAD_CHAIN;347}348}349350/**351* All chain chomp movement behavior, including the cutscene after being352* released.353*/354static void chain_chomp_act_move(void) {355f32 maxDistToPivot;356357// Unload chain if mario is far enough358if (o->oChainChompReleaseStatus == CHAIN_CHOMP_NOT_RELEASED && o->oDistanceToMario > 4000.0f * configDrawDistanceMultiplier && configDrawDistanceMultiplier > 0.0f) {359o->oAction = CHAIN_CHOMP_ACT_UNLOAD_CHAIN;360o->oForwardVel = o->oVelY = 0.0f;361} else {362cur_obj_update_floor_and_walls();363364switch (o->oChainChompReleaseStatus) {365case CHAIN_CHOMP_NOT_RELEASED:366switch (o->oSubAction) {367case CHAIN_CHOMP_SUB_ACT_TURN:368chain_chomp_sub_act_turn();369break;370case CHAIN_CHOMP_SUB_ACT_LUNGE:371chain_chomp_sub_act_lunge();372break;373}374break;375case CHAIN_CHOMP_RELEASED_TRIGGER_CUTSCENE:376chain_chomp_released_trigger_cutscene();377break;378case CHAIN_CHOMP_RELEASED_LUNGE_AROUND:379chain_chomp_released_lunge_around();380break;381case CHAIN_CHOMP_RELEASED_BREAK_GATE:382chain_chomp_released_break_gate();383break;384case CHAIN_CHOMP_RELEASED_JUMP_AWAY:385chain_chomp_released_jump_away();386break;387case CHAIN_CHOMP_RELEASED_END_CUTSCENE:388chain_chomp_released_end_cutscene();389break;390}391392cur_obj_move_standard(78);393394// Segment 0 connects the pivot to the chain chomp itself395o->oChainChompSegments[0].posX = o->oPosX - o->parentObj->oPosX;396o->oChainChompSegments[0].posY = o->oPosY - o->parentObj->oPosY;397o->oChainChompSegments[0].posZ = o->oPosZ - o->parentObj->oPosZ;398399o->oChainChompDistToPivot =400sqrtf(o->oChainChompSegments[0].posX * o->oChainChompSegments[0].posX401+ o->oChainChompSegments[0].posY * o->oChainChompSegments[0].posY402+ o->oChainChompSegments[0].posZ * o->oChainChompSegments[0].posZ);403404// If the chain is fully stretched405maxDistToPivot = o->oChainChompMaxDistFromPivotPerChainPart * 5;406if (o->oChainChompDistToPivot > maxDistToPivot) {407f32 ratio = maxDistToPivot / o->oChainChompDistToPivot;408o->oChainChompDistToPivot = maxDistToPivot;409410o->oChainChompSegments[0].posX *= ratio;411o->oChainChompSegments[0].posY *= ratio;412o->oChainChompSegments[0].posZ *= ratio;413414if (o->oChainChompReleaseStatus == CHAIN_CHOMP_NOT_RELEASED) {415// Restrict chain chomp position416o->oPosX = o->parentObj->oPosX + o->oChainChompSegments[0].posX;417o->oPosY = o->parentObj->oPosY + o->oChainChompSegments[0].posY;418o->oPosZ = o->parentObj->oPosZ + o->oChainChompSegments[0].posZ;419420o->oChainChompRestrictedByChain = TRUE;421} else {422// Move pivot like the chain chomp is pulling it along423f32 oldPivotY = o->parentObj->oPosY;424425o->parentObj->oPosX = o->oPosX - o->oChainChompSegments[0].posX;426o->parentObj->oPosY = o->oPosY - o->oChainChompSegments[0].posY;427o->parentObj->oVelY = o->parentObj->oPosY - oldPivotY;428o->parentObj->oPosZ = o->oPosZ - o->oChainChompSegments[0].posZ;429}430} else {431o->oChainChompRestrictedByChain = FALSE;432}433434chain_chomp_update_chain_segments();435436// Begin a lunge if mario tries to attack437if (obj_check_attacks(&sChainChompHitbox, o->oAction)) {438o->oSubAction = CHAIN_CHOMP_SUB_ACT_LUNGE;439o->oChainChompMaxDistFromPivotPerChainPart = 900.0f / 5;440o->oForwardVel = 0.0f;441o->oVelY = 300.0f;442o->oGravity = -4.0f;443o->oChainChompTargetPitch = -0x3000;444}445}446}447448/**449* Hide and free the chain chomp segments. The chain objects will unload450* themselves when they see that the chain chomp is in this action.451*/452static void chain_chomp_act_unload_chain(void) {453cur_obj_hide();454mem_pool_free(gObjectMemoryPool, o->oChainChompSegments);455456o->oAction = CHAIN_CHOMP_ACT_UNINITIALIZED;457458if (o->oChainChompReleaseStatus != CHAIN_CHOMP_NOT_RELEASED) {459obj_mark_for_deletion(o);460}461}462463/**464* Update function for chain chomp.465*/466void bhv_chain_chomp_update(void) {467switch (o->oAction) {468case CHAIN_CHOMP_ACT_UNINITIALIZED:469chain_chomp_act_uninitialized();470break;471case CHAIN_CHOMP_ACT_MOVE:472chain_chomp_act_move();473break;474case CHAIN_CHOMP_ACT_UNLOAD_CHAIN:475chain_chomp_act_unload_chain();476break;477}478}479480/**481* Update function for wooden post.482*/483void bhv_wooden_post_update(void) {484// When ground pounded by mario, drop by -45 + -20485if (!o->oWoodenPostMarioPounding) {486if ((o->oWoodenPostMarioPounding = cur_obj_is_mario_ground_pounding_platform())) {487cur_obj_play_sound_2(SOUND_GENERAL_POUND_WOOD_POST);488o->oWoodenPostSpeedY = -70.0f;489}490} else if (approach_f32_ptr(&o->oWoodenPostSpeedY, 0.0f, 25.0f)) {491// Stay still until mario is done ground pounding492o->oWoodenPostMarioPounding = cur_obj_is_mario_ground_pounding_platform();493} else if ((o->oWoodenPostOffsetY += o->oWoodenPostSpeedY) < -190.0f) {494// Once pounded, if this is the chain chomp's post, release the chain495// chomp496o->oWoodenPostOffsetY = -190.0f;497if (o->parentObj != o) {498play_puzzle_jingle();499o->parentObj->oChainChompReleaseStatus = CHAIN_CHOMP_RELEASED_TRIGGER_CUTSCENE;500o->parentObj = o;501}502}503504if (o->oWoodenPostOffsetY != 0.0f) {505o->oPosY = o->oHomeY + o->oWoodenPostOffsetY;506} else if (!(o->oBehParams & WOODEN_POST_BP_NO_COINS_MASK)) {507// Reset the timer once mario is far enough508if (o->oDistanceToMario > 400.0f) {509o->oTimer = o->oWoodenPostTotalMarioAngle = 0;510} else {511// When mario runs around the post 3 times within 200 frames, spawn512// coins513o->oWoodenPostTotalMarioAngle += (s16)(o->oAngleToMario - o->oWoodenPostPrevAngleToMario);514if (absi(o->oWoodenPostTotalMarioAngle) > 0x30000 && o->oTimer < 200) {515obj_spawn_loot_yellow_coins(o, 5, 20.0f);516set_object_respawn_info_bits(o, 1);517}518}519520o->oWoodenPostPrevAngleToMario = o->oAngleToMario;521}522}523524/**525* Init function for chain chomp gate.526*/527void bhv_chain_chomp_gate_init(void) {528o->parentObj = cur_obj_nearest_object_with_behavior(bhvChainChomp);529}530531/**532* Update function for chain chomp gate533*/534void bhv_chain_chomp_gate_update(void) {535if (o->parentObj->oChainChompHitGate) {536spawn_mist_particles_with_sound(SOUND_GENERAL_WALL_EXPLOSION);537set_camera_shake_from_point(SHAKE_POS_SMALL, o->oPosX, o->oPosY, o->oPosZ);538spawn_mist_particles_variable(0, 0x7F, 200.0f);539spawn_triangle_break_particles(30, MODEL_DIRT_ANIMATION, 3.0f, 4);540obj_mark_for_deletion(o);541}542}543544545