281 lines
9.2 KiB
C
281 lines
9.2 KiB
C
/**
|
|
* @file fish.inc.c
|
|
* Implements behaviour and spawning for fish located in the Secret Aquarium and other levels.
|
|
*/
|
|
|
|
/**
|
|
* Spawns fish with settings chosen by the field o->oBehParams2ndByte.
|
|
* These settings are animations, colour, and spawn quantity.
|
|
* Fish spawning restricted to within a set distance from Mario.
|
|
*/
|
|
void fish_act_spawn(void) {
|
|
s32 i;
|
|
s32 schoolQuantity;
|
|
s16 model;
|
|
f32 minDistToMario;
|
|
struct Animation **fishAnimation;
|
|
struct Object *fishObject;
|
|
switch (o->oBehParams2ndByte) {
|
|
|
|
// Blue fish with a quanitiy of twenty.
|
|
case 0:
|
|
model = MODEL_FISH; schoolQuantity = 20; minDistToMario = 1500.0f; fishAnimation = blue_fish_seg3_anims_0301C2B0;
|
|
break;
|
|
|
|
// Blue fish with a quanitiy of five.
|
|
case 1:
|
|
model = MODEL_FISH; schoolQuantity = 5; minDistToMario = 1500.0f; fishAnimation = blue_fish_seg3_anims_0301C2B0;
|
|
break;
|
|
|
|
// Cyan fish with a quanitiy of twenty.
|
|
case 2:
|
|
model = MODEL_CYAN_FISH; schoolQuantity = 20; minDistToMario = 1500.0f; fishAnimation = cyan_fish_seg6_anims_0600E264;
|
|
break;
|
|
|
|
// Cyan fish with a quanitiy of five.
|
|
case 3:
|
|
model = MODEL_CYAN_FISH; schoolQuantity = 5; minDistToMario = 1500.0f; fishAnimation = cyan_fish_seg6_anims_0600E264;
|
|
break;
|
|
}
|
|
/**
|
|
* Spawn and animate the schoolQuantity of fish if Mario enters render distance
|
|
* If the current level is Secret Aquarium, ignore this requirement.
|
|
* Fish moves at random with a max-range of 700.0f.
|
|
*/
|
|
if (o->oDistanceToMario < minDistToMario || gCurrLevelNum == LEVEL_SA) {
|
|
for (i = 0; i < schoolQuantity; i++) {
|
|
fishObject = spawn_object(o, model, bhvFish);
|
|
fishObject->oBehParams2ndByte = o->oBehParams2ndByte;
|
|
obj_init_animation_with_sound(fishObject, fishAnimation, 0);
|
|
obj_translate_xyz_random(fishObject, 700.0f);
|
|
}
|
|
o->oAction = FISH_ACT_ACTIVE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If the current level is not Secret Aquarium and the distance from Mario's
|
|
* Y coordinate is greater than 2000.0f then spawn another fish.
|
|
*/
|
|
void fish_act_respawn(void) {
|
|
if (gCurrLevelNum != LEVEL_SA) {
|
|
if (gMarioObject->oPosY - o->oPosY > 2000.0f) {
|
|
o->oAction = FISH_ACT_RESPAWN;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the next call of sFishActions to spawn a new fish.
|
|
*/
|
|
void fish_act_init(void) {
|
|
o->oAction = FISH_ACT_INIT;
|
|
}
|
|
|
|
/**
|
|
* An array of action methods chosen one at a time by bhv_fish_loop
|
|
*/
|
|
void (*sFishActions[])(void) = {
|
|
fish_act_spawn, fish_act_respawn, fish_act_init
|
|
};
|
|
|
|
void bhv_large_fish_group_loop(void) {
|
|
cur_obj_call_action_function(sFishActions);
|
|
}
|
|
|
|
/**
|
|
* Adjusts the Y coordinate of fish depending on circumstances
|
|
* such as proximity to other fish.
|
|
*/
|
|
void fish_regroup(s32 speed) {
|
|
// Store parentY for calculating when the fish should move towards oFishPosY.
|
|
f32 parentY = o->parentObj->oPosY;
|
|
|
|
// Sets speed of fish in SA to a leisurely speed of 10 when close to other fish.
|
|
if (gCurrLevelNum == LEVEL_SA) {
|
|
if (500.0f < absf(o->oPosY - o->oFishPosY)) {
|
|
speed = 10;
|
|
}
|
|
// Applies movement to fish.
|
|
o->oPosY = approach_f32_symmetric(o->oPosY, o->oFishPosY, speed);
|
|
/**
|
|
* Brings fish Y coordinate towards another fish if they are too far apart.
|
|
*/
|
|
} else if (parentY - 100.0f - o->oFishDepthDistance < o->oPosY
|
|
&& o->oPosY < parentY + 1000.0f + o->oFishDepthDistance) {
|
|
o->oPosY = approach_f32_symmetric(o->oPosY, o->oFishPosY, speed);
|
|
}
|
|
}
|
|
/**
|
|
* Moves fish forward at a random velocity and sets a random rotation.
|
|
*/
|
|
void fish_group_act_rotation(void) {
|
|
f32 fishY = o->oPosY - gMarioObject->oPosY;
|
|
|
|
// Alters speed of animation for natural movement.
|
|
if (o->oTimer < 10) {
|
|
cur_obj_init_animation_with_accel_and_sound(0, 2.0f);
|
|
} else {
|
|
cur_obj_init_animation_with_accel_and_sound(0, 1.0f);
|
|
}
|
|
|
|
/**
|
|
* Assigns oForwardVel, oFishRandomOffset, & oFishRespawnDistance to a random floats.
|
|
* Determines fish movement.
|
|
*/
|
|
if (o->oTimer == 0) {
|
|
o->oForwardVel = random_float() * 2 + 3.0f;
|
|
if (gCurrLevelNum == LEVEL_SA) {
|
|
o->oFishRandomOffset = random_float() * 700.0f;
|
|
} else {
|
|
o->oFishRandomOffset = random_float() * 100.0f;
|
|
}
|
|
o->oFishRespawnDistance = random_float() * 500 + 200.0f;
|
|
}
|
|
|
|
// Interact with Mario through rotating towards him.
|
|
o->oFishPosY = gMarioObject->oPosY + o->oFishRandomOffset;
|
|
cur_obj_rotate_yaw_toward(o->oAngleToMario, 0x400);
|
|
|
|
// If fish groups are too close, call fish_regroup()
|
|
if (o->oPosY < o->oFishWaterLevel - 50.0f) {
|
|
if (fishY < 0.0f) {
|
|
fishY = 0.0f - fishY;
|
|
}
|
|
if (fishY < 500.0f) {
|
|
fish_regroup(2);
|
|
} else {
|
|
fish_regroup(4);
|
|
}
|
|
} else {
|
|
o->oPosY = o->oFishWaterLevel - 50.0f;
|
|
if (fishY > 300.0f) {
|
|
o->oPosY = o->oPosY - 1.0f;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Delete current fish and create a new one if distance to Mario is
|
|
* smaller than his distance to oFishRespawnDistance + 150.0f.
|
|
*/
|
|
if (o->oDistanceToMario < o->oFishRespawnDistance + 150.0f) {
|
|
o->oAction = FISH_ACT_RESPAWN;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Interactively maneuver fish in relation to its distance from other fish and Mario.
|
|
*/
|
|
void fish_group_act_move(void) {
|
|
f32 fishY = o->oPosY - gMarioObject->oPosY;
|
|
// Marked unused, but has arithmetic performed on it in a useless manner.
|
|
UNUSED s32 distance;
|
|
o->oFishPosY = gMarioObject->oPosY + o->oFishRandomOffset;
|
|
/**
|
|
* Set fish variables to random floats when timer reaches zero and plays sound effect.
|
|
* This allows fish to move in seemingly natural patterns.
|
|
*/
|
|
if (o->oTimer == 0) {
|
|
o->oFishActiveDistance = random_float() * 300.0f;
|
|
o->oFishRandomSpeed = random_float() * 1024.0f + 1024.0f;
|
|
o->oFishRandomVel = random_float() * 4.0f + 8.0f + 5.0f;
|
|
if (o->oDistanceToMario < 600.0f) {
|
|
distance = 1;
|
|
} else {
|
|
distance = (s32)(1.0 / (o->oDistanceToMario / 600.0));
|
|
}
|
|
distance *= 127;
|
|
cur_obj_play_sound_2(SOUND_GENERAL_MOVING_WATER);
|
|
}
|
|
// Enable fish animation in a natural manner.
|
|
if (o->oTimer < 20) {
|
|
cur_obj_init_animation_with_accel_and_sound(0, 4.0f);
|
|
} else {
|
|
cur_obj_init_animation_with_accel_and_sound(0, 1.0f);
|
|
}
|
|
// Set randomized forward velocity so fish have differing velocities
|
|
if (o->oForwardVel < o->oFishRandomVel) {
|
|
o->oForwardVel = o->oForwardVel + 0.5;
|
|
}
|
|
o->oFishPosY = gMarioObject->oPosY + o->oFishRandomOffset;
|
|
// Rotate fish away from Mario.
|
|
cur_obj_rotate_yaw_toward(o->oAngleToMario + 0x8000, o->oFishRandomSpeed);
|
|
// If fish groups are too close, call fish_regroup()
|
|
if (o->oPosY < o->oFishWaterLevel - 50.0f) {
|
|
if (fishY < 0.0f) {
|
|
fishY = 0.0f - fishY;
|
|
}
|
|
if (fishY < 500.0f) {
|
|
fish_regroup(2);
|
|
} else {
|
|
fish_regroup(4);
|
|
}
|
|
} else {
|
|
o->oPosY = o->oFishWaterLevel - 50.0f;
|
|
if (fishY > 300.0f) {
|
|
o->oPosY -= 1.0f;
|
|
}
|
|
}
|
|
// If distance to Mario is too great, then set fish to active.
|
|
if (o->oDistanceToMario > o->oFishActiveDistance + 500.0f) {
|
|
o->oAction = FISH_ACT_ACTIVE;
|
|
}
|
|
}
|
|
/**
|
|
* Animate fish and alter scaling at random for a magnifying effect from the water.
|
|
*/
|
|
void fish_group_act_animate(void) {
|
|
cur_obj_init_animation_with_accel_and_sound(0, 1.0f);
|
|
o->header.gfx.unk38.animFrame = (s16)(random_float() * 28.0f);
|
|
o->oFishDepthDistance = random_float() * 300.0f;
|
|
cur_obj_scale(random_float() * 0.4 + 0.8);
|
|
o->oAction = FISH_ACT_ACTIVE;
|
|
}
|
|
|
|
void (*sFishGroupActions[])(void) = {
|
|
fish_group_act_animate, fish_group_act_rotation, fish_group_act_move
|
|
};
|
|
|
|
/**
|
|
* Main loop for fish
|
|
*/
|
|
void bhv_fish_loop(void)
|
|
{
|
|
UNUSED s32 unused[4];
|
|
cur_obj_scale(1.0f);
|
|
|
|
/**
|
|
* Tracks water level to delete fish outside of bounds.
|
|
* In SA oFishWaterLevel is set to zero because fish cannot exit the water.
|
|
* This prevents accidental deletion.
|
|
*/
|
|
o->oFishWaterLevel = find_water_level(o->oPosX, o->oPosZ);
|
|
if (gCurrLevelNum == LEVEL_SA) {
|
|
o->oFishWaterLevel = 0.0f;
|
|
}
|
|
// Apply hitbox and resolve wall collisions
|
|
o->oWallHitboxRadius = 30.0f;
|
|
cur_obj_resolve_wall_collisions();
|
|
|
|
// Delete fish below the water depth bounds of -10000.0f.
|
|
if (gCurrLevelNum != LEVEL_UNKNOWN_32) {
|
|
if (o->oFishWaterLevel < -10000.0f) {
|
|
obj_mark_for_deletion(o);
|
|
return;
|
|
}
|
|
|
|
// Unreachable code, perhaps for debugging or testing.
|
|
} else {
|
|
o->oFishWaterLevel = 1000.0f;
|
|
}
|
|
|
|
// Call fish action methods and apply physics engine.
|
|
cur_obj_call_action_function(sFishGroupActions);
|
|
cur_obj_move_using_fvel_and_gravity();
|
|
|
|
// If the parent object has action set to two, then delete the fish object.
|
|
if (o->parentObj->oAction == FISH_ACT_RESPAWN) {
|
|
obj_mark_for_deletion(o);
|
|
}
|
|
}
|