201 lines
6.5 KiB
C
201 lines
6.5 KiB
C
|
|
/**
|
|
* Behavior for bhvSpiny.
|
|
* When spawned by lakitu, its parent object is the lakitu.
|
|
* Lakitu comes before it spawned spinies in processing order.
|
|
*/
|
|
|
|
/**
|
|
* Hitbox for spiny both while thrown and walking. The interaction type is
|
|
* changed to INTERACT_UNKNOWN_08 while walking.
|
|
*/
|
|
static struct ObjectHitbox sSpinyHitbox = {
|
|
/* interactType: */ INTERACT_MR_BLIZZARD,
|
|
/* downOffset: */ 0,
|
|
/* damageOrCoinValue: */ 2,
|
|
/* health: */ 0,
|
|
/* numLootCoins: */ 0,
|
|
/* radius: */ 80,
|
|
/* height: */ 50,
|
|
/* hurtboxRadius: */ 40,
|
|
/* hurtboxHeight: */ 40,
|
|
};
|
|
|
|
/**
|
|
* Attack handlers for spiny while walking.
|
|
*/
|
|
static u8 sSpinyWalkAttackHandlers[] = {
|
|
/* ATTACK_PUNCH: */ ATTACK_HANDLER_KNOCKBACK,
|
|
/* ATTACK_KICK_OR_TRIP: */ ATTACK_HANDLER_KNOCKBACK,
|
|
/* ATTACK_FROM_ABOVE: */ ATTACK_HANDLER_NOP,
|
|
/* ATTACK_GROUND_POUND_OR_TWIRL: */ ATTACK_HANDLER_NOP,
|
|
/* ATTACK_FAST_ATTACK: */ ATTACK_HANDLER_KNOCKBACK,
|
|
/* ATTACK_FROM_BELOW: */ ATTACK_HANDLER_KNOCKBACK,
|
|
};
|
|
|
|
/**
|
|
* If the spiny was spawned by lakitu and mario is far away, despawn.
|
|
*/
|
|
static s32 spiny_check_active(void) {
|
|
if (o->parentObj != o) {
|
|
if (o->oDistanceToMario > 2500.0f) {
|
|
//! It's possible for the lakitu to despawn while the spiny still
|
|
// references it. This line allows us to decrement the 0x1B field
|
|
// in an object that loads into the lakitu's former slot.
|
|
// This can be used in practice to corrupt a huge goomba to
|
|
// behave similar to a regular goomba.
|
|
// It can also be used on a bob-omb respawner to change its model
|
|
// to a butterfly or fish.
|
|
o->parentObj->oEnemyLakituNumSpinies -= 1;
|
|
obj_mark_for_deletion(o);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Walk around randomly, and dick around with oFlags, oMoveFlags, and oInteractType.
|
|
*/
|
|
static void spiny_act_walk(void) {
|
|
if (spiny_check_active()) {
|
|
cur_obj_update_floor_and_walls();
|
|
|
|
o->oGraphYOffset = -17.0f;
|
|
cur_obj_init_animation_with_sound(0);
|
|
|
|
if (o->oMoveFlags & OBJ_MOVE_MASK_ON_GROUND) {
|
|
// After touching the ground for the first time, stop. From now on,
|
|
// ensure that face angle and move angle agree
|
|
if (!(o->oFlags & OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW)) {
|
|
if (obj_forward_vel_approach(0.0f, 1.0f)) {
|
|
o->oFlags |= OBJ_FLAG_SET_FACE_YAW_TO_MOVE_YAW;
|
|
o->oMoveAngleYaw = o->oFaceAngleYaw;
|
|
}
|
|
} else {
|
|
obj_forward_vel_approach(1.0f, 0.2f);
|
|
}
|
|
|
|
if (o->oSpinyTurningAwayFromWall) {
|
|
o->oSpinyTurningAwayFromWall =
|
|
obj_resolve_collisions_and_turn(o->oSpinyTargetYaw, 0x80);
|
|
} else {
|
|
if (!(o->oSpinyTurningAwayFromWall =
|
|
obj_bounce_off_walls_edges_objects(&o->oSpinyTargetYaw))) {
|
|
// Walk and occasionally randomly change direction
|
|
if (o->oSpinyTimeUntilTurn != 0) {
|
|
o->oSpinyTimeUntilTurn -= 1;
|
|
} else {
|
|
o->oSpinyTargetYaw = o->oMoveAngleYaw + (s16) random_sign() * 0x2000;
|
|
o->oSpinyTimeUntilTurn = random_linear_offset(100, 100);
|
|
}
|
|
}
|
|
|
|
cur_obj_rotate_yaw_toward(o->oSpinyTargetYaw, 0x80);
|
|
}
|
|
|
|
} else if (o->oMoveFlags & OBJ_MOVE_HIT_WALL) {
|
|
// Bounce off walls while falling
|
|
o->oMoveAngleYaw = cur_obj_reflect_move_angle_off_wall();
|
|
}
|
|
|
|
cur_obj_move_standard(-78);
|
|
|
|
if (obj_handle_attacks(&sSpinyHitbox, SPINY_ACT_ATTACKED_MARIO, sSpinyWalkAttackHandlers)) {
|
|
// When attacked by mario, lessen the knockback
|
|
o->oAction = SPINY_ACT_WALK;
|
|
o->oForwardVel *= 0.1f;
|
|
o->oVelY *= 0.7f;
|
|
|
|
o->oMoveFlags = 0; // weird flex but okay
|
|
|
|
// Don't allow mario to punch the spiny two frames in a row?
|
|
o->oInteractType = INTERACT_MR_BLIZZARD;
|
|
} else {
|
|
o->oInteractType = INTERACT_UNKNOWN_08;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wait for the lakitu to throw the spiny. The spiny is placed in this action
|
|
* after being spawned by a lakitu.
|
|
*/
|
|
static void spiny_act_held_by_lakitu(void) {
|
|
o->oGraphYOffset = 15.0f;
|
|
cur_obj_init_animation_with_sound(0);
|
|
|
|
o->oParentRelativePosX = -50.0f;
|
|
o->oParentRelativePosY = 35.0f;
|
|
o->oParentRelativePosZ = -100.0f;
|
|
|
|
if (o->parentObj->prevObj == NULL) {
|
|
o->oAction = SPINY_ACT_THROWN_BY_LAKITU;
|
|
o->oMoveAngleYaw = o->parentObj->oFaceAngleYaw;
|
|
|
|
// Move more quickly if the lakitu is moving forward
|
|
o->oForwardVel =
|
|
o->parentObj->oForwardVel * coss(o->oMoveAngleYaw - o->parentObj->oMoveAngleYaw) + 10.0f;
|
|
o->oVelY = 30.0f;
|
|
|
|
o->oMoveFlags = 0; // you do you, spiny
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Spin around. After landing, enter the walk action.
|
|
*/
|
|
static void spiny_act_thrown_by_lakitu(void) {
|
|
if (spiny_check_active()) {
|
|
cur_obj_update_floor_and_walls();
|
|
|
|
o->oGraphYOffset = 15.0f;
|
|
o->oFaceAnglePitch -= 0x2000;
|
|
|
|
cur_obj_init_animation_with_sound(0);
|
|
|
|
if (o->oMoveFlags & OBJ_MOVE_LANDED) {
|
|
cur_obj_play_sound_2(SOUND_OBJ_SPINY_UNK59);
|
|
cur_obj_set_model(MODEL_SPINY);
|
|
obj_init_animation_with_sound(o, spiny_seg5_anims_05016EAC, 0);
|
|
o->oGraphYOffset = -17.0f;
|
|
|
|
o->oFaceAnglePitch = 0;
|
|
o->oAction = SPINY_ACT_WALK;
|
|
} else if (o->oMoveFlags & OBJ_MOVE_HIT_WALL) {
|
|
o->oMoveAngleYaw = cur_obj_reflect_move_angle_off_wall();
|
|
}
|
|
|
|
cur_obj_move_standard(-78);
|
|
|
|
if (obj_check_attacks(&sSpinyHitbox, o->oAction)) {
|
|
if (o->parentObj != o) {
|
|
o->parentObj->oEnemyLakituNumSpinies -= 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update function for bhvSpiny.
|
|
*/
|
|
void bhv_spiny_update(void) {
|
|
// PARTIAL_UPDATE
|
|
|
|
switch (o->oAction) {
|
|
case SPINY_ACT_WALK:
|
|
spiny_act_walk();
|
|
break;
|
|
case SPINY_ACT_HELD_BY_LAKITU:
|
|
spiny_act_held_by_lakitu();
|
|
break;
|
|
case SPINY_ACT_THROWN_BY_LAKITU:
|
|
spiny_act_thrown_by_lakitu();
|
|
break;
|
|
case SPINY_ACT_ATTACKED_MARIO:
|
|
obj_move_for_one_second(SPINY_ACT_WALK);
|
|
break;
|
|
}
|
|
}
|