Implement & link ac_ins_tonbo

This commit is contained in:
Cuyler36
2025-01-26 16:14:51 -05:00
parent aadfac0914
commit 44227e89a6
5 changed files with 878 additions and 1 deletions
+1 -1
View File
@@ -1003,7 +1003,7 @@ config.libs = [
Object(NonMatching, "actor/ac_ins_mino.c"),
Object(NonMatching, "actor/ac_ins_semi.c"),
Object(NonMatching, "actor/ac_ins_tentou.c"),
Object(NonMatching, "actor/ac_ins_tonbo.c"),
Object(Matching, "actor/ac_ins_tonbo.c"),
Object(Matching, "actor/ac_insect.c"),
Object(Matching, "actor/ac_intro_demo.c"),
Object(Matching, "actor/ac_kago.c"),
+17
View File
@@ -0,0 +1,17 @@
#ifndef AC_INS_TONBO_H
#define AC_INS_TONBO_H
#include "types.h"
#include "ac_insect_h.h"
#ifdef __cplusplus
extern "C" {
#endif
extern void aITB_actor_init(ACTOR* actorx, GAME* game);
#ifdef __cplusplus
}
#endif
#endif
+10
View File
@@ -88,6 +88,16 @@ enum {
aINS_PROGRAM_NUM
};
enum {
aINS_BG_CHECK_TYPE_NONE,
aINS_BG_CHECK_TYPE_REG_ATTR,
aINS_BG_CHECK_TYPE_REG_NO_ATTR,
aINS_BG_CHECK_TYPE_NO_UNIT_COLUMN_ATTR,
aINS_BG_CHECK_TYPE_NO_UNIT_COLUMN_NO_ATTR,
aINS_BG_CHECK_TYPE_NUM
};
/* sizeof(aINS_Init_c) == 0x18 */
typedef struct insect_init_s {
/* 0x00 */ int insect_type;
+3
View File
@@ -29,6 +29,9 @@ extern "C" {
/* Macro to generate a random float in the range of [0, n) */
#define RANDOM_F(n) (fqrand() * (f32)(n))
/* Macro to generate a random float in range of [-n/2, n/2) */
#define RANDOM_CENTER_F(n) ((fqrand() - 0.5f) * (f32)(n))
/* Macro to generate a random integer in the range of [0, n) */
#define RANDOM(n) ((int)RANDOM_F(n))
+847
View File
@@ -0,0 +1,847 @@
/**
* @file ac_ins_tonbo.c
* @brief Implementation of the dragonfly actor.
*
* This file contains the functions and logic for initializing, updating, and controlling
* the behavior of dragonfly (tonbo) actors.
*/
#include "ac_ins_tonbo.h"
#include "m_name_table.h"
#include "m_common_data.h"
#include "m_player_lib.h"
enum {
aITB_ACT_AVOID,
aITB_ACT_LET_ESCAPE,
aITB_ACT_FLY,
aITB_ACT_ONIYANMA_FLY,
aITB_ACT_WAIT,
aITB_ACT_TOUCH_WATER,
aITB_ACT_TOUCH_WATER_REVERSE,
aITB_ACT_HOVER_WAIT_ON_WATER,
aITB_ACT_FLY_ON_NOTICE,
aITB_ACT_REST_ON_NOTICE,
aITB_ACT_NUM
};
#define aITB_BG_CHECK_RESULT_NONE 0
#define aITB_BG_CHECK_RESULT_COLLIDE 1
#define aITB_BG_CHECK_RESULT_ESCAPE 2
#define aITB_STOP_TYPE_NONE 0
#define aITB_STOP_TYPE_COLLISION 1
#define aITB_STOP_TYPE_REST 2
// Speed settings
#define aITB_ONIYANMA_FLY_SPEED 4.0f // Banded dragonfly fly speed
#define aITB_DARNER_FLY_SPEED 2.4f // Darner dragonfly fly speed
#define aITB_COMMON_FLY_SPEED 2.0f // Common & red dragonfly fly speed
#define aITB_SPEED_VARIANCE 2.0f // Dragonfly speed variance
#define aITB_AVOID_SPEED 4.0f // Avoid speed
// Range settings
#define aITB_ONIYAMA_MAX_RANGE (12 * mFI_UNIT_BASE_SIZE_F) // Banded dragonfly max range
#define aITB_OTHER_MAX_RANGE (6 * mFI_UNIT_BASE_SIZE_F) // Other dragonfly max range
// Timer settings
#define aITB_DARNER_FLY_TIMER 24 // Darner dragonfly fly timer frames
#define aITB_OTHER_FLY_TIMER 20 // Common & red dragonfly fly timer frames
#define aITB_AVOID_TIMER 20 // Avoid timer frames
#define aITB_TOUCH_WATER_REVERSE_TIMER 6 // Touch water reverse timer frames
#define aITB_HOVER_WAIT_ON_WATER_TIMER 20 // Hover wait on water timer frames
#define aITB_REST_ON_NOTICE_TIMER 200 // Rest on notice timer frames
// Dragonfly-specific work variables
#define aITB_GET_HEIGHT(ins) ((ins)->f32_work0)
#define aITB_SET_HEIGHT(ins, h) ((ins)->f32_work0 = (h))
#define aITB_GET_RESERVE_DUMMY_HEIGHT(ins) ((ins)->f32_work1)
#define aITB_SET_RESERVE_DUMMY_HEIGHT(ins, h) ((ins)->f32_work1 = (h))
#define aITB_GET_BK_CENTER_X(ins) ((ins)->f32_work2)
#define aITB_GET_BK_CENTER_Z(ins) ((ins)->f32_work3)
#define aITB_GET_STOP_STATE(ins) ((ins)->s32_work0)
#define aITB_GET_REST_ANIME_TIMER(ins) ((ins)->s32_work2)
static void aITB_actor_move(ACTOR* actorx, GAME* game);
static void aITB_setupAction(aINS_INSECT_ACTOR* insect, int action, GAME* game);
static int aITB_check_live_condition(aINS_INSECT_ACTOR* insect);
/**
* Initializes the dragonfly actor.
*
* @param actorx Pointer to the actor to initialize.
* @param game Pointer to the game context.
*/
extern void aITB_actor_init(ACTOR* actorx, GAME* game) {
aINS_INSECT_ACTOR* ins = (aINS_INSECT_ACTOR*)actorx;
// Set movement procedure and gravity
actorx->mv_proc = &aITB_actor_move;
actorx->gravity = 0.1f;
ins->bg_type = aINS_BG_CHECK_TYPE_REG_ATTR;
// Assign item type based on insect type
switch (ins->type) {
case aINS_INSECT_TYPE_COMMON_DRAGONFLY:
ins->item = ITM_INSECT09;
break;
case aINS_INSECT_TYPE_RED_DRAGONFLY:
ins->item = ITM_INSECT10;
break;
case aINS_INSECT_TYPE_DARNER_DRAGONFLY:
ins->item = ITM_INSECT11;
break;
case aINS_INSECT_TYPE_BANDED_DRAGONFLY:
ins->item = ITM_INSECT12;
break;
}
ins->insect_flags.bit_4 = FALSE;
// Check if the dragonfly was released
if (actorx->actor_specific == 0) {
// Dragonfly was not released
if (aITB_check_live_condition(ins) == TRUE) {
int act;
// Set initial position and angle
aITB_SET_HEIGHT(ins, 52.0f + RANDOM_F(10.0f));
actorx->world.position.y = 20.0f + mCoBG_GetBgY_OnlyCenter_FromWpos(actorx->world.position, 0.0f);
actorx->world.angle.y = DEG2SHORT_ANGLE3(180.0f) - RANDOM_F(DEG2SHORT_ANGLE3(360.0f));
actorx->shape_info.rotation.y = actorx->world.angle.y;
mFI_BkNum2WposXZ(&aITB_GET_BK_CENTER_X(ins), &aITB_GET_BK_CENTER_Z(ins), actorx->block_x, actorx->block_z);
aITB_GET_BK_CENTER_X(ins) += mFI_BK_WORLDSIZE_HALF_X_F;
aITB_GET_BK_CENTER_Z(ins) += mFI_BK_WORLDSIZE_HALF_Z_F;
// Set action based on insect type
if (ins->type == aINS_INSECT_TYPE_BANDED_DRAGONFLY) {
act = aITB_ACT_ONIYANMA_FLY;
} else {
act = aITB_ACT_FLY;
}
aITB_setupAction(ins, act, game);
}
} else {
// Dragonfly was released
ACTOR* playerx = GET_PLAYER_ACTOR_GAME_ACTOR(game);
if (playerx != NULL) {
// Set initial rotation based on player's rotation
actorx->shape_info.rotation.y = actorx->world.angle.y = playerx->shape_info.rotation.y + (s16)RANDOM_CENTER_F(DEG2SHORT_ANGLE2(120.0f));
}
aITB_setupAction(ins, aITB_ACT_LET_ESCAPE, game);
}
}
/**
* Checks if the dragonfly can live in the current conditions.
*
* @param insect Pointer to the insect actor.
* @return TRUE if the dragonfly can live, FALSE otherwise.
*/
static int aITB_check_live_condition(aINS_INSECT_ACTOR* insect) {
int ret = TRUE;
// Check that the dragonfly isn't at exactly ground height
if (mCoBG_ExistHeightGap_KeepAndNow(insect->tools_actor.actor_class.world.position) == TRUE) {
ret = FALSE;
// Check for rain
} else if (Common_Get(weather) == mEnv_WEATHER_RAIN) {
ret = FALSE;
} else {
// Check for valid unit
mActor_name_t* fg_p = mFI_GetUnitFG(insect->tools_actor.actor_class.world.position);
if (fg_p == NULL || *fg_p == RSV_NO || ITEM_NAME_GET_TYPE(*fg_p) == NAME_TYPE_STRUCT) {
ret = FALSE;
}
}
// Mark for destruction if conditions are not met
if (ret == FALSE) {
insect->insect_flags.destruct = TRUE;
}
return ret;
}
/**
* Checks for background collisions and adjusts the dragonfly's angle.
*
* @param actorx Pointer to the actor.
* @param game Pointer to the game context.
* @return aITB_BG_CHECK_RESULT_COLLIDE if collision detected, aITB_BG_CHECK_RESULT_ESCAPE if collided 4+ times, aITB_BG_CHECK_RESULT_NONE otherwise.
*/
static int aITB_BGcheck(ACTOR* actorx, GAME* game) {
aINS_INSECT_ACTOR* ins = (aINS_INSECT_ACTOR*)actorx;
int ret = aITB_BG_CHECK_RESULT_NONE; // no collision
// Check for wall or ground collision
if (((actorx->bg_collision_check.result.hit_wall & mCoBG_HIT_WALL_FRONT) != 0 ||
(actorx->bg_collision_check.result.hit_attribute_wall & mCoBG_HIT_WALL_FRONT) != 0) ||
actorx->bg_collision_check.result.on_ground) {
s16 angle = 0;
int count = actorx->bg_collision_check.result.hit_wall_count;
int i;
if (count != 0) {
for (i = 0; i < actorx->bg_collision_check.result.hit_wall_count; i++) {
if (actorx->bg_collision_check.wall_info[i].type == mCoBG_WALL_TYPE0) {
angle = actorx->bg_collision_check.wall_info[i].angleY - (DEG2SHORT_ANGLE2(180.0f) + actorx->world.angle.y);
break;
}
}
}
// Adjust angle based on collision
if ((s16)(actorx->world.angle.y - angle) >= 0) {
angle += (int)RANDOM_F(DEG2SHORT_ANGLE2(45.0f));
} else {
angle -= (int)RANDOM_F(DEG2SHORT_ANGLE2(45.0f));
}
actorx->world.angle.y = angle;
ins->continue_timer++;
if (ins->continue_timer >= 4) {
// Dragonfly has collided with wall or ground more than four times,
// so treat it as trapped and set escape action
aITB_setupAction(ins, aITB_ACT_LET_ESCAPE, game);
ret = aITB_BG_CHECK_RESULT_ESCAPE;
} else {
// Dragonfly has collided with wall or ground
ret = aITB_BG_CHECK_RESULT_COLLIDE;
}
} else {
ins->continue_timer = 0;
}
return ret;
}
/**
* Controls the height of the dragonfly.
*
* @param actorx Pointer to the actor.
*/
static void aITB_height_ctrl(ACTOR* actorx) {
aINS_INSECT_ACTOR* insect = (aINS_INSECT_ACTOR*)actorx;
f32 height = mCoBG_GetBgY_OnlyCenter_FromWpos(insect->tools_actor.actor_class.world.position, 0.0f);
insect->tools_actor.actor_class.max_velocity_y = (aITB_GET_HEIGHT(insect) + height > insect->tools_actor.actor_class.world.position.y) ? 1.0f : -1.0f;
}
/**
* Checks if the dragonfly touches water.
*
* @param insect Pointer to the insect actor.
* @param game Pointer to the game context.
* @return TRUE if the dragonfly touches water, FALSE otherwise.
*/
static int aITB_check_water_touch(aINS_INSECT_ACTOR* insect, GAME* game) {
GAME_PLAY* play = (GAME_PLAY*)game;
int bx;
int bz;
int ret = FALSE;
if (mFI_Wpos2BlockNum(&bx, &bz, insect->tools_actor.actor_class.world.position) == TRUE) {
if (bz < FG_BLOCK_Z_NUM && (play->game_frame % 100) < 20) {
u32 attr = mCoBG_Wpos2Attribute(insect->tools_actor.actor_class.world.position, NULL);
if (mCoBG_CheckWaterAttribute_OutOfSea(attr) == TRUE) {
ret = TRUE;
}
}
}
return ret;
}
/**
* Controls the flying behavior of the dragonfly.
*
* @param insect Pointer to the insect actor.
* @param game Pointer to the game context.
*/
static void aITB_fly_ctrl(aINS_INSECT_ACTOR* insect, GAME* game) {
if (insect->tools_actor.actor_class.speed == insect->target_speed) {
if (insect->timer <= 0) {
if (F32_IS_ZERO(insect->tools_actor.actor_class.speed)) {
int action;
if (insect->type != aINS_INSECT_TYPE_BANDED_DRAGONFLY && aITB_check_water_touch(insect, game) == TRUE) {
action = aITB_ACT_TOUCH_WATER;
insect->flag = 1;
} else {
action = aITB_ACT_WAIT;
}
aITB_setupAction(insect, action, game);
} else {
insect->target_speed = 0.0f;
}
} else {
insect->timer--;
}
}
}
/**
* Sets the movement speed of the dragonfly.
*
* @param insect Pointer to the insect actor.
*/
static void aITB_move_spd_set(aINS_INSECT_ACTOR* insect) {
switch (insect->type) {
case aINS_INSECT_TYPE_BANDED_DRAGONFLY:
insect->target_speed = aITB_ONIYANMA_FLY_SPEED + RANDOM_F(aITB_SPEED_VARIANCE);
break;
case aINS_INSECT_TYPE_DARNER_DRAGONFLY:
insect->target_speed = aITB_DARNER_FLY_SPEED + RANDOM_F(aITB_SPEED_VARIANCE);
insect->timer = aITB_DARNER_FLY_TIMER;
break;
default:
insect->target_speed = aITB_COMMON_FLY_SPEED + RANDOM_F(aITB_SPEED_VARIANCE);
insect->timer = aITB_OTHER_FLY_TIMER;
break;
}
insect->speed_step = 0.2f;
}
/**
* Processes the animation of the dragonfly.
*
* @param insect Pointer to the insect actor.
*/
static void aITB_anime_proc(aINS_INSECT_ACTOR* insect) {
insect->_1E0 += 0.5f;
if (insect->_1E0 >= 2.0f) {
insect->_1E0 -= 2.0f;
}
}
/**
* Checks if the dragonfly is near a reserved dummy.
*
* @param insect Pointer to the insect actor.
* @return TRUE if near a reserved dummy, FALSE otherwise.
*/
static int aITB_check_reserve_dummy(aINS_INSECT_ACTOR* insect) {
int ret = FALSE;
if (insect->patience < 40.0f) {
mActor_name_t* fg_p = mFI_GetUnitFG(insect->tools_actor.actor_class.world.position);
if (fg_p != NULL && *fg_p == DUMMY_RESERVE) {
aITB_GET_STOP_STATE(insect) = aITB_STOP_TYPE_REST;
aITB_SET_RESERVE_DUMMY_HEIGHT(insect, mCoBG_Wpos2BgUtCenterHeight_AddColumn(insect->tools_actor.actor_class.world.position));
ret = TRUE;
}
}
return ret;
}
/**
* Checks for wall and ground collisions.
*
* @param insect Pointer to the insect actor.
* @return TRUE if collision detected, FALSE otherwise.
*/
static int aITB_check_wall_and_ground(aINS_INSECT_ACTOR* insect) {
int ret = FALSE;
if ((insect->tools_actor.actor_class.bg_collision_check.result.hit_wall & mCoBG_HIT_WALL_FRONT) != 0 ||
(insect->tools_actor.actor_class.bg_collision_check.result.hit_attribute_wall & mCoBG_HIT_WALL_FRONT) != 0 ||
insect->tools_actor.actor_class.bg_collision_check.result.on_ground) {
aITB_GET_STOP_STATE(insect) = aITB_STOP_TYPE_COLLISION;
ret = TRUE;
}
return ret;
}
/**
* Checks if the dragonfly should stop.
*
* @param insect Pointer to the insect actor.
* @return TRUE if the dragonfly should stop, FALSE otherwise.
*/
static int aITB_check_stop(aINS_INSECT_ACTOR* insect) {
int ret = FALSE;
aITB_GET_STOP_STATE(insect) = aITB_STOP_TYPE_NONE;
if (insect->type != aINS_INSECT_TYPE_BANDED_DRAGONFLY) {
if (aITB_check_reserve_dummy(insect) == TRUE) {
ret = TRUE;
} else if (aITB_check_wall_and_ground(insect) == TRUE) {
ret = TRUE;
}
}
return ret;
}
/**
* Avoid action for the dragonfly.
*
* @param actorx Pointer to the actor.
* @param game Pointer to the game context.
*/
static void aITB_avoid(ACTOR* actorx, GAME* game) {
aINS_INSECT_ACTOR* ins = (aINS_INSECT_ACTOR*)actorx;
if (ins->type != aINS_INSECT_TYPE_BANDED_DRAGONFLY) {
aITB_fly_ctrl(ins, game);
}
aITB_BGcheck(actorx, game);
add_calc_short_angle2(&actorx->shape_info.rotation.y, actorx->world.angle.y, 1.0f - sqrtf(0.7f), 2500, 0);
}
/**
* Let escape action for the dragonfly.
*
* @param actorx Pointer to the actor.
* @param game Pointer to the game context.
*/
static void aITB_let_escape(ACTOR* actorx, GAME* game) {
aINS_INSECT_ACTOR* ins = (aINS_INSECT_ACTOR*)actorx;
f32 gravity = actorx->gravity;
gravity *= 1.1f;
if (gravity > 12.0f) {
gravity = 12.0f;
}
actorx->gravity = gravity;
}
/**
* Fly action for the dragonfly.
*
* @param actorx Pointer to the actor.
* @param game Pointer to the game context.
*/
static void aITB_fly(ACTOR* actorx, GAME* game) {
aINS_INSECT_ACTOR* ins = (aINS_INSECT_ACTOR*)actorx;
add_calc_short_angle2(&actorx->shape_info.rotation.y, actorx->world.angle.y, 1.0f - sqrtf(0.7f), 2500, 0);
aITB_height_ctrl(actorx);
if (aITB_check_stop(ins) == TRUE) {
int action = aITB_ACT_WAIT;
if (aITB_GET_STOP_STATE(ins) == aITB_STOP_TYPE_REST) {
action = aITB_ACT_FLY_ON_NOTICE;
}
aITB_setupAction(ins, action, game);
} else {
aITB_fly_ctrl(ins, game);
}
}
/**
* Oniyanma fly action for the dragonfly.
*
* @param actorx Pointer to the actor.
* @param game Pointer to the game context.
*/
static void aITB_oniyanma_fly(ACTOR* actorx, GAME* game) {
aINS_INSECT_ACTOR* ins = (aINS_INSECT_ACTOR*)actorx;
f32 dx;
f32 dz;
add_calc_short_angle2(&actorx->shape_info.rotation.y, actorx->world.angle.y, 1.0f - sqrtf(0.7f), 2500, 0);
aITB_height_ctrl(actorx);
aITB_BGcheck(actorx, game);
dx = aITB_GET_BK_CENTER_X(ins) - actorx->world.position.x;
dz = aITB_GET_BK_CENTER_Z(ins) - actorx->world.position.z;
if (SQ(dx) + SQ(dz) >= SQ(aITB_ONIYAMA_MAX_RANGE)) {
// Banded dragonfly has flown too far from center of acre, so escape
aITB_setupAction(ins, aITB_ACT_LET_ESCAPE, game);
}
}
/**
* Wait action for the dragonfly.
*
* @param actorx Pointer to the actor.
* @param game Pointer to the game context.
*/
static void aITB_wait(ACTOR* actorx, GAME* game) {
aINS_INSECT_ACTOR* ins = (aINS_INSECT_ACTOR*)actorx;
int diff_angle;
add_calc_short_angle2(&actorx->shape_info.rotation.y, actorx->world.angle.y, 1.0f - sqrtf(0.7f), 2500, 0);
aITB_height_ctrl(actorx);
diff_angle = actorx->shape_info.rotation.y - actorx->world.angle.y;
if (ABS((s16)diff_angle) < DEG2SHORT_ANGLE2(2.8125f)) {
aITB_GET_STOP_STATE(ins) = aITB_STOP_TYPE_NONE;
aITB_setupAction(ins, aITB_ACT_FLY, game);
}
}
/**
* Touch water action for the dragonfly.
*
* @param actorx Pointer to the actor.
* @param game Pointer to the game context.
*/
static void aITB_touch_water(ACTOR* actorx, GAME* game) {
aINS_INSECT_ACTOR* ins = (aINS_INSECT_ACTOR*)actorx;
add_calc_short_angle2(&actorx->shape_info.rotation.y, actorx->world.angle.y, 1.0f - sqrtf(0.7f), 2500, 0);
if (actorx->bg_collision_check.result.is_in_water) {
aITB_setupAction(ins, aITB_ACT_TOUCH_WATER_REVERSE, game);
}
}
/**
* Touch water reverse action for the dragonfly.
*
* @param actorx Pointer to the actor.
* @param game Pointer to the game context.
*/
static void aITB_touch_water_reverse(ACTOR* actorx, GAME* game) {
aINS_INSECT_ACTOR* ins = (aINS_INSECT_ACTOR*)actorx;
add_calc_short_angle2(&actorx->shape_info.rotation.y, actorx->world.angle.y, 1.0f - sqrtf(0.7f), 2500, 0);
if (ins->timer > 0) {
ins->timer--;
}
if (ins->timer == 0) {
aITB_setupAction(ins, aITB_ACT_HOVER_WAIT_ON_WATER, game);
}
}
/**
* Hover wait on water action for the dragonfly.
*
* @param actorx Pointer to the actor.
* @param game Pointer to the game context.
*/
static void aITB_hover_wait_on_water(ACTOR* actorx, GAME* game) {
aINS_INSECT_ACTOR* ins = (aINS_INSECT_ACTOR*)actorx;
if (ins->timer > 0) {
ins->timer--;
}
if (ins->timer == 0) {
int action;
if (ins->flag < 2) {
ins->flag++;
action = aITB_ACT_TOUCH_WATER;
} else {
action = aITB_ACT_WAIT;
}
aITB_setupAction(ins, action, game);
}
}
/**
* Fly on notice action for the dragonfly.
*
* @param actorx Pointer to the actor.
* @param game Pointer to the game context.
*/
static void aITB_fly_on_notice(ACTOR* actorx, GAME* game) {
aINS_INSECT_ACTOR* ins = (aINS_INSECT_ACTOR*)actorx;
add_calc_short_angle2(&actorx->shape_info.rotation.y, actorx->world.angle.y, 1.0f - sqrtf(0.7f), 2500, 0);
if (ins->patience < 90.0f) {
xyz_t pos_in_unit;
add_calc(&actorx->world.position.y, aITB_GET_RESERVE_DUMMY_HEIGHT(ins), 1.0f - sqrtf(0.8f), 0.5f, 0.0f);
mFI_Wpos2UtCenterWpos(&pos_in_unit, actorx->world.position);
if (SQ(pos_in_unit.x - actorx->world.position.x) + SQ(pos_in_unit.z - actorx->world.position.z) > SQ(5.0f)) {
actorx->world.angle.y = search_position_angleY(&actorx->world.position, &pos_in_unit);
} else {
f32 diff_y = aITB_GET_RESERVE_DUMMY_HEIGHT(ins) - actorx->world.position.y;
if (SQ(diff_y) < SQ(3.0f)) {
aITB_setupAction(ins, aITB_ACT_REST_ON_NOTICE, game);
}
}
} else {
aITB_GET_STOP_STATE(ins) = aITB_STOP_TYPE_NONE;
ins->patience = 100.0f;
aITB_setupAction(ins, aITB_ACT_FLY, game);
}
}
/**
* Rest on notice action for the dragonfly.
*
* @param actorx Pointer to the actor.
* @param game Pointer to the game context.
*/
static void aITB_rest_on_notice(ACTOR* actorx, GAME* game) {
aINS_INSECT_ACTOR* ins = (aINS_INSECT_ACTOR*)actorx;
if (ins->timer > 0 && ins->patience < 90.0f) {
add_calc(&actorx->world.position.y, aITB_GET_RESERVE_DUMMY_HEIGHT(ins), 1.0f - sqrtf(0.8f), 0.5f, 0.0f);
aITB_GET_REST_ANIME_TIMER(ins)++;
if (aITB_GET_REST_ANIME_TIMER(ins) < 20) {
aITB_anime_proc(ins);
}
if (ins->timer > 0) {
ins->timer--;
}
} else {
aITB_GET_STOP_STATE(ins) = aITB_STOP_TYPE_NONE;
ins->patience = 100.0f;
aITB_setupAction(ins, aITB_ACT_FLY, game);
}
}
/**
* Initializes the avoid action for the dragonfly.
*
* @param insect Pointer to the insect actor.
* @param game Pointer to the game context.
*/
static void aITB_avoid_init(aINS_INSECT_ACTOR* insect, GAME* game) {
ACTOR* playerx = GET_PLAYER_ACTOR_GAME_ACTOR(game);
if (playerx != NULL) {
insect->tools_actor.actor_class.world.angle.y = search_position_angleY(&playerx->world.position, &insect->tools_actor.actor_class.world.position);
}
insect->continue_timer = 0;
insect->target_speed = aITB_AVOID_SPEED + RANDOM_F(aITB_SPEED_VARIANCE);
insect->timer = aITB_AVOID_TIMER;
}
/**
* Initializes the let escape action for the dragonfly.
*
* @param insect Pointer to the insect actor.
* @param game Pointer to the game context.
*/
static void aITB_let_escape_init(aINS_INSECT_ACTOR* insect, GAME* game) {
insect->life_time = 0;
insect->alpha_time = 80;
aITB_SET_HEIGHT(insect, 0.0f);
insect->tools_actor.actor_class.speed = 4.0f;
insect->tools_actor.actor_class.max_velocity_y = 12.0f;
insect->tools_actor.actor_class.gravity = 0.06f;
insect->tools_actor.actor_class.shape_info.rotation.x = 0;
insect->tools_actor.actor_class.position_speed.y = 0.0f;
insect->insect_flags.bit_1 = TRUE;
insect->insect_flags.bit_2 = TRUE;
}
/**
* Initializes the fly action for the dragonfly.
*
* @param insect Pointer to the insect actor.
* @param game Pointer to the game context.
*/
static void aITB_fly_init(aINS_INSECT_ACTOR* insect, GAME* game) {
insect->tools_actor.actor_class.shape_info.rotation.y = insect->tools_actor.actor_class.world.angle.y;
aITB_move_spd_set(insect);
}
/**
* Initializes the wait action for the dragonfly.
*
* @param insect Pointer to the insect actor.
* @param game Pointer to the game context.
*/
static void aITB_wait_init(aINS_INSECT_ACTOR* insect, GAME* game) {
static s16 angl_add[] = {
DEG2SHORT_ANGLE2(-67.50f),
DEG2SHORT_ANGLE2(-33.75f),
DEG2SHORT_ANGLE2( 33.75f),
DEG2SHORT_ANGLE2( 67.50f),
};
int i;
s16 angle;
u32 attr;
if (aITB_GET_STOP_STATE(insect) == aITB_STOP_TYPE_NONE) {
f32 dx = aITB_GET_BK_CENTER_X(insect) - insect->tools_actor.actor_class.world.position.x;
f32 dz = aITB_GET_BK_CENTER_Z(insect) - insect->tools_actor.actor_class.world.position.z;
if (SQ(dx) + SQ(dz) < SQ(aITB_OTHER_MAX_RANGE)) {
// Fly around randomly
insect->tools_actor.actor_class.world.angle.y += angl_add[RANDOM(ARRAY_COUNT(angl_add))];
} else {
// We're too far from the center of the acre, so turn towards it
insect->tools_actor.actor_class.world.angle.y = atans_table(dz, dx);
}
}
insect->patience = 0.0f;
insect->continue_timer = 0;
aITB_GET_REST_ANIME_TIMER(insect) = 0;
insect->tools_actor.actor_class.speed = 0.0f;
insect->speed_step = 0.0f;
insect->timer = 0;
angle = insect->tools_actor.actor_class.shape_info.rotation.y;
insect->tools_actor.actor_class.shape_info.rotation.y = insect->tools_actor.actor_class.world.angle.y;
mCoBG_BgCheckControll(NULL, (ACTOR*)insect, 12.0f, insect->bg_height, TRUE, mCoBG_REVERSE_TYPE_REVERSE, mCoBG_CHECK_TYPE_NORMAL);
insect->tools_actor.actor_class.shape_info.rotation.y = angle;
for (i = 0; i < 4; i++) {
// Check up to 4 times for wall or ground collision.
// Each check should handle moving the actor slightly if
// it is too close to a wall or ground.
if (aITB_BGcheck((ACTOR*)insect, game) != aITB_BG_CHECK_RESULT_COLLIDE) {
return;
}
insect->tools_actor.actor_class.shape_info.rotation.y = insect->tools_actor.actor_class.world.angle.y;
mCoBG_BgCheckControll(NULL, (ACTOR*)insect, 12.0f, insect->bg_height, TRUE, mCoBG_REVERSE_TYPE_REVERSE, mCoBG_CHECK_TYPE_NORMAL);
}
insect->tools_actor.actor_class.shape_info.rotation.y = angle;
}
/**
* Initializes the touch water action for the dragonfly.
*
* @param insect Pointer to the insect actor.
* @param game Pointer to the game context.
*/
static void aITB_touch_water_init(aINS_INSECT_ACTOR* insect, GAME* game) {
aITB_wait_init(insect, game);
insect->tools_actor.actor_class.max_velocity_y = -1.0f;
}
/**
* Initializes the touch water reverse action for the dragonfly.
*
* @param insect Pointer to the insect actor.
* @param game Pointer to the game context.
*/
static void aITB_touch_water_reverse_init(aINS_INSECT_ACTOR* insect, GAME* game) {
eEC_CLIP->effect_make_proc(eEC_EFFECT_TURI_HAMON, insect->tools_actor.actor_class.world.position, 1, insect->tools_actor.actor_class.world.angle.y, game, EMPTY_NO, 4, 0);
insect->tools_actor.actor_class.max_velocity_y = 4.0f;
insect->tools_actor.actor_class.position_speed.y = 4.0f;
insect->timer = aITB_TOUCH_WATER_REVERSE_TIMER;
}
/**
* Initializes the hover wait on water action for the dragonfly.
*
* @param insect Pointer to the insect actor.
* @param game Pointer to the game context.
*/
static void aITB_hover_wait_on_water_init(aINS_INSECT_ACTOR* insect, GAME* game) {
insect->tools_actor.actor_class.max_velocity_y = 0.0f;
insect->tools_actor.actor_class.position_speed.y = 0.0f;
insect->timer = aITB_HOVER_WAIT_ON_WATER_TIMER;
}
/**
* Initializes the fly on notice action for the dragonfly.
*
* @param insect Pointer to the insect actor.
* @param game Pointer to the game context.
*/
static void aITB_fly_on_notice_init(aINS_INSECT_ACTOR* insect, GAME* game) {
insect->patience = 0.0f;
insect->continue_timer = 0;
insect->flag = 0;
aITB_GET_REST_ANIME_TIMER(insect) = 0;
insect->tools_actor.actor_class.max_velocity_y = 0.0f;
insect->tools_actor.actor_class.speed = 1.0f;
insect->target_speed = 2.0f + RANDOM_F(aITB_SPEED_VARIANCE);
}
/**
* Initializes the rest on notice action for the dragonfly.
*
* @param insect Pointer to the insect actor.
* @param game Pointer to the game context.
*/
static void aITB_rest_on_notice_init(aINS_INSECT_ACTOR* insect, GAME* game) {
insect->tools_actor.actor_class.speed = 0.0f;
insect->speed_step = 0.0f;
insect->target_speed = 0.0f;
insect->timer = aITB_REST_ON_NOTICE_TIMER;
}
typedef void (*aITB_INIT_PROC)(aINS_INSECT_ACTOR* insect, GAME* game);
/**
* Sets up the action for the dragonfly.
*
* @param insect Pointer to the insect actor.
* @param action Action to set up.
* @param game Pointer to the game context.
*/
static void aITB_setupAction(aINS_INSECT_ACTOR* insect, int action, GAME* game) {
static aITB_INIT_PROC init_proc[] = {
aITB_avoid_init,
aITB_let_escape_init,
aITB_fly_init,
aITB_fly_init,
aITB_wait_init,
aITB_touch_water_init,
aITB_touch_water_reverse_init,
aITB_hover_wait_on_water_init,
aITB_fly_on_notice_init,
aITB_rest_on_notice_init,
};
static aINS_ACTION_PROC act_proc[] = {
aITB_avoid,
aITB_let_escape,
aITB_fly,
aITB_oniyanma_fly,
aITB_wait,
aITB_touch_water,
aITB_touch_water_reverse,
aITB_hover_wait_on_water,
aITB_fly_on_notice,
aITB_rest_on_notice,
};
insect->action = action;
insect->action_proc = act_proc[action];
init_proc[action](insect, game);
}
/**
* Dragonfly move proc.
*
* @param actorx Pointer to the actor.
* @param game Pointer to the game context.
*/
static void aITB_actor_move(ACTOR* actorx, GAME* game) {
aINS_INSECT_ACTOR* ins = (aINS_INSECT_ACTOR*)actorx;
u32 catch_label = (u32)mPlib_Get_item_net_catch_label();
if (catch_label == (u32)ins) {
ins->alpha0 = 255;
aITB_setupAction(ins, aITB_ACT_LET_ESCAPE, game);
} else if (ins->insect_flags.bit_3 == TRUE && ins->insect_flags.bit_2 == FALSE) {
aITB_setupAction(ins, aITB_ACT_LET_ESCAPE, game);
} else {
ins->action_proc(actorx, game);
if (aITB_GET_STOP_STATE(ins) != aITB_STOP_TYPE_REST) {
aITB_anime_proc(ins);
}
}
}