Implement & link ac_ins_hotaru

This commit is contained in:
Cuyler36
2025-01-28 15:10:19 -05:00
parent f6b18f4f28
commit 0439094cbe
2 changed files with 492 additions and 0 deletions
+17
View File
@@ -0,0 +1,17 @@
#ifndef AC_INS_HOTARU_H
#define AC_INS_HOTARU_H
#include "types.h"
#include "ac_insect_h.h"
#ifdef __cplusplus
extern "C" {
#endif
extern void aIHT_actor_init(ACTOR* actorx, GAME* game);
#ifdef __cplusplus
}
#endif
#endif
+475
View File
@@ -0,0 +1,475 @@
/**
* @file ac_ins_hotaru.c
* @brief Implements firefly (hotaru) behavior.
*
* Handles firefly actors which appear near water at night. They emit light,
* float around in patterns, and escape when disturbed. The fireflies' behavior
* includes smooth animation transitions and realistic light effects.
*/
#include "ac_ins_hotaru.h"
#include "m_name_table.h"
#include "m_common_data.h"
#include "m_player_lib.h"
// Actor-specific work variables
#define aIHT_TARGET_ANGLE(ins) ((ins)->s32_work0)
#define aIHT_FLOAT_ANGLE(ins) ((ins)->s32_work2)
#define aIHT_WALL_HIT(ins) ((ins)->s32_work3)
#define aIHT_TARGET_X(ins) ((ins)->f32_work0)
#define aIHT_TARGET_Z(ins) ((ins)->f32_work1)
#define aIHT_CENTER_X(ins) ((ins)->f32_work2)
#define aIHT_CENTER_Z(ins) ((ins)->f32_work3)
enum {
aIHT_ACT_AVOID,
aIHT_ACT_LET_ESCAPE,
aIHT_ACT_FLY,
aIHT_ACT_NUM
};
static void aIHT_actor_move(ACTOR* actorx, GAME* game);
static void aIHT_setupAction(aINS_INSECT_ACTOR* insect, int action, GAME* game);
/**
* Initializes the firefly actor.
* Handles spawn conditions near water and initial positioning.
*
* @param actorx Actor to initialize
* @param game Current game context
*/
extern void aIHT_actor_init(ACTOR* actorx, GAME* game) {
// Birth check positions relative to spawn point
static xyz_t hotaru_birth_check_data[] = {
// clang-format off
{ 0.0f, 0.0f, 0.0f },
{ mFI_UT_WORLDSIZE_X_F, 0.0f, 0.0f },
{ -mFI_UT_WORLDSIZE_X_F, 0.0f, 0.0f },
{ 0.0f, 0.0f, mFI_UT_WORLDSIZE_Z_F },
{ 0.0f, 0.0f, -mFI_UT_WORLDSIZE_Z_F },
{ 2 * mFI_UT_WORLDSIZE_X_F, 0.0f, 0.0f },
{ 2 * -mFI_UT_WORLDSIZE_X_F, 0.0f, 0.0f },
{ 0.0f, 0.0f, 2 * mFI_UT_WORLDSIZE_Z_F },
{ 0.0f, 0.0f, 2 * -mFI_UT_WORLDSIZE_Z_F },
// clang-format on
};
aINS_INSECT_ACTOR* insect = (aINS_INSECT_ACTOR*)actorx;
xyz_t pos = { 0.0f, 0.0f, 0.0f };
int spawn;
insect->bg_type = aINS_BG_CHECK_TYPE_REG_NO_ATTR;
actorx->mv_proc = aIHT_actor_move;
insect->item = ITM_INSECT27;
if (actorx->actor_specific == aINS_INIT_NORMAL) {
if (Common_Get(weather) == mEnv_WEATHER_RAIN) {
// Don't let fireflies exist in the rain
CLIP(insect_clip)->dt_proc(actorx, game);
} else {
u32 attr;
int i;
xyz_t birth_pos;
xyz_t_move(&birth_pos, &actorx->world.position);
for (i = 0, spawn = FALSE; i < ARRAY_COUNT(hotaru_birth_check_data); i++) {
xyz_t_move(&pos, &birth_pos);
pos.x += hotaru_birth_check_data[i].x;
pos.z += hotaru_birth_check_data[i].z;
attr = mCoBG_Wpos2Attribute(pos, NULL);
if (!spawn && mCoBG_CheckWaterAttribute_OutOfSea(attr) == TRUE) {
spawn = TRUE;
} else if (mCoBG_ExistHeightGap_KeepAndNow(pos) != TRUE) {
xyz_t_move(&actorx->world.position, &pos);
}
}
if (spawn && (pos.x || pos.z)) {
actorx->world.position.y = mCoBG_GetBgY_OnlyCenter_FromWpos(actorx->world.position, -70.0f);
actorx->home.position.y = actorx->world.position.y;
aIHT_setupAction(insect, aIHT_ACT_FLY, game);
aIHT_FLOAT_ANGLE(insect) = RANDOM_F(DEG2SHORT_ANGLE2(360.0f));
actorx->world.position.x += RANDOM_F(mFI_UT_WORLDSIZE_X_F) - mFI_UT_WORLDSIZE_HALF_X_F;
actorx->world.position.z += RANDOM_F(mFI_UT_WORLDSIZE_Z_F) - mFI_UT_WORLDSIZE_HALF_Z_F;
aIHT_TARGET_X(insect) = actorx->world.position.x;
aIHT_TARGET_Z(insect) = actorx->world.position.z;
insect->speed_step = 0.0f;
insect->target_speed = 10.0f + RANDOM_F(15.0f);
insect->continue_timer = RANDOM_F(5.0f);
// Get block center position
mFI_BkNum2WposXZ(&aIHT_CENTER_X(insect), &aIHT_CENTER_Z(insect), actorx->block_x, actorx->block_z);
aIHT_CENTER_X(insect) += mFI_BK_WORLDSIZE_HALF_X_F;
aIHT_CENTER_Z(insect) += mFI_BK_WORLDSIZE_HALF_Z_F;
} else {
CLIP(insect_clip)->dt_proc(actorx, game);
}
}
} else {
// aINS_INIT_RELEASE
insect->_1E0 = 0.0f;
insect->_1E4 = 0.0f;
insect->alpha0 = 255;
insect->alpha1 = 255;
insect->alpha2 = 0;
aIHT_setupAction(insect, aIHT_ACT_LET_ESCAPE, game);
}
}
/**
* Handles the firefly's animation cycles, including light intensity changes.
* Updates alpha values for smooth transitions.
*
* @param insect Pointer to the insect actor
*/
static void aIHT_anime_proc(aINS_INSECT_ACTOR* insect) {
// Animation sequence data for light pulsing
static f32 aIHT_anim_data[] = {
// clang-format off
0.0f,
1.0f,
2.0f,
3.0f,
2.0f,
1.0f,
// clang-format on
};
if (insect->timer > 0) {
insect->timer--;
insect->alpha0 += 25;
if (insect->alpha0 > 255) {
insect->alpha0 = 255;
}
insect->alpha1 -= 25;
if (insect->alpha1 < 0) {
insect->alpha1 = 0;
}
} else {
insect->timer = 10;
insect->_1E4 = aIHT_anim_data[insect->continue_timer];
insect->alpha0 = 0;
insect->alpha1 = 255;
insect->continue_timer++;
if (insect->continue_timer > 5) {
insect->continue_timer = 0;
}
insect->_1E0 = aIHT_anim_data[insect->continue_timer];
}
}
/**
* Checks and handles firefly collision with background elements.
* Adjusts position if hitting walls.
*
* @param actorx Actor to check collisions for
*/
static void aIHT_BGcheck(ACTOR* actorx) {
aINS_INSECT_ACTOR* insect = (aINS_INSECT_ACTOR*)actorx;
if (actorx->bg_collision_check.result.hit_wall & mCoBG_HIT_WALL_FRONT) {
if (aIHT_WALL_HIT(insect) == FALSE) {
u16 angle = actorx->world.angle.y;
f32 dx = aIHT_CENTER_X(insect) - actorx->world.position.x;
f32 dz = aIHT_CENTER_Z(insect) - actorx->world.position.z;
if (angle > DEG2SHORT_ANGLE2(45.0f) && angle <= DEG2SHORT_ANGLE2(135.0f)) {
if (dx >= -200.0f) {
aIHT_TARGET_X(insect) -= mFI_UT_WORLDSIZE_X_F;
}
} else if (angle > DEG2SHORT_ANGLE2(135.0f) && angle <= DEG2SHORT_ANGLE2(225.0f)) {
if (dz >= -200.0f) {
aIHT_TARGET_Z(insect) -= mFI_UT_WORLDSIZE_Z_F;
}
} else if (angle > DEG2SHORT_ANGLE2(225.0f) && angle <= DEG2SHORT_ANGLE2(315.0f)) {
if (dx <= 200.0f) {
aIHT_TARGET_X(insect) += mFI_UT_WORLDSIZE_X_F;
}
} else {
if (dz <= 200.0f) {
aIHT_TARGET_Z(insect) += mFI_UT_WORLDSIZE_Z_F;
}
}
aIHT_WALL_HIT(insect) = TRUE;
}
} else {
aIHT_WALL_HIT(insect) = FALSE;
}
}
/**
* Creates floating motion by manipulating Y position with sine waves.
*
* @param insect Pointer to the insect actor
* @param fuwafuwa_flag If TRUE, uses faster/higher amplitude motion
*/
static void aIHT_fuwafuwa(aINS_INSECT_ACTOR* insect, int fuwafuwa_flag) {
f32 pos_y;
f32 sin;
f32 ofs_y;
if (fuwafuwa_flag == FALSE) {
aIHT_FLOAT_ANGLE(insect) += (int)((RANDOM_F((f32)0x600) + (f32)0x200) * 0.5f);
} else {
aIHT_FLOAT_ANGLE(insect) += 0x400;
insect->tools_actor.actor_class.speed = 1.5f;
}
sin = sin_s(aIHT_FLOAT_ANGLE(insect));
pos_y = insect->tools_actor.actor_class.world.position.y;
ofs_y = sin * 10.0f;
insect->tools_actor.actor_class.position_speed.y = (insect->tools_actor.actor_class.home.position.y + ofs_y) - pos_y;
}
/**
* Handles the firefly's light effect processing.
* Controls light radius, color and transitions.
*
* @param insect Pointer to the insect actor
* @param game Current game context
*/
static void aIHT_light_proc(aINS_INSECT_ACTOR* insect, GAME* game) {
GAME_PLAY* play = (GAME_PLAY*)game;
if (insect->tools_actor.init_matrix == TRUE || insect->insect_flags.bit_1 == TRUE) {
s16 radius;
if (mPlib_get_player_actor_main_index(game) == mPlayer_INDEX_PUTAWAY_NET) {
if ((play->game_frame & 1) == 0 && (int)insect->_1E0 > 0) {
insect->_1E0 -= 1.0f;
}
} else {
insect->light_step = 0;
}
switch (insect->light_flag) {
case 0:
radius = fabsf(sin_s(insect->light_counter)) * 15.0f + 25.0f;
// clang-format off
Light_point_ct(
&insect->point_light,
insect->tools_actor.actor_class.world.position.x,
insect->tools_actor.actor_class.world.position.y - 35.0f,
insect->tools_actor.actor_class.world.position.z,
120, 255, 180,
radius
);
// clang-format on
insect->light_list = Global_light_list_new(game, &play->global_light, &insect->point_light);
insect->light_flag++;
break;
case 1:
insect->timer = 0;
insect->continue_timer = 0;
insect->light_flag++;
break;
case 2:
radius = fabsf(sin_s(insect->light_counter)) * 15.0f + 25.0f;
// clang-format off
Light_point_ct(
&insect->point_light,
insect->tools_actor.actor_class.world.position.x,
insect->tools_actor.actor_class.world.position.y - 35.0f,
insect->tools_actor.actor_class.world.position.z,
120, 255, 180,
radius
);
// clang-format on
break;
}
add_calc_short_angle2(&insect->light_counter, (s16)(insect->_1E0 * 0x1555), 1.0f - sqrtf(0.7f), 2500, 0);
}
}
/**
* Avoidance behavior when the firefly is disturbed.
*
* @param actorx Actor performing avoidance
* @param game Current game context
*/
static void aIHT_avoid(ACTOR* actorx, GAME* game) {
aINS_INSECT_ACTOR* insect = (aINS_INSECT_ACTOR*)actorx;
aIHT_fuwafuwa(insect, TRUE);
actorx->gravity += 0.75f;
actorx->position_speed.y += actorx->gravity;
}
/**
* Normal flying behavior for the firefly.
* Handles movement patterns and maintains position within valid area.
*
* @param actorx Actor to update
* @param game Current game context
*/
static void aIHT_fly(ACTOR* actorx, GAME* game) {
aINS_INSECT_ACTOR* insect = (aINS_INSECT_ACTOR*)actorx;
aIHT_BGcheck(actorx);
actorx->scale.x = 0.01f;
actorx->scale.y = 0.01f;
actorx->scale.z = 0.01f;
insect->target_speed = 1.5f;
insect->speed_step = 0.1f;
aIHT_fuwafuwa(insect, FALSE);
if ((fabsf(aIHT_TARGET_X(insect) - actorx->world.position.x) > 30.0f) || (fabsf(aIHT_TARGET_Z(insect) - actorx->world.position.z) > 30.0f)) {
xyz_t pos;
pos.x = aIHT_TARGET_X(insect);
pos.y = actorx->world.position.y;
pos.z = aIHT_TARGET_Z(insect);
aIHT_TARGET_ANGLE(insect) = search_position_angleY(&actorx->world.position, &pos) + (s16)(DEG2SHORT_ANGLE2(67.5f) - (int)RANDOM_F(DEG2SHORT_ANGLE2(135.0f)));
if (insect->patience > 90.0f) {
// The firefly is scared so fly away from the player if not too far from
// center of the block. Otherwise, fly towards center of the block.
f32 dx = aIHT_CENTER_X(insect) - actorx->world.position.x;
f32 dz = aIHT_CENTER_Z(insect) - actorx->world.position.z;
if (ABS(dx) > 240.0f || ABS(dz) > 240.0f) {
aIHT_TARGET_ANGLE(insect) = atans_table(dz, dx);
} else {
aIHT_TARGET_ANGLE(insect) = actorx->player_angle_y + DEG2SHORT_ANGLE2(180.0f);
}
insect->flag = TRUE;
} else if (insect->flag == TRUE && insect->patience < 10.0f) {
// If the firefly is not disturbed, fly towards
// the center of the current unit.
xyz_t center_pos;
if (mFI_Wpos2UtCenterWpos(&center_pos, actorx->world.position) == TRUE) {
aIHT_TARGET_X(insect) = center_pos.x;
aIHT_TARGET_Z(insect) = center_pos.z;
}
insect->flag = FALSE;
}
}
add_calc_short_angle2(&actorx->world.angle.y, aIHT_TARGET_ANGLE(insect), 1.0f - sqrtf(0.9f), 250, 0);
}
/**
* Common initialization for avoidance behaviors.
*
* @param insect Pointer to the insect actor
* @param game Current game context
*/
static void aIHT_avoid_init_sub(aINS_INSECT_ACTOR* insect, GAME* game) {
insect->life_time = 0;
insect->alpha_time = 80;
insect->tools_actor.actor_class.shape_info.rotation.x = 0;
if (insect->insect_flags.bit_1 == FALSE) {
ACTOR* playerx = GET_PLAYER_ACTOR_GAME_ACTOR(game);
if (playerx != NULL) {
insect->tools_actor.actor_class.world.angle.y = playerx->shape_info.rotation.y + (s16)RANDOM_CENTER_F(DEG2SHORT_ANGLE2(120.0f));
}
}
insect->insect_flags.bit_1 = TRUE;
aIHT_FLOAT_ANGLE(insect) = 0;
aIHT_TARGET_ANGLE(insect) = 0;
insect->tools_actor.actor_class.shape_info.draw_shadow = TRUE;
}
/**
* Initializes the avoid state.
*
* @param insect Pointer to the insect actor
* @param game Current game context
*/
static void aIHT_avoid_init(aINS_INSECT_ACTOR* insect, GAME* game) {
aIHT_avoid_init_sub(insect, game);
insect->_1E0 = 0.0f;
insect->_1E4 = insect->_1E0;
insect->alpha0 = 255;
insect->alpha1 = 255;
insect->alpha2 = 0;
}
/**
* Initializes the escape state.
*
* @param insect Pointer to the insect actor
* @param game Current game context
*/
static void aIHT_let_escape_init(aINS_INSECT_ACTOR* insect, GAME* game) {
aIHT_avoid_init_sub(insect, game);
insect->insect_flags.bit_2 = TRUE;
}
/**
* Initializes the normal flying state.
*
* @param insect Pointer to the insect actor
* @param game Current game context
*/
static void aIHT_fly_init(aINS_INSECT_ACTOR* insect, GAME* game) {
insect->flag = FALSE;
insect->tools_actor.actor_class.world.angle.x = 0;
}
typedef void (*aIHT_INIT_PROC)(aINS_INSECT_ACTOR* insect, GAME* game);
/**
* Sets up a new action state for the firefly.
*
* @param insect Pointer to the insect actor
* @param action New action to perform
* @param game Current game context
*/
static void aIHT_setupAction(aINS_INSECT_ACTOR* insect, int action, GAME* game) {
static aIHT_INIT_PROC init_proc[] = {
aIHT_avoid_init,
aIHT_let_escape_init,
aIHT_fly_init,
};
static aINS_ACTION_PROC act_proc[] = {
aIHT_avoid,
(aINS_ACTION_PROC)none_proc1,
aIHT_fly,
};
insect->action = action;
insect->action_proc = act_proc[action];
(*init_proc[action])(insect, game);
}
/**
* Main movement update function for firefly actors.
* Handles state transitions and updates light effects.
*
* @param actorx Actor to update
* @param game Current game context
*/
static void aIHT_actor_move(ACTOR* actorx, GAME* game) {
aINS_INSECT_ACTOR* insect = (aINS_INSECT_ACTOR*)actorx;
aIHT_light_proc(insect, game);
if (mPlib_get_player_actor_main_index(game) != mPlayer_INDEX_PUTAWAY_NET) {
aIHT_anime_proc(insect);
}
if ((ACTOR*)mPlib_Get_item_net_catch_label() == actorx) {
aIHT_setupAction(insect, aIHT_ACT_LET_ESCAPE, game);
} else if (insect->insect_flags.bit_3 == TRUE && insect->insect_flags.bit_2 == FALSE) {
aIHT_setupAction(insect, aIHT_ACT_LET_ESCAPE, game);
} else {
insect->action_proc(actorx, game);
}
}