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

422 lines
12 KiB
C

// bobomb.c.inc
static struct ObjectHitbox sBobombHitbox = {
/* interactType: */ INTERACT_GRABBABLE,
/* downOffset: */ 0,
/* damageOrCoinValue: */ 0,
/* health: */ 0,
/* numLootCoins: */ 0,
/* radius: */ 65,
/* height: */ 113,
/* hurtboxRadius: */ 0,
/* hurtboxHeight: */ 0,
};
void bhv_bobomb_init(void) {
o->oGravity = 2.5;
o->oFriction = 0.8;
o->oBuoyancy = 1.3;
o->oInteractionSubtype = INT_SUBTYPE_KICKABLE;
}
void bobomb_spawn_coin(void) {
if (((o->oBehParams >> 8) & 0x1) == 0) {
obj_spawn_yellow_coins(o, 1);
o->oBehParams = 0x100;
set_object_respawn_info_bits(o, 1);
}
}
void bobomb_act_explode(void) {
struct Object *explosion;
if (o->oTimer < 5)
cur_obj_scale(1.0 + (f32) o->oTimer / 5.0);
else {
explosion = spawn_object(o, MODEL_EXPLOSION, bhvExplosion);
explosion->oGraphYOffset += 100.0f;
bobomb_spawn_coin();
create_respawner(MODEL_BLACK_BOBOMB, bhvBobomb, 3000);
o->activeFlags = 0;
}
}
void bobomb_check_interactions(void) {
obj_set_hitbox(o, &sBobombHitbox);
if ((o->oInteractStatus & INT_STATUS_INTERACTED) != 0)
{
if ((o->oInteractStatus & INT_STATUS_MARIO_UNK1) != 0)
{
o->oMoveAngleYaw = gMarioObject->header.gfx.angle[1];
o->oForwardVel = 25.0;
o->oVelY = 30.0;
o->oAction = BOBOMB_ACT_LAUNCHED;
}
if ((o->oInteractStatus & INT_STATUS_TOUCHED_BOB_OMB) != 0)
o->oAction = BOBOMB_ACT_EXPLODE;
o->oInteractStatus = 0;
}
if (obj_attack_collided_from_other_object(o) == 1)
o->oAction = BOBOMB_ACT_EXPLODE;
}
void bobomb_act_patrol(void) {
UNUSED s8 filler[4];
UNUSED s16 sp22;
s16 collisionFlags;
sp22 = o->header.gfx.unk38.animFrame;
o->oForwardVel = 5.0;
collisionFlags = object_step();
if ((obj_return_home_if_safe(o, o->oHomeX, o->oHomeY, o->oHomeZ, 400) == 1)
&& (obj_check_if_facing_toward_angle(o->oMoveAngleYaw, o->oAngleToMario, 0x2000) == 1)) {
o->oBobombFuseLit = 1;
o->oAction = BOBOMB_ACT_CHASE_MARIO;
}
obj_check_floor_death(collisionFlags, sObjFloor);
}
void bobomb_act_chase_mario(void) {
UNUSED u8 filler[4];
s16 sp1a, collisionFlags;
sp1a = ++o->header.gfx.unk38.animFrame;
o->oForwardVel = 20.0;
collisionFlags = object_step();
if (sp1a == 5 || sp1a == 16)
cur_obj_play_sound_2(SOUND_OBJ_BOBOMB_WALK);
obj_turn_toward_object(o, gMarioObject, 16, 0x800);
obj_check_floor_death(collisionFlags, sObjFloor);
}
void bobomb_act_launched(void) {
s16 collisionFlags = 0;
collisionFlags = object_step();
if ((collisionFlags & OBJ_COL_FLAG_GROUNDED) == OBJ_COL_FLAG_GROUNDED)
o->oAction = BOBOMB_ACT_EXPLODE; /* bit 0 */
}
void generic_bobomb_free_loop(void) {
switch (o->oAction) {
case BOBOMB_ACT_PATROL:
bobomb_act_patrol();
break;
case BOBOMB_ACT_LAUNCHED:
bobomb_act_launched();
break;
case BOBOMB_ACT_CHASE_MARIO:
bobomb_act_chase_mario();
break;
case BOBOMB_ACT_EXPLODE:
bobomb_act_explode();
break;
case BOBOMB_ACT_LAVA_DEATH:
if (obj_lava_death() == 1)
create_respawner(MODEL_BLACK_BOBOMB, bhvBobomb, 3000);
break;
case BOBOMB_ACT_DEATH_PLANE_DEATH:
o->activeFlags = 0;
create_respawner(MODEL_BLACK_BOBOMB, bhvBobomb, 3000);
break;
}
bobomb_check_interactions();
if (o->oBobombFuseTimer >= 151)
o->oAction = 3;
}
void stationary_bobomb_free_loop(void) {
switch (o->oAction) {
case BOBOMB_ACT_LAUNCHED:
bobomb_act_launched();
break;
case BOBOMB_ACT_EXPLODE:
bobomb_act_explode();
break;
case BOBOMB_ACT_LAVA_DEATH:
if (obj_lava_death() == 1)
create_respawner(MODEL_BLACK_BOBOMB, bhvBobomb, 3000);
break;
case BOBOMB_ACT_DEATH_PLANE_DEATH:
o->activeFlags = 0;
create_respawner(MODEL_BLACK_BOBOMB, bhvBobomb, 3000);
break;
}
bobomb_check_interactions();
if (o->oBobombFuseTimer >= 151)
o->oAction = 3;
}
void bobomb_free_loop(void) {
if (o->oBehParams2ndByte == BOBOMB_BP_STYPE_GENERIC)
generic_bobomb_free_loop();
else
stationary_bobomb_free_loop();
}
void bobomb_held_loop(void) {
o->header.gfx.node.flags |= GRAPH_RENDER_INVISIBLE;
cur_obj_init_animation(1);
cur_obj_set_pos_relative(gMarioObject, 0, 60.0f, 100.0);
o->oBobombFuseLit = 1;
if (o->oBobombFuseTimer >= 151) {
//! Although the Bob-omb's action is set to explode when the fuse timer expires,
// bobomb_act_explode() will not execute until the bob-omb's held state changes.
// This allows the Bob-omb to be regrabbed indefinitely.
gMarioObject->oInteractStatus |= INT_STATUS_MARIO_DROP_OBJECT;
o->oAction = BOBOMB_ACT_EXPLODE;
}
}
void bobomb_dropped_loop(void) {
cur_obj_get_dropped();
o->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE;
cur_obj_init_animation(0);
o->oHeldState = 0;
o->oAction = BOBOMB_ACT_PATROL;
}
void bobomb_thrown_loop(void) {
cur_obj_enable_rendering_2();
o->header.gfx.node.flags &= ~GRAPH_RENDER_INVISIBLE;
o->oHeldState = 0;
o->oFlags &= ~0x8; /* bit 3 */
o->oForwardVel = 25.0;
o->oVelY = 20.0;
o->oAction = BOBOMB_ACT_LAUNCHED;
}
// sp18 = blinkTimer
void curr_obj_random_blink(s32 *blinkTimer) {
if (*blinkTimer == 0) {
if ((s16)(random_float() * 100.0f) == 0) {
o->oAnimState = 1;
*blinkTimer = 1;
}
} else {
(*blinkTimer)++;
if (*blinkTimer >= 6)
o->oAnimState = 0;
if (*blinkTimer >= 11)
o->oAnimState = 1;
if (*blinkTimer >= 16) {
o->oAnimState = 0;
*blinkTimer = 0;
}
}
}
void bhv_bobomb_loop(void) {
s8 dustPeriodMinus1;
if (is_point_within_radius_of_mario(o->oPosX, o->oPosY, o->oPosZ, 4000) != 0) {
switch (o->oHeldState) {
case HELD_FREE:
bobomb_free_loop();
break;
case HELD_HELD:
bobomb_held_loop();
break;
case HELD_THROWN:
bobomb_thrown_loop();
break;
case HELD_DROPPED:
bobomb_dropped_loop();
break;
}
curr_obj_random_blink(&o->oBobombBlinkTimer);
if (o->oBobombFuseLit == 1) {
if (o->oBobombFuseTimer >= 121)
dustPeriodMinus1 = 1;
else
dustPeriodMinus1 = 7;
if ((dustPeriodMinus1 & o->oBobombFuseTimer)
== 0) /* oBobombFuseTimer % 2 or oBobombFuseTimer % 8 */
spawn_object(o, MODEL_SMOKE, bhvBobombFuseSmoke);
cur_obj_play_sound_1(SOUND_AIR_BOBOMB_LIT_FUSE);
o->oBobombFuseTimer++;
}
}
}
void bhv_bobomb_fuse_smoke_init(void) {
o->oPosX += (s32)(random_float() * 80.0f) - 40;
o->oPosY += (s32)(random_float() * 80.0f) + 60;
o->oPosZ += (s32)(random_float() * 80.0f) - 40;
cur_obj_scale(1.2f);
}
void bhv_bobomb_buddy_init(void) {
o->oGravity = 2.5;
o->oFriction = 0.8;
o->oBuoyancy = 1.3;
o->oInteractionSubtype = INT_SUBTYPE_NPC;
}
void bobomb_buddy_act_idle(void) {
UNUSED u8 filler[4];
s16 sp1a = o->header.gfx.unk38.animFrame;
UNUSED s16 collisionFlags = 0;
o->oBobombBuddyPosXCopy = o->oPosX;
o->oBobombBuddyPosYCopy = o->oPosY;
o->oBobombBuddyPosZCopy = o->oPosZ;
collisionFlags = object_step();
if ((sp1a == 5) || (sp1a == 16))
cur_obj_play_sound_2(SOUND_OBJ_BOBOMB_WALK);
if (o->oDistanceToMario < 1000.0f)
o->oMoveAngleYaw = approach_s16_symmetric(o->oMoveAngleYaw, o->oAngleToMario, 0x140);
if (o->oInteractStatus == INT_STATUS_INTERACTED)
o->oAction = BOBOMB_BUDDY_ACT_TURN_TO_TALK;
}
/**
* Function for the Bob-omb Buddy cannon guy.
* dialogFirstText is the first dialogID called when Bob-omb Buddy
* starts to talk to Mario to prepare the cannon(s) for him.
* Then the camera goes to the nearest cannon, to play the "prepare cannon" cutscene
* dialogSecondText is called after Bob-omb Buddy has the cannon(s) ready and
* then tells Mario that is "Ready for blastoff".
*/
void bobomb_buddy_cannon_dialog(s16 dialogFirstText, s16 dialogSecondText) {
struct Object *cannonClosed;
s16 buddyText, cutscene;
switch (o->oBobombBuddyCannonStatus) {
case BOBOMB_BUDDY_CANNON_UNOPENED:
buddyText = cutscene_object_with_dialog(CUTSCENE_DIALOG, o, dialogFirstText);
if (buddyText != 0) {
save_file_set_cannon_unlocked();
cannonClosed = cur_obj_nearest_object_with_behavior(bhvCannonClosed);
if (cannonClosed != 0)
o->oBobombBuddyCannonStatus = BOBOMB_BUDDY_CANNON_OPENING;
else
o->oBobombBuddyCannonStatus = BOBOMB_BUDDY_CANNON_STOP_TALKING;
}
break;
case BOBOMB_BUDDY_CANNON_OPENING:
cannonClosed = cur_obj_nearest_object_with_behavior(bhvCannonClosed);
cutscene = cutscene_object(CUTSCENE_PREPARE_CANNON, cannonClosed);
if (cutscene == -1)
o->oBobombBuddyCannonStatus = BOBOMB_BUDDY_CANNON_OPENED;
break;
case BOBOMB_BUDDY_CANNON_OPENED:
buddyText = cutscene_object_with_dialog(CUTSCENE_DIALOG, o, dialogSecondText);
if (buddyText != 0)
o->oBobombBuddyCannonStatus = BOBOMB_BUDDY_CANNON_STOP_TALKING;
break;
case BOBOMB_BUDDY_CANNON_STOP_TALKING:
set_mario_npc_dialog(0);
o->activeFlags &= ~0x20; /* bit 5 */
o->oBobombBuddyHasTalkedToMario = BOBOMB_BUDDY_HAS_TALKED;
o->oInteractStatus = 0;
o->oAction = BOBOMB_BUDDY_ACT_IDLE;
o->oBobombBuddyCannonStatus = BOBOMB_BUDDY_CANNON_OPENED;
break;
}
}
void bobomb_buddy_act_talk(void) {
if (set_mario_npc_dialog(1) == 2) {
o->activeFlags |= 0x20; /* bit 5 */
switch (o->oBobombBuddyRole) {
case BOBOMB_BUDDY_ROLE_ADVICE:
if (cutscene_object_with_dialog(CUTSCENE_DIALOG, o, o->oBehParams2ndByte)
!= BOBOMB_BUDDY_BP_STYPE_GENERIC) {
set_mario_npc_dialog(0);
o->activeFlags &= ~0x20; /* bit 5 */
o->oBobombBuddyHasTalkedToMario = BOBOMB_BUDDY_HAS_TALKED;
o->oInteractStatus = 0;
o->oAction = BOBOMB_BUDDY_ACT_IDLE;
}
break;
case BOBOMB_BUDDY_ROLE_CANNON:
if (gCurrCourseNum == COURSE_BOB)
bobomb_buddy_cannon_dialog(DIALOG_004, DIALOG_105);
else
bobomb_buddy_cannon_dialog(DIALOG_047, DIALOG_106);
break;
}
}
}
void bobomb_buddy_act_turn_to_talk(void) {
s16 sp1e = o->header.gfx.unk38.animFrame;
if ((sp1e == 5) || (sp1e == 16))
cur_obj_play_sound_2(SOUND_OBJ_BOBOMB_WALK);
o->oMoveAngleYaw = approach_s16_symmetric(o->oMoveAngleYaw, o->oAngleToMario, 0x1000);
if ((s16) o->oMoveAngleYaw == (s16) o->oAngleToMario)
o->oAction = BOBOMB_BUDDY_ACT_TALK;
cur_obj_play_sound_2(SOUND_ACTION_READ_SIGN);
}
void bobomb_buddy_actions(void) {
switch (o->oAction) {
case BOBOMB_BUDDY_ACT_IDLE:
bobomb_buddy_act_idle();
break;
case BOBOMB_BUDDY_ACT_TURN_TO_TALK:
bobomb_buddy_act_turn_to_talk();
break;
case BOBOMB_BUDDY_ACT_TALK:
bobomb_buddy_act_talk();
break;
}
set_object_visibility(o, 3000);
}
void bhv_bobomb_buddy_loop(void) {
bobomb_buddy_actions();
curr_obj_random_blink(&o->oBobombBuddyBlinkTimer);
o->oInteractStatus = 0;
}