diff --git a/configure.py b/configure.py index 5ce04417..463a5e1e 100644 --- a/configure.py +++ b/configure.py @@ -1097,7 +1097,7 @@ config.libs = [ Object(Matching, "actor/npc/ac_countdown_npc0.c"), Object(Matching, "actor/npc/ac_countdown_npc1.c"), Object(Matching, "actor/npc/ac_go_home_npc.c"), - Object(NonMatching, "actor/npc/ac_groundhog_npc0.c"), + Object(Matching, "actor/npc/ac_groundhog_npc0.c"), Object(Matching, "actor/npc/ac_halloween_npc.c"), Object(Matching, "actor/npc/ac_hanabi_npc0.c"), Object(Matching, "actor/npc/ac_hanabi_npc1.c"), diff --git a/include/ac_groundhog_control.h b/include/ac_groundhog_control.h index 39d25cfd..e6b1833a 100644 --- a/include/ac_groundhog_control.h +++ b/include/ac_groundhog_control.h @@ -9,57 +9,69 @@ extern "C" { #endif -#define aGHC_TIME_CHECK(hour, min, sec) (Common_Get(time.now_sec) >= ((hour) * mTM_SECONDS_IN_HOUR + (min) * mTM_SECONDS_IN_MINUTE + (sec))) +#define aGHC_TIME_CHECK(hour, min, sec) \ + (Common_Get(time.now_sec) >= ((hour) * mTM_SECONDS_IN_HOUR + (min) * mTM_SECONDS_IN_MINUTE + (sec))) typedef struct groundhog_control_actor_s GROUNDHOG_CONTROL_ACTOR; enum { - aGHC_ACTION_BEFORE_800, - aGHC_ACTION_BIRTH_RESET_WAIT, - aGHC_ACTION_BIRTH_RESET, - aGHC_ACTION_RETIRE_RESET_WAIT, - aGHC_ACTION_RESET_SPEECH_BGM_START_WAIT, - aGHC_ACTION_SONCHO_SPEECH_START_WAIT, - aGHC_ACTION_SONCHO_SPEECH_END_WAIT, - aGHC_ACTION_FADE_OUT_START_WAIT, - aGHC_ACTION_AFTER_800, + aGHC_ACTION_BEFORE_800, + aGHC_ACTION_BIRTH_RESET_WAIT, + aGHC_ACTION_BIRTH_RESET, + aGHC_ACTION_RETIRE_RESET_WAIT, + aGHC_ACTION_RESET_SPEECH_BGM_START_WAIT, + aGHC_ACTION_SONCHO_SPEECH_START_WAIT, + aGHC_ACTION_SONCHO_SPEECH_END_WAIT, + aGHC_ACTION_FADE_OUT_START_WAIT, + aGHC_ACTION_AFTER_800, - aGHC_ACTION_NUM + aGHC_ACTION_NUM }; enum { - aGHC_ATTENTION_SONCHO, - aGHC_ATTENTION_GROUNDHOG, + aGHC_ATTENTION_SONCHO, + aGHC_ATTENTION_GROUNDHOG, - aGHC_ATTENTION_NUM + aGHC_ATTENTION_NUM }; enum { - aGHC_EVENT_STATE_NONE, /* nothing to report */ - aGHC_EVENT_STATE_MAJIN_DONE, /* ac_ev_majin signals it is done */ - aGHC_EVENT_STATE_SONCHO_DONE, /* ac_ev_speech_soncho signals it is done */ + aGHC_EVENT_STATE_NONE, /* nothing to report */ + aGHC_EVENT_STATE_MAJIN_DONE, /* ac_ev_majin signals it is done */ + aGHC_EVENT_STATE_SONCHO_DONE, /* ac_ev_speech_soncho signals it is done */ - aGHC_EVENT_STATE_NUM + aGHC_EVENT_STATE_NUM +}; + +enum { + aGHC_TERM_1_HOUR, + aGHC_TERM_30_MIN, + aGHC_TERM_15_MIN, + aGHC_TERM_5_MIN, + aGHC_TERM_1_MIN, + aGHC_TERM_EVENT_TIME, + + aGHC_TERM_NUM }; typedef struct groundhog_control_clip_s { - int now_term; - ACTOR* groundhog_npc_actor; - ACTOR* attention_target_actor; - int fading_title; + int now_term; + ACTOR* groundhog_npc_actor; + ACTOR* attention_target_actor; + int fading_title; } aGHC_Clip_c; typedef void (*aGHC_ACTION_PROC)(GROUNDHOG_CONTROL_ACTOR*, GAME_PLAY*); struct groundhog_control_actor_s { - ACTOR actor_class; - int action; - aGHC_ACTION_PROC action_proc; - int event_state; - int timer; - aGHC_Clip_c clip; - aGHC_event_area_c* event_area_p; - int attention_mode; + ACTOR actor_class; + int action; + aGHC_ACTION_PROC action_proc; + int event_state; + int timer; + aGHC_Clip_c clip; + aGHC_event_area_c* event_area_p; + int attention_mode; }; typedef struct groundhog_save_s { diff --git a/include/ac_groundhog_npc0.h b/include/ac_groundhog_npc0.h index e0901678..46de6a4a 100644 --- a/include/ac_groundhog_npc0.h +++ b/include/ac_groundhog_npc0.h @@ -3,11 +3,27 @@ #include "types.h" #include "m_actor.h" +#include "ac_npc.h" #ifdef __cplusplus extern "C" { #endif +typedef struct groundhog_npc0_actor_s GROUNDHOG_NPC0_ACTOR; + +typedef void (*aGH0_ACT_PROC)(GROUNDHOG_NPC0_ACTOR* actor, GAME_PLAY* play); + +struct groundhog_npc0_actor_s { + NPC_ACTOR npc_class; + int action; + int next_action; + aGH0_ACT_PROC act_proc; + int now_term; + int event_idx; + s16 def_angle; + int start_kf_pos_randomized; +}; + extern ACTOR_PROFILE Groundhog_Npc0_Profile; #ifdef __cplusplus @@ -15,4 +31,3 @@ extern ACTOR_PROFILE Groundhog_Npc0_Profile; #endif #endif - diff --git a/src/actor/ac_groundhog_control.c b/src/actor/ac_groundhog_control.c index 2f2e0647..2ea028e4 100644 --- a/src/actor/ac_groundhog_control.c +++ b/src/actor/ac_groundhog_control.c @@ -62,22 +62,22 @@ static int aGHC_get_now_term() { int res; if (aGHC_TIME_CHECK(8, 0, 0)) { - res = 5; + res = aGHC_TERM_EVENT_TIME; } else if (aGHC_TIME_CHECK(7, 59, 0)) { - res = 4; + res = aGHC_TERM_1_MIN; } else if (aGHC_TIME_CHECK(7, 55, 0)) { - res = 3; + res = aGHC_TERM_5_MIN; } else if (aGHC_TIME_CHECK(7, 45, 0)) { - res = 2; + res = aGHC_TERM_15_MIN; } else if (aGHC_TIME_CHECK(7, 30, 0)) { - res = 1; + res = aGHC_TERM_30_MIN; } else { - res = 0; + res = aGHC_TERM_1_HOUR; } return res; diff --git a/src/actor/npc/ac_groundhog_npc0.c b/src/actor/npc/ac_groundhog_npc0.c new file mode 100644 index 00000000..15ab1bb9 --- /dev/null +++ b/src/actor/npc/ac_groundhog_npc0.c @@ -0,0 +1,93 @@ +#include "ac_groundhog_npc0.h" + +#include "ac_groundhog_control.h" +#include "m_common_data.h" +#include "m_player_lib.h" +#include "m_font.h" +#include "m_msg.h" + +enum { + aGH0_ACT_WAIT, + aGH0_ACT_TURN, + + aGH0_ACT_NUM +}; + +static void aGH0_actor_ct(ACTOR* actorx, GAME* game); +static void aGH0_actor_dt(ACTOR* actorx, GAME* game); +static void aGH0_actor_move(ACTOR* actorx, GAME* game); +static void aGH0_actor_draw(ACTOR* actorx, GAME* game); +static void aGH0_actor_save(ACTOR* actorx, GAME* game); +static void aGH0_actor_init(ACTOR* actorx, GAME* game); + +// clang-format off +ACTOR_PROFILE Groundhog_Npc0_Profile = { + mAc_PROFILE_GROUNDHOG_NPC0, + ACTOR_PART_NPC, + ACTOR_STATE_NONE, + EMPTY_NO, + ACTOR_OBJ_BANK_KEEP, + sizeof(GROUNDHOG_NPC0_ACTOR), + aGH0_actor_ct, + aGH0_actor_dt, + aGH0_actor_init, + mActor_NONE_PROC1, + aGH0_actor_save, +}; +// clang-format on + +static void aGH0_force_talk_request(ACTOR* actorx, GAME* game); +static void aGH0_norm_talk_request(ACTOR* actorx, GAME* game); +static int aGH0_talk_init(ACTOR* actorx, GAME* game); +static int aGH0_talk_end_chk(ACTOR* actorx, GAME* game); + +static void aGH0_schedule_proc(NPC_ACTOR* nactorx, GAME_PLAY* play, int type); + +static void aGH0_setupAction(GROUNDHOG_NPC0_ACTOR* actor, int action); + +static void aGH0_actor_ct(ACTOR* actorx, GAME* game) { + static aNPC_ct_data_c ct_data = { + aGH0_actor_move, + aGH0_actor_draw, + aNPC_CT_SCHED_TYPE_SPECIAL, + aGH0_norm_talk_request, + aGH0_talk_init, + aGH0_talk_end_chk, + 0, + }; + f32 speed; + + if (NPC_CLIP->birth_check_proc(actorx, game) == TRUE) { + GROUNDHOG_NPC0_ACTOR* actor = (GROUNDHOG_NPC0_ACTOR*)actorx; + + actor->npc_class.schedule.schedule_proc = aGH0_schedule_proc; + NPC_CLIP->ct_proc(actorx, game, &ct_data); + actor->npc_class.draw.anim_speed_type = aNPC_ANIM_SPEED_TYPE_FREE; + + speed = (RANDOM(3.0f) * 0.5f) / 2.0f; + actor->npc_class.draw.frame_speed = speed + 0.4f; + } +} + +static void aGH0_actor_save(ACTOR* actorx, GAME* game) { + NPC_CLIP->save_proc(actorx, game); +} + +static void aGH0_actor_dt(ACTOR* actorx, GAME* game) { + NPC_CLIP->dt_proc(actorx, game); +} + +static void aGH0_actor_init(ACTOR* actorx, GAME* game) { + NPC_CLIP->init_proc(actorx, game); +} + +static void aGH0_actor_move(ACTOR* actorx, GAME* game) { + NPC_CLIP->move_proc(actorx, game); +} + +#include "../src/actor/npc/ac_groundhog_npc0_anime.c_inc" +#include "../src/actor/npc/ac_groundhog_npc0_talk.c_inc" + +static void aGH0_actor_draw(ACTOR* actorx, GAME* game) { + NPC_CLIP->draw_proc(actorx, game); +} diff --git a/src/actor/npc/ac_groundhog_npc0_anime.c_inc b/src/actor/npc/ac_groundhog_npc0_anime.c_inc new file mode 100644 index 00000000..1e6930b5 --- /dev/null +++ b/src/actor/npc/ac_groundhog_npc0_anime.c_inc @@ -0,0 +1,21 @@ +static void aGH0_set_animation(GROUNDHOG_NPC0_ACTOR* actor, int action) { + // clang-format off + static int animeSeqNo[] = { + aNPC_ANIM_WAIT1, + aNPC_ANIM_RUN1, + }; + // clang-format on + + NPC_CLIP->animation_init_proc((ACTOR*)actor, animeSeqNo[action], FALSE); + + if (!actor->start_kf_pos_randomized && action == aGH0_ACT_WAIT) { + f32 frames = actor->npc_class.draw.main_animation.keyframe.frame_control.max_frames - + actor->npc_class.draw.main_animation.keyframe.frame_control.start_frame; + + if (frames > 0.0f) { + actor->npc_class.draw.main_animation.keyframe.frame_control.current_frame = + actor->npc_class.draw.main_animation.keyframe.frame_control.start_frame + RANDOM(frames); + actor->start_kf_pos_randomized = TRUE; + } + } +} diff --git a/src/actor/npc/ac_groundhog_npc0_talk.c_inc b/src/actor/npc/ac_groundhog_npc0_talk.c_inc new file mode 100644 index 00000000..bd4868f3 --- /dev/null +++ b/src/actor/npc/ac_groundhog_npc0_talk.c_inc @@ -0,0 +1,229 @@ +static void aGH0_set_request_act(GROUNDHOG_NPC0_ACTOR* actor) { + actor->npc_class.request.act_priority = 4; + actor->npc_class.request.act_idx = aNPC_ACT_SPECIAL; + actor->npc_class.request.act_type = aNPC_ACT_TYPE_DEFAULT; +} + +static void aGH0_turn(GROUNDHOG_NPC0_ACTOR* actor, GAME_PLAY* play) { + if (actor->npc_class.actor_class.shape_info.rotation.y == actor->def_angle) { + actor->next_action = aGH0_ACT_WAIT; + actor->npc_class.action.step = aNPC_ACTION_END_STEP; + } +} + +static void aGH0_setupAction(GROUNDHOG_NPC0_ACTOR* actor, int action) { + // clang-format off + static aGH0_ACT_PROC process[] = { + (aGH0_ACT_PROC)none_proc1, + aGH0_turn, + }; + // clang-format on + + actor->npc_class.action.step = 0; + actor->action = action; + actor->act_proc = process[action]; + aGH0_set_animation(actor, action); +} + +static void aGH0_act_chg_data_proc(NPC_ACTOR* nactorx, GAME_PLAY* play) { + nactorx->action.act_obj = aNPC_ACT_OBJ_PLAYER; +} + +static void aGH0_act_init_proc(NPC_ACTOR* nactorx, GAME_PLAY* play) { + GROUNDHOG_NPC0_ACTOR* actor = (GROUNDHOG_NPC0_ACTOR*)nactorx; + + aGH0_setupAction(actor, aGH0_ACT_TURN); + switch (actor->now_term) { + case aGHC_TERM_EVENT_TIME: + actor->npc_class.talk_info.talk_request_proc = aGH0_norm_talk_request; + break; + case aGHC_TERM_1_MIN: + actor->npc_class.talk_info.talk_request_proc = (aNPC_TALK_REQUEST_PROC)none_proc1; + break; + default: + actor->npc_class.talk_info.talk_request_proc = aGH0_norm_talk_request; + break; + } +} + +static void aGH0_act_main_proc(NPC_ACTOR* nactorx, GAME_PLAY* play) { + GROUNDHOG_NPC0_ACTOR* actor = (GROUNDHOG_NPC0_ACTOR*)nactorx; + + actor->act_proc(actor, play); +} + +static void aGH0_act_proc(NPC_ACTOR* nactorx, GAME_PLAY* play, int type) { + static aNPC_SUB_PROC act_proc[] = { aGH0_act_init_proc, aGH0_act_chg_data_proc, aGH0_act_main_proc }; + + (*act_proc[type])(nactorx, play); +} + +static int aGH0_get_now_term(void) { + int ret = aGHC_TERM_EVENT_TIME; + + if (CLIP(groundhog_control_clip) != NULL) { + ret = CLIP(groundhog_control_clip)->now_term; + } + + return ret; +} + +static void aGH0_set_term(GROUNDHOG_NPC0_ACTOR* actor, GAME_PLAY* play) { + actor->now_term = aGH0_get_now_term(); +} + +static void aGH0_think_main_proc(NPC_ACTOR* nactorx, GAME_PLAY* play) { + GROUNDHOG_NPC0_ACTOR* actor = (GROUNDHOG_NPC0_ACTOR*)nactorx; + + if (actor->npc_class.action.step == aNPC_ACTION_END_STEP) { + switch (actor->npc_class.action.idx) { + case aNPC_ACT_SPECIAL: + if (actor->next_action != -1) { + aGH0_setupAction(actor, actor->next_action); + aGH0_set_request_act(actor); + } + break; + case aNPC_ACT_TALK: + actor->npc_class.movement.mv_angl = actor->def_angle; + aGH0_set_request_act(actor); + break; + } + + actor->next_action = -1; + } else { + aGH0_set_term(actor, play); + } + + switch (actor->now_term) { + case aGHC_TERM_EVENT_TIME: + actor->npc_class.talk_info.talk_request_proc = aGH0_norm_talk_request; + break; + case aGHC_TERM_1_MIN: + actor->npc_class.talk_info.talk_request_proc = (aNPC_TALK_REQUEST_PROC)none_proc1; + break; + default: + actor->npc_class.talk_info.talk_request_proc = aGH0_norm_talk_request; + break; + } +} + +static void aGH0_think_init_proc(NPC_ACTOR* nactorx, GAME_PLAY* play) { + GROUNDHOG_NPC0_ACTOR* actor = (GROUNDHOG_NPC0_ACTOR*)nactorx; + + nactorx->think.interrupt_flags = 0; + nactorx->action.act_proc = aGH0_act_proc; + aGH0_set_request_act(actor); +} + +static void aGH0_think_proc(NPC_ACTOR* nactorx, GAME_PLAY* play, int type) { + static aNPC_SUB_PROC think_proc[] = { aGH0_think_init_proc, aGH0_think_main_proc }; + + (*think_proc[type])(nactorx, play); +} + +static void aGH0_schedule_init_proc(NPC_ACTOR* nactorx, GAME_PLAY* play) { + static s16 def_angle[] = { 0x6B00, 0x8000, 0x8000, 0x9500 }; + static float def_pos[][2] = { + {0.0f, -20.0f}, + {20.0f, 0.0f}, + {20.0f, 0.0f}, + {0.0f, -20.0f}, + }; + GROUNDHOG_NPC0_ACTOR* actor = (GROUNDHOG_NPC0_ACTOR*)nactorx; + ACTOR* actorx = (ACTOR*)nactorx; + int event_idx; + + nactorx->think.think_proc = aGH0_think_proc; + nactorx->condition_info.demo_flg = aNPC_COND_DEMO_SKIP_FORWARD_CHECK | aNPC_COND_DEMO_SKIP_BGCHECK | aNPC_COND_DEMO_SKIP_MOVE_Y | aNPC_COND_DEMO_SKIP_MOVE_CIRCLE_REV | aNPC_COND_DEMO_SKIP_MOVE_RANGE_CHECK; + nactorx->condition_info.hide_request = FALSE; + nactorx->palActorIgnoreTimer = -1; + actor->next_action = -1; + actor->now_term = aGH0_get_now_term(); + nactorx->talk_info.default_animation = aNPC_ANIM_WAIT1; + nactorx->collision.check_kind = aNPC_BG_CHECK_TYPE_ONLY_GROUND; + actorx->status_data.weight = MASSTYPE_HEAVY; + + event_idx = nactorx->actor_class.npc_id - SP_NPC_EV_GROUNDHOG_0; + if (event_idx < 0 || event_idx >= 4) { + event_idx = 0; + } + actor->event_idx = event_idx; + + { + s16 angle = def_angle[event_idx]; + actorx->shape_info.rotation.y = angle; + actorx->world.angle.y = angle; + nactorx->movement.mv_angl = angle; + actor->def_angle = angle; + } + + nactorx->actor_class.world.position.x += def_pos[event_idx][0]; + nactorx->actor_class.world.position.z += def_pos[event_idx][1]; + + + actor->start_kf_pos_randomized = FALSE; + + if (RANDOM(2) == 1.0f) { + nactorx->head.angle_add_y = 0x100; + } + + if (actor->now_term == aGHC_TERM_1_MIN) { + nactorx->talk_info.talk_request_proc = (aNPC_TALK_REQUEST_PROC)none_proc1; + } + + NPC_CLIP->think_proc(nactorx, play, aNPC_THINK_SPECIAL, aNPC_THINK_TYPE_INIT); +} + +static void aGH0_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 aGH0_schedule_proc(NPC_ACTOR* nactorx, GAME_PLAY* play, int type) { + static aNPC_SUB_PROC sche_proc[] = { aGH0_schedule_init_proc, aGH0_schedule_main_proc }; + + (*sche_proc[type])(nactorx, play); +} + +static void aGH0_set_norm_talk_info(ACTOR* actorx) { + static int msg_base[mNpc_LOOKS_NUM] = { 0x3D52, 0x3D71, 0x3CF5, 0x3D14, 0x3D33, 0x3D90 }; + GROUNDHOG_NPC0_ACTOR* actor = (GROUNDHOG_NPC0_ACTOR*)actorx; + int looks = mNpc_GetNpcLooks(actorx); + int now_term = actor->now_term; + int msg_no; + + if (now_term >= aGHC_TERM_1_MIN) { + now_term--; + } + + msg_no = msg_base[looks] + now_term * 3 + RANDOM(3); + mDemo_Set_msg_num(msg_no); +} + +static void aGH0_norm_talk_request(ACTOR* actorx, GAME* game) { + mDemo_Request(mDemo_TYPE_TALK, actorx, aGH0_set_norm_talk_info); +} + +static int aGH0_talk_init(ACTOR* actorx, GAME* game) { + GROUNDHOG_NPC0_ACTOR* actor = (GROUNDHOG_NPC0_ACTOR*)actorx; + + if (actor->now_term == aGHC_TERM_1_MIN) { + actor->npc_class.talk_info.talk_request_proc = (aNPC_TALK_REQUEST_PROC)none_proc1; + } else { + actor->npc_class.talk_info.talk_request_proc = aGH0_norm_talk_request; + } + + mDemo_Set_ListenAble(); + return TRUE; +} + +static int aGH0_talk_end_chk(ACTOR* actorx, GAME* game) { + int ret = FALSE; + + if (!mDemo_Check(mDemo_TYPE_TALK, actorx)) { + ret = TRUE; + } + + return ret; +} diff --git a/src/actor/npc/event/ac_ev_speech_soncho_talk.c_inc b/src/actor/npc/event/ac_ev_speech_soncho_talk.c_inc index 41e6f263..0f5df4b4 100644 --- a/src/actor/npc/event/ac_ev_speech_soncho_talk.c_inc +++ b/src/actor/npc/event/ac_ev_speech_soncho_talk.c_inc @@ -51,7 +51,7 @@ void aESS_think_init_proc(ACTOR* actorx, GAME_PLAY* play) { aESS_set_request_act(soncho); if (Common_Get(time).now_sec < 0x7080) { next = 0; - if (CLIP(groundhog_control_clip) != NULL && CLIP(groundhog_control_clip)->now_term == 4) { + if (CLIP(groundhog_control_clip) != NULL && CLIP(groundhog_control_clip)->now_term == aGHC_TERM_1_MIN) { next = 0x1; } } else { @@ -120,7 +120,7 @@ void aESS_schedule_proc(NPC_SPEECH_SONCHO* soncho, GAME_PLAY* play, int sche_idx void aESS_set_norm_talk_info(ACTOR* actorx) { int msg_no; aGHC_Clip_c* clip = CLIP(groundhog_control_clip); - if (clip != NULL && clip->now_term != 5) { + if (clip != NULL && clip->now_term != aGHC_TERM_EVENT_TIME) { msg_no = mString_SPEECH_SONCHO_START + 1 + (int)RANDOM_F(5); } else { switch (Common_Get(weather)) { @@ -165,10 +165,10 @@ int aESS_norm_talk_end_chk(NPC_SPEECH_SONCHO* soncho) { int think_proc_idk = 4; if (CLIP(groundhog_control_clip) != NULL) { switch (CLIP(groundhog_control_clip)->now_term) { - case 4: + case aGHC_TERM_1_MIN: think_proc_idk = 0x1; break; - case 5: + case aGHC_TERM_EVENT_TIME: break; default: think_proc_idk = 0;