Implement & link ac_groundhog_npc0

This commit is contained in:
Cuyler36
2025-06-14 03:49:29 -04:00
parent b1b3e187d8
commit c130410fa3
8 changed files with 412 additions and 42 deletions
+1 -1
View File
@@ -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"),
+42 -30
View File
@@ -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 {
+16 -1
View File
@@ -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
+6 -6
View File
@@ -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;
+93
View File
@@ -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);
}
@@ -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;
}
}
}
+229
View File
@@ -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;
}
@@ -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;