diff --git a/configure.py b/configure.py index 647cdaa8..9c547222 100644 --- a/configure.py +++ b/configure.py @@ -1070,7 +1070,7 @@ config.libs = [ Object(Matching, "actor/ac_super.c"), Object(Matching, "actor/ac_tama.c"), Object(Matching, "actor/ac_tent.c"), - Object(NonMatching, "actor/ac_tokyoso_control.c"), + Object(Matching, "actor/ac_tokyoso_control.c"), Object(Matching, "actor/ac_tools.c"), Object(Matching, "actor/ac_toudai.c"), Object(Matching, "actor/ac_train0.c"), diff --git a/include/ac_tokyoso_control.h b/include/ac_tokyoso_control.h index 4c41da68..6f01e6ef 100644 --- a/include/ac_tokyoso_control.h +++ b/include/ac_tokyoso_control.h @@ -8,20 +8,82 @@ extern "C" { #endif +#define aTKC_LAP_NUM 4 +#define aTKC_SUBLAP_NUM (2 * aTKC_LAP_NUM) // split into half of the circuit +#define aTKC_NPC_NUM 2 + +#define aTKC_ANGLE_FOR_LAP_COMPLETION 4000 // approximately 22 degrees + +#define aTKC_FLAG_5 (1 << 5) +#define aTKC_FLAG_6 (1 << 6) +#define aTKC_FLAG_7 (1 << 7) +#define aTKC_FLAG_8 (1 << 8) +#define aTKC_FLAG_9 (1 << 9) #define aTKC_FLAG_RACE_ACTIVE (1 << 10) +#define aTKC_FLAG_11 (1 << 11) +#define aTKC_FLAG_12 (1 << 12) +#define aTKC_FLAG_13 (1 << 13) +#define aTKC_FLAG_14 (1 << 14) +#define aTKC_FLAG_15 (1 << 15) + +#define aTKC_MASK_13_14 (aTKC_FLAG_13 | aTKC_FLAG_14) + +enum { + aTKC_GOAL_NONE, + aTKC_GOAL_WON, + aTKC_GOAL_LOSE, + + aTKC_GOAL_NUM +}; typedef struct ac_tokyoso_event_s { u8 _00; u8 _01; u16 flags; - s16 pos_04[3]; + s16 yasiro_pos[3]; // shrine pos (wishing well in AC and e+) s16 pos_0A[2][2]; s16 angle[2]; - s16 pos_16[2]; - u8 _1A[2]; - u8 _1C[2]; + s16 next_angle[2]; + u8 lap[2]; + u8 goal[2]; } aEv_tokyoso_c; +typedef void (*aTKC_NEXT_RUN_PROC)(ACTOR* actorx); +typedef s16 (*aTKC_GET_ANGLE_PROC)(ACTOR* actorx); +typedef void (*aTKC_RUN_PROC)(ACTOR* actorx); +typedef s16 (*aTKC_RUN_CHECK_PROC)(ACTOR* actorx); +typedef s16 (*aTKC_TOP_CHECK_PROC)(ACTOR* actorx); +typedef s16 (*aTKC_GOAL_CHECK_PROC)(ACTOR* actorx); +typedef void (*aTKC_NPC1_THINK_INIT_PROC)(ACTOR* actorx, GAME_PLAY* play, u8 type); +typedef void (*aTKC_NEXT_POS_PROC)(ACTOR* actorx, int idx); +typedef void (*aTKC_NEXT_WARMUP_PROC)(ACTOR* actorx); +typedef void (*aTKC_SET_TALK_REQUEST_PROC)(ACTOR* actorx); + +typedef struct tokyoso_control_clip_s { + aTKC_NEXT_RUN_PROC next_run_proc; + aTKC_GET_ANGLE_PROC get_angle_proc; + aTKC_RUN_PROC run_proc; + aTKC_RUN_CHECK_PROC run_check_proc; + aTKC_TOP_CHECK_PROC top_check_proc; + aTKC_GOAL_CHECK_PROC goal_check_proc; + aTKC_NPC1_THINK_INIT_PROC npc1_think_init_proc; + aTKC_NEXT_POS_PROC next_pos_proc; + aTKC_NEXT_WARMUP_PROC next_warmup_proc; + aTKC_SET_TALK_REQUEST_PROC set_talk_request_proc; +} aTKC_clip_c; + +typedef struct tokyoso_control_actor_s TOKYOSO_CONTROL_ACTOR; + +typedef void (*aTKC_PROC)(TOKYOSO_CONTROL_ACTOR* actor, GAME_PLAY* play); + +struct tokyoso_control_actor_s { + ACTOR actor_class; + aTKC_clip_c clip; + aTKC_PROC act_proc; + int action; + s16 timer; +}; + extern ACTOR_PROFILE Tokyoso_Control_Profile; #ifdef __cplusplus diff --git a/include/ac_tokyoso_npc1.h b/include/ac_tokyoso_npc1.h index 32699737..ab5705ae 100644 --- a/include/ac_tokyoso_npc1.h +++ b/include/ac_tokyoso_npc1.h @@ -1,13 +1,33 @@ #ifndef AC_TOKYOSO_NPC1_H #define AC_TOKYOSO_NPC1_H +#include "lb_reki.h" #include "types.h" #include "m_actor.h" +#include "ac_npc.h" #ifdef __cplusplus extern "C" { #endif +typedef struct tokyoso_npc1_actor_s TOKYOSO_NPC1_ACTOR; + +// TODO: finish this +struct tokyoso_npc1_actor_s { + NPC_ACTOR npc_class; + void* think_proc; + int msg_start_no; + f32 speed; + s16 _9A0; + s16 start_pos[2]; + s16 timer; + u8 _9A8; + u8 _9A9; + u8 _9AA; + u8 talk_idx; + u8 _9AC; +}; + extern ACTOR_PROFILE Tokyoso_Npc1_Profile; #ifdef __cplusplus @@ -15,4 +35,3 @@ extern ACTOR_PROFILE Tokyoso_Npc1_Profile; #endif #endif - diff --git a/include/m_clip.h b/include/m_clip.h index d7c6bf1a..44bb5c65 100644 --- a/include/m_clip.h +++ b/include/m_clip.h @@ -37,6 +37,7 @@ #include "ac_mikanbox_clip.h" #include "ac_needlework_indoor_clip.h" #include "ac_countdown_clip.h" +#include "ac_tokyoso_control.h" #ifdef __cplusplus extern "C" { @@ -90,7 +91,7 @@ typedef struct clip_s { /* 0x0C0 */ aGRGR_clip_c* garagara_clip; /* 0x0C4 */ aHTMD_clip_c* hatumode_clip; /* 0x0C8 */ void* shrine_clip; - /* 0x0CC */ void* _0CC; + /* 0x0CC */ aTKC_clip_c* tokyoso_clip; /* 0x0D0 */ aCOU_Clip_c* countdown_clip; /* 0x0D4 */ CLIP_NONE_PROC ball_redma_proc; /* removed in DnM+ */ /* 0x0D8 */ aMKBC_Clip_c* mikanbox_clip; diff --git a/src/actor/ac_tokyoso_clip.c_inc b/src/actor/ac_tokyoso_clip.c_inc new file mode 100644 index 00000000..ca739132 --- /dev/null +++ b/src/actor/ac_tokyoso_clip.c_inc @@ -0,0 +1,348 @@ +static int aTKN1_set_request_act(TOKYOSO_NPC1_ACTOR* actor, u8 prio, u8 idx, u8 type, u16 obj, s16 move_x, s16 move_z) { + int ret = FALSE; + + if (prio >= actor->npc_class.request.act_priority) { + u16 arg_data[aNPC_REQUEST_ARG_NUM]; + + bzero(arg_data, sizeof(arg_data)); + arg_data[0] = obj; + arg_data[2] = move_x; + arg_data[3] = move_z; + + actor->npc_class.request.act_priority = prio; + actor->npc_class.request.act_idx = idx; + actor->npc_class.request.act_type = type; + mem_copy((u8*)actor->npc_class.request.act_args, (u8*)arg_data, sizeof(arg_data)); + ret = TRUE; + } + + return ret; +} + +static void aTKN1_normal_wait_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + aTKN1_set_request_act(actor, 4, aNPC_ACT_WAIT, aNPC_ACT_TYPE_DEFAULT, aNPC_ACT_OBJ_DEFAULT, 0, 0); +} + +static void aTKN1_wait_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + actor->npc_class.action.idx = aNPC_ACT_WAIT; + aTKN1_normal_wait_init(actor, play); +} + +static void aTKN1_run_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + aTKN1_set_request_act(actor, 4, aNPC_ACT_RUN, aNPC_ACT_TYPE_TO_POINT, aNPC_ACT_OBJ_DEFAULT, actor->start_pos[0], actor->start_pos[1]); + actor->npc_class.actor_class.status_data.weight = MASSTYPE_HEAVY-4; + actor->npc_class.talk_info.default_animation = aNPC_ANIM_ASIHUMI1; + actor->npc_class.talk_info.turn = aNPC_TALK_TURN_NORMAL; +} + +static void aTKN1_turn_d_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + ACTOR* actorx = (ACTOR*)actor; + + aTKN1_set_request_act(actor, 4, aNPC_ACT_TURN, aNPC_ACT_TYPE_TO_POINT, aNPC_ACT_OBJ_DEFAULT, actorx->world.position.x, actorx->world.position.z - 10.0f); +} + +static void aTKN1_turn_f_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + ACTOR* actorx = (ACTOR*)actor; + + aTKN1_set_request_act(actor, 4, aNPC_ACT_TURN, aNPC_ACT_TYPE_TO_POINT, aNPC_ACT_OBJ_DEFAULT, actorx->world.position.x, actorx->world.position.z + 10.0f); +} + +static void aTKN1_turn_l_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + ACTOR* actorx = (ACTOR*)actor; + + aTKN1_set_request_act(actor, 4, aNPC_ACT_TURN, aNPC_ACT_TYPE_TO_POINT, aNPC_ACT_OBJ_DEFAULT, actorx->world.position.x - 10.0f, actorx->world.position.z); +} + +static void aTKN1_turn_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + aTKN1_set_request_act(actor, 4, aNPC_ACT_TURN, aNPC_ACT_TYPE_TO_POINT, aNPC_ACT_OBJ_DEFAULT, actor->start_pos[0], actor->start_pos[1]); + actor->npc_class.condition_info.demo_flg &= ~(aNPC_COND_DEMO_SKIP_MOVE_Y | aNPC_COND_DEMO_SKIP_BGCHECK); +} + +static void aTKN1_run_turn_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + aTKC_clip_next_run((ACTOR*)actor); + aTKN1_turn_init(actor, play); + actor->npc_class.talk_info.default_animation = aNPC_ANIM_ASIHUMI1; + actor->npc_class.talk_info.turn = aNPC_TALK_TURN_NORMAL; +} + +static void aTKN1_warmup_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + NPC_CLIP->animation_init_proc((ACTOR*)actor, aNPC_ANIM_WARMUP1, FALSE); + tokyoso->flags &= ~(aTKC_FLAG_5 << ((actor->npc_class.actor_class.npc_id - SP_NPC_EV_TOKYOSO_0) & 1)); + actor->_9A0 = 240; +} + +static void aTKN1_ready_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + aTKN1_wait_init(actor, play); + NPC_CLIP->animation_init_proc((ACTOR*)actor, aNPC_ANIM_READY1, FALSE); + actor->npc_class.talk_info.default_animation = aNPC_ANIM_READY1; + actor->npc_class.talk_info.turn = aNPC_TALK_TURN_NONE; +} + +static void aTKN1_kokeru_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + NPC_CLIP->animation_init_proc((ACTOR*)actor, aNPC_ANIM_KOKERU1, FALSE); + actor->npc_class.draw.main_animation.keyframe.frame_control.mode = cKF_FRAMECONTROL_STOP; + actor->npc_class.movement.speed.max_speed = 0.0f; + actor->npc_class.movement.speed.deceleration = 0.2f; + actor->npc_class.movement.mv_add_angl = 0; + actor->npc_class.talk_info.default_animation = aNPC_ANIM_KOKERU1; + actor->npc_class.talk_info.turn = aNPC_TALK_TURN_NONE; +} + +static void aTKN1_getup_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + NPC_CLIP->animation_init_proc((ACTOR*)actor, aNPC_ANIM_KOKERU_GETUP1, FALSE); + actor->npc_class.draw.main_animation.keyframe.frame_control.mode = cKF_FRAMECONTROL_STOP; + actor->npc_class.movement.speed.deceleration = 0.6f; + actor->npc_class.talk_info.default_animation = aNPC_ANIM_KOKERU_GETUP1; + actor->npc_class.talk_info.turn = aNPC_TALK_TURN_NONE; +} + +static void aTKN1_goal_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + ACTOR* actorx = (ACTOR*)actor; + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + actor->npc_class.actor_class.status_data.weight = MASSTYPE_HEAVY; + aTKN1_turn_f_init(actor, play); + actor->npc_class.talk_info.default_animation = aNPC_ANIM_WAIT1; + actor->npc_class.talk_info.turn = aNPC_TALK_TURN_NORMAL; + tokyoso->_01 |= (u8)(1 << ((2 + actorx->npc_id - SP_NPC_EV_TOKYOSO_0) & 3)); +} + +static void aTKN1_goal1_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + aTKN1_wait_init(actor, play); + NPC_CLIP->animation_init_proc((ACTOR*)actor, aNPC_ANIM_BANZAI1, FALSE); + actor->npc_class.draw.main_animation.keyframe.frame_control.mode = cKF_FRAMECONTROL_REPEAT; +} + +static void aTKN1_goal2_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + aTKN1_wait_init(actor, play); + NPC_CLIP->animation_init_proc((ACTOR*)actor, aNPC_ANIM_TIRED1, FALSE); + actor->npc_class.draw.main_animation.keyframe.frame_control.mode = cKF_FRAMECONTROL_REPEAT; +} + +static void aTKN1_kansen_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + aTKN1_normal_wait_init(actor, play); + actor->npc_class.condition_info.demo_flg |= (aNPC_COND_DEMO_SKIP_BGCHECK | aNPC_COND_DEMO_SKIP_MOVE_Y); +} + +static void aTKN1_omedeto_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + aTKN1_wait_init(actor, play); + NPC_CLIP->animation_init_proc((ACTOR*)actor, aNPC_ANIM_CLAP1, FALSE); + actor->npc_class.draw.main_animation.keyframe.frame_control.mode = cKF_FRAMECONTROL_REPEAT; + actor->npc_class.condition_info.demo_flg |= (aNPC_COND_DEMO_SKIP_BGCHECK | aNPC_COND_DEMO_SKIP_MOVE_Y); +} + +static void aTKN1_omedeto_af_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + actor->npc_class.action.idx = aNPC_ACT_RUN; + aTKN1_normal_wait_init(actor, play); + actor->npc_class.condition_info.demo_flg |= (aNPC_COND_DEMO_SKIP_BGCHECK | aNPC_COND_DEMO_SKIP_MOVE_Y); +} + +static void aTKN1_move_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + aTKN1_set_request_act(actor, 4, aNPC_ACT_WALK, aNPC_ACT_TYPE_TO_POINT, aNPC_ACT_OBJ_DEFAULT, actor->start_pos[0], actor->start_pos[1]); +} + +static void aTKN1_lookl_init(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play) { + ACTOR* actorx = (ACTOR*)actor; + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + aTKN1_turn_l_init(actor, play); + tokyoso->flags &= ~(aTKC_FLAG_11 << ((actorx->npc_id - SP_NPC_EV_TOKYOSO_0) & 1)); +} + +typedef void (*aTKN1_THINK_INIT_PROC)(TOKYOSO_NPC1_ACTOR* actor, GAME_PLAY* play); +// clang-format off +static aTKN1_THINK_INIT_PROC init_table[] = { + (aTKN1_THINK_INIT_PROC)none_proc1, + aTKN1_normal_wait_init, + aTKN1_wait_init, + aTKN1_run_init, + aTKN1_turn_d_init, + aTKN1_turn_l_init, + aTKN1_turn_f_init, + aTKN1_turn_init, + aTKN1_warmup_init, + aTKN1_ready_init, + aTKN1_kokeru_init, + aTKN1_getup_init, + aTKN1_goal_init, + aTKN1_goal1_init, + aTKN1_goal2_init, + aTKN1_omedeto_init, + aTKN1_move_init, + aTKN1_lookl_init, + aTKN1_run_turn_init, + aTKN1_kansen_init, + aTKN1_omedeto_af_init, +}; +// clang-fomrat on + +static void aTKC_clip_npc1_think_init(ACTOR* actorx, GAME_PLAY* play, u8 think_init) { + (*init_table[think_init])((TOKYOSO_NPC1_ACTOR*)actorx, play); +} + +static void aTKC_clip_next_run(ACTOR* actorx) { + TOKYOSO_NPC1_ACTOR* actor = (TOKYOSO_NPC1_ACTOR*)actorx; + s16 angle = aTKC_clip_get_angle(actorx); + f32 f; + int idx = (actorx->npc_id - SP_NPC_EV_TOKYOSO_0) & 1; + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + angle += (s16)(aTKC_ANGLE_FOR_LAP_COMPLETION + RANDOM_F(aTKC_ANGLE_FOR_LAP_COMPLETION)); + f = (160.0f + RANDOM_F(20.0f)) - idx * 35.0f; + + actor->speed = (4.0f + f * (1.0f / 90.0f)) - idx * 0.5f; + if (aTKC_clip_top_check(actorx)) { + actor->speed -= 1.0f; + } + + actor->start_pos[0] = tokyoso->yasiro_pos[0] + f * cos_s(angle); + actor->start_pos[1] = tokyoso->yasiro_pos[1] - f * sin_s(angle); + tokyoso->next_angle[idx] = angle; + actor->npc_class.movement.speed.deceleration = 0.6f; + + if (tokyoso->lap[idx] == aTKC_SUBLAP_NUM && tokyoso->next_angle[idx] > 0) { + actor->start_pos[0] = (tokyoso->yasiro_pos[0] + 220.0f) - idx * 60.0f + (RANDOM_F(10.0f) - 5.0f); + actor->start_pos[1] = (tokyoso->yasiro_pos[1] - 130.0f) + idx * 60.0f + (RANDOM_F(10.0f) - 5.0f); + } + + actor->timer = 300; +} + +static s16 aTKC_clip_get_angle(ACTOR* actorx) { + f32 dx; + f32 dz; + s16 angle; + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + dx = (tokyoso->yasiro_pos[0] + 20) - actorx->world.position.x; + dz = tokyoso->yasiro_pos[1] - actorx->world.position.z; + angle = atans_table(dz, dx); + return DEG2SHORT_ANGLE(90.0f) + angle; +} + +static void aTKC_clip_run_proc(ACTOR* actorx) { + int idx = (actorx->npc_id - SP_NPC_EV_TOKYOSO_0) & 1; + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + tokyoso->pos_0A[idx][0] = actorx->world.position.x; + tokyoso->pos_0A[idx][1] = actorx->world.position.z; + tokyoso->angle[idx] = aTKC_clip_get_angle(actorx); +} + +static s16 aTKC_clip_run_check(ACTOR* actorx) { + int idx = (actorx->npc_id - SP_NPC_EV_TOKYOSO_0) & 1; + s16 angle; + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + TOKYOSO_NPC1_ACTOR* actor = (TOKYOSO_NPC1_ACTOR*)actorx; + + if (actor->timer > 0) { + actor->timer--; + if (actor->timer == 0) { + return TRUE; + } + } + + if (tokyoso->lap[idx] == aTKC_SUBLAP_NUM && tokyoso->next_angle[idx] > 0) { + return FALSE; + } + + angle = tokyoso->angle[idx] - tokyoso->next_angle[idx]; + +#ifdef BUGFIXES + if (angle > DEG2SHORT_ANGLE2(-22.5f) && angle < DEG2SHORT_ANGLE2(22.5f)) { +#else + // @BUG - did the devs miss a hex specifier here? Shouldn't this be angle >= -[0x]1000? + if (angle > -1000 && angle < DEG2SHORT_ANGLE2(22.5f)) { +#endif + return TRUE; + } + + return FALSE; +} + +static s16 aTKC_clip_top_check(ACTOR* actorx) { + int idx0 = (actorx->npc_id - SP_NPC_EV_TOKYOSO_0) & 1; + int idx1 = (idx0 + 1) & 1; + s16 angle; + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + if ((tokyoso->flags & aTKC_FLAG_RACE_ACTIVE) == 0) { + return FALSE; + } + + angle = tokyoso->angle[idx0] - tokyoso->angle[idx1]; + if (tokyoso->lap[idx0] > tokyoso->lap[idx1] || (tokyoso->lap[idx0] == tokyoso->lap[idx1] && angle > 0)) { + return TRUE; + } + + return FALSE; +} + +static s16 aTKC_clip_goal_check(ACTOR* actorx) { + int idx = (actorx->npc_id - SP_NPC_EV_TOKYOSO_0) & 1; + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + if ((tokyoso->flags & aTKC_FLAG_RACE_ACTIVE) == 0) { + return FALSE; + } + + if (tokyoso->goal[idx] == aTKC_GOAL_NONE) { + return FALSE; + } + + return tokyoso->goal[idx]; +} + +static void aTKC_clip_next_pos(ACTOR* actorx, int idx) { + static s16 tableX[] = { 200, 160, 280, 280 }; + static s16 tableZ[] = { 0, 0, 40, 0 }; + TOKYOSO_NPC1_ACTOR* actor = (TOKYOSO_NPC1_ACTOR*)actorx; + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + actor->start_pos[0] = tokyoso->yasiro_pos[0] + tableX[idx]; + actor->start_pos[1] = tokyoso->yasiro_pos[1] + tableZ[idx]; +} + +static void aTKC_clip_next_warmup(ACTOR* actorx) { + static s16 tableX[] = { 200, 160 }; + static f32 tableXR[] = { 40.0f, -40.0f }; + int idx = (actorx->npc_id - SP_NPC_EV_TOKYOSO_0) & 1; + TOKYOSO_NPC1_ACTOR* actor = (TOKYOSO_NPC1_ACTOR*)actorx; + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + actor->start_pos[0] = tokyoso->yasiro_pos[0] + tableX[idx] + (s16)RANDOM_F(tableXR[idx]); + actor->start_pos[1] = tokyoso->yasiro_pos[1] + RANDOM_F(80.0f) - 40.0f; +} + +typedef struct { + int msg_no; + u8 turn_flag; + u8 camera_type; +} aTCK_npc1_talk_data_c; + +static void aTKC_clip_set_talk_request(ACTOR* actorx) { + static aTCK_npc1_talk_data_c dt_tbl[] = { + {14, FALSE, CAMERA2_PROCESS_TALK}, + { 2, FALSE, CAMERA2_PROCESS_TALK}, + {14, FALSE, CAMERA2_PROCESS_TALK}, + { 4, FALSE, CAMERA2_PROCESS_TALK}, + { 6, TRUE, CAMERA2_PROCESS_TALK}, + { 8, TRUE, CAMERA2_PROCESS_TALK}, + { 8, TRUE, CAMERA2_PROCESS_TALK}, + {10, FALSE, CAMERA2_PROCESS_TALK}, + {10, FALSE, CAMERA2_PROCESS_TALK}, + {12, TRUE, CAMERA2_PROCESS_TALK}, + {12, TRUE, CAMERA2_PROCESS_TALK}, + {12, TRUE, CAMERA2_PROCESS_TALK}, + {12, TRUE, CAMERA2_PROCESS_TALK}, + }; + + TOKYOSO_NPC1_ACTOR* actor = (TOKYOSO_NPC1_ACTOR*)actorx; + aTCK_npc1_talk_data_c* data_p = &dt_tbl[actor->talk_idx]; + + mDemo_Set_msg_num(actor->msg_start_no + data_p->msg_no + RANDOM(2)); + mDemo_Set_talk_turn(data_p->turn_flag); + mDemo_Set_camera(data_p->camera_type); +} diff --git a/src/actor/ac_tokyoso_control.c b/src/actor/ac_tokyoso_control.c new file mode 100644 index 00000000..926c5922 --- /dev/null +++ b/src/actor/ac_tokyoso_control.c @@ -0,0 +1,239 @@ +#include "ac_tokyoso_control.h" +#include "m_actor.h" +#include "m_actor_type.h" +#include "m_common_data.h" +#include "m_event.h" +#include "m_field_info.h" +#include "m_name_table.h" +#include "ac_tokyoso_npc1.h" +#include "libultra/libultra.h" + +enum { + aTKC_ACT_WAIT, + aTKC_ACT_WAIT2, + aTKC_ACT_STRECH, + aTKC_ACT_READY, + aTKC_ACT_SET, + aTKC_ACT_GO, + aTKC_ACT_GOAL, + aTKC_ACT_IREKAE_1, + aTKC_ACT_IREKAE_2, + + aTKC_ACT_NUM +}; + +static void aTKC_actor_ct(ACTOR* actorx, GAME* game); +static void aTKC_actor_dt(ACTOR* actorx, GAME* game); +static void aTKC_actor_move(ACTOR* actorx, GAME* game); + +// clang-format off +ACTOR_PROFILE Tokyoso_Control_Profile = { + mAc_PROFILE_TOKYOSO_CONTROL, + ACTOR_PART_CONTROL, + ACTOR_STATE_NO_MOVE_WHILE_CULLED, + EMPTY_NO, + ACTOR_OBJ_BANK_KEEP, + sizeof(TOKYOSO_CONTROL_ACTOR), + aTKC_actor_ct, + aTKC_actor_dt, + aTKC_actor_move, + mActor_NONE_PROC1, + NULL, +}; +// clang-format on + +static void aTKC_setupAction(TOKYOSO_CONTROL_ACTOR* actor, int action); + +// clip declarations +static void aTKC_clip_next_run(ACTOR* actorx); +static s16 aTKC_clip_get_angle(ACTOR* actorx); +static void aTKC_clip_run_proc(ACTOR* actorx); +static s16 aTKC_clip_run_check(ACTOR* actorx); +static s16 aTKC_clip_top_check(ACTOR* actorx); +static s16 aTKC_clip_goal_check(ACTOR* actorx); +static void aTKC_clip_npc1_think_init(ACTOR* actorx, GAME_PLAY* play, u8 type); +static void aTKC_clip_next_pos(ACTOR* actorx, int idx); +static void aTKC_clip_next_warmup(ACTOR* actorx); +static void aTKC_clip_set_talk_request(ACTOR* actorx); + +static void aTKC_actor_ct(ACTOR* actorx, GAME* game) { + TOKYOSO_CONTROL_ACTOR* actor = (TOKYOSO_CONTROL_ACTOR*)actorx; + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + if (tokyoso == NULL) { + tokyoso = (aEv_tokyoso_c*)mEv_reserve_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + tokyoso->_00 = 0; + tokyoso->flags = 0; + } + + actor->clip.next_run_proc = aTKC_clip_next_run; + actor->clip.get_angle_proc = aTKC_clip_get_angle; + actor->clip.run_proc = aTKC_clip_run_proc; + actor->clip.run_check_proc = aTKC_clip_run_check; + actor->clip.top_check_proc = aTKC_clip_top_check; + actor->clip.goal_check_proc = aTKC_clip_goal_check; + actor->clip.npc1_think_init_proc = aTKC_clip_npc1_think_init; + actor->clip.next_pos_proc = aTKC_clip_next_pos; + actor->clip.next_warmup_proc = aTKC_clip_next_warmup; + actor->clip.set_talk_request_proc = aTKC_clip_set_talk_request; + CLIP(tokyoso_clip) = &actor->clip; + aTKC_setupAction(actor, aTKC_ACT_WAIT); +} + +static void aTKC_actor_dt(ACTOR* actorx, GAME* game) { + CLIP(tokyoso_clip) = NULL; + mEv_actor_dying_message(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, actorx); +} + +static void aTKC_actor_move(ACTOR* actorx, GAME* game) { + TOKYOSO_CONTROL_ACTOR* actor = (TOKYOSO_CONTROL_ACTOR*)actorx; + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + if (tokyoso->_00 == 3 && actor->action != aTKC_ACT_WAIT2) { + aTKC_setupAction(actor, aTKC_ACT_WAIT2); + } else { + actor->act_proc(actor, (GAME_PLAY*)game); + } +} + +static void aTKC_irekae_2(TOKYOSO_CONTROL_ACTOR* actor, GAME_PLAY* play) { + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + if ((tokyoso->flags & aTKC_FLAG_11) == 0) { + aTKC_setupAction(actor, aTKC_ACT_STRECH); + actor->timer = 600; + } +} + +static void aTKC_irekae_1(TOKYOSO_CONTROL_ACTOR* actor, GAME_PLAY* play) { + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + if ((tokyoso->flags & aTKC_FLAG_12) == 0) { + aTKC_setupAction(actor, aTKC_ACT_IREKAE_2); + tokyoso->flags |= aTKC_FLAG_5; + tokyoso->flags |= aTKC_FLAG_11; + } +} + +static void aTKC_goal(TOKYOSO_CONTROL_ACTOR* actor, GAME_PLAY* play) { + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + if ((tokyoso->flags & aTKC_MASK_13_14) == aTKC_MASK_13_14) { + if (actor->timer > 0) { + actor->timer--; + } else { + aTKC_setupAction(actor, aTKC_ACT_IREKAE_1); + tokyoso->flags |= aTKC_FLAG_6; + tokyoso->flags |= aTKC_FLAG_12; + tokyoso->flags &= ~aTKC_FLAG_RACE_ACTIVE; + tokyoso->flags &= ~aTKC_MASK_13_14; + } + } +} + +static void aTKC_go(TOKYOSO_CONTROL_ACTOR* actor, GAME_PLAY* play) { + int i; + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + for (i = 0; i < aTKC_NPC_NUM; i++) { + if (tokyoso->lap[i] & 1) { + if (tokyoso->angle[i] < -aTKC_ANGLE_FOR_LAP_COMPLETION) { + tokyoso->lap[i]++; + } + } else { + if (tokyoso->angle[i] > aTKC_ANGLE_FOR_LAP_COMPLETION) { + tokyoso->lap[i]++; + } + } + + if (tokyoso->lap[i] > aTKC_SUBLAP_NUM && tokyoso->goal[i] == aTKC_GOAL_NONE) { + if (tokyoso->goal[(i + 1) & 1] == aTKC_GOAL_NONE) { + tokyoso->goal[i] = aTKC_GOAL_WON; + } else { + aTKC_setupAction(actor, aTKC_ACT_GOAL); + tokyoso->goal[i] = aTKC_GOAL_LOSE; + actor->timer = 600; + } + } + } +} + +static void aTKC_set(TOKYOSO_CONTROL_ACTOR* actor, GAME_PLAY* play) { + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + if ((tokyoso->flags & (aTKC_FLAG_7 | aTKC_FLAG_8 | aTKC_FLAG_9)) == 0) { + aTKC_setupAction(actor, aTKC_ACT_GO); // @BUG - this is called twice + tokyoso->flags |= aTKC_FLAG_RACE_ACTIVE; + tokyoso->lap[0] = tokyoso->lap[1] = 0; + tokyoso->goal[0] = tokyoso->goal[1] = aTKC_GOAL_NONE; + aTKC_setupAction(actor, aTKC_ACT_GO); + } +} + +static void aTKC_ready(TOKYOSO_CONTROL_ACTOR* actor, GAME_PLAY* play) { + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + if ((tokyoso->flags & (aTKC_FLAG_7 | aTKC_FLAG_8 | aTKC_FLAG_9)) == 0) { + if (actor->timer > 0) { + actor->timer--; + } else { + aTKC_setupAction(actor, aTKC_ACT_SET); + tokyoso->flags |= (aTKC_FLAG_7 | aTKC_FLAG_8 | aTKC_FLAG_9); + } + } +} + +static void aTKC_strech(TOKYOSO_CONTROL_ACTOR* actor, GAME_PLAY* play) { + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + if ((tokyoso->flags & (aTKC_FLAG_5 | aTKC_FLAG_6)) == 0) { + if (actor->timer > 0) { + actor->timer--; + } else { + actor->timer = 120; + aTKC_setupAction(actor, aTKC_ACT_READY); + tokyoso->flags |= (aTKC_FLAG_7 | aTKC_FLAG_8 | aTKC_FLAG_9); + } + } +} + +static void aTKC_wait2(TOKYOSO_CONTROL_ACTOR* actor, GAME_PLAY* play) { + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + if (tokyoso->_00 == 0) { + tokyoso->_00 = 1; + aTKC_setupAction(actor, aTKC_ACT_STRECH); + actor->timer = 600; + } +} + +static void aTKC_wait(TOKYOSO_CONTROL_ACTOR* actor, GAME_PLAY* play) { + aEv_tokyoso_c* tokyoso = (aEv_tokyoso_c*)mEv_get_save_area(mEv_EVENT_SPORTS_FAIR_FOOT_RACE, 8); + + if (mFI_SetOyasiroPos(tokyoso->yasiro_pos)) { + tokyoso->_00 = 1; + aTKC_setupAction(actor, aTKC_ACT_STRECH); + actor->timer = 600; + } +} + +static void aTKC_setupAction(TOKYOSO_CONTROL_ACTOR* actor, int action) { + // clang-format off + static aTKC_PROC process[] = { + aTKC_wait, + aTKC_wait2, + aTKC_strech, + aTKC_ready, + aTKC_set, + aTKC_go, + aTKC_goal, + aTKC_irekae_1, + aTKC_irekae_2, + }; + // clang-format on + + actor->action = action; + actor->act_proc = process[action]; +} + +#include "../src/actor/ac_tokyoso_clip.c_inc"