sm64pc/src/game/behaviors/water_bomb.inc.c

256 lines
8.2 KiB
C

/**
* Behaviors for bhvWaterBombSpawner, bhvWaterBomb, and bhvWaterBombShadow.
* The spawner spawns the water bombs that fall on mario from above. These ones
* start in the WATER_BOMB_ACT_INIT action.
* Water bombs can also be shot by cannons. These ones stay in the
* WATER_BOMB_ACT_SHOT_FROM_CANNON action.
* The water bomb shadow is only spawned by water bomb spawners.
*/
/**
* Hitbox for water bombs that are spawned by a water bomb spawner. The water
* bombs that are shot from cannons are intangible.
*/
static struct ObjectHitbox sWaterBombHitbox = {
/* interactType: */ INTERACT_MR_BLIZZARD,
/* downOffset: */ 25,
/* damageOrCoinValue: */ 1,
/* health: */ 99,
/* numLootCoins: */ 0,
/* radius: */ 80,
/* height: */ 50,
/* hurtboxRadius: */ 60,
/* hurtboxHeight: */ 50,
};
/**
* Update function for bhvWaterBombSpawner.
* Spawn water bombs targeting mario when he comes in range.
*/
void bhv_water_bomb_spawner_update(void) {
f32 latDistToMario;
f32 spawnerRadius;
spawnerRadius = 50 * (u16)(o->oBehParams >> 16) + 200.0f;
latDistToMario = lateral_dist_between_objects(o, gMarioObject);
// When mario is in range and a water bomb isn't already active
if (!o->oWaterBombSpawnerBombActive && latDistToMario < spawnerRadius
&& gMarioObject->oPosY - o->oPosY < 1000.0f) {
if (o->oWaterBombSpawnerTimeToSpawn != 0) {
o->oWaterBombSpawnerTimeToSpawn -= 1;
} else {
struct Object *waterBomb =
spawn_object_relative(0, 0, 2000, 0, o, MODEL_WATER_BOMB, bhvWaterBomb);
if (waterBomb != NULL) {
// Drop farther ahead of mario when he is moving faster
f32 waterBombDistToMario = 28.0f * gMarioStates[0].forwardVel + 100.0f;
waterBomb->oAction = WATER_BOMB_ACT_INIT;
waterBomb->oPosX =
gMarioObject->oPosX + waterBombDistToMario * sins(gMarioObject->oMoveAngleYaw);
waterBomb->oPosZ =
gMarioObject->oPosZ + waterBombDistToMario * coss(gMarioObject->oMoveAngleYaw);
spawn_object(waterBomb, MODEL_WATER_BOMB_SHADOW, bhvWaterBombShadow);
o->oWaterBombSpawnerBombActive = TRUE;
o->oWaterBombSpawnerTimeToSpawn = random_linear_offset(0, 50);
}
}
}
}
/**
* Spawn particles when the water bomb explodes.
*/
void water_bomb_spawn_explode_particles(s8 offsetY, s8 forwardVelRange, s8 velYBase) {
static struct SpawnParticlesInfo sWaterBombExplodeParticles = {
/* behParam: */ 0,
/* count: */ 5,
/* model: */ MODEL_BUBBLE,
/* offsetY: */ 20,
/* forwardVelBase: */ 20,
/* forwardVelRange: */ 60,
/* velYBase: */ 10,
/* velYRange: */ 10,
/* gravity: */ -2,
/* dragStrength: */ 0,
/* sizeBase: */ 35.0f,
/* sizeRange: */ 10.0f,
};
sWaterBombExplodeParticles.offsetY = offsetY;
sWaterBombExplodeParticles.forwardVelRange = forwardVelRange;
sWaterBombExplodeParticles.velYBase = velYBase;
cur_obj_spawn_particles(&sWaterBombExplodeParticles);
}
/**
* Enter the drop action with -40 y vel.
*/
static void water_bomb_act_init(void) {
cur_obj_play_sound_2(SOUND_OBJ_SOMETHING_LANDING);
o->oAction = WATER_BOMB_ACT_DROP;
o->oMoveFlags = 0;
o->oVelY = -40.0f;
}
/**
* Explode on impact, and otherwise bounce a few times on the ground and then
* explode.
*/
static void water_bomb_act_drop(void) {
f32 stretch;
obj_set_hitbox(o, &sWaterBombHitbox);
// Explode if touched or if hit water
if ((o->oInteractStatus & INT_STATUS_INTERACTED) || (o->oMoveFlags & OBJ_MOVE_ENTERED_WATER)) {
create_sound_spawner(SOUND_OBJ_DIVING_IN_WATER);
set_camera_shake_from_point(SHAKE_POS_SMALL, o->oPosX, o->oPosY, o->oPosZ);
o->oAction = WATER_BOMB_ACT_EXPLODE;
} else if (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) {
// On impact with the ground, begin getting squished
if (!o->oWaterBombOnGround) {
o->oWaterBombOnGround = TRUE;
if ((o->oWaterBombNumBounces += 1.0f) < 3.0f) {
cur_obj_play_sound_2(SOUND_OBJ_WATER_BOMB_BOUNCING);
} else {
create_sound_spawner(SOUND_OBJ_DIVING_IN_WATER);
}
set_camera_shake_from_point(SHAKE_POS_SMALL, o->oPosX, o->oPosY, o->oPosZ);
// Move toward mario
o->oMoveAngleYaw = o->oAngleToMario;
o->oForwardVel = 10.0f;
o->oWaterBombStretchSpeed = -40.0f;
}
// Make less of an attempt to unsquish on each bounce
o->oWaterBombStretchSpeed += 15.0f - o->oWaterBombNumBounces * 2.8f;
o->oWaterBombVerticalStretch += o->oWaterBombStretchSpeed * 0.01f;
// After being too squished, explode
if (o->oWaterBombVerticalStretch < -0.8f) {
o->oAction = WATER_BOMB_ACT_EXPLODE;
} else if (o->oWaterBombVerticalStretch > 0.1f) {
// Begin bounce trajectory
o->oVelY = 1.8f * o->oWaterBombStretchSpeed;
}
} else {
approach_f32_ptr(&o->oWaterBombVerticalStretch, 0.0f, 0.008f);
o->oWaterBombOnGround = FALSE;
}
o->header.gfx.scale[1] = o->oWaterBombVerticalStretch + 1.0f;
stretch = o->oWaterBombVerticalStretch;
if (o->oWaterBombNumBounces == 3.0f) {
stretch *= 4.0f;
}
o->header.gfx.scale[0] = o->header.gfx.scale[2] = 1.0f - stretch;
cur_obj_move_standard(78);
}
/**
* Spawn particles, then despawn. This action informs the water bomb shadow to
* despawn as well.
*/
static void water_bomb_act_explode(void) {
water_bomb_spawn_explode_particles(25, 60, 10);
o->parentObj->oWaterBombSpawnerBombActive = FALSE;
obj_mark_for_deletion(o);
}
/**
* Despawn after 100 frames.
*/
static void water_bomb_act_shot_from_cannon(void) {
static struct SpawnParticlesInfo sWaterBombCannonParticle = {
/* behParam: */ 0,
/* count: */ 1,
/* model: */ MODEL_BUBBLE,
/* offsetY: */ 236,
/* forwardVelBase: */ 20,
/* forwardVelRange: */ 5,
/* velYBase: */ 0,
/* velYRange: */ 0,
/* gravity: */ -2,
/* dragStrength: */ 0,
/* sizeBase: */ 20.0f,
/* sizeRange: */ 5.0f,
};
if (o->oTimer > 100) {
obj_mark_for_deletion(o);
} else {
if (o->oTimer < 7) {
if (o->oTimer == 1) {
water_bomb_spawn_explode_particles(-20, 10, 30);
}
cur_obj_spawn_particles(&sWaterBombCannonParticle);
}
if (o->header.gfx.scale[1] > 1.2f) {
o->header.gfx.scale[1] -= 0.1f;
}
o->header.gfx.scale[0] = o->header.gfx.scale[2] = 2.0f - o->header.gfx.scale[1];
cur_obj_set_pos_via_transform();
}
}
/**
* Update function for bhvWaterBomb.
*/
void bhv_water_bomb_update(void) {
if (o->oAction == WATER_BOMB_ACT_SHOT_FROM_CANNON) {
water_bomb_act_shot_from_cannon();
} else {
o->oGraphYOffset = 40.0f * o->header.gfx.scale[1];
cur_obj_update_floor_and_walls();
switch (o->oAction) {
case WATER_BOMB_ACT_INIT:
water_bomb_act_init();
break;
case WATER_BOMB_ACT_DROP:
water_bomb_act_drop();
break;
case WATER_BOMB_ACT_EXPLODE:
water_bomb_act_explode();
break;
}
}
}
/**
* Update function for bhvWaterBombShadow.
* Despawn when the parent water bomb does.
*/
void bhv_water_bomb_shadow_update(void) {
if (o->parentObj->oAction == WATER_BOMB_ACT_EXPLODE) {
obj_mark_for_deletion(o);
} else {
// TODO: What is happening here
f32 bombHeight = o->parentObj->oPosY - o->parentObj->oFloorHeight;
if (bombHeight > 500.0f) {
bombHeight = 500.0f;
}
obj_copy_pos(o, o->parentObj);
o->oPosY = o->parentObj->oFloorHeight + bombHeight;
obj_copy_scale(o, o->parentObj);
}
}