From 8422652bbfd28cb14c42a6594a9cc60aa705c13e Mon Sep 17 00:00:00 2001 From: Cuyler36 <24523422+Cuyler36@users.noreply.github.com> Date: Mon, 8 Jul 2024 20:24:31 -0400 Subject: [PATCH] Implement & link ac_npc_guide2 --- config/rel_slices.yml | 4 + include/ac_npc_guide2.h | 28 +- src/ac_npc_guide2.c | 138 +++++ src/ac_npc_guide2_animation.c_inc | 45 ++ src/ac_npc_guide2_move.c_inc | 823 ++++++++++++++++++++++++++++++ src/ac_npc_guide_animation.c_inc | 5 +- 6 files changed, 1041 insertions(+), 2 deletions(-) create mode 100644 src/ac_npc_guide2.c create mode 100644 src/ac_npc_guide2_animation.c_inc create mode 100644 src/ac_npc_guide2_move.c_inc diff --git a/config/rel_slices.yml b/config/rel_slices.yml index aede44d1..f5705fbc 100644 --- a/config/rel_slices.yml +++ b/config/rel_slices.yml @@ -764,6 +764,10 @@ ac_npc_guide.c: .text: [0x80552B5C, 0x80554B00] .rodata: [0x80649620, 0x806496A0] .data: [0x806A6208, 0x806A6508] +ac_npc_guide2.c: + .text: [0x80554B00, 0x80556714] + .rodata: [0x806496A0, 0x80649720] + .data: [0x806A6508, 0x806A67E0] ac_npc_rcn_guide.c: .text: [0x8056EED0, 0x8056FFF0] .rodata: [0x806499C0, 0x806499E8] diff --git a/include/ac_npc_guide2.h b/include/ac_npc_guide2.h index f48a12ba..1cb62651 100644 --- a/include/ac_npc_guide2.h +++ b/include/ac_npc_guide2.h @@ -3,11 +3,38 @@ #include "types.h" #include "m_actor.h" +#include "ac_npc.h" #ifdef __cplusplus extern "C" { #endif +typedef struct npc_guide2_actor_s NPC_GUIDE2_ACTOR; + +typedef void (*aNG2_PROC)(NPC_GUIDE2_ACTOR*, GAME_PLAY*); + +struct npc_guide2_actor_s { + NPC_ACTOR npc_class; + int action; + int next_action; + aNG2_PROC proc; + int lock_camera_flag; + int camera_morph_counter; + s16 camera_move; + s8 camera_move_cnt; + s8 camera_move_set_counter; + f32 camera_move_y; + f32 camera_move_range; + s16 camera_tilt_cur_angle; + s16 camera_tilt_goal_angle; + s16 camera_tilt_add; + int answer_flags; + ACTOR* train_door_actor; + int obj_look_type; + f32 obj_dist_ground; + int camera_eyes_flag; +}; + extern ACTOR_PROFILE Npc_Guide2_Profile; #ifdef __cplusplus @@ -15,4 +42,3 @@ extern ACTOR_PROFILE Npc_Guide2_Profile; #endif #endif - diff --git a/src/ac_npc_guide2.c b/src/ac_npc_guide2.c new file mode 100644 index 00000000..73008c9b --- /dev/null +++ b/src/ac_npc_guide2.c @@ -0,0 +1,138 @@ +#include "ac_npc_guide2.h" + +#include "m_play.h" +#include "m_common_data.h" +#include "ac_train_door.h" +#include "m_player_lib.h" +#include "m_font.h" +#include "m_msg.h" +#include "m_timeIn_ovl.h" +#include "m_ledit_ovl.h" +#include "libultra/libultra.h" +#include "m_bgm.h" +#include "m_soncho.h" + +enum { + aNG2_ACTION_ENTER, + aNG2_ACTION_APPROACH, + aNG2_ACTION_TALK_START_WAIT, + aNG2_ACTION_WAIT_PERMISSION, + aNG2_ACTION_SITDOWN, + aNG2_ACTION_SDON_AND_PB_WAIT, + aNG2_ACTION_PNAME_MENU_OPEN_WAIT, + aNG2_ACTION_PNAME_MENU_CLOSE_WAIT, + aNG2_ACTION_MSG_WIN_OPEN_WAIT, + aNG2_ACTION_PNAME_NG, + aNG2_ACTION_SEX_SELECT_WAIT, + aNG2_ACTION_SEX_SELECT_WAIT2, + aNG2_ACTION_SEX_SELECT_AFTER, + aNG2_ACTION_STANDUP_START_WAIT, + aNG2_ACTION_STANDUP, + aNG2_ACTION_MOVE_READY, + aNG2_ACTION_MOVE_TO_AISLE, + aNG2_ACTION_MOVE_TO_DOOR, + aNG2_ACTION_MOVE_TO_DECK, + aNG2_ACTION_KEITAI_ON, + aNG2_ACTION_KEITAI_TALK, + aNG2_ACTION_KEITAI_OFF, + aNG2_ACTION_OPEN_DOOR, + aNG2_ACTION_RETURN_APPROACH, + aNG2_ACTION_LAST_TALK_START_WAIT, + aNG2_ACTION_SITDOWN2_START_WAIT, + aNG2_ACTION_SITDOWN2, + aNG2_ACTION_LAST_TALK_END_WAIT, + aNG2_ACTION_SCENE_CHANGE_WAIT, + + aNG2_ACTION_NUM +}; + +enum { + aNG2_OBJ_LOOK_TYPE_NORMAL, + aNG2_OBJ_LOOK_TYPE_TALK, + + aNG2_OBJ_LOOK_TYPE_NUM +}; + +static void aNG2_actor_ct(ACTOR* actorx, GAME* game); +static void aNG2_actor_dt(ACTOR* actorx, GAME* game); +static void aNG2_actor_init(ACTOR* actorx, GAME* game); +static void aNG2_actor_save(ACTOR* actorx, GAME* game); + +// clang-format off +ACTOR_PROFILE Npc_Guide2_Profile = { + mAc_PROFILE_NPC_GUIDE2, + ACTOR_PART_NPC, + ACTOR_STATE_NO_DRAW_WHILE_CULLED | ACTOR_STATE_NO_MOVE_WHILE_CULLED, + SP_NPC_GUIDE2, + ACTOR_OBJ_BANK_KEEP, + sizeof(NPC_GUIDE2_ACTOR), + &aNG2_actor_ct, + &aNG2_actor_dt, + &aNG2_actor_init, + mActor_NONE_PROC1, + &aNG2_actor_save, +}; +// clang-format on + +static void aNG2_actor_move(ACTOR* actorx, GAME* game); +static void aNG2_actor_draw(ACTOR* actorx, GAME* game); + +static void aNG2_setupAction(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play, int action); + +static void aNG2_actor_ct(ACTOR* actorx, GAME* game) { + static aNPC_ct_data_c ct_data = { &aNG2_actor_move, &aNG2_actor_draw, 0, NULL, NULL, NULL, 0 }; + NPC_GUIDE2_ACTOR* guide2 = (NPC_GUIDE2_ACTOR*)actorx; + GAME_PLAY* play = (GAME_PLAY*)game; + PLAYER_ACTOR* player; + + Common_Get(clip).npc_clip->ct_proc(actorx, game, &ct_data); + guide2->npc_class.condition_info.hide_flg = FALSE; // show guide2 actor + guide2->npc_class.condition_info.demo_flg = + aNPC_COND_DEMO_SKIP_ENTRANCE_CHECK | aNPC_COND_DEMO_SKIP_HEAD_LOOKAT | aNPC_COND_DEMO_SKIP_TALK_CHECK | + aNPC_COND_DEMO_SKIP_ITEM | aNPC_COND_DEMO_SKIP_FORWARD_CHECK | aNPC_COND_DEMO_SKIP_BGCHECK | + aNPC_COND_DEMO_SKIP_OBJ_COL_CHECK | aNPC_COND_DEMO_SKIP_MOVE_Y | aNPC_COND_DEMO_SKIP_MOVE_CIRCLE_REV | + aNPC_COND_DEMO_SKIP_MOVE_RANGE_CHECK; + guide2->camera_morph_counter = 40; + guide2->obj_look_type = aNG2_OBJ_LOOK_TYPE_NORMAL; + guide2->npc_class.eye_y = 30.0f; + guide2->camera_move_set_counter = 1; + guide2->npc_class.palActorIgnoreTimer = -1; + guide2->train_door_actor = Actor_info_fgName_search(&play->actor_info, TRAIN_DOOR, ACTOR_PART_BG); + guide2->npc_class.actor_class.shape_info.draw_shadow = TRUE; + guide2->npc_class.actor_class.world.position.z = 130.0f; + + /* Put the player into demo wait state */ + mPlib_request_main_demo_wait_type1(game, 0, NULL); + player = GET_PLAYER_ACTOR(play); + if (player != NULL) { + /* Make the player invisible during the intro train ride */ + player->actor_class.state_bitfield |= ACTOR_STATE_INVISIBLE; + } + + aNG2_setupAction(guide2, play, aNG2_ACTION_ENTER); + + /* Play train noises sfx repeatedly */ + sAdo_SysLevStart(NA_SE_TRAIN_RIDE); +} + +static void aNG2_actor_save(ACTOR* actorx, GAME* game) { + Common_Get(clip).npc_clip->save_proc(actorx, game); +} + +static void aNG2_actor_dt(ACTOR* actorx, GAME* game) { + Common_Get(clip).npc_clip->dt_proc(actorx, game); + + /* Stop train noise sfx */ + sAdo_SysLevStop(NA_SE_TRAIN_RIDE); +} + +static void aNG2_actor_init(ACTOR* actorx, GAME* game) { + Common_Get(clip).npc_clip->init_proc(actorx, game); +} + +static void aNG2_actor_draw(ACTOR* actorx, GAME* game) { + Common_Get(clip).npc_clip->draw_proc(actorx, game); +} + +#include "../src/ac_npc_guide2_animation.c_inc" +#include "../src/ac_npc_guide2_move.c_inc" diff --git a/src/ac_npc_guide2_animation.c_inc b/src/ac_npc_guide2_animation.c_inc new file mode 100644 index 00000000..d97d7618 --- /dev/null +++ b/src/ac_npc_guide2_animation.c_inc @@ -0,0 +1,45 @@ +typedef struct npc_guide_animation_s { + int anim_id; + int talk_flag; +} aNG2_anime_c; + +static void aNG2_set_animation(NPC_GUIDE2_ACTOR* guide2, int action) { + // clang-format off + static aNG2_anime_c anime[] = { + { aNPC_ANIM_OPEN_D1, FALSE }, + { aNPC_ANIM_WALK1, FALSE }, + { aNPC_ANIM_WAIT1, TRUE }, + { aNPC_ANIM_WAIT1, TRUE }, + { aNPC_ANIM_SITDOWN_D1, TRUE }, + { aNPC_ANIM_SITDOWN_WAIT_D1, TRUE }, + { aNPC_ANIM_SITDOWN_WAIT_D1, TRUE }, + { aNPC_ANIM_SITDOWN_WAIT_D1, TRUE }, + { aNPC_ANIM_SITDOWN_WAIT_D1, TRUE }, + { aNPC_ANIM_SITDOWN_WAIT_D1, TRUE }, + { aNPC_ANIM_SITDOWN_WAIT_D1, TRUE }, + { aNPC_ANIM_SITDOWN_WAIT_D1, TRUE }, + { aNPC_ANIM_SITDOWN_WAIT_D1, TRUE }, + { aNPC_ANIM_SITDOWN_WAIT_D1, TRUE }, + { aNPC_ANIM_STANDUP_D1, FALSE }, + { aNPC_ANIM_WAIT1, FALSE }, + { aNPC_ANIM_WALK1, FALSE }, + { aNPC_ANIM_WALK1, FALSE }, + { aNPC_ANIM_TO_DECK_D1, FALSE }, + { aNPC_ANIM_KEITAI_ON1, FALSE }, + { aNPC_ANIM_KEITAI_TALK1, TRUE }, + { aNPC_ANIM_KEITAI_OFF1, FALSE }, + { aNPC_ANIM_OPEN_D2, FALSE }, + { aNPC_ANIM_WALK1, FALSE }, + { aNPC_ANIM_WAIT1, FALSE }, + { aNPC_ANIM_WAIT1, TRUE }, + { aNPC_ANIM_SITDOWN_D1, TRUE }, + { aNPC_ANIM_SITDOWN_WAIT_D1, TRUE }, + { aNPC_ANIM_SITDOWN_WAIT_D1, FALSE }, + }; + // clang-format on + + aNG2_anime_c* anime_info = &anime[action]; + + Common_Get(clip).npc_clip->animation_init_proc(&guide2->npc_class.actor_class, anime_info->anim_id, + anime_info->talk_flag); +} diff --git a/src/ac_npc_guide2_move.c_inc b/src/ac_npc_guide2_move.c_inc new file mode 100644 index 00000000..b9fb762d --- /dev/null +++ b/src/ac_npc_guide2_move.c_inc @@ -0,0 +1,823 @@ +static void aNG2_set_camera(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + static s8 mov_def_cnt[] = { 3, 0 }; + static f32 obj_look_y_max[] = { 30.0f, 20.0f }; + static f32 obj_look_y_spd[] = { 0.5f, 2.5f }; + + xyz_t eye; + xyz_t pos; + xyz_t ground; + s16 move; + f32 moveX; + f32 moveY; + int obj_look_type = guide2->obj_look_type; + s16 cur_angle; + f32 inter; + f32 sin; + f32 ground_y; + s16 angle; + + move = guide2->camera_move; + guide2->camera_move += 0xE20; + angle = move; + angle += 0xE20; + moveX = cos_s(angle) * 0.1f; + angle = move; + angle += 0xE20; + moveY = sin_s(angle) * guide2->camera_move_range; + + if (guide2->camera_move_y <= 0.0f && moveY >= 0.0f) { + s8 cnt = guide2->camera_move_cnt; + + cnt--; + if (cnt < 0) { + s8 set_cnt = guide2->camera_move_set_counter; + + set_cnt--; + if (set_cnt < 0) { + set_cnt = ARRAY_COUNT(mov_def_cnt) - 1; + } + + guide2->camera_move_set_counter = set_cnt; + cnt = mov_def_cnt[set_cnt]; + guide2->camera_move_range = 0.3f; + } else { + guide2->camera_move_range *= 0.35f; + } + + guide2->camera_move_cnt = cnt; + } + + guide2->camera_move_y = moveY; + cur_angle = guide2->camera_tilt_cur_angle; + chase_angle(&cur_angle, guide2->camera_tilt_goal_angle, guide2->camera_tilt_add); + guide2->camera_tilt_cur_angle = cur_angle; + sin = sin_s(cur_angle); + + /* Set camera eye pos */ + eye.x = moveX + sin * 20.0f + 100.0f; + eye.y = moveY + sin * -5.0f + 80.0f; + eye.z = 400.0f; + + /* Move NPC head to current target */ + chase_f(&guide2->npc_class.eye_y, obj_look_y_max[obj_look_type], obj_look_y_spd[obj_look_type] * 0.5f); + + /* Set ground pos */ + ground.x = guide2->npc_class.draw.shadow_pos.x; + ground.z = guide2->npc_class.draw.shadow_pos.z; + if (obj_look_type == aNG2_OBJ_LOOK_TYPE_TALK) { + /* Use the guide2's NPC shadow position for Y */ + ground_y = guide2->npc_class.draw.shadow_pos.y; + } else { + /* Get the ground position for Y */ + ground_y = mCoBG_GetBgY_OnlyCenter_FromWpos2(ground, 0.0f); + } + + chase_f(&guide2->obj_dist_ground, ground_y, 0.5f); + ground.y = guide2->obj_dist_ground; + + /* Set camera center pos */ + if (guide2->lock_camera_flag == TRUE) { + pos.x = ground.x; + pos.y = ground.y + guide2->npc_class.eye_y; + pos.z = ground.z; + } else if (mDemo_Check(mDemo_TYPE_SPEAK, &guide2->npc_class.actor_class) == TRUE) { + int morph_counter = guide2->camera_morph_counter; + f32 r; + + guide2->camera_morph_counter = morph_counter - 1; + if ((morph_counter - 1) == 0) { + guide2->lock_camera_flag = TRUE; + } + + r = (40.0f - (f32)(morph_counter - 1)) / 40.0f; + inter = cKF_HermitCalc(r, 1.0f, 0.0f, 1.0f, 3.2f, 0.0f); + + pos.x = (ground.x - 90.0f) * inter + 90.0f; + pos.y = ((ground.y + guide2->npc_class.eye_y) - 80.0f) * inter + 80.0f; + pos.z = (ground.z - 280.0f) * inter + 280.0f; + } else { + /* Default position */ + pos.x = 90.0f; + pos.y = 80.0f; + pos.z = 280.0f; + } + + pos.x += moveX; + pos.y += moveY; + + Camera2_change_priority(play, 0); + Camera2_request_main_lock(play, &pos, &eye, 40.0f, 0, 60.0f, 800.0f, 5); +} + +static void aNG2_set_camera_eyes(NPC_GUIDE2_ACTOR* guide2) { + if (guide2->camera_eyes_flag == TRUE) { + guide2->npc_class.request.head_pos.x = 100.0f; + guide2->npc_class.request.head_pos.y = guide2->npc_class.actor_class.eye.position.y; + guide2->npc_class.request.head_pos.z = 400.0f; + guide2->npc_class.request.head_priority = 3; + guide2->npc_class.request.head_type = 2; + } +} + +static void aNG2_set_walk_spd(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + guide2->npc_class.movement.max_speed = 1.0f; + guide2->npc_class.movement.acceleration = 0.1f; + guide2->npc_class.movement.deceleration = 0.2f; +} + +static void aNG2_set_walk_spd2(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + guide2->npc_class.movement.max_speed = 1.5f; + guide2->npc_class.movement.acceleration = 0.15f; + guide2->npc_class.movement.deceleration = 0.3f; +} + +static void aNG2_set_stop_spd(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + guide2->npc_class.actor_class.speed = 0.0f; + guide2->npc_class.movement.max_speed = 0.0f; + guide2->npc_class.movement.acceleration = 0.0f; + guide2->npc_class.movement.deceleration = 0.0f; +} + +static int aNG2_make_keitai(NPC_GUIDE2_ACTOR* guide2, GAME* game) { + int res = FALSE; + + if (guide2->npc_class.right_hand.item_actor_p == NULL) { + if (Common_Get(clip).tools_clip != NULL) { + ACTOR* keitai = Common_Get(clip).tools_clip->aTOL_birth_proc( + TOOL_KEITAI, aTOL_ACTION_TAKEOUT, &guide2->npc_class.actor_class, game, -1, NULL); + + if (keitai != NULL) { + guide2->npc_class.right_hand.item_actor_p = keitai; + res = TRUE; + } + } + } else { + res = TRUE; + } + + return res; +} + +static int aNG2_chg_cond_keitai(NPC_GUIDE2_ACTOR* guide2, int cond) { + int res = FALSE; + + if (Common_Get(clip).tools_clip != NULL) { + if (Common_Get(clip).tools_clip->aTOL_chg_request_mode_proc( + &guide2->npc_class.actor_class, guide2->npc_class.right_hand.item_actor_p, cond) == TRUE) { + res = TRUE; + } + } + + return res; +} + +static void aNG2_talk_demo_proc(ACTOR* actorx) { + if (mDemo_Check(mDemo_TYPE_SPEAK, actorx) == TRUE) { + Common_Get(clip).npc_clip->talk_demo_proc(actorx); + } +} + +static u8* aNG2_getP_other_pl_name(void) { + Private_c* priv; + u32 player_no; + int i; + + player_no = Common_Get(player_no); + priv = Save_Get(private_data) + player_no; + + /* Search for players before this one's player no */ + for (i = player_no; i != 0; i--) { + priv--; + if (mPr_NullCheckPersonalID(&priv->player_ID) != TRUE) { + return priv->player_ID.player_name; + } + } + + priv = Save_Get(private_data) + player_no; + /* Search for players after this one's player no */ + for (i = (PLAYER_NUM - 1) - player_no; i != 0; i--) { + priv++; + if (mPr_NullCheckPersonalID(&priv->player_ID) != TRUE) { + return priv->player_ID.player_name; + } + } + + /* No other players to reference */ + return NULL; +} + +static int aNG2_check_pname(void) { + int ok = TRUE; + int i; + + for (i = 0; i < PLAYER_NUM; i++) { + Private_c* priv = Save_GetPointer(private_data[i]); + + if (i != Common_Get(player_no)) { + if (mem_cmp(Now_Private->player_ID.player_name, priv->player_ID.player_name, PLAYER_NAME_LEN) == TRUE) { + /* Another player has the same name in this town */ + ok = FALSE; + break; + } + } + } + + return ok; +} + +static void aNG2_check_talk_msg_no(NPC_GUIDE2_ACTOR* guide2) { + if (mDemo_Check(mDemo_TYPE_SPEAK, &guide2->npc_class.actor_class) == TRUE) { + int msg_no = mMsg_GET_MSG_NUM(); + int flags; + + switch (msg_no) { + case 0x2AC9: + flags = (1 << 3); + break; + case 0x2ADB: + flags = (1 << 2); + break; + case 0x2ADD: + flags = (1 << 1); + break; + case 0x2AD3: + flags = (1 << 0); + break; + default: + flags = 0; + break; + } + + guide2->answer_flags |= flags; + } +} + +static void aNG2_set_pl_face_type(NPC_GUIDE2_ACTOR* guide2) { + static int face_type_table[mPr_SEX_NUM][mPr_FACE_TYPE_NUM] = { + /* Male faces */ + { + mPr_FACE_TYPE5, + mPr_FACE_TYPE6, + mPr_FACE_TYPE1, + mPr_FACE_TYPE4, + mPr_FACE_TYPE0, + mPr_FACE_TYPE2, + mPr_FACE_TYPE7, + mPr_FACE_TYPE3, + }, + /* Female faces */ + { + mPr_FACE_TYPE0, + mPr_FACE_TYPE5, + mPr_FACE_TYPE2, + mPr_FACE_TYPE6, + mPr_FACE_TYPE4, + mPr_FACE_TYPE7, + mPr_FACE_TYPE3, + mPr_FACE_TYPE1, + }, + }; + + int answer_flags = guide2->answer_flags; + int face_type; + + if ((answer_flags & 1) == 0) { + /* Select a random face for the player */ + face_type = RANDOM(mPr_FACE_TYPE_NUM); + } else { + /* Select a face based on choices */ + face_type = face_type_table[Now_Private->gender][answer_flags >> 1]; + } + + Now_Private->face = face_type; +} + +static void aNG2_calc_body_angl(NPC_GUIDE2_ACTOR* guide2, f32 pos_x, f32 pos_z) { + f32 dX = pos_x - guide2->npc_class.actor_class.world.position.x; + f32 dZ = pos_z - guide2->npc_class.actor_class.world.position.z; + s16 angl = atans_table(dZ, dX); + + chase_angle(&guide2->npc_class.actor_class.shape_info.rotation.y, angl, DEG2SHORT_ANGLE2(11.25f)); + guide2->npc_class.actor_class.world.angle.y = guide2->npc_class.actor_class.shape_info.rotation.y; +} + +static void aNG2_set_default_talk_info(void) { + mDemo_Set_camera(CAMERA2_PROCESS_NUM); + mDemo_Set_talk_change_player(FALSE); + mDemo_Set_use_zoom_sound(TRUE); +} + +static void aNG2_enter(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + if (guide2->npc_class.draw.main_animation_state == cKF_STATE_STOPPED) { + guide2->camera_eyes_flag = TRUE; + aNG2_setupAction(guide2, play, aNG2_ACTION_APPROACH); + } else if (cKF_FrameControl_passCheck_now(&guide2->npc_class.draw.main_animation.keyframe.frame_control, 20.0f) == + TRUE) { + TRAINDOOR_ACTOR* train_door = (TRAINDOOR_ACTOR*)guide2->train_door_actor; + + train_door->open_flag = TRUE; + } +} + +static void aNG2_approach(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + if (guide2->npc_class.actor_class.world.position.z >= 290.0f) { + guide2->npc_class.actor_class.world.position.z = 290.0f; + aNG2_setupAction(guide2, play, aNG2_ACTION_TALK_START_WAIT); + } +} + +static void aNG2_set_talk_info_talk_start_wait(ACTOR* actorx) { + mMsg_SET_FREE_STR(mMsg_FREE_STR0, aNG2_getP_other_pl_name(), PLAYER_NAME_LEN); + mDemo_Set_msg_num(0x2AD5); + aNG2_set_default_talk_info(); + ((NPC_GUIDE2_ACTOR*)actorx)->obj_look_type = aNG2_OBJ_LOOK_TYPE_TALK; +} + +static void aNG2_talk_start_wait(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + ACTOR* actorx = (ACTOR*)guide2; + + if (mDemo_Check(mDemo_TYPE_SPEAK, actorx) == TRUE) { + if (mDemo_Check_ListenAble() == FALSE && + chase_angle(&actorx->shape_info.rotation.y, actorx->player_angle_y, 0x400) == TRUE) { + guide2->camera_eyes_flag = FALSE; + aNG2_setupAction(guide2, play, aNG2_ACTION_WAIT_PERMISSION); + mDemo_Set_ListenAble(); + } + } else { + mDemo_Request(mDemo_TYPE_SPEAK, actorx, &aNG2_set_talk_info_talk_start_wait); + } +} + +static void aNG2_wait_permission(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + if (mMsg_CHECK_MAINNORMALCONTINUE() == TRUE) { + aNG2_setupAction(guide2, play, aNG2_ACTION_SITDOWN); + } +} + +static void aNG2_sitdown(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + guide2->npc_class.actor_class.world.position.x = 100.0f; + guide2->npc_class.actor_class.world.position.z = 280.0f; + guide2->npc_class.actor_class.shape_info.rotation.y = 0; + + /* Wait for the sit animation to finish before continuing */ + if (guide2->npc_class.draw.main_animation_state == cKF_STATE_STOPPED) { + mMsg_UNSET_LOCKCONTINUE(); + guide2->next_action = aNG2_ACTION_PNAME_MENU_OPEN_WAIT; + aNG2_setupAction(guide2, play, aNG2_ACTION_SDON_AND_PB_WAIT); + } +} + +static void aNG2_sdon_and_pb_wait(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + u16 order = mDemo_Get_OrderValue(mDemo_ORDER_NPC0, 9); + + if (order != 0 && mMsg_CHECK_MAINNORMALCONTINUE()) { + aNG2_setupAction(guide2, play, guide2->next_action); + mDemo_Set_OrderValue(mDemo_ORDER_NPC0, 9, 0); + } +} + +static void aNG2_pname_menu_open_wait(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + if (mMsg_CHECK_MAIN_WAIT() == TRUE) { + aNG2_setupAction(guide2, play, 7); + } +} + +static void aNG2_pname_menu_close_wait(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + if (play->submenu.open_flag == FALSE) { + aNG2_setupAction(guide2, play, 8); + } +} + +static void aNG2_msg_win_open_wait(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + static int msg_no[] = { 0x2ACA, 0x2AE2 }; + static int next_act_idx[] = { 10, 9 }; + mMsg_Window_c* msg_p = mMsg_Get_base_window_p(); + + if (mMsg_Check_not_series_main_wait(msg_p) == TRUE) { + int idx = 0; + + mMsg_Unset_LockContinue(msg_p); + + /* Check if the player's name already exists in town */ + if (aNG2_check_pname() == FALSE) { + idx = 1; + } + + mMsg_Set_continue_msg_num(msg_p, msg_no[idx]); + mMsg_Set_ForceNext(msg_p); + aNG2_setupAction(guide2, play, next_act_idx[idx]); + } +} + +static void aNG2_sex_select_wait(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + if (mMsg_CHECK_MAINNORMALCONTINUE() == TRUE) { + switch (mChoice_GET_CHOSENUM()) { + case mChoice_CHOICE0: // male + case mChoice_CHOICE1: // female + aNG2_setupAction(guide2, play, aNG2_ACTION_SEX_SELECT_WAIT2); + break; + case mChoice_CHOICE2: // 'That's not right' + guide2->next_action = aNG2_ACTION_PNAME_MENU_OPEN_WAIT; + aNG2_setupAction(guide2, play, aNG2_ACTION_SDON_AND_PB_WAIT); + break; + } + } +} + +static void aNG2_sex_select_wait2(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + static int next_act_idx[] = { aNG2_ACTION_STANDUP_START_WAIT, aNG2_ACTION_SEX_SELECT_AFTER }; + int order = mDemo_Get_OrderValue(mDemo_ORDER_NPC0, 9); + + if (order != 0) { + mMsg_Window_c* msg_p = mMsg_Get_base_window_p(); + + if (mMsg_Check_MainNormalContinue(msg_p) == TRUE) { + int idx = -1; + + switch (mChoice_GET_CHOSENUM()) { + case mChoice_CHOICE0: // male + switch (order) { + case 1: // That's right + Now_Private->gender = mPr_SEX_MALE; + break; + case 2: // I'm not a boy + Now_Private->gender = mPr_SEX_FEMALE; + break; + } + + idx = 0; + mMsg_Set_continue_msg_num(msg_p, 0x2AD9); + mMsg_Set_ForceNext(msg_p); + break; + case mChoice_CHOICE1: // female + switch (order) { + case 1: // That's right + Now_Private->gender = mPr_SEX_FEMALE; + break; + case 2: // I'm not a girl + Now_Private->gender = mPr_SEX_MALE; + break; + } + idx = 1; + break; + } + + if (idx != -1) { + aNG2_setupAction(guide2, play, next_act_idx[idx]); + mDemo_Set_OrderValue(mDemo_ORDER_NPC0, 9, 0); + } + } + } +} + +static void aNG2_sex_select_after(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + mMsg_Window_c* msg_p = mMsg_Get_base_window_p(); + u16 order = mDemo_Get_OrderValue(mDemo_ORDER_NPC0, 9); + + if (order != 0 && mMsg_Check_MainNormalContinue(msg_p)) { + aNG2_setupAction(guide2, play, aNG2_ACTION_STANDUP_START_WAIT); + mMsg_Set_continue_msg_num(msg_p, 0x2AD9); + mDemo_Set_OrderValue(mDemo_ORDER_NPC0, 9, 0); + } +} + +static void aNG2_standup_start_wait(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + /* Wait for guide2 to stop speaking to us */ + if (mDemo_Check(mDemo_TYPE_SPEAK, &guide2->npc_class.actor_class) == FALSE) { + guide2->obj_look_type = aNG2_OBJ_LOOK_TYPE_NORMAL; + aNG2_setupAction(guide2, play, aNG2_ACTION_STANDUP); + } +} + +static void aNG2_standup(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + /* Wait for animation to finish */ + if (guide2->npc_class.draw.main_animation_state == cKF_STATE_STOPPED) { + aNG2_setupAction(guide2, play, guide2->next_action); + } +} + +static void aNG2_move_ready(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + guide2->npc_class.actor_class.world.position.x = 100.0f; + guide2->npc_class.actor_class.world.position.z = 300.0f; + aNG2_setupAction(guide2, play, aNG2_ACTION_MOVE_TO_AISLE); +} + +static void aNG2_move_to_aisle(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + aNG2_calc_body_angl(guide2, 140.0f, 290.0f); + if (guide2->npc_class.actor_class.world.position.x > 140.0f) { + aNG2_setupAction(guide2, play, aNG2_ACTION_MOVE_TO_DOOR); + } +} + +static void aNG2_move_to_door(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + aNG2_calc_body_angl(guide2, 140.0f, 130.0f); + /* Check if we reached the door */ + if (guide2->npc_class.actor_class.world.position.z < 130.0f) { + aNG2_setupAction(guide2, play, aNG2_ACTION_MOVE_TO_DECK); + } + + /* Check if we should start rotating */ + if (guide2->npc_class.draw.shadow_pos.z < 140.0f) { + guide2->camera_tilt_goal_angle = DEG2SHORT_ANGLE2(90.0f); + guide2->camera_tilt_add = DEG2SHORT_ANGLE2(2.8125f); + } +} + +static void aNG2_move_to_deck(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + if (cKF_FrameControl_passCheck_now(&guide2->npc_class.draw.main_animation.keyframe.frame_control, 9.0f) == TRUE) { + TRAINDOOR_ACTOR* train_door = (TRAINDOOR_ACTOR*)guide2->train_door_actor; + + train_door->open_flag = TRUE; + } + + /* Reuse standup state to wait for animation to finish */ + aNG2_standup(guide2, play); +} + +static void aNG2_set_talk_info_keitai_on(ACTOR* actorx) { + mDemo_Set_msg_num(0x2ADE); + aNG2_set_default_talk_info(); +} + +static void aNG2_keitai_on(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + static f32 anmSpd[] = { 0.0f, 0.5f }; + int idx = aNG2_make_keitai(guide2, &play->game); + + guide2->npc_class.draw.main_animation.keyframe.frame_control.speed = anmSpd[idx]; + if (idx != 0) { + if (mDemo_Check(mDemo_TYPE_SPEAK, (ACTOR*)guide2) == TRUE) { + if (mDemo_Check_ListenAble() == FALSE) { + mDemo_Set_ListenAble(); + } + } else { + /* Request to start speaking */ + mDemo_Request(mDemo_TYPE_SPEAK, (ACTOR*)guide2, &aNG2_set_talk_info_keitai_on); + } + } + + /* Wait for animation to finish */ + if (guide2->npc_class.draw.main_animation_state == cKF_STATE_STOPPED) { + aNG2_setupAction(guide2, play, aNG2_ACTION_KEITAI_TALK); + } +} + +static void aNG2_keitai_talk(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + if (mDemo_Check(mDemo_TYPE_SPEAK, &guide2->npc_class.actor_class) == FALSE) { + aNG2_setupAction(guide2, play, aNG2_ACTION_KEITAI_OFF); + } else if (guide2->npc_class.draw.main_animation_state == cKF_STATE_STOPPED) { + Common_Get(clip).npc_clip->animation_init_proc(&guide2->npc_class.actor_class, aNPC_ANIM_KEITAI_TALK2, TRUE); + } +} + +static void aNG2_open_door(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + chase_angle(&guide2->npc_class.actor_class.shape_info.rotation.y, DEG2SHORT_ANGLE2(-180.0f), + DEG2SHORT_ANGLE2(0.703125f)); + if (cKF_FrameControl_passCheck_now(&guide2->npc_class.draw.main_animation.keyframe.frame_control, 22.0f) == TRUE) { + TRAINDOOR_ACTOR* train_door = (TRAINDOOR_ACTOR*)guide2->train_door_actor; + + train_door->open_flag = TRUE; + if (guide2->npc_class.draw.shadow_pos.z < 140.0f) { + guide2->camera_tilt_goal_angle = 0; + guide2->camera_tilt_add = 0x600; + } + } + + /* Reuse standup state to wait for animation to finish */ + aNG2_standup(guide2, play); +} + +static void aNG2_return_approach(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + guide2->npc_class.actor_class.world.position.x = 140.0f; + guide2->npc_class.actor_class.world.angle.y = 0; + guide2->npc_class.actor_class.shape_info.rotation.y = 0; + if (guide2->npc_class.actor_class.world.position.z > 290.0f) { + aNG2_setupAction(guide2, play, aNG2_ACTION_LAST_TALK_START_WAIT); + } +} + +static void aNG2_set_talk_info_last_talk_start_wait(ACTOR* actorx) { + mDemo_Set_msg_num(0x2AD1); + aNG2_set_default_talk_info(); + ((NPC_GUIDE2_ACTOR*)actorx)->obj_look_type = aNG2_OBJ_LOOK_TYPE_TALK; +} + +static void aNG2_last_talk_start_wait(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + ACTOR* actorx = (ACTOR*)guide2; + + if (mDemo_Check(mDemo_TYPE_SPEAK, actorx) == TRUE) { + if (mDemo_Check_ListenAble() == FALSE && + chase_angle(&actorx->shape_info.rotation.y, actorx->player_angle_y, 0x400) == TRUE) { + guide2->camera_eyes_flag = FALSE; + aNG2_setupAction(guide2, play, aNG2_ACTION_SITDOWN2_START_WAIT); + mDemo_Set_ListenAble(); + } + } else { + mDemo_Request(mDemo_TYPE_SPEAK, actorx, &aNG2_set_talk_info_last_talk_start_wait); + } +} + +static void aNG2_sitdown2_start_wait(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + u16 order = mDemo_Get_OrderValue(mDemo_ORDER_NPC0, 9); + + if (order != 0) { + aNG2_setupAction(guide2, play, aNG2_ACTION_SITDOWN2); + mDemo_Set_OrderValue(mDemo_ORDER_NPC0, 9, 0); + } +} + +static void aNG2_sitdown2(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + guide2->npc_class.actor_class.world.position.x = 100.0f; + guide2->npc_class.actor_class.world.position.z = 280.0f; + guide2->npc_class.actor_class.shape_info.rotation.y = 0; + + /* Wait for animation to finish */ + aNG2_standup(guide2, play); +} + +static void aNG2_last_talk_end_wait(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + if (mDemo_Check(mDemo_TYPE_SPEAK, &guide2->npc_class.actor_class) == FALSE) { + aNG2_setupAction(guide2, play, aNG2_ACTION_SCENE_CHANGE_WAIT); + } +} + +/* Init procs */ + +static void aNG2_sitdown_init(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + mMsg_SET_LOCKCONTINUE(); +} + +static void aNG2_pname_menu_open_wait_init(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + mMsg_Window_c* msg_p = mMsg_Get_base_window_p(); + + mMsg_Set_LockContinue(msg_p); + mMsg_request_main_disappear_wait_type1(msg_p); +} + +static void aNG2_pname_menu_close_wait_init(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + Submenu* submenu = &play->submenu; + + mSM_open_submenu(submenu, mSM_OVL_LEDIT, mLE_TYPE_PLAYER_NAME, 0); +} + +static void aNG2_msg_win_open_wait_init(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + mMsg_REQUEST_MAIN_APPEAR_WAIT_TYPE1(); +} + +static void aNG2_pname_ng_init(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + guide2->next_action = 6; +} + +static void aNG2_standup_init(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + guide2->next_action = aNG2_ACTION_MOVE_READY; +} + +static void aNG2_move_ready_init(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + guide2->npc_class.draw.main_animation.keyframe.morph_counter = 0.0f; +} + +static void aNG2_move_to_aisle_init(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + aNG2_set_walk_spd2(guide2, play); +} + +static void aNG2_move_to_deck_init(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + aNG2_set_stop_spd(guide2, play); + guide2->npc_class.actor_class.world.position.x = 140.0f; + guide2->npc_class.actor_class.world.position.z = 130.0f; + guide2->next_action = aNG2_ACTION_KEITAI_ON; +} + +static void aNG2_keitai_off_init(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + aNG2_chg_cond_keitai(guide2, aTOL_ACTION_PUTAWAY); + guide2->next_action = aNG2_ACTION_OPEN_DOOR; +} + +static void aNG2_open_door_init(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + guide2->next_action = aNG2_ACTION_RETURN_APPROACH; +} + +static void aNG2_return_approach_init(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + guide2->camera_eyes_flag = TRUE; + aNG2_set_walk_spd2(guide2, play); + guide2->npc_class.draw.main_animation.keyframe.morph_counter = 0.0f; +} + +static void aNG2_sitdown2_start_wait_init(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + mDemo_Set_OrderValue(mDemo_ORDER_NPC0, 9, 0); +} + +static void aNG2_sitdown2_init(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + guide2->npc_class.draw.main_animation.keyframe.morph_counter = 0.0f; + guide2->next_action = aNG2_ACTION_LAST_TALK_END_WAIT; +} + +static void aNG2_last_talk_end_wait_init(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + aNG2_chg_cond_keitai(guide2, aTOL_ACTION_DESTRUCT); + guide2->npc_class.right_hand.item_actor_p = NULL; +} + +static void aNG2_scene_change_wait_init(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play) { + /* Init next scene transition */ + goto_next_scene(play, 0, 1); + play->fb_wipe_type = 3; + Common_Get(transition).wipe_type = 4; + + mEv_SetFirstJob(); + mEv_SetFirstIntro(); + mEnv_DecideWeather_NormalGameStart(); + aNG2_set_pl_face_type(guide2); + mPr_SetNowPrivateCloth(); + Common_Set(submenu_disabled, TRUE); + mBGMPsComp_make_ps_wipe(0x41C2); + mBGMPsComp_scene_mode(18); +} + +typedef void (*aNG2_INIT_PROC)(NPC_GUIDE2_ACTOR*, GAME_PLAY*); + +static void aNG2_init_proc(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play, int action) { + static aNG2_INIT_PROC init_proc[] = { + (aNG2_INIT_PROC)&none_proc1, + &aNG2_set_walk_spd, + &aNG2_set_stop_spd, + (aNG2_INIT_PROC)&none_proc1, + &aNG2_sitdown_init, + (aNG2_INIT_PROC)&none_proc1, + &aNG2_pname_menu_open_wait_init, + &aNG2_pname_menu_close_wait_init, + &aNG2_msg_win_open_wait_init, + &aNG2_pname_ng_init, + (aNG2_INIT_PROC)&none_proc1, + (aNG2_INIT_PROC)&none_proc1, + (aNG2_INIT_PROC)&none_proc1, + (aNG2_INIT_PROC)&none_proc1, + &aNG2_standup_init, + &aNG2_move_ready_init, + &aNG2_move_to_aisle_init, + (aNG2_INIT_PROC)&none_proc1, + &aNG2_move_to_deck_init, + (aNG2_INIT_PROC)&none_proc1, + (aNG2_INIT_PROC)&none_proc1, + &aNG2_keitai_off_init, + &aNG2_open_door_init, + &aNG2_return_approach_init, + &aNG2_set_stop_spd, + &aNG2_sitdown2_start_wait_init, + &aNG2_sitdown2_init, + &aNG2_last_talk_end_wait_init, + &aNG2_scene_change_wait_init, + }; + + (*init_proc[action])(guide2, play); +} + +static void aNG2_setupAction(NPC_GUIDE2_ACTOR* guide2, GAME_PLAY* play, int action) { + static aNG2_PROC process[] = { + &aNG2_enter, + &aNG2_approach, + &aNG2_talk_start_wait, + &aNG2_wait_permission, + &aNG2_sitdown, + &aNG2_sdon_and_pb_wait, + &aNG2_pname_menu_open_wait, + &aNG2_pname_menu_close_wait, + &aNG2_msg_win_open_wait, + &aNG2_sdon_and_pb_wait, + &aNG2_sex_select_wait, + &aNG2_sex_select_wait2, + &aNG2_sex_select_after, + &aNG2_standup_start_wait, + &aNG2_standup, + &aNG2_move_ready, + &aNG2_move_to_aisle, + &aNG2_move_to_door, + &aNG2_move_to_deck, + &aNG2_keitai_on, + &aNG2_keitai_talk, + &aNG2_standup, + &aNG2_open_door, + &aNG2_return_approach, + &aNG2_last_talk_start_wait, + &aNG2_sitdown2_start_wait, + &aNG2_sitdown2, + &aNG2_last_talk_end_wait, + (aNG2_PROC)&none_proc1, + }; + + guide2->action = action; + guide2->proc = process[action]; + aNG2_set_animation(guide2, action); + aNG2_init_proc(guide2, play, action); +} + +static void aNG2_actor_move(ACTOR* actorx, GAME* game) { + NPC_GUIDE2_ACTOR* guide2 = (NPC_GUIDE2_ACTOR*)actorx; + GAME_PLAY* play = (GAME_PLAY*)game; + + Common_Get(clip).npc_clip->move_before_proc(actorx, game); + aNG2_talk_demo_proc(actorx); + aNG2_check_talk_msg_no(guide2); + (*guide2->proc)(guide2, play); + Common_Get(clip).npc_clip->move_after_proc(actorx, game); + aNG2_set_camera(guide2, play); + aNG2_set_camera_eyes(guide2); + mSC_change_player_freeze(play); +} diff --git a/src/ac_npc_guide_animation.c_inc b/src/ac_npc_guide_animation.c_inc index eb525a82..7c6b5279 100644 --- a/src/ac_npc_guide_animation.c_inc +++ b/src/ac_npc_guide_animation.c_inc @@ -47,7 +47,10 @@ static void aNGD_set_animation(NPC_GUIDE_ACTOR* guide, int action) { int anim_id = anime_info->anim_id; int talk_flag = anime_info->talk_flag; - if ((action == 4 || action == 7 || action == 8) && guide->next_action != 5 && guide->next_action != 6) { + if ((action == aNGD_ACTION_SDON_AND_PB_WAIT || action == aNGD_ACTION_MSG_WIN_OPEN_WAIT || + action == aNGD_ACTION_CONFIRM_INPUT_DATA) && + guide->next_action != aNGD_ACTION_TIME_MENU_OPEN_WAIT && + guide->next_action != aNGD_ACTION_TIME_MENU_CLOSE_WAIT) { anim_id = aNPC_ANIM_SITDOWN_WAIT_D1; talk_flag = TRUE; }