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

202 lines
5.8 KiB
C

/**
* Behavior for bhvEnemyLakitu.
* Lakitu comes before it spawned spinies in processing order.
* TODO: bhvCloud processing oredr
*/
/**
* Hitbox for evil lakitu.
*/
static struct ObjectHitbox sEnemyLakituHitbox = {
/* interactType: */ INTERACT_HIT_FROM_BELOW,
/* downOffset: */ 0,
/* damageOrCoinValue: */ 2,
/* health: */ 0,
/* numLootCoins: */ 5,
/* radius: */ 50,
/* height: */ 50,
/* hurtboxRadius: */ 40,
/* hurtboxHeight: */ 50,
};
/**
* Wait for mario to approach, then spawn the cloud and become visible.
*/
static void enemy_lakitu_act_uninitialized(void) {
if (o->oDistanceToMario < 2000.0f) {
spawn_object_relative_with_scale(CLOUD_BP_LAKITU_CLOUD, 0, 0, 0, 2.0f, o, MODEL_MIST, bhvCloud);
cur_obj_unhide();
o->oAction = ENEMY_LAKITU_ACT_MAIN;
}
}
/**
* Accelerate toward mario vertically.
*/
static void enemy_lakitu_update_vel_y(f32 offsetY) {
// In order to encourage oscillation, pass mario by a small margin before
// accelerating the opposite direction
f32 margin;
if (o->oVelY < 0.0f) {
margin = -3.0f;
} else {
margin = 3.0f;
}
if (o->oPosY < gMarioObject->oPosY + offsetY + margin) {
obj_y_vel_approach(4.0f, 0.4f);
} else {
obj_y_vel_approach(-4.0f, 0.4f);
}
}
/**
* Control speed based on distance to mario, turn toward mario, and change move
* angle toward mario.
*/
static void enemy_lakitu_update_speed_and_angle(void) {
f32 minSpeed;
s16 turnSpeed;
f32 distToMario = o->oDistanceToMario;
if (distToMario > 500.0f) {
distToMario = 500.0f;
}
// Move faster the farther away mario is and the faster mario is moving
if ((minSpeed = 1.2f * gMarioStates[0].forwardVel) < 8.0f) {
minSpeed = 8.0f;
}
o->oForwardVel = distToMario * 0.04f;
clamp_f32(&o->oForwardVel, minSpeed, 40.0f);
// Accelerate toward mario vertically
enemy_lakitu_update_vel_y(300.0f);
// Turn toward mario except right after throwing a spiny
if (o->oEnemyLakituFaceForwardCountdown != 0) {
o->oEnemyLakituFaceForwardCountdown -= 1;
} else {
obj_face_yaw_approach(o->oAngleToMario, 0x600);
}
// Change move angle toward mario faster when farther from mario
turnSpeed = (s16)(distToMario * 2);
clamp_s16(&turnSpeed, 0xC8, 0xFA0);
cur_obj_rotate_yaw_toward(o->oAngleToMario, turnSpeed);
}
/**
* When close enough to mario and facing roughly toward him, spawn a spiny and
* hold it, then enter the hold spiny sub-action.
*/
static void enemy_lakitu_sub_act_no_spiny(void) {
cur_obj_init_animation_with_sound(1);
if (o->oEnemyLakituSpinyCooldown != 0) {
o->oEnemyLakituSpinyCooldown -= 1;
} else if (o->oEnemyLakituNumSpinies < 3 && o->oDistanceToMario < 800.0f
&& abs_angle_diff(o->oAngleToMario, o->oFaceAngleYaw) < 0x4000) {
struct Object *spiny = spawn_object(o, MODEL_SPINY_BALL, bhvSpiny);
if (spiny != NULL) {
o->prevObj = spiny;
spiny->oAction = SPINY_ACT_HELD_BY_LAKITU;
obj_init_animation_with_sound(spiny, spiny_egg_seg5_anims_050157E4, 0);
o->oEnemyLakituNumSpinies += 1;
o->oSubAction = ENEMY_LAKITU_SUB_ACT_HOLD_SPINY;
o->oEnemyLakituSpinyCooldown = 30;
}
}
}
/**
* When close to mario and facing toward him or when mario gets far enough away,
* enter the throw spiny sub-action.
*/
static void enemy_lakitu_sub_act_hold_spiny(void) {
cur_obj_init_anim_extend(3);
if (o->oEnemyLakituSpinyCooldown != 0) {
o->oEnemyLakituSpinyCooldown -= 1;
}
// TODO: Check if anything interesting happens if we bypass this with speed
else if (o->oDistanceToMario > o->oDrawingDistance - 100.0f
|| (o->oDistanceToMario < 500.0f
&& abs_angle_diff(o->oAngleToMario, o->oFaceAngleYaw) < 0x2000)) {
o->oSubAction = ENEMY_LAKITU_SUB_ACT_THROW_SPINY;
o->oEnemyLakituFaceForwardCountdown = 20;
}
}
/**
* Throw the spiny, then enter the no spiny sub-action.
*/
static void enemy_lakitu_sub_act_throw_spiny(void) {
if (cur_obj_init_anim_check_frame(2, 2)) {
cur_obj_play_sound_2(SOUND_OBJ_EVIL_LAKITU_THROW);
o->prevObj = NULL;
}
if (cur_obj_check_if_near_animation_end()) {
o->oSubAction = ENEMY_LAKITU_SUB_ACT_NO_SPINY;
o->oEnemyLakituSpinyCooldown = random_linear_offset(100, 100);
}
}
/**
* Main update function.
*/
static void enemy_lakitu_act_main(void) {
cur_obj_play_sound_1(SOUND_AIR_LAKITU_FLY);
cur_obj_update_floor_and_walls();
enemy_lakitu_update_speed_and_angle();
if (o->oMoveFlags & OBJ_MOVE_HIT_WALL) {
o->oMoveAngleYaw = cur_obj_reflect_move_angle_off_wall();
}
obj_update_blinking(&o->oEnemyLakituBlinkTimer, 20, 40, 4);
switch (o->oSubAction) {
case ENEMY_LAKITU_SUB_ACT_NO_SPINY:
enemy_lakitu_sub_act_no_spiny();
break;
case ENEMY_LAKITU_SUB_ACT_HOLD_SPINY:
enemy_lakitu_sub_act_hold_spiny();
break;
case ENEMY_LAKITU_SUB_ACT_THROW_SPINY:
enemy_lakitu_sub_act_throw_spiny();
break;
}
cur_obj_move_standard(78);
// Die and drop held spiny when attacked by mario
if (obj_check_attacks(&sEnemyLakituHitbox, o->oAction)) {
// The spiny uses this as a signal to get thrown
o->prevObj = NULL;
}
}
/**
* Update function for bhvEnemyLakitu.
*/
void bhv_enemy_lakitu_update(void) {
// PARTIAL_UPDATE
treat_far_home_as_mario(2000.0f);
switch (o->oAction) {
case ENEMY_LAKITU_ACT_UNINITIALIZED:
enemy_lakitu_act_uninitialized();
break;
case ENEMY_LAKITU_ACT_MAIN:
enemy_lakitu_act_main();
break;
}
}