fish.inc.c (8559B)
1 2 /** 3 * @file fish.inc.c 4 * Implements behaviour and spawning for fish located in the Secret Aquarium and other levels. 5 */ 6 7 /** 8 * Spawns fish with settings chosen by oBhvParams2ndByte. 9 * These settings are animations, color, and spawn quantity. 10 */ 11 static void fish_spawner_act_spawn(void) { 12 s32 i; 13 s32 schoolQuantity; 14 s16 model; 15 f32 minDistToMario; 16 const struct Animation * const *fishAnimation; 17 18 switch (o->oBhvParams2ndByte) { 19 // Cases need to be on one line to match with and without optimizations. 20 case FISH_SPAWNER_BP_MANY_BLUE: 21 model = MODEL_FISH; schoolQuantity = 20; minDistToMario = 1500.0f; fishAnimation = blue_fish_seg3_anims_0301C2B0; 22 break; 23 24 case FISH_SPAWNER_BP_FEW_BLUE: 25 model = MODEL_FISH; schoolQuantity = 5; minDistToMario = 1500.0f; fishAnimation = blue_fish_seg3_anims_0301C2B0; 26 break; 27 28 case FISH_SPAWNER_BP_MANY_CYAN: 29 model = MODEL_CYAN_FISH; schoolQuantity = 20; minDistToMario = 1500.0f; fishAnimation = cyan_fish_seg6_anims_0600E264; 30 break; 31 32 case FISH_SPAWNER_BP_FEW_CYAN: 33 model = MODEL_CYAN_FISH; schoolQuantity = 5; minDistToMario = 1500.0f; fishAnimation = cyan_fish_seg6_anims_0600E264; 34 break; 35 } 36 37 // Spawn and animate the schoolQuantity of fish if Mario enters render distance 38 // or the stage is Secret Aquarium. 39 // Fish moves randomly within a range of 700.0f. 40 if (o->oDistanceToMario < minDistToMario || gCurrLevelNum == LEVEL_SA) { 41 for (i = 0; i < schoolQuantity; i++) { 42 struct Object *fishObject = spawn_object(o, model, bhvFish); 43 fishObject->oBhvParams2ndByte = o->oBhvParams2ndByte; 44 obj_init_animation_with_sound(fishObject, fishAnimation, 0); 45 obj_translate_xyz_random(fishObject, 700.0f); 46 } 47 o->oAction = FISH_SPAWNER_ACT_IDLE; 48 } 49 } 50 51 /** 52 * Sets the spawner to respawn fish if the stage is not Secret Aquarium and 53 * Mario is more than 2000 units higher. 54 */ 55 static void fish_spawner_act_idle(void) { 56 if ((gCurrLevelNum != LEVEL_SA) && (gMarioObject->oPosY - o->oPosY > 2000.0f)) { 57 o->oAction = FISH_SPAWNER_ACT_RESPAWN; 58 } 59 } 60 61 /** 62 * Temp action that sets the action to spawn fish. This triggers the old fish to despawn. 63 */ 64 static void fish_spawner_act_respawn(void) { 65 o->oAction = FISH_SPAWNER_ACT_SPAWN; 66 } 67 68 static void (*sFishSpawnerActions[])(void) = { 69 fish_spawner_act_spawn, 70 fish_spawner_act_idle, 71 fish_spawner_act_respawn, 72 }; 73 74 void bhv_fish_spawner_loop(void) { 75 cur_obj_call_action_function(sFishSpawnerActions); 76 } 77 78 /** 79 * Allows the fish to swim vertically. 80 */ 81 static void fish_vertical_roam(s32 speed) { 82 f32 parentY = o->parentObj->oPosY; 83 84 // If the stage is Secret Aquarium, the fish can 85 // travel as far vertically as they wish. 86 if (gCurrLevelNum == LEVEL_SA) { 87 if (500.0f < absf(o->oPosY - o->oFishGoalY)) { 88 speed = 10; 89 } 90 o->oPosY = approach_f32_symmetric(o->oPosY, o->oFishGoalY, speed); 91 92 // Allow the fish to roam vertically if within 93 // range of the fish spawner. 94 } else if (parentY - 100.0f - o->oFishDepthDistance < o->oPosY 95 && o->oPosY < parentY + 1000.0f + o->oFishDepthDistance) { 96 o->oPosY = approach_f32_symmetric(o->oPosY, o->oFishGoalY, speed); 97 } 98 } 99 100 /** 101 * Fish action that randomly roams within a set range. 102 */ 103 static void fish_act_roam(void) { 104 f32 fishY = o->oPosY - gMarioObject->oPosY; 105 106 // Alters speed of animation for natural movement. 107 if (o->oTimer < 10) { 108 cur_obj_init_animation_with_accel_and_sound(0, 2.0f); 109 } else { 110 cur_obj_init_animation_with_accel_and_sound(0, 1.0f); 111 } 112 113 // Initializes some variables when the fish first begins roaming. 114 if (o->oTimer == 0) { 115 o->oForwardVel = random_float() * 2 + 3.0f; 116 if (gCurrLevelNum == LEVEL_SA) { 117 o->oFishHeightOffset = random_float() * 700.0f; 118 } else { 119 o->oFishHeightOffset = random_float() * 100.0f; 120 } 121 o->oFishRoamDistance = random_float() * 500 + 200.0f; 122 } 123 124 o->oFishGoalY = gMarioObject->oPosY + o->oFishHeightOffset; 125 126 // Rotate the fish towards Mario. 127 cur_obj_rotate_yaw_toward(o->oAngleToMario, 0x400); 128 129 if (o->oPosY < o->oFishWaterLevel - 50.0f) { 130 if (fishY < 0.0f) { 131 fishY = 0.0f - fishY; 132 } 133 if (fishY < 500.0f) { 134 fish_vertical_roam(2); 135 } else { 136 fish_vertical_roam(4); 137 } 138 139 // Don't let the fish leave the water vertically. 140 } else { 141 o->oPosY = o->oFishWaterLevel - 50.0f; 142 if (fishY > 300.0f) { 143 o->oPosY = o->oPosY - 1.0f; 144 } 145 } 146 147 // Flee from Mario if the fish gets too close. 148 if (o->oDistanceToMario < o->oFishRoamDistance + 150.0f) { 149 o->oAction = FISH_ACT_FLEE; 150 } 151 } 152 153 /** 154 * Interactively maneuver fish in relation to its distance from other fish and Mario. 155 */ 156 static void fish_act_flee(void) { 157 f32 fishY = o->oPosY - gMarioObject->oPosY; 158 UNUSED s32 distance; 159 160 o->oFishGoalY = gMarioObject->oPosY + o->oFishHeightOffset; 161 162 // Initialize some variables when the flee action first starts. 163 if (o->oTimer == 0) { 164 o->oFishActiveDistance = random_float() * 300.0f; 165 o->oFishYawVel = random_float() * 1024.0f + 1024.0f; 166 o->oFishGoalVel = random_float() * 4.0f + 8.0f + 5.0f; 167 168 if (o->oDistanceToMario < 600.0f) { 169 distance = 1; 170 } else { 171 distance = (s32)(1.0 / (o->oDistanceToMario / 600.0)); 172 } 173 174 distance *= 127; 175 176 cur_obj_play_sound_2(SOUND_GENERAL_MOVING_WATER); 177 } 178 179 // Speed the animation up over time. 180 if (o->oTimer < 20) { 181 cur_obj_init_animation_with_accel_and_sound(0, 4.0f); 182 } else { 183 cur_obj_init_animation_with_accel_and_sound(0, 1.0f); 184 } 185 186 // Accelerate over time. 187 if (o->oForwardVel < o->oFishGoalVel) { 188 o->oForwardVel = o->oForwardVel + 0.5; 189 } 190 191 o->oFishGoalY = gMarioObject->oPosY + o->oFishHeightOffset; 192 193 // Rotate fish away from Mario. 194 cur_obj_rotate_yaw_toward(o->oAngleToMario + 0x8000, o->oFishYawVel); 195 196 if (o->oPosY < o->oFishWaterLevel - 50.0f) { 197 if (fishY < 0.0f) { 198 fishY = 0.0f - fishY; 199 } 200 201 if (fishY < 500.0f) { 202 fish_vertical_roam(2); 203 } else { 204 fish_vertical_roam(4); 205 } 206 207 // Don't let the fish leave the water vertically. 208 } else { 209 o->oPosY = o->oFishWaterLevel - 50.0f; 210 if (fishY > 300.0f) { 211 o->oPosY -= 1.0f; 212 } 213 } 214 215 // If distance to Mario is too great, then set fish to active. 216 if (o->oDistanceToMario > o->oFishActiveDistance + 500.0f) { 217 o->oAction = FISH_ACT_ROAM; 218 } 219 } 220 221 /** 222 * Animate fish and alter scaling at random for a magnifying effect from the water. 223 */ 224 static void fish_act_init(void) { 225 cur_obj_init_animation_with_accel_and_sound(0, 1.0f); 226 o->header.gfx.animInfo.animFrame = (s16)(random_float() * 28.0f); 227 o->oFishDepthDistance = random_float() * 300.0f; 228 cur_obj_scale(random_float() * 0.4 + 0.8); 229 o->oAction = FISH_ACT_ROAM; 230 } 231 232 static void (*sFishActions[])(void) = { 233 fish_act_init, 234 fish_act_roam, 235 fish_act_flee, 236 }; 237 238 /** 239 * Main loop for fish 240 */ 241 void bhv_fish_loop(void) { 242 UNUSED u8 filler[16]; 243 cur_obj_scale(1.0f); 244 245 // oFishWaterLevel tracks if a fish has roamed out of water. 246 // This can't happen in Secret Aquarium, so set it to 0. 247 o->oFishWaterLevel = find_water_level(o->oPosX, o->oPosZ); 248 if (gCurrLevelNum == LEVEL_SA) { 249 o->oFishWaterLevel = 0.0f; 250 } 251 252 // Apply hitbox and resolve wall collisions 253 o->oWallHitboxRadius = 30.0f; 254 cur_obj_resolve_wall_collisions(); 255 256 // Delete fish if it's drifted to an area with no water. 257 if (gCurrLevelNum != LEVEL_UNKNOWN_32) { 258 if (o->oFishWaterLevel < FLOOR_LOWER_LIMIT_MISC) { 259 obj_mark_for_deletion(o); 260 return; 261 } 262 263 // Unreachable code, perhaps for debugging or testing. 264 } else { 265 o->oFishWaterLevel = 1000.0f; 266 } 267 268 // Call fish action methods and apply physics engine. 269 cur_obj_call_action_function(sFishActions); 270 cur_obj_move_using_fvel_and_gravity(); 271 272 // If the parent object has action set to two, then delete the fish object. 273 if (o->parentObj->oAction == FISH_SPAWNER_ACT_RESPAWN) { 274 obj_mark_for_deletion(o); 275 } 276 }