amp.inc.c (11367B)
1 2 /** 3 * Behavior for bhvHomingAmp and bhvCirclingAmp. 4 * These are distinct objects; one chases (homes in on) Mario, 5 * while the other circles around a fixed location with a radius 6 * of 200, 300, 400, or 0 (stationary). 7 */ 8 9 static struct ObjectHitbox sAmpHitbox = { 10 /* interactType: */ INTERACT_SHOCK, 11 /* downOffset: */ 40, 12 /* damageOrCoinValue: */ 1, 13 /* health: */ 0, 14 /* numLootCoins: */ 0, 15 /* radius: */ 40, 16 /* height: */ 50, 17 /* hurtboxRadius: */ 50, 18 /* hurtboxHeight: */ 60, 19 }; 20 21 /** 22 * Homing amp initialization function. 23 */ 24 void bhv_homing_amp_init(void) { 25 o->oHomeX = o->oPosX; 26 o->oHomeY = o->oPosY; 27 o->oHomeZ = o->oPosZ; 28 o->oGravity = 0.0f; 29 o->oFriction = 1.0f; 30 o->oBuoyancy = 1.0f; 31 o->oHomingAmpAvgY = o->oHomeY; 32 33 // Homing amps start at 1/10th their normal size. 34 // They grow when they "appear" to Mario. 35 cur_obj_scale(0.1f); 36 37 // Hide the amp (until Mario gets near). 38 o->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE; 39 } 40 41 /** 42 * Amps' attack handler, shared by both types of amp. 43 */ 44 static void check_amp_attack(void) { 45 // Strange placement for this call. The hitbox is never cleared. 46 // For perspective, this code is run every frame of bhv_circling_amp_loop 47 // and every frame of a homing amp's HOMING_AMP_ACT_CHASE action. 48 obj_set_hitbox(o, &sAmpHitbox); 49 50 if (o->oInteractStatus & INT_STATUS_INTERACTED) { 51 // Unnecessary if statement, maybe caused by a macro for 52 // if (o->oInteractStatus & INT_STATUS_INTERACTED) { 53 // o->oAction = X; 54 // } 55 // ? 56 if (o->oInteractStatus & INT_STATUS_INTERACTED) { 57 // This function is used for both normal amps and homing amps, 58 // AMP_ACT_ATTACK_COOLDOWN == HOMING_AMP_ACT_ATTACK_COOLDOWN 59 o->oAction = AMP_ACT_ATTACK_COOLDOWN; 60 } 61 62 // Clear interact status 63 o->oInteractStatus = 0; 64 } 65 } 66 67 /** 68 * Unhide the amp and grow until normal size, then begin chasing Mario. 69 */ 70 static void homing_amp_appear_loop(void) { 71 // gLakituState.goalPos is the position lakitu is moving towards. 72 // In Lakitu and Mario cam, it is usually very close to the current camera position. 73 // In Fixed cam, it is the point behind Mario the camera will go to when transitioning 74 // to Lakitu cam. Homing amps will point themselves towards this point when appearing. 75 f32 relativeTargetX = gLakituState.goalPos[0] - o->oPosX; 76 f32 relativeTargetZ = gLakituState.goalPos[2] - o->oPosZ; 77 s16 targetYaw = atan2s(relativeTargetZ, relativeTargetX); 78 79 o->oMoveAngleYaw = approach_s16_symmetric(o->oMoveAngleYaw, targetYaw, 0x1000); 80 81 // For 30 frames, make the amp "appear" by increasing its size by 0.03 each frame, 82 // except for the first frame (when oTimer == 0) because the expression in cur_obj_scale 83 // evaluates to 0.1, which is the same as it was before. After 30 frames, it ends at 84 // a scale factor of 0.97. The amp remains at 97% of its real height for 60 more frames. 85 if (o->oTimer < 30) { 86 cur_obj_scale(0.1 + 0.9 * (f32)(o->oTimer / 30.0f)); 87 } else { 88 o->oAnimState = 1; 89 } 90 91 // Once the timer becomes greater than 90, i.e. 91 frames have passed, 92 // reset the amp's size and start chasing Mario. 93 if (o->oTimer > 90) { 94 cur_obj_scale(1.0f); 95 o->oAction = HOMING_AMP_ACT_CHASE; 96 o->oAmpYPhase = 0; 97 } 98 } 99 100 /** 101 * Chase Mario. 102 */ 103 static void homing_amp_chase_loop(void) { 104 // Lock on to Mario if he ever goes within 11.25 degrees of the amp's line of sight 105 if ((o->oAngleToMario - 0x400 < o->oMoveAngleYaw) 106 && (o->oMoveAngleYaw < o->oAngleToMario + 0x400)) { 107 o->oHomingAmpLockedOn = TRUE; 108 o->oTimer = 0; 109 } 110 111 // If the amp is locked on to Mario, start "chasing" him by moving 112 // in a straight line at 15 units/second for 32 frames. 113 if (o->oHomingAmpLockedOn == TRUE) { 114 o->oForwardVel = 15.0f; 115 116 // Move the amp's average Y (the Y value it oscillates around) to align with 117 // Mario's head. Mario's graphics' Y + 150 is around the top of his head. 118 // Note that the average Y will slowly go down to approach his head if the amp 119 // is above his head, but if the amp is below it will instantly snap up. 120 if (o->oHomingAmpAvgY > gMarioObject->header.gfx.pos[1] + 150.0f) { 121 o->oHomingAmpAvgY -= 10.0f; 122 } else { 123 o->oHomingAmpAvgY = gMarioObject->header.gfx.pos[1] + 150.0f; 124 } 125 126 if (o->oTimer > 30) { 127 o->oHomingAmpLockedOn = FALSE; 128 } 129 } else { 130 // If the amp is not locked on to Mario, move forward at 10 units/second 131 // while curving towards him. 132 o->oForwardVel = 10.0f; 133 134 obj_turn_toward_object(o, gMarioObject, 16, 0x400); 135 136 // The amp's average Y will approach Mario's graphical Y position + 250 137 // at a rate of 10 units per frame. Interestingly, this is different from 138 // the + 150 used while chasing him. Could this be a typo? 139 if (o->oHomingAmpAvgY < gMarioObject->header.gfx.pos[1] + 250.0f) { 140 o->oHomingAmpAvgY += 10.0f; 141 } 142 } 143 144 // The amp's position will sinusoidally oscillate 40 units around its average Y. 145 o->oPosY = o->oHomingAmpAvgY + sins(o->oAmpYPhase * 0x400) * 20.0f; 146 147 // Handle attacks 148 check_amp_attack(); 149 150 // Give up if Mario goes further than 1500 units from the amp's original position 151 if (!is_point_within_radius_of_mario(o->oHomeX, o->oHomeY, o->oHomeZ, 1500)) { 152 o->oAction = HOMING_AMP_ACT_GIVE_UP; 153 } 154 } 155 156 /** 157 * Give up on chasing Mario. 158 */ 159 static void homing_amp_give_up_loop(void) { 160 UNUSED u8 filler[8]; 161 162 // Move forward for 152 frames 163 o->oForwardVel = 15.0f; 164 165 if (o->oTimer > 150) { 166 // Hide the amp and reset it back to its inactive state 167 o->oPosX = o->oHomeX; 168 o->oPosY = o->oHomeY; 169 o->oPosZ = o->oHomeZ; 170 o->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE; 171 o->oAction = HOMING_AMP_ACT_INACTIVE; 172 o->oAnimState = 0; 173 o->oForwardVel = 0.0f; 174 o->oHomingAmpAvgY = o->oHomeY; 175 } 176 } 177 178 /** 179 * Cool down after a successful attack, shared by both types of amp. 180 */ 181 static void amp_attack_cooldown_loop(void) { 182 // Turn intangible and wait for 90 frames before chasing Mario again after hitting him. 183 o->header.gfx.animInfo.animFrame += 2; 184 o->oForwardVel = 0.0f; 185 186 cur_obj_become_intangible(); 187 188 if (o->oTimer > 30) { 189 o->oAnimState = 0; 190 } 191 192 if (o->oTimer > 90) { 193 o->oAnimState = 1; 194 cur_obj_become_tangible(); 195 o->oAction = HOMING_AMP_ACT_CHASE; 196 } 197 } 198 199 /** 200 * Homing amp update function. 201 */ 202 void bhv_homing_amp_loop(void) { 203 switch (o->oAction) { 204 case HOMING_AMP_ACT_INACTIVE: 205 if (is_point_within_radius_of_mario(o->oHomeX, o->oHomeY, o->oHomeZ, 800) == TRUE) { 206 // Make the amp start to appear, and un-hide it. 207 o->oAction = HOMING_AMP_ACT_APPEAR; 208 o->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE; 209 } 210 break; 211 212 case HOMING_AMP_ACT_APPEAR: 213 homing_amp_appear_loop(); 214 break; 215 216 case HOMING_AMP_ACT_CHASE: 217 homing_amp_chase_loop(); 218 cur_obj_play_sound_1(SOUND_AIR_AMP_BUZZ); 219 break; 220 221 case HOMING_AMP_ACT_GIVE_UP: 222 homing_amp_give_up_loop(); 223 break; 224 225 case HOMING_AMP_ACT_ATTACK_COOLDOWN: 226 amp_attack_cooldown_loop(); 227 break; 228 } 229 230 object_step(); 231 232 // Oscillate 233 o->oAmpYPhase++; 234 } 235 236 /** 237 * Circling amp initialization function. 238 */ 239 void bhv_circling_amp_init(void) { 240 o->oHomeX = o->oPosX; 241 o->oHomeY = o->oPosY; 242 o->oHomeZ = o->oPosZ; 243 o->oAnimState = 1; 244 245 // Determine the radius of the circling amp's circle 246 switch (o->oBhvParams2ndByte) { 247 case AMP_BP_ROT_RADIUS_200: 248 o->oAmpRadiusOfRotation = 200.0f; 249 break; 250 251 case AMP_BP_ROT_RADIUS_300: 252 o->oAmpRadiusOfRotation = 300.0f; 253 break; 254 255 case AMP_BP_ROT_RADIUS_400: 256 o->oAmpRadiusOfRotation = 400.0f; 257 break; 258 259 case AMP_BP_ROT_RADIUS_0: 260 break; 261 } 262 263 // Choose a random point along the amp's circle. 264 // The amp's move angle represents its angle along the circle. 265 o->oMoveAngleYaw = random_u16(); 266 267 o->oAction = AMP_ACT_IDLE; 268 } 269 270 /** 271 * Main update function for fixed amps. 272 * Fixed amps are a sub-species of circling amps, with circle radius 0. 273 */ 274 static void fixed_circling_amp_idle_loop(void) { 275 // Turn towards Mario, in both yaw and pitch. 276 f32 xToMario = gMarioObject->header.gfx.pos[0] - o->oPosX; 277 f32 yToMario = gMarioObject->header.gfx.pos[1] + 120.0f - o->oPosY; 278 f32 zToMario = gMarioObject->header.gfx.pos[2] - o->oPosZ; 279 s16 vAngleToMario = atan2s(sqrtf(xToMario * xToMario + zToMario * zToMario), -yToMario); 280 281 obj_turn_toward_object(o, gMarioObject, 19, 0x1000); 282 o->oFaceAnglePitch = approach_s16_symmetric(o->oFaceAnglePitch, vAngleToMario, 0x1000); 283 284 // Oscillate 40 units up and down. 285 // Interestingly, 0x458 (1112 in decimal) is a magic number with no apparent significance. 286 // It is slightly larger than the 0x400 figure used for homing amps, which makes 287 // fixed amps oscillate slightly quicker. 288 // Also, this uses the cosine, which starts at 1 instead of 0. 289 o->oPosY = o->oHomeY + coss(o->oAmpYPhase * 0x458) * 20.0f; 290 291 // Handle attacks 292 check_amp_attack(); 293 294 // Oscillate 295 o->oAmpYPhase++; 296 297 // Where there is a cur_obj_play_sound_1 call in the main circling amp update function, 298 // there is nothing here. Fixed amps are the only amps that never play 299 // the "amp buzzing" sound. 300 } 301 302 /** 303 * Main update function for regular circling amps. 304 */ 305 static void circling_amp_idle_loop(void) { 306 // Move in a circle. 307 // The Y oscillation uses the magic number 0x8B0 (2224), which is 308 // twice that of the fixed amp. In other words, circling amps will 309 // oscillate twice as fast. Also, unlike all other amps, circling 310 // amps oscillate 60 units around their average Y instead of 40. 311 o->oPosX = o->oHomeX + sins(o->oMoveAngleYaw) * o->oAmpRadiusOfRotation; 312 o->oPosZ = o->oHomeZ + coss(o->oMoveAngleYaw) * o->oAmpRadiusOfRotation; 313 o->oPosY = o->oHomeY + coss(o->oAmpYPhase * 0x8B0) * 30.0f; 314 o->oMoveAngleYaw += 0x400; 315 o->oFaceAngleYaw = o->oMoveAngleYaw + 0x4000; 316 317 // Handle attacks 318 check_amp_attack(); 319 320 // Oscillate 321 o->oAmpYPhase++; 322 323 cur_obj_play_sound_1(SOUND_AIR_AMP_BUZZ); 324 } 325 326 /** 327 * Circling amp update function. 328 * This calls the main update functions for both types of circling amps, 329 * and calls the common amp cooldown function when the amp is cooling down. 330 */ 331 void bhv_circling_amp_loop(void) { 332 switch (o->oAction) { 333 case AMP_ACT_IDLE: 334 if (o->oBhvParams2ndByte == AMP_BP_ROT_RADIUS_0) { 335 fixed_circling_amp_idle_loop(); 336 } else { 337 circling_amp_idle_loop(); 338 } 339 break; 340 341 case AMP_ACT_ATTACK_COOLDOWN: 342 amp_attack_cooldown_loop(); 343 break; 344 } 345 }