From 29783fb14c1c9802b5a248eed3fcb3a124ad5819 Mon Sep 17 00:00:00 2001 From: Cuyler36 Date: Sat, 14 Jun 2025 07:18:38 -0400 Subject: [PATCH] Implement & link ac_npc_station_master --- configure.py | 2 +- include/ac_npc_station_master.h | 8 + src/actor/npc/ac_npc_station_master.c | 126 +++++ .../npc/ac_npc_station_master_move.c_inc | 24 + .../npc/ac_npc_station_master_schedule.c_inc | 463 ++++++++++++++++++ .../npc/ac_npc_station_master_talk.c_inc | 124 +++++ 6 files changed, 746 insertions(+), 1 deletion(-) create mode 100644 src/actor/npc/ac_npc_station_master.c create mode 100644 src/actor/npc/ac_npc_station_master_move.c_inc create mode 100644 src/actor/npc/ac_npc_station_master_schedule.c_inc create mode 100644 src/actor/npc/ac_npc_station_master_talk.c_inc diff --git a/configure.py b/configure.py index 463a5e1e..d25bffee 100644 --- a/configure.py +++ b/configure.py @@ -1142,7 +1142,7 @@ config.libs = [ Object(Matching, "actor/npc/ac_npc_shop_mastersp.c"), Object(Matching, "actor/npc/ac_npc_sleep_obaba.c"), Object(Matching, "actor/npc/ac_npc_soncho.c"), - Object(NonMatching, "actor/npc/ac_npc_station_master.c"), + Object(Matching, "actor/npc/ac_npc_station_master.c"), Object(Matching, "actor/npc/ac_npc_super_master.c"), Object(Matching, "actor/npc/ac_npc_totakeke.c"), Object(Matching, "actor/npc/ac_present_npc.c"), diff --git a/include/ac_npc_station_master.h b/include/ac_npc_station_master.h index 8e0863ab..b26d0827 100644 --- a/include/ac_npc_station_master.h +++ b/include/ac_npc_station_master.h @@ -46,6 +46,14 @@ enum { aSTM_THINK_PROC_NUM }; +enum { + aSTM_TALK_REQUEST_NONE, + aSTM_TALK_REQUEST_NORM, + aSTM_TALK_REQUEST_FORCE, + + aSTM_TALK_REQUEST_NUM, +}; + typedef struct npc_station_actor_s NPC_STATION_MASTER_ACTOR; typedef void (*aSTM_TALK_PROC)(NPC_STATION_MASTER_ACTOR*, GAME_PLAY*); diff --git a/src/actor/npc/ac_npc_station_master.c b/src/actor/npc/ac_npc_station_master.c new file mode 100644 index 00000000..b7bed861 --- /dev/null +++ b/src/actor/npc/ac_npc_station_master.c @@ -0,0 +1,126 @@ +#include "ac_npc_station_master.h" + +#include "libultra/libultra.h" +#include "m_common_data.h" +#include "m_msg.h" +#include "m_font.h" +#include "m_player_lib.h" +#include "m_string.h" +#include "m_bgm.h" +#include "m_train_control.h" +#include "ac_intro_demo.h" +#include "ac_ride_off_demo.h" + +enum { + aSTM_THINK_GET_OFF_WAIT, + aSTM_THINK_TURN, + aSTM_THINK_2, + aSTM_THINK_3, + aSTM_THINK_4, + aSTM_THINK_5, + aSTM_THINK_INTERRUPT_TURN, + aSTM_THINK_INTERRUPT_MOVE, + aSTM_THINK_8, + aSTM_THINK_9, + aSTM_THINK_MAKE_TRAIN_BF, + aSTM_THINK_MAKE_TRAIN, + aSTM_THINK_TRAIN_ARRIVE_WAIT, + aSTM_THINK_MOVE_TURN_BF, + aSTM_THINK_14, + aSTM_THINK_MOVE, + aSTM_THINK_16, + aSTM_THINK_17, + aSTM_THINK_IN_TRAIN, + aSTM_THINK_IN_TRAIN2, + aSTM_THINK_SEE_OFF, + aSTM_THINK_GAME_END, + + aSTM_THINK_NUM +}; + +static void aSTM_actor_ct(ACTOR* actorx, GAME* game); +static void aSTM_actor_dt(ACTOR* actorx, GAME* game); +static void aSTM_actor_init(ACTOR* actorx, GAME* game); +static void aSTM_actor_save(ACTOR* actorx, GAME* game); +static void aSTM_actor_move(ACTOR* actorx, GAME* game); +static void aSTM_actor_draw(ACTOR* actorx, GAME* game); +static int aSTM_talk_init(ACTOR* actorx, GAME* game); +static int aSTM_talk_end_chk(ACTOR* actorx, GAME* game); +static void aSTM_schedule_proc(NPC_ACTOR*, GAME_PLAY*, int); +static void aSTM_talk_request(ACTOR* actorx, GAME* game); +static int aSTM_change_talk_proc(NPC_STATION_MASTER_ACTOR*, u8); +static void aSTM_setup_think_proc(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play, u8 think_idx); + +// clang-format off +ACTOR_PROFILE Npc_Station_Master_Profile = { + mAc_PROFILE_NPC_STATION_MASTER, + ACTOR_PART_NPC, + ACTOR_STATE_NONE, + SP_NPC_STATION_MASTER, + ACTOR_OBJ_BANK_KEEP, + sizeof(NPC_STATION_MASTER_ACTOR), + aSTM_actor_ct, + aSTM_actor_dt, + aSTM_actor_init, + mActor_NONE_PROC1, + aSTM_actor_save +}; +// clang-format on + +static void aSTM_actor_ct(ACTOR* actorx, GAME* game) { + // clang-format off + static aNPC_ct_data_c ct_data = { + aSTM_actor_move, + aSTM_actor_draw, + aNPC_CT_SCHED_TYPE_SPECIAL, + (aNPC_TALK_REQUEST_PROC)none_proc1, + aSTM_talk_init, + aSTM_talk_end_chk, + 0 + }; + // clang-format on + + if (NPC_CLIP->birth_check_proc(actorx, game) == TRUE) { + NPC_STATION_MASTER_ACTOR* actor = (NPC_STATION_MASTER_ACTOR*)actorx; + + actor->npc_class.schedule.schedule_proc = aSTM_schedule_proc; + NPC_CLIP->ct_proc(actorx, game, &ct_data); + actor->sound = 0; + actor->happening_sound = 0; + actor->melody_save = 0; + actor->npc_class.palActorIgnoreTimer = -1; + } +} + +static void aSTM_actor_save(ACTOR* actorx, GAME* game) { + mNpc_RenewalSetNpc(actorx); +} + +static void aSTM_actor_dt(ACTOR* actorx, GAME* game) { + NPC_STATION_MASTER_ACTOR* actor = (NPC_STATION_MASTER_ACTOR*)actorx; + + if (actor->sound != 0) { + mBGMPsComp_delete_ps_demo(actor->sound, 0x168); + } + + switch (actor->happening_sound) { + case BGM_INTRO_ARRIVE: + case BGM_INTRO_NEW_TOWN: + mBGMPsComp_delete_ps_happening(actor->happening_sound, 0x168); + break; + } + + NPC_CLIP->dt_proc(actorx, game); +} + +static void aSTM_actor_init(ACTOR* actorx, GAME* game) { + NPC_CLIP->init_proc(actorx, game); +} + +static void aSTM_actor_draw(ACTOR* actorx, GAME* game) { + NPC_CLIP->draw_proc(actorx, game); +} + +#include "../src/actor/npc/ac_npc_station_master_move.c_inc" +#include "../src/actor/npc/ac_npc_station_master_talk.c_inc" +#include "../src/actor/npc/ac_npc_station_master_schedule.c_inc" diff --git a/src/actor/npc/ac_npc_station_master_move.c_inc b/src/actor/npc/ac_npc_station_master_move.c_inc new file mode 100644 index 00000000..7098a5b7 --- /dev/null +++ b/src/actor/npc/ac_npc_station_master_move.c_inc @@ -0,0 +1,24 @@ +static int aSTM_set_request_act(NPC_STATION_MASTER_ACTOR* actor, u8 priority, u8 act_idx, u8 act_type, u16 act_obj, s16 move_x, s16 move_z) { + int ret = FALSE; + + if (priority >= actor->npc_class.request.act_priority) { + u16 arg_data[aNPC_REQUEST_ARG_NUM]; + + bzero(arg_data, sizeof(arg_data)); + arg_data[0] = act_obj; + arg_data[2] = move_x; + arg_data[3] = move_z; + + actor->npc_class.request.act_priority = priority; + actor->npc_class.request.act_idx = act_idx; + actor->npc_class.request.act_type = act_type; + mem_copy((u8*)actor->npc_class.request.act_args, (u8*)arg_data, sizeof(arg_data)); + ret = TRUE; + } + + return ret; +} + +static void aSTM_actor_move(ACTOR* actorx, GAME* game) { + NPC_CLIP->move_proc(actorx, game); +} diff --git a/src/actor/npc/ac_npc_station_master_schedule.c_inc b/src/actor/npc/ac_npc_station_master_schedule.c_inc new file mode 100644 index 00000000..4634c341 --- /dev/null +++ b/src/actor/npc/ac_npc_station_master_schedule.c_inc @@ -0,0 +1,463 @@ +static void aSTM_Change_sound(NPC_STATION_MASTER_ACTOR* actor, u8 sound) { + if (actor->sound != sound) { + mBGMPsComp_make_ps_demo(sound, 0x168); + if (actor->sound == BGM_FIELD_00) { + mBGMPsComp_delete_ps_quiet(); + } + + actor->sound = sound; + } +} + +typedef struct npc_p_sel2_door_data_s { + int scene_no; + u8 drt; + u8 type; + u16 arg; + s16 pos_x; + s16 pos_z; +} aNPS2_door_data_c; + +static void aNPS2_make_door_data(Door_data_c* door_data) { + static aNPS2_door_data_c demo2_data = { SCENE_START_DEMO3, mSc_DIRECT_NORTH, 0, 0, 120, 340 }; + + door_data->next_scene_id = demo2_data.scene_no; + door_data->exit_orientation = demo2_data.drt; + door_data->exit_type = demo2_data.type; + door_data->extra_data = demo2_data.arg; + door_data->exit_position.x = demo2_data.pos_x; + door_data->exit_position.y = 0; + door_data->exit_position.z = demo2_data.pos_z; + door_data->door_actor_name = EMPTY_NO; + door_data->wipe_type = 5; + Common_Get(transition).wipe_type = 3; +} + +static int aNPS2_setup_game_start(GAME_PLAY* play) { + Door_data_c door_data; + + aNPS2_make_door_data(&door_data); + + if (goto_other_scene(play, &door_data, TRUE) != TRUE) { + return FALSE; + } + + mBGMPsComp_make_ps_wipe(0x328A); + return TRUE; +} + +static void aSTM_look_player(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + if (actor->npc_class.action.step == aNPC_ACTION_END_STEP) { + s16 diff_angle = DIFF_SHORT_ANGLE(actor->npc_class.actor_class.player_angle_y, actor->npc_class.actor_class.shape_info.rotation.y); + u8 action = aNPC_ACT_TURN2; + + if (ABS(diff_angle) < DEG2SHORT_ANGLE2(67.5f)) { + action = aNPC_ACT_WAIT; + } else { + actor->npc_class.condition_info.demo_flg |= aNPC_COND_DEMO_SKIP_FOOTSTEPS; + } + + aSTM_set_request_act(actor, 4, action, aNPC_ACT_TYPE_SEARCH, aNPC_ACT_OBJ_PLAYER, 0, 0); + } +} + +static void aSTM_talk_wait(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + ACTOR* playerx = GET_PLAYER_ACTOR_ACTOR(play); + + aSTM_look_player(actor, play); + + if (actor->happening_sound == BGM_INTRO_ARRIVE || actor->happening_sound == BGM_INTRO_NEW_TOWN) { + if (playerx != NULL && playerx->world.position.z >= 970.0f) { + if (actor->happening_sound == BGM_INTRO_ARRIVE) { + mBGMPsComp_delete_ps_happening(BGM_INTRO_ARRIVE, 0x384); + } else { + mBGMPsComp_delete_ps_happening(BGM_INTRO_NEW_TOWN, 0x528A); + } + + mBGMPsComp_make_ps_co_quiet(0x384, 0x12C); + actor->happening_sound = BGM_SILENCE; + } + } +} + +static f32 player_x_drt(NPC_STATION_MASTER_ACTOR* actor) { + if (actor->npc_class.actor_class.player_angle_y < 0) { + return 1.0f; + } else { + return -1.0f; + } +} + +static void aSTM_get_off_wait(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + aSTM_look_player(actor, play); + + if (CLIP(demo_clip) != NULL) { + ACTOR* demox = (ACTOR*)CLIP(demo_clip)->demo_class; + + if (demox != NULL) { + switch (CLIP(demo_clip)->type) { + case mDemo_CLIP_TYPE_INTRO_DEMO: { + INTRO_DEMO_ACTOR* demo_actor = (INTRO_DEMO_ACTOR*)demox; + + // train1 arg0 is passenger flag + if (mEv_CheckFirstIntro() && demo_actor->train1_actor_p != NULL && !demo_actor->train1_actor_p->arg0) { + if (mPlib_get_player_actor_main_index((GAME*)play) != mPlayer_INDEX_DEMO_GETOFF_TRAIN) { + aSTM_setup_think_proc(actor, play, aSTM_THINK_TURN); + } + } + + break; + } + + case mDemo_CLIP_TYPE_RIDE_OFF_DEMO: { + RIDE_OFF_DEMO_ACTOR* demo_actor = (RIDE_OFF_DEMO_ACTOR*)demox; + + actor->happening_sound = mBGMPsComp_execute_bgm_num_get(); + // train1 arg0 is passenger flag + if (demo_actor->train_actor != NULL && !demo_actor->train_actor->arg0) { + if (mPlib_get_player_actor_main_index((GAME*)play) != mPlayer_INDEX_DEMO_GETOFF_TRAIN) { + aSTM_setup_think_proc(actor, play, aSTM_THINK_TURN); + mBGMPsComp_scene_mode(1); + + if (Common_Get(player_no) != mPr_FOREIGNER) { + actor->next_think_idx = aSTM_THINK_3; + } + } + } + + break; + } + } + } + } +} + +static void aSTM_turn(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + aSTM_setup_think_proc(actor, play, actor->next_think_idx); +} + +static void aSTM_interrupt_turn(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + aSTM_setup_think_proc(actor, play, actor->think_idx + 1); +} + +static void aSTM_interrupt_move(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + u8 think_idx = aSTM_THINK_9; + + mEv_UnSetGateway(); + if (mEv_CheckFirstIntro() == TRUE) { + think_idx = aSTM_THINK_4; + } else if (Common_Get(player_no) < mPr_FOREIGNER) { + think_idx = aSTM_THINK_8; + } + + actor->npc_class.condition_info.demo_flg = 0; + play->submenu.start_refuse = FALSE; + aSTM_setup_think_proc(actor, play, think_idx); +} + +static void aSTM_make_train_bf(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + STRUCTURE_ACTOR* train1 = (STRUCTURE_ACTOR*)Actor_info_fgName_search(&play->actor_info, TRAIN1, ACTOR_PART_ITEM); + + if (actor->timer != 0) { + actor->timer--; + } else if (train1 == NULL || train1->action > 5) { // TODO: train1 needs action enum + actor->train_actor_p = NULL; + aSTM_setup_think_proc(actor, play, aSTM_THINK_MAKE_TRAIN); + } else { + actor->train_actor_p = (ACTOR*)train1; + Common_Set(train_coming_flag, 2); + aSTM_setup_think_proc(actor, play, aSTM_THINK_TRAIN_ARRIVE_WAIT); + aSTM_Change_sound(actor, BGM_TRAIN_PULL_INTO_TOWN); + } +} + +static void aSTM_make_train(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + STRUCTURE_ACTOR* train1 = (STRUCTURE_ACTOR*)Actor_info_fgName_search(&play->actor_info, TRAIN1, ACTOR_PART_ITEM); + + if (train1 != NULL && train1->action <= 5) { + actor->train_actor_p = (ACTOR*)train1; + aSTM_setup_think_proc(actor, play, aSTM_THINK_TRAIN_ARRIVE_WAIT); + } + + if (Common_Get(train_approaching_flag) == TRUE) { + aSTM_Change_sound(actor, BGM_TRAIN_PULL_INTO_TOWN); + } +} + +static void aSTM_train_arrive_wait(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + STRUCTURE_ACTOR* train1 = (STRUCTURE_ACTOR*)actor->train_actor_p; + + if (train1 != NULL && train1->action == 5) { + aSTM_setup_think_proc(actor, play, aSTM_THINK_14); + } + + if (Common_Get(train_approaching_flag) == TRUE) { + aSTM_Change_sound(actor, BGM_TRAIN_PULL_INTO_TOWN); + } +} + +static void aSTM_move_turn_bf(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + aSTM_setup_think_proc(actor, play, aSTM_THINK_14); +} + +static void aSTM_move(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + if (mPlib_get_player_actor_main_index((GAME*)play) != mPlayer_INDEX_DEMO_WALK) { + mPlib_request_main_demo_walk_type1((GAME*)play, actor->npc_class.actor_class.home.position.x + mFI_UT_WORLDSIZE_X_F, actor->train_actor_p->world.position.z + 2 * mFI_UT_WORLDSIZE_Z_F, 2.0f, FALSE); + } else { + aSTM_setup_think_proc(actor, play, aSTM_THINK_16); + } +} + +static void aSTM_in_train(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + xyz_t pos; + + aSTM_look_player(actor, play); + if (mPlib_get_player_actor_main_index((GAME*)play) != mPlayer_INDEX_DEMO_GETON_TRAIN) { + pos.x = actor->npc_class.actor_class.home.position.x + mFI_UT_WORLDSIZE_X_F; + pos.y = actor->train_actor_p->world.position.y + 20.0f; + pos.z = actor->train_actor_p->world.position.z + 2 * mFI_UT_WORLDSIZE_Z_F; + mPlib_request_main_demo_geton_train_type1((GAME*)play, &pos); + } else { + aSTM_setup_think_proc(actor, play, aSTM_THINK_IN_TRAIN2); + } +} + +static void aSTM_in_train2(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + aSTM_look_player(actor, play); + if (mPlib_get_player_actor_main_index((GAME*)play) != mPlayer_INDEX_DEMO_GETON_TRAIN) { + aSTM_setup_think_proc(actor, play, aSTM_THINK_SEE_OFF); + } +} + +static void aSTM_see_off(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + f32 dist = 315.5f - (actor->npc_class.actor_class.world.position.x - actor->npc_class.actor_class.home.position.x); + + aSTM_look_player(actor, play); + if (actor->npc_class.actor_class.player_distance_xz > dist) { + aSTM_setup_think_proc(actor, play, aSTM_THINK_GAME_END); + } +} + +typedef void (*aSTM_THINK_PROC)(NPC_STATION_MASTER_ACTOR*, GAME_PLAY*); + +// clang-format off +static aSTM_THINK_PROC proc_table[] = { + (aSTM_THINK_PROC)none_proc1, + aSTM_get_off_wait, + aSTM_turn, + aSTM_look_player, + aSTM_interrupt_turn, + aSTM_interrupt_move, + aSTM_make_train_bf, + aSTM_make_train, + aSTM_train_arrive_wait, + aSTM_move_turn_bf, + aSTM_move, + aSTM_in_train, + aSTM_in_train2, + aSTM_see_off, + aSTM_talk_wait, +}; +// clang-format on + +static void aSTM_think_main_proc(NPC_ACTOR* nactorx, GAME_PLAY* play) { + NPC_STATION_MASTER_ACTOR* actor = (NPC_STATION_MASTER_ACTOR*)nactorx; + + if (nactorx->action.step == aNPC_ACTION_END_STEP || actor->think_idx == aSTM_THINK_SEE_OFF) { + (*proc_table[actor->think_proc_idx])(actor, play); + } +} + +static void aSTM_think_init_proc(NPC_ACTOR* nactorx, GAME_PLAY* play) { + NPC_STATION_MASTER_ACTOR* actor = (NPC_STATION_MASTER_ACTOR*)nactorx; + u8 think_idx; + ACTOR* actorx = (ACTOR*)actor; + + if (mEv_CheckFirstIntro() == TRUE || Actor_info_name_search(&play->actor_info, mAc_PROFILE_RIDE_OFF_DEMO, ACTOR_PART_CONTROL) != NULL) { + think_idx = aSTM_THINK_GET_OFF_WAIT; + actorx->shape_info.rotation.y += DEG2SHORT_ANGLE2(-90.0f); + actorx->world.angle.y += DEG2SHORT_ANGLE2(-90.0f); + } else if (mEv_CheckFirstJob() == TRUE) { + think_idx = aSTM_THINK_5; + } else { + if (mEv_CheckTitleDemo() <= 0) { + actorx->world.position.x += mFI_UT_WORLDSIZE_X_F; + } + + if (Common_Get(player_no) < mPr_FOREIGNER) { + think_idx = aSTM_THINK_8; + } else { + think_idx = aSTM_THINK_9; + } + } + + actorx->status_data.weight = MASSTYPE_IMMOVABLE; + nactorx->condition_info.hide_request = FALSE; + aSTM_setup_think_proc(actor, play, think_idx); +} + +static void aSTM_get_off_wait_init(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + actor->npc_class.condition_info.demo_flg |= aNPC_COND_DEMO_SKIP_FOOTSTEPS; + aSTM_set_request_act(actor, 4, aNPC_ACT_TURN2, aNPC_ACT_TYPE_SEARCH, aNPC_ACT_OBJ_PLAYER, 0, 0); +} + +static void aSTM_intro_demo_wait_init(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + static xyz_t enter_pos = { 54.5f * mFI_UT_WORLDSIZE_X_F, 0.0f, 20.5f * mFI_UT_WORLDSIZE_Z_F }; + + aSTM_get_off_wait_init(actor, play); + mCoBG_SetAttribute(enter_pos, mCoBG_ATTRIBUTE_32); +} + +static void aSTM_interrupt_turn_init(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + actor->npc_class.condition_info.demo_flg |= aNPC_COND_DEMO_SKIP_FOOTSTEPS; + aSTM_set_request_act(actor, 4, aNPC_ACT_TURN2, aNPC_ACT_TYPE_TO_POINT, aNPC_ACT_OBJ_DEFAULT, actor->npc_class.actor_class.world.position.x + mFI_UT_WORLDSIZE_X_F, actor->npc_class.actor_class.world.position.z); +} + +static void aSTM_interrupt_move_init(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + actor->npc_class.condition_info.demo_flg = aNPC_COND_DEMO_SKIP_FORWARD_CHECK | aNPC_COND_DEMO_SKIP_OBJ_COL_CHECK; + aSTM_set_request_act(actor, 4, aNPC_ACT_WALK, aNPC_ACT_TYPE_TO_POINT, aNPC_ACT_OBJ_DEFAULT, actor->npc_class.actor_class.world.position.x + mFI_UT_WORLDSIZE_X_F, actor->npc_class.actor_class.world.position.z); + actor->npc_class.condition_info.demo_flg &= ~aNPC_COND_DEMO_SKIP_FOOTSTEPS; // @unnecessary +} + +static void aSTM_make_train_bf_init(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + actor->timer = 60; + play->submenu.start_refuse = TRUE; +} + +static void aSTM_make_train_init(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + play->submenu.start_refuse = TRUE; + Common_Set(train_coming_flag, 2); +} + +static void aSTM_move_turn_init(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + play->submenu.start_refuse = TRUE; + actor->move_ofs_x = player_x_drt(actor) * mFI_UNIT_BASE_SIZE_F; + actor->npc_class.movement.avoid_pos_x = actor->npc_class.actor_class.home.position.x + mFI_UT_WORLDSIZE_X_F + actor->move_ofs_x; + actor->npc_class.movement.avoid_pos_z = actor->train_actor_p->world.position.z + 2 * mFI_UT_WORLDSIZE_Z_F; + + aSTM_set_request_act(actor, 4, aNPC_ACT_TURN2, aNPC_ACT_TYPE_TO_POINT, aNPC_ACT_OBJ_DEFAULT, actor->npc_class.movement.avoid_pos_x, actor->npc_class.movement.avoid_pos_z); +} + +static void aSTM_move_init(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + aSTM_set_request_act(actor, 4, aNPC_ACT_WALK, aNPC_ACT_TYPE_TO_POINT, aNPC_ACT_OBJ_DEFAULT, actor->npc_class.movement.avoid_pos_x, actor->npc_class.movement.avoid_pos_z); + actor->npc_class.condition_info.demo_flg &= ~aNPC_COND_DEMO_SKIP_FOOTSTEPS; +} + +static void aSTM_see_off_init(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + STRUCTURE_ACTOR* train1 = (STRUCTURE_ACTOR*)actor->train_actor_p; + + // TODO: train1 arg1 = 'player boarding' flag + train1->arg1 = TRUE; + Common_Set(train_control_state, 0); + aSTM_get_off_wait_init(actor, play); + actor->npc_class.actor_class.state_bitfield |= ACTOR_STATE_NO_MOVE_WHILE_CULLED; +} + +static void aSTM_game_end_init(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play) { + if (actor->station_check_res == TRUE) { + aNPS2_setup_game_start(play); + } else { + mBGMPsComp_make_ps_wipe(0x328A); + play->fb_fade_type = 5; + play->fb_wipe_type = 5; + mPlib_request_main_invade_type1((GAME*)play); + } + + mBGMPsComp_scene_mode(11); +} + +typedef struct { + u8 think_proc_idx; + u8 think_init_idx; + u8 talk_request_idx; + u8 talk_idx; + u8 think_idx_after_talk; +} aSTM_think_data_c; + +// clang-format off +static aSTM_think_data_c dt_tbl[] = { + {aSTM_THINK_PROC_GET_OFF_WAIT, aSTM_THINK_INIT_GET_OFF_WAIT, aSTM_TALK_REQUEST_NONE, 0x00, aSTM_THINK_GET_OFF_WAIT}, + {aSTM_THINK_PROC_TURN, aSTM_THINK_INIT_NONE, aSTM_TALK_REQUEST_NONE, 0x00, aSTM_THINK_2}, + {aSTM_THINK_PROC_NONE, aSTM_THINK_INIT_NONE, aSTM_TALK_REQUEST_FORCE, 0x00, aSTM_THINK_INTERRUPT_TURN}, + {aSTM_THINK_PROC_NONE, aSTM_THINK_INIT_NONE, aSTM_TALK_REQUEST_FORCE, 0x01, aSTM_THINK_INTERRUPT_TURN}, + {aSTM_THINK_PROC_TALK_WAIT, aSTM_THINK_INIT_INTRO_DEMO_WAIT, aSTM_TALK_REQUEST_NORM, 0x00, aSTM_THINK_4}, + {aSTM_THINK_PROC_TALK_WAIT, aSTM_THINK_INIT_GET_OFF_WAIT, aSTM_TALK_REQUEST_NORM, 0x01, aSTM_THINK_5}, + {aSTM_THINK_PROC_INTERRUPT_TURN, aSTM_THINK_INIT_INTERRUPT_TURN, aSTM_TALK_REQUEST_NONE, 0x00, aSTM_THINK_INTERRUPT_TURN}, + {aSTM_THINK_PROC_INTERRUPT_MOVE, aSTM_THINK_INIT_INTERRUPT_MOVE, aSTM_TALK_REQUEST_NONE, 0x00, aSTM_THINK_INTERRUPT_MOVE}, + {aSTM_THINK_PROC_TALK_WAIT, aSTM_THINK_INIT_GET_OFF_WAIT, aSTM_TALK_REQUEST_NORM, 0x02, aSTM_THINK_8}, + {aSTM_THINK_PROC_TALK_WAIT, aSTM_THINK_INIT_GET_OFF_WAIT, aSTM_TALK_REQUEST_NORM, 0x03, aSTM_THINK_9}, + {aSTM_THINK_PROC_MAKE_TRAIN_BF, aSTM_THINK_INIT_MAKE_TRAIN_BF, aSTM_TALK_REQUEST_NONE, 0x00, aSTM_THINK_MAKE_TRAIN_BF}, + {aSTM_THINK_PROC_MAKE_TRAIN, aSTM_THINK_INIT_MAKE_TRAIN, aSTM_TALK_REQUEST_NONE, 0x00, aSTM_THINK_MAKE_TRAIN}, + {aSTM_THINK_PROC_TRAIN_ARRIVE_WAIT, aSTM_THINK_INIT_NONE, aSTM_TALK_REQUEST_NONE, 0x00, aSTM_THINK_TRAIN_ARRIVE_WAIT}, + {aSTM_THINK_PROC_MOVE_TURN_BF, aSTM_THINK_INIT_MAKE_TRAIN, aSTM_TALK_REQUEST_NONE, 0x00, aSTM_THINK_14}, + {aSTM_THINK_PROC_INTERRUPT_TURN, aSTM_THINK_INIT_MOVE_TURN, aSTM_TALK_REQUEST_NONE, 0x00, aSTM_THINK_14}, + {aSTM_THINK_PROC_MOVE, aSTM_THINK_INIT_MOVE, aSTM_TALK_REQUEST_NONE, 0x00, aSTM_THINK_MOVE}, + {aSTM_THINK_PROC_INTERRUPT_TURN, aSTM_THINK_INIT_LOOK_P, aSTM_TALK_REQUEST_NONE, 0x00, aSTM_THINK_16}, + {aSTM_THINK_PROC_NONE, aSTM_THINK_INIT_NONE, aSTM_TALK_REQUEST_FORCE, 0x02, aSTM_THINK_IN_TRAIN}, + {aSTM_THINK_PROC_IN_TRAIN, aSTM_THINK_INIT_NONE, aSTM_TALK_REQUEST_NONE, 0x00, aSTM_THINK_IN_TRAIN}, + {aSTM_THINK_PROC_IN_TRAIN2, aSTM_THINK_INIT_NONE, aSTM_TALK_REQUEST_NONE, 0x00, aSTM_THINK_IN_TRAIN2}, + {aSTM_THINK_PROC_SEE_OFF, aSTM_THINK_INIT_SEE_OFF, aSTM_TALK_REQUEST_NONE, 0x00, aSTM_THINK_SEE_OFF}, + {aSTM_THINK_PROC_LOOK_PLAYER, aSTM_THINK_INIT_GAME_END, aSTM_TALK_REQUEST_NONE, 0x00, aSTM_THINK_GAME_END}, +}; +// clang-format on + +typedef void (*aSTM_THINK_INIT_PROC)(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play); + +// clang-format off +static aSTM_THINK_INIT_PROC init_table[] = { + (aSTM_THINK_INIT_PROC)none_proc1, + aSTM_get_off_wait_init, + aSTM_intro_demo_wait_init, + aSTM_interrupt_turn_init, + aSTM_interrupt_move_init, + aSTM_make_train_init, + aSTM_make_train_bf_init, + aSTM_move_turn_init, + aSTM_move_init, + aSTM_look_player, + aSTM_see_off_init, + aSTM_game_end_init, +}; +// clang-format on + +static void aSTM_setup_think_proc(NPC_STATION_MASTER_ACTOR* actor, GAME_PLAY* play, u8 think_idx) { + aSTM_think_data_c* data_p = &dt_tbl[think_idx]; + // clang-format off + static aNPC_TALK_REQUEST_PROC talk_request_table[] = { + (aNPC_TALK_REQUEST_PROC)none_proc1, + aSTM_norm_talk_request, + aSTM_force_talk_request, + }; + // clang-format on + + actor->think_idx = think_idx; + actor->think_proc_idx = data_p->think_proc_idx; + actor->talk_proc_idx = data_p->talk_request_idx; + actor->npc_class.talk_info.talk_request_proc = talk_request_table[data_p->talk_request_idx]; + actor->talk_idx = data_p->talk_idx; + actor->next_think_idx = data_p->think_idx_after_talk; + (*init_table[data_p->think_init_idx])(actor, play); +} + +static void aSTM_think_proc(NPC_ACTOR* nactorx, GAME_PLAY* play, int type) { + static aNPC_SUB_PROC think_proc[] = { aSTM_think_init_proc, aSTM_think_main_proc }; + + (*think_proc[type])(nactorx, play); +} + +static void aSTM_schedule_init_proc(NPC_ACTOR* nactorx, GAME_PLAY* play) { + nactorx->think.think_proc = aSTM_think_proc; + NPC_CLIP->think_proc(nactorx, play, aNPC_THINK_SPECIAL, aNPC_THINK_TYPE_INIT); +} + +static void aSTM_schedule_main_proc(NPC_ACTOR* nactorx, GAME_PLAY* play) { + if (!NPC_CLIP->think_proc(nactorx, play, -1, aNPC_THINK_TYPE_CHK_INTERRUPT)) { + NPC_CLIP->think_proc(nactorx, play, -1, aNPC_THINK_TYPE_MAIN); + } +} + +static void aSTM_schedule_proc(NPC_ACTOR* nactorx, GAME_PLAY* play, int type) { + static aNPC_SUB_PROC sche_proc[] = { aSTM_schedule_init_proc, aSTM_schedule_main_proc }; + + (*sche_proc[type])(nactorx, play); +} diff --git a/src/actor/npc/ac_npc_station_master_talk.c_inc b/src/actor/npc/ac_npc_station_master_talk.c_inc new file mode 100644 index 00000000..44279146 --- /dev/null +++ b/src/actor/npc/ac_npc_station_master_talk.c_inc @@ -0,0 +1,124 @@ +static void aSTM_train_go_ok(void) { + Common_Set(train_control_state, 0); + Common_Set(train_last_control_state, 0); +} + +typedef struct { + int msg_no; + u8 talk_idx; + u8 cam_type; +} aSTM_talk_data_c; + +static void aSTM_set_force_talk_info(ACTOR* actorx) { + // clang-format off + static aSTM_talk_data_c dt_tbl[] = { + {0x07DD, aSTM_TALK_END_WAIT, CAMERA2_PROCESS_NORMAL}, + {0x0966, aSTM_TALK_END_WAIT, CAMERA2_PROCESS_NORMAL}, + {0x0965, aSTM_TALK_SAYONARA, CAMERA2_PROCESS_TALK}, + }; + // clang-format on + + NPC_STATION_MASTER_ACTOR* actor = (NPC_STATION_MASTER_ACTOR*)actorx; + aSTM_talk_data_c* data_p = &dt_tbl[actor->talk_idx]; + + if (CLIP(station_clip) != NULL) { + CLIP(station_clip)->change_talk_proc((ACTOR*)actor, data_p->talk_idx); + } else { + actor->talk_proc = (aSTM_TALK_PROC)none_proc1; + } + + mDemo_Set_msg_num(data_p->msg_no); + mDemo_Set_talk_turn(TRUE); + mDemo_Set_camera(data_p->cam_type); + mDemo_Set_talk_return_demo_wait(TRUE); + + if (actor->talk_idx == 2) { + if (actor->npc_class.talk_info.melody_inst != 0) { + actor->melody_save = actor->npc_class.talk_info.melody_inst; + } + + actor->npc_class.talk_info.melody_inst = 0; + } +} + +static void aSTM_force_talk_request(ACTOR* actorx, GAME* game) { + mDemo_Request(mDemo_TYPE_SPEAK, actorx, aSTM_set_force_talk_info); +} + +static void aSTM_set_norm_talk_info(ACTOR* actorx) { + // clang-format off + static aSTM_talk_data_c dt_tbl[] = { + {0x0831, aSTM_TALK_END_WAIT, CAMERA2_PROCESS_TALK}, + {0x07ED, aSTM_TALK_END_WAIT, CAMERA2_PROCESS_TALK}, + {0x0943, aSTM_TALK_CHK_LEAVE_TALK, CAMERA2_PROCESS_TALK}, + {0x095C, aSTM_TALK_CHK_LEAVE_TALK, CAMERA2_PROCESS_TALK}, + }; + // clang-format on + + NPC_STATION_MASTER_ACTOR* actor = (NPC_STATION_MASTER_ACTOR*)actorx; + aSTM_talk_data_c* data_p = &dt_tbl[actor->talk_idx]; + + if (CLIP(aprilfool_control_clip) != NULL && !CLIP(aprilfool_control_clip)->talk_chk_proc(SP_NPC_STATION_MASTER)) { + mDemo_Set_msg_num(CLIP(aprilfool_control_clip)->get_msg_num_proc(SP_NPC_STATION_MASTER, TRUE)); + CLIP(station_clip)->change_talk_proc(actorx, aSTM_TALK_END_WAIT); + } else { + if (actor->talk_idx == 2 && !Save_Get(save_exist)) { + mDemo_Set_msg_num(0x0945); + actor->talk_proc = (aSTM_TALK_PROC)none_proc1; + } else { + mDemo_Set_msg_num(data_p->msg_no); + if (CLIP(station_clip) != NULL) { + CLIP(station_clip)->change_talk_proc(actorx, data_p->talk_idx); + } else { + actor->talk_proc = (aSTM_TALK_PROC)none_proc1; + } + } + } + + mDemo_Set_talk_turn(TRUE); + mDemo_Set_camera(CAMERA2_PROCESS_TALK); +} + +static void aSTM_norm_talk_request(ACTOR* actorx, GAME* game) { + mDemo_Request(mDemo_TYPE_TALK, actorx, aSTM_set_norm_talk_info); +} + +static int aSTM_talk_init(ACTOR* actorx, GAME* game) { + NPC_STATION_MASTER_ACTOR* actor = (NPC_STATION_MASTER_ACTOR*)actorx; + + actor->npc_class.talk_info.talk_request_proc = (aNPC_TALK_REQUEST_PROC)none_proc1; + mDemo_Set_ListenAble(); + mDemo_Start(actorx); + if (actor->talk_proc_idx == 1 && !mEv_CheckFirstJob()) { + actor->happening_sound = mBGMPsComp_execute_bgm_num_get(); + } + + return TRUE; +} + +static int aSTM_talk_end_chk(ACTOR* actorx, GAME* game) { + NPC_STATION_MASTER_ACTOR* actor = (NPC_STATION_MASTER_ACTOR*)actorx; + GAME_PLAY* play = (GAME_PLAY*)game; + int ret = FALSE; + + actor->talk_proc(actor, play); + if (mDemo_CAN_ACTOR_TALK(actorx)) { + switch (actor->next_think_idx) { + case aSTM_THINK_8: + case aSTM_THINK_9: + aSTM_train_go_ok(); + break; + } + + aSTM_setup_think_proc(actor, play, actor->next_think_idx); + + if (actor->melody_save != 0) { + actor->npc_class.talk_info.melody_inst = actor->melody_save; + actor->melody_save = 0; + } + + ret = TRUE; + } + + return ret; +}