From 2f436ab3efac10ed175a45717cdc5bca108454cc Mon Sep 17 00:00:00 2001 From: Cuyler36 Date: Sat, 24 May 2025 16:23:00 -0400 Subject: [PATCH] Implement & link ac_mailbox --- configure.py | 2 +- include/ac_mailbox.h | 40 ++++++- src/actor/ac_mailbox.c | 199 ++++++++++++++++++++++++++++++ src/actor/ac_mailbox_move.c_inc | 206 ++++++++++++++++++++++++++++++++ 4 files changed, 445 insertions(+), 2 deletions(-) create mode 100644 src/actor/ac_mailbox.c create mode 100644 src/actor/ac_mailbox_move.c_inc diff --git a/configure.py b/configure.py index 3955dc84..794d36a0 100644 --- a/configure.py +++ b/configure.py @@ -1014,7 +1014,7 @@ config.libs = [ Object(Matching, "actor/ac_koinobori.c"), Object(NonMatching, "actor/ac_lighthouse_switch.c"), Object(Matching, "actor/ac_lotus.c"), - Object(NonMatching, "actor/ac_mailbox.c"), + Object(Matching, "actor/ac_mailbox.c"), Object(Matching, "actor/ac_mbg.c"), Object(Matching, "actor/ac_mikanbox.c"), Object(Matching, "actor/ac_mikuji.c"), diff --git a/include/ac_mailbox.h b/include/ac_mailbox.h index a03fbae6..d8efd8c6 100644 --- a/include/ac_mailbox.h +++ b/include/ac_mailbox.h @@ -3,11 +3,50 @@ #include "types.h" #include "m_actor.h" +#include "c_keyframe.h" #ifdef __cplusplus extern "C" { #endif +#define aMBX_PLAYER_OPEN_ANGLE DEG2SHORT_ANGLE2(38.2379150390625f) + +typedef struct mailbox_actor_s MAILBOX_ACTOR; + +typedef void (*aMBX_ACT_PROC)(MAILBOX_ACTOR* actor, GAME_PLAY* play); + +#define aMBX_JOINT_NUM 6 + +enum { + aMBX_REQUEST_NONE, // no current request + aMBX_REQUEST_DELIVERY, // delivery by Pete, open -> close + aMBX_REQUEST_OPEN, // player opens mailbox, hold state for submenu + + aMBX_REQUEST_NUM +}; + +struct mailbox_actor_s { + ACTOR actor_class; + void* segp; + + int kf0_state; + cKF_SkeletonInfo_R_c kf0; + s_xyz joint0[aMBX_JOINT_NUM+1]; + s_xyz morph0[aMBX_JOINT_NUM+1]; + int anim_idx0; + + int kf1_state; + cKF_SkeletonInfo_R_c kf1; + s_xyz joint1[aMBX_JOINT_NUM+1]; + s_xyz morph1[aMBX_JOINT_NUM+1]; + int anim_idx1; + + int action; + aMBX_ACT_PROC act_proc; + int arrange_idx; + int req; +}; + extern ACTOR_PROFILE MailBox_Profile; #ifdef __cplusplus @@ -15,4 +54,3 @@ extern ACTOR_PROFILE MailBox_Profile; #endif #endif - diff --git a/src/actor/ac_mailbox.c b/src/actor/ac_mailbox.c new file mode 100644 index 00000000..57243402 --- /dev/null +++ b/src/actor/ac_mailbox.c @@ -0,0 +1,199 @@ +#include "ac_mailbox.h" + +#include "m_common_data.h" +#include "sys_matrix.h" +#include "m_rcp.h" +#include "m_house.h" +#include "m_player_lib.h" + +enum { + aMBX_ACT_WAIT, + aMBX_ACT_PL_WAIT, + aMBX_ACT_OPEN_AND_CLOSE, + aMBX_ACT_PL_OPEN, + aMBX_ACT_PL_CLOSE, + + aMBX_ACT_NUM +}; + +enum { + aMBX_ANIME_WAIT, + aMBX_ANIME_OPEN_AND_CLOSE, + aMBX_ANIME_PL_OPEN, + aMBX_ANIME_PL_CLOSE, + aMBX_ANIME_FLAG_UP, + aMBX_ANIME_FLAG_UP_WAIT, + aMBX_ANIME_FLAG_DOWN, + + aMBX_ANIME_NUM +}; + +static void aMBX_actor_ct(ACTOR* actorx, GAME* game); +static void aMBX_actor_dt(ACTOR* actorx, GAME* game); +static void aMBX_actor_move(ACTOR* actorx, GAME* game); +static void aMBX_actor_init(ACTOR* actorx, GAME* game); +static void aMBX_actor_draw(ACTOR* actorx, GAME* game); + +// clang-format off +ACTOR_PROFILE MailBox_Profile = { + mAc_PROFILE_MAILBOX, + ACTOR_PART_ITEM, + ACTOR_STATE_NONE, + ACTOR_PROP_MAILBOX0, + ACTOR_OBJ_BANK_26, + sizeof(MAILBOX_ACTOR), + aMBX_actor_ct, + aMBX_actor_dt, + aMBX_actor_init, + mActor_NONE_PROC1, + NULL, +}; +// clang-format on + +extern cKF_Skeleton_R_c cKF_bs_r_obj_s_post; +extern cKF_Skeleton_R_c cKF_bs_r_obj_w_post; +extern cKF_Animation_R_c cKF_ba_r_obj_s_post_delivery1; +extern cKF_Animation_R_c cKF_ba_r_obj_s_post_flag_off1; +extern cKF_Animation_R_c cKF_ba_r_obj_s_post_flag_on1; +extern cKF_Animation_R_c cKF_ba_r_obj_s_post_flag_on_wait1; +extern cKF_Animation_R_c cKF_ba_r_obj_s_post_open1; +extern cKF_Animation_R_c cKF_ba_r_obj_s_post; + +typedef struct { + cKF_Animation_R_c* anim; + f32 start; + f32 end; +} aMBX_anime_info_c; + +static aMBX_anime_info_c aMBX_animeTable[7] = { + { &cKF_ba_r_obj_s_post, 1.0f, 2.0f }, + { &cKF_ba_r_obj_s_post_delivery1, 1.0f, 98.0f }, + { &cKF_ba_r_obj_s_post_open1, 1.0f, 31.0f }, + { &cKF_ba_r_obj_s_post_open1, 31.0f, 1.0f }, + { &cKF_ba_r_obj_s_post_flag_on1, 1.0f, 17.0f }, + { &cKF_ba_r_obj_s_post_flag_on_wait1, 1.0f, 31.0f }, + { &cKF_ba_r_obj_s_post_flag_off1, 1.0f, 17.0f }, +}; + +static int aMBX_animeSeqNoTable[5] = { + aMBX_ANIME_WAIT, + aMBX_ANIME_WAIT, + aMBX_ANIME_OPEN_AND_CLOSE, + aMBX_ANIME_PL_OPEN, + aMBX_ANIME_PL_CLOSE, +}; + +static cKF_Skeleton_R_c* aMBX_skeleton[2] = { &cKF_bs_r_obj_s_post, &cKF_bs_r_obj_w_post }; + +static void aMBX_check_flag(MAILBOX_ACTOR* actor); +static void aMBX_setupAction(MAILBOX_ACTOR* actor, int action); + +static void aMBX_actor_ct(ACTOR* actorx, GAME* game) { + static s16 angle_table[] = { + DEG2SHORT_ANGLE(90.0f), + DEG2SHORT_ANGLE(0.0f), + DEG2SHORT_ANGLE(90.0f), + DEG2SHORT_ANGLE(0.0f), + }; + + MAILBOX_ACTOR* actor = (MAILBOX_ACTOR*)actorx; + int season = Common_Get(time.season) == mTM_SEASON_WINTER; + int idx = actorx->npc_id - ACTOR_PROP_MAILBOX0; + cKF_Skeleton_R_c* skeleton_p = aMBX_skeleton[season]; + + cKF_SkeletonInfo_R_ct(&actor->kf0, skeleton_p, NULL, actor->joint0, actor->morph0); + cKF_SkeletonInfo_R_ct(&actor->kf1, skeleton_p, NULL, actor->joint1, actor->morph1); + actor->segp = ((GAME_PLAY*)game)->object_exchange.banks[actorx->data_bank_id].ram_start; + actor->anim_idx0 = aMBX_ANIME_NUM; + actor->arrange_idx = idx; + actor->anim_idx1 = aMBX_ANIME_NUM; + actorx->shape_info.rotation.y = angle_table[idx]; + aMBX_check_flag(actor); + actor->kf0.frame_control.current_frame = actor->kf0.frame_control.end_frame; +} + +static void aMBX_actor_dt(ACTOR* actorx, GAME* game) { + MAILBOX_ACTOR* actor = (MAILBOX_ACTOR*)actorx; + + cKF_SkeletonInfo_R_dt(&actor->kf0); // @BUG - why not dt on the second skeleton?? +} + +#include "../src/actor/ac_mailbox_move.c_inc" + +static Gfx post_flag_saki_common_DL[] = { + gsSPTexture(65535, 65535, 0, 0, G_ON), + gsDPSetRenderMode(G_RM_FOG_SHADE_A, G_RM_AA_ZB_TEX_EDGE2), + gsDPSetPrimColor(0, 128, 255, 255, 255, 255), + gsSPLoadGeometryMode(G_ZBUFFER | G_SHADE | G_FOG | G_SHADING_SMOOTH), + gsSPEndDisplayList(), +}; + +static Gfx post_flag_saki_model_type0[] = { + gsSPDisplayList(post_flag_saki_common_DL), + gsDPSetCombineLERP(0, 0, 0, TEXEL0, 0, 0, 0, TEXEL0, PRIMITIVE, 0, COMBINED, 0, 0, 0, 0, COMBINED), + gsDPLoadTextureBlock_4b_Dolphin(anime_1_txt, G_IM_FMT_CI, 16, 32, 15, GX_MIRROR, GX_CLAMP, 0, 0), + gsDPSetTileSize(0, 0, 0, 124, 124), + gsSPEndDisplayList(), +}; + +static Gfx post_flag_saki_model_type1[] = { + gsSPDisplayList(post_flag_saki_common_DL), + gsDPSetCombineLERP(TEXEL0, 0, SHADE, 0, 0, 0, 0, TEXEL0, PRIMITIVE, 0, COMBINED, 0, 0, 0, 0, COMBINED), + gsDPLoadTextureBlock_4b_Dolphin(anime_2_txt, G_IM_FMT_CI, 16, 32, 15, GX_MIRROR, GX_CLAMP, 0, 0), + gsSPEndDisplayList(), +}; + +static int aMBX_actor_draw_before(GAME* game, cKF_SkeletonInfo_R_c* keyframe, int joint_idx, Gfx** joint_shape, + u8* joint_flags, void* arg, s_xyz* joint_rot, xyz_t* joint_pos) { + static Gfx* post_flag_saki_model[] = { post_flag_saki_model_type0, post_flag_saki_model_type1 }; + MAILBOX_ACTOR* actor = (MAILBOX_ACTOR*)arg; + + if (joint_idx == 3) { + GRAPH* graph = game->graph; + int idx = actor->anim_idx1 == aMBX_ANIME_FLAG_UP_WAIT; + + OPEN_POLY_OPA_DISP(graph); + + gSPDisplayList(POLY_OPA_DISP++, post_flag_saki_model[idx]); + + CLOSE_POLY_OPA_DISP(graph); + } + + return TRUE; +} + +extern u8 obj_s_post_flag2_TA_tex_txt[]; +extern u8 obj_w_post_flag2_TA_tex_txt[]; + +extern EVW_ANIME_DATA obj_s_post_flag_on_wait1_evw_anime; +extern EVW_ANIME_DATA obj_w_post_flag_on_wait1_evw_anime; + +static void aMBX_actor_draw(ACTOR* actorx, GAME* game) { + static u8* tex_table[] = { obj_s_post_flag2_TA_tex_txt, obj_w_post_flag2_TA_tex_txt }; + static EVW_ANIME_DATA* evw_table[] = { &obj_s_post_flag_on_wait1_evw_anime, &obj_w_post_flag_on_wait1_evw_anime }; + MAILBOX_ACTOR* actor = (MAILBOX_ACTOR*)actorx; + cKF_SkeletonInfo_R_c* kf0 = &actor->kf0; + GRAPH* graph = game->graph; + GAME_PLAY* play = (GAME_PLAY*)game; + Mtx* mtx = GRAPH_ALLOC_TYPE(graph, Mtx, kf0->skeleton->num_shown_joints); + + if (mtx != NULL) { + _texture_z_light_fog_prim(graph); + + if (actor->anim_idx1 == aMBX_ANIME_FLAG_UP_WAIT) { + Evw_Anime_Set(play, evw_table[Common_Get(time.season) == mTM_SEASON_WINTER]); + if (Common_Get(player_no) == mHS_get_pl_no(actor->arrange_idx) && + play->game_frame % FRAMES_PER_SECOND == 0) { + sAdo_OngenTrgStart(0x43B, &actorx->world.position); + } + } else { + OPEN_POLY_OPA_DISP(graph); + + gSPSegment(POLY_OPA_DISP++, ANIME_1_TXT_SEG, tex_table[Common_Get(time.season) == mTM_SEASON_WINTER]); + + CLOSE_POLY_OPA_DISP(graph); + } + + cKF_Si3_draw_R_SV(game, kf0, mtx, aMBX_actor_draw_before, NULL, actorx); + } +} diff --git a/src/actor/ac_mailbox_move.c_inc b/src/actor/ac_mailbox_move.c_inc new file mode 100644 index 00000000..f344ccb1 --- /dev/null +++ b/src/actor/ac_mailbox_move.c_inc @@ -0,0 +1,206 @@ +static void aMBX_anime_proc(ACTOR* actorx, GAME_PLAY* play) { + static s8 part_tbl[] = { 0, 0, 0, 1, 1, 0, 0 }; + MAILBOX_ACTOR* actor = (MAILBOX_ACTOR*)actorx; + + actor->kf0_state = cKF_SkeletonInfo_R_combine_play(&actor->kf0, &actor->kf1, part_tbl); +} + +static void aMBX_setup_flag_anime(MAILBOX_ACTOR* actor, int anime_idx) { + aMBX_anime_info_c* info_p = &aMBX_animeTable[anime_idx]; + cKF_SkeletonInfo_R_c* kf1 = &actor->kf1; + int mode = cKF_FRAMECONTROL_STOP; + + if (anime_idx == aMBX_ANIME_FLAG_UP_WAIT) { + mode = cKF_FRAMECONTROL_REPEAT; + } + + cKF_SkeletonInfo_R_init(kf1, kf1->skeleton, info_p->anim, info_p->start, info_p->end, info_p->start, 0.5f, 0.0f, mode, NULL); + actor->anim_idx1 = anime_idx; +} + +static void aMBX_check_take_mail(MAILBOX_ACTOR* actor, GAME_PLAY* play) { + ACTOR* playerx = GET_PLAYER_ACTOR_ACTOR(play); + + if (playerx != NULL) { + PLAYER_ACTOR* player = (PLAYER_ACTOR*)playerx; + int arrange_idx = actor->arrange_idx; + int pl_no = mHS_get_pl_no(arrange_idx); + ACTOR* actorx = (ACTOR*)actor; + int mbox_angle = actorx->shape_info.rotation.y + DEG2SHORT_ANGLE2(-45.0f); + int mbox_pl_angle = actorx->player_angle_y; + int dAngle = (s16)(mbox_pl_angle - mbox_angle); + int abs_dAngle = ABS(dAngle); + + if (abs_dAngle < aMBX_PLAYER_OPEN_ANGLE && player->a_btn_pressed == TRUE && pl_no == Common_Get(player_no) && player->item_in_front == (DUMMY_MAILBOX0 + arrange_idx)) { + if (Common_Get(reset_flag) == TRUE && Now_Private->reset_count < 3) { + Common_Set(reset_type, 5); + } else if (!mPr_NullCheckPersonalID(&Save_Get(homes[arrange_idx]).ownerID)) { + Submenu* submenu = &play->submenu; + + if (submenu->start_refuse == FALSE && submenu->start_refuse_timer == 0 && mPlib_able_submenu_type1((GAME*)play) && actor->req == 0) { + player->a_btn_triggers_submenu = TRUE; + actor->req = aMBX_REQUEST_OPEN; + } + } + } + } +} + +static void aMBX_check_flag(MAILBOX_ACTOR* actor) { + int anime_idx = aMBX_ANIME_FLAG_DOWN; + int mail_count = mMl_count_use_mail_space(Save_Get(homes[actor->arrange_idx]).mailbox, HOME_MAILBOX_SIZE); + + if (mail_count != 0) { + if (actor->anim_idx1 == aMBX_ANIME_FLAG_UP_WAIT || actor->anim_idx1 == aMBX_ANIME_NUM) { + anime_idx = aMBX_ANIME_FLAG_UP_WAIT; + } else { + anime_idx = aMBX_ANIME_FLAG_UP; + } + } + + if (actor->anim_idx1 != anime_idx) { + aMBX_setup_flag_anime(actor, anime_idx); + } +} + +static void aMBX_setup_flag_se_sub(u16 se_no, MAILBOX_ACTOR* actor, f32 frame) { + if (cKF_FrameControl_passCheck_now(&actor->kf1.frame_control, frame) == TRUE) { + sAdo_OngenTrgStart(se_no, &actor->actor_class.world.position); + } +} + +static void aMBX_setup_flag_se(MAILBOX_ACTOR* actor) { + switch (actor->anim_idx1) { + case aMBX_ANIME_FLAG_UP: + aMBX_setup_flag_se_sub(0x433, actor, 15.0f); + break; + case aMBX_ANIME_FLAG_DOWN: + aMBX_setup_flag_se_sub(0x434, actor, 15.0f); + break; + } +} + +static void aMBX_wait(MAILBOX_ACTOR* actor, GAME_PLAY* play) { + if (actor->kf0_state == cKF_STATE_STOPPED) { + aMBX_check_take_mail(actor, play); + switch (actor->req) { + case aMBX_REQUEST_DELIVERY: + aMBX_setupAction(actor, aMBX_ACT_OPEN_AND_CLOSE); + break; + case aMBX_REQUEST_OPEN: + aMBX_setupAction(actor, aMBX_ACT_PL_WAIT); + break; + } + } + + aMBX_check_flag(actor); + if (actor->anim_idx1 == aMBX_ANIME_FLAG_UP && actor->kf1.frame_control.current_frame == actor->kf1.frame_control.end_frame) { + aMBX_setup_flag_anime(actor, aMBX_ANIME_FLAG_UP_WAIT); + } +} + +static void aMBX_pl_wait(MAILBOX_ACTOR* actor, GAME_PLAY* play) { + static s16 angle[] = { DEG2SHORT_ANGLE2(-135.0f), DEG2SHORT_ANGLE2(135.0f) }; + static f32 posX[] = { 24.0f, -24.0f }; + + if (mPlib_get_player_actor_main_index((GAME*)play) == mPlayer_INDEX_MAIL_JUMP) { + aMBX_setupAction(actor, aMBX_ACT_PL_OPEN); + } else { + xyz_t pos; + int idx = actor->arrange_idx & 1; + + pos.x = actor->actor_class.world.position.x + posX[idx]; + pos.z = actor->actor_class.world.position.z + 24.0f; + mPlib_request_main_mail_jump_type1((GAME*)play, &pos, angle[idx]); + } +} + +static void aMBX_open_and_close(MAILBOX_ACTOR* actor, GAME_PLAY* play) { + cKF_FrameControl_c* fc0 = &actor->kf0.frame_control; + + if (cKF_FrameControl_passCheck_now(fc0, 88.0f) == TRUE) { + aMBX_check_flag(actor); + } else if (cKF_FrameControl_passCheck_now(fc0, 14.0f) == TRUE) { + sAdo_OngenTrgStart(0x146, &actor->actor_class.world.position); + } else if (cKF_FrameControl_passCheck_now(fc0, 80.0f) == TRUE) { + sAdo_OngenTrgStart(0x147, &actor->actor_class.world.position); + } + + if (actor->kf0_state == cKF_STATE_STOPPED) { + actor->req = aMBX_REQUEST_NONE; + aMBX_setupAction(actor, aMBX_ACT_WAIT); + } +} + +static void aMBX_pl_open(MAILBOX_ACTOR* actor, GAME_PLAY* play) { + cKF_FrameControl_c* fc0 = &actor->kf0.frame_control; + + if (cKF_FrameControl_passCheck_now(fc0, 18.0f) == TRUE) { + sAdo_OngenTrgStart(0x146, &actor->actor_class.world.position); + } + + if (actor->kf0_state == cKF_STATE_STOPPED) { + Submenu* submenu = &play->submenu; + + mSM_open_submenu(submenu, mSM_OVL_INVENTORY, mSM_IV_OPEN_MAILBOX, 0); + aMBX_setupAction(actor, aMBX_ACT_PL_CLOSE); + } +} + +static void aMBX_pl_close(MAILBOX_ACTOR* actor, GAME_PLAY* play) { + cKF_FrameControl_c* fc0 = &actor->kf0.frame_control; + + if (cKF_FrameControl_passCheck_now(fc0, 4.0f) == TRUE) { + sAdo_OngenTrgStart(0x147, &actor->actor_class.world.position); + } + + if (actor->kf0_state == cKF_STATE_STOPPED) { + aMBX_check_flag(actor); + actor->req = aMBX_REQUEST_NONE; + aMBX_setupAction(actor, aMBX_ACT_WAIT); + } +} + +static void aMBX_setupAction(MAILBOX_ACTOR* actor, int action) { + // clang-format off + static aMBX_ACT_PROC process[] = { + aMBX_wait, + aMBX_pl_wait, + aMBX_open_and_close, + aMBX_pl_open, + aMBX_pl_close, + }; + // clang-format on + + int anim_idx = aMBX_animeSeqNoTable[action]; + aMBX_anime_info_c* info; + cKF_SkeletonInfo_R_c* kf = &actor->kf0; + + actor->action = action; + actor->act_proc = process[action]; + + info = &aMBX_animeTable[anim_idx]; + cKF_SkeletonInfo_R_init(kf, kf->skeleton, info->anim, info->start, info->end, info->start, 0.5f, 0.0f, cKF_FRAMECONTROL_STOP, NULL); + actor->anim_idx0 = anim_idx; +} + +static void aMBX_actor_move(ACTOR* actorx, GAME* game) { + MAILBOX_ACTOR* actor = (MAILBOX_ACTOR*)actorx; + GAME_PLAY* play = (GAME_PLAY*)game; + + aMBX_anime_proc(actorx, play); + actor->act_proc(actor, play); + aMBX_setup_flag_se(actor); +} + +static void aMBX_actor_init(ACTOR* actorx, GAME* game) { + MAILBOX_ACTOR* actor = (MAILBOX_ACTOR*)actorx; + + actorx->mv_proc = aMBX_actor_move; + actorx->dw_proc = aMBX_actor_draw; + mFI_SetFG_common(DUMMY_MAILBOX0 + actor->arrange_idx, actorx->world.position, FALSE); + aMBX_setupAction(actor, aMBX_ACT_WAIT); + aMBX_check_flag(actor); + actor->kf1.frame_control.current_frame = actor->kf1.frame_control.end_frame; + aMBX_actor_move(actorx, game); +}