/**1* Behavior for bhvHomingAmp and bhvCirclingAmp.2* These are distinct objects; one chases (homes in on) Mario,3* while the other circles around a fixed location with a radius4* of 200, 300, 400, or 0 (stationary).5*/67static struct ObjectHitbox sAmpHitbox = {8/* interactType: */ INTERACT_SHOCK,9/* downOffset: */ 40,10/* damageOrCoinValue: */ 1,11/* health: */ 0,12/* numLootCoins: */ 0,13/* radius: */ 40,14/* height: */ 50,15/* hurtboxRadius: */ 50,16/* hurtboxHeight: */ 60,17};1819/**20* Homing amp initialization function.21*/22void bhv_homing_amp_init(void) {23o->oHomeX = o->oPosX;24o->oHomeY = o->oPosY;25o->oHomeZ = o->oPosZ;26o->oGravity = 0;27o->oFriction = 1.0;28o->oBuoyancy = 1.0;29o->oHomingAmpAvgY = o->oHomeY;3031// Homing amps start at 1/10th their normal size.32// They grow when they "appear" to Mario.33cur_obj_scale(0.1f);3435// Hide the amp (until Mario gets near).36o->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE;37}3839/**40* Amps' attack handler, shared by both types of amp.41*/42static void check_amp_attack(void) {43// Strange placement for this call. The hitbox is never cleared.44// For perspective, this code is run every frame of bhv_circling_amp_loop45// and every frame of a homing amp's HOMING_AMP_ACT_CHASE action.46obj_set_hitbox(o, &sAmpHitbox);4748if (o->oInteractStatus & INT_STATUS_INTERACTED) {49// Unnecessary if statement, maybe caused by a macro for50// if (o->oInteractStatus & INT_STATUS_INTERACTED)51// o->oAction = X;52// ?53if (o->oInteractStatus & INT_STATUS_INTERACTED) {54// This function is used for both normal amps and homing amps,55// AMP_ACT_ATTACK_COOLDOWN == HOMING_AMP_ACT_ATTACK_COOLDOWN56o->oAction = AMP_ACT_ATTACK_COOLDOWN;57}5859// Clear interact status60o->oInteractStatus = 0;61}62}6364/**65* Unhide the amp and grow until normal size, then begin chasing Mario.66*/67static void homing_amp_appear_loop(void) {68// gLakituState.goalPos is the position lakitu is moving towards.69// In Lakitu and Mario cam, it is usually very close to the current camera position.70// In Fixed cam, it is the point behind Mario the camera will go to when transitioning71// to Lakitu cam. Homing amps will point themselves towards this point when appearing.72f32 relativeTargetX = gLakituState.goalPos[0] - o->oPosX;73f32 relativeTargetZ = gLakituState.goalPos[2] - o->oPosZ;74s16 targetYaw = atan2s(relativeTargetZ, relativeTargetX);7576o->oMoveAngleYaw = approach_s16_symmetric(o->oMoveAngleYaw, targetYaw, 0x1000);7778// For 30 frames, make the amp "appear" by increasing its size by 0.03 each frame,79// except for the first frame (when oTimer == 0) because the expression in cur_obj_scale80// evaluates to 0.1, which is the same as it was before. After 30 frames, it ends at81// a scale factor of 0.97. The amp remains at 97% of its real height for 60 more frames.82if (o->oTimer < 30) {83cur_obj_scale(0.1 + 0.9 * (f32)(o->oTimer / 30.0f));84} else {85o->oAnimState = 1;86}8788// Once the timer becomes greater than 90, i.e. 91 frames have passed,89// reset the amp's size and start chasing Mario.90if (o->oTimer >= 91) {91cur_obj_scale(1.0f);92o->oAction = HOMING_AMP_ACT_CHASE;93o->oAmpYPhase = 0;94}95}9697/**98* Chase Mario.99*/100static void homing_amp_chase_loop(void) {101// Lock on to Mario if he ever goes within 11.25 degrees of the amp's line of sight102if ((o->oAngleToMario - 0x400 < o->oMoveAngleYaw)103&& (o->oMoveAngleYaw < o->oAngleToMario + 0x400)) {104o->oHomingAmpLockedOn = TRUE;105o->oTimer = 0;106}107108// If the amp is locked on to Mario, start "chasing" him by moving109// in a straight line at 15 units/second for 32 frames.110if (o->oHomingAmpLockedOn == TRUE) {111o->oForwardVel = 15.0f;112113// Move the amp's average Y (the Y value it oscillates around) to align with114// Mario's head. Mario's graphics' Y + 150 is around the top of his head.115// Note that the average Y will slowly go down to approach his head if the amp116// is above his head, but if the amp is below it will instantly snap up.117if (o->oHomingAmpAvgY > gMarioObject->header.gfx.pos[1] + 150.0f) {118o->oHomingAmpAvgY -= 10.0f;119} else {120o->oHomingAmpAvgY = gMarioObject->header.gfx.pos[1] + 150.0f;121}122123if (o->oTimer >= 31) {124o->oHomingAmpLockedOn = FALSE;125}126} else {127// If the amp is not locked on to Mario, move forward at 10 units/second128// while curving towards him.129o->oForwardVel = 10.0f;130131obj_turn_toward_object(o, gMarioObject, 16, 0x400);132133// The amp's average Y will approach Mario's graphical Y position + 250134// at a rate of 10 units per frame. Interestingly, this is different from135// the + 150 used while chasing him. Could this be a typo?136if (o->oHomingAmpAvgY < gMarioObject->header.gfx.pos[1] + 250.0f) {137o->oHomingAmpAvgY += 10.0f;138}139}140141// The amp's position will sinusoidally oscillate 40 units around its average Y.142o->oPosY = o->oHomingAmpAvgY + sins(o->oAmpYPhase * 0x400) * 20.0f;143144// Handle attacks145check_amp_attack();146147// Give up if Mario goes further than 1500 units from the amp's original position148if (!is_point_within_radius_of_mario(o->oHomeX, o->oHomeY, o->oHomeZ, 1500)) {149o->oAction = HOMING_AMP_ACT_GIVE_UP;150}151}152153/**154* Give up on chasing Mario.155*/156static void homing_amp_give_up_loop(void) {157UNUSED u8 filler[8];158159// Move forward for 152 frames160o->oForwardVel = 15.0f;161162if (o->oTimer >= 151) {163// Hide the amp and reset it back to its inactive state164o->oPosX = o->oHomeX;165o->oPosY = o->oHomeY;166o->oPosZ = o->oHomeZ;167o->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE;168o->oAction = HOMING_AMP_ACT_INACTIVE;169o->oAnimState = 0;170o->oForwardVel = 0;171o->oHomingAmpAvgY = o->oHomeY;172}173}174175/**176* Cool down after a successful attack, shared by both types of amp.177*/178static void amp_attack_cooldown_loop(void) {179// Turn intangible and wait for 90 frames before chasing Mario again after hitting him.180o->header.gfx.animInfo.animFrame += 2;181o->oForwardVel = 0;182183cur_obj_become_intangible();184185if (o->oTimer >= 31) {186o->oAnimState = 0;187}188189if (o->oTimer >= 91) {190o->oAnimState = 1;191cur_obj_become_tangible();192o->oAction = HOMING_AMP_ACT_CHASE;193}194}195196/**197* Homing amp update function.198*/199void bhv_homing_amp_loop(void) {200switch (o->oAction) {201case HOMING_AMP_ACT_INACTIVE:202if (is_point_within_radius_of_mario(o->oHomeX, o->oHomeY, o->oHomeZ, 800) == TRUE) {203// Make the amp start to appear, and un-hide it.204o->oAction = HOMING_AMP_ACT_APPEAR;205o->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE;206}207break;208209case HOMING_AMP_ACT_APPEAR:210homing_amp_appear_loop();211break;212213case HOMING_AMP_ACT_CHASE:214homing_amp_chase_loop();215cur_obj_play_sound_1(SOUND_AIR_AMP_BUZZ);216break;217218case HOMING_AMP_ACT_GIVE_UP:219homing_amp_give_up_loop();220break;221222case HOMING_AMP_ACT_ATTACK_COOLDOWN:223amp_attack_cooldown_loop();224break;225}226227object_step();228229// Oscillate230o->oAmpYPhase++;231}232233/**234* Circling amp initialization function.235*/236void bhv_circling_amp_init(void) {237o->oHomeX = o->oPosX;238o->oHomeY = o->oPosY;239o->oHomeZ = o->oPosZ;240o->oAnimState = 1;241242// Determine the radius of the circling amp's circle243switch (o->oBehParams2ndByte) {244case AMP_BP_ROT_RADIUS_200:245o->oAmpRadiusOfRotation = 200.0f;246break;247248case AMP_BP_ROT_RADIUS_300:249o->oAmpRadiusOfRotation = 300.0f;250break;251252case AMP_BP_ROT_RADIUS_400:253o->oAmpRadiusOfRotation = 400.0f;254break;255256case AMP_BP_ROT_RADIUS_0:257break;258}259260// Choose a random point along the amp's circle.261// The amp's move angle represents its angle along the circle.262o->oMoveAngleYaw = random_u16();263264o->oAction = AMP_ACT_IDLE;265}266267/**268* Main update function for fixed amps.269* Fixed amps are a sub-species of circling amps, with circle radius 0.270*/271static void fixed_circling_amp_idle_loop(void) {272// Turn towards Mario, in both yaw and pitch.273f32 xToMario = gMarioObject->header.gfx.pos[0] - o->oPosX;274f32 yToMario = gMarioObject->header.gfx.pos[1] + 120.0f - o->oPosY;275f32 zToMario = gMarioObject->header.gfx.pos[2] - o->oPosZ;276s16 vAngleToMario = atan2s(sqrtf(xToMario * xToMario + zToMario * zToMario), -yToMario);277278obj_turn_toward_object(o, gMarioObject, 19, 0x1000);279o->oFaceAnglePitch = approach_s16_symmetric(o->oFaceAnglePitch, vAngleToMario, 0x1000);280281// Oscillate 40 units up and down.282// Interestingly, 0x458 (1112 in decimal) is a magic number with no apparent significance.283// It is slightly larger than the 0x400 figure used for homing amps, which makes284// fixed amps oscillate slightly quicker.285// Also, this uses the cosine, which starts at 1 instead of 0.286o->oPosY = o->oHomeY + coss(o->oAmpYPhase * 0x458) * 20.0f;287288// Handle attacks289check_amp_attack();290291// Oscillate292o->oAmpYPhase++;293294// Where there is a cur_obj_play_sound_1 call in the main circling amp update function,295// there is nothing here. Fixed amps are the only amps that never play296// the "amp buzzing" sound.297}298299/**300* Main update function for regular circling amps.301*/302static void circling_amp_idle_loop(void) {303// Move in a circle.304// The Y oscillation uses the magic number 0x8B0 (2224), which is305// twice that of the fixed amp. In other words, circling amps will306// oscillate twice as fast. Also, unlike all other amps, circling307// amps oscillate 60 units around their average Y instead of 40.308o->oPosX = o->oHomeX + sins(o->oMoveAngleYaw) * o->oAmpRadiusOfRotation;309o->oPosZ = o->oHomeZ + coss(o->oMoveAngleYaw) * o->oAmpRadiusOfRotation;310o->oPosY = o->oHomeY + coss(o->oAmpYPhase * 0x8B0) * 30.0f;311o->oMoveAngleYaw += 0x400;312o->oFaceAngleYaw = o->oMoveAngleYaw + 0x4000;313314// Handle attacks315check_amp_attack();316317// Oscillate318o->oAmpYPhase++;319320cur_obj_play_sound_1(SOUND_AIR_AMP_BUZZ);321}322323/**324* Circling amp update function.325* This calls the main update functions for both types of circling amps,326* and calls the common amp cooldown function when the amp is cooling down.327*/328void bhv_circling_amp_loop(void) {329switch (o->oAction) {330case AMP_ACT_IDLE:331if (o->oBehParams2ndByte == AMP_BP_ROT_RADIUS_0) {332fixed_circling_amp_idle_loop();333} else {334circling_amp_idle_loop();335}336337break;338339case AMP_ACT_ATTACK_COOLDOWN:340amp_attack_cooldown_loop();341break;342}343}344345346