diff --git a/configure.py b/configure.py index c8c05ed0..8cd88626 100644 --- a/configure.py +++ b/configure.py @@ -1173,7 +1173,7 @@ config.libs = [ Object(Matching, "actor/npc/event/ac_ev_gypsy.c"), Object(Matching, "actor/npc/event/ac_ev_kabuPeddler.c"), Object(Matching, "actor/npc/event/ac_ev_majin.c"), - Object(NonMatching, "actor/npc/event/ac_ev_miko.c"), + Object(Matching, "actor/npc/event/ac_ev_miko.c"), Object(NonMatching, "actor/npc/event/ac_ev_pumpkin.c"), Object(Matching, "actor/npc/event/ac_ev_santa.c"), Object(Matching, "actor/npc/event/ac_ev_soncho.c"), diff --git a/include/ac_ev_miko.h b/include/ac_ev_miko.h index 4665f83b..ef687c0b 100644 --- a/include/ac_ev_miko.h +++ b/include/ac_ev_miko.h @@ -3,11 +3,24 @@ #include "types.h" #include "m_actor.h" +#include "ac_npc.h" #ifdef __cplusplus extern "C" { #endif +typedef struct ev_miko_actor_s EV_MIKO_ACTOR; + +typedef void (*aEMK_TALK_PROC)(EV_MIKO_ACTOR* miko, GAME_PLAY* play); + +struct ev_miko_actor_s { + NPC_ACTOR npc_class; + int talk_action; + aEMK_TALK_PROC talk_proc; + int omikuji; + u8 in_front; +}; + extern ACTOR_PROFILE Ev_Miko_Profile; #ifdef __cplusplus @@ -15,4 +28,3 @@ extern ACTOR_PROFILE Ev_Miko_Profile; #endif #endif - diff --git a/src/actor/npc/event/ac_ev_miko.c b/src/actor/npc/event/ac_ev_miko.c new file mode 100644 index 00000000..1f65afbe --- /dev/null +++ b/src/actor/npc/event/ac_ev_miko.c @@ -0,0 +1,105 @@ +#include "ac_ev_miko.h" + +#include "m_common_data.h" +#include "m_player_lib.h" +#include "m_string.h" +#include "m_handbill.h" +#include "m_msg.h" +#include "libultra/libultra.h" + +enum { + aEMK_TALK_END_WAIT, + aEMK_TALK_SELECT, + aEMK_TALK_OMIKUJI, + aEMK_TALK_GIVE, + + aEMK_TALK_NUM +}; + +enum { + aEMK_OMIKUJI_NORMAL, + aEMK_OMIKUJI_BAD, + aEMK_OMIKUJI_MONEY, + aEMK_OMIKUJI_GOODS, + + aEMK_OMIKUJI_NUM +}; + +static void aEMK_actor_ct(ACTOR* actorx, GAME* game); +static void aEMK_actor_dt(ACTOR* actorx, GAME* game); +static void aEMK_actor_move(ACTOR* actorx, GAME* game); +static void aEMK_actor_draw(ACTOR* actorx, GAME* game); +static void aEMK_actor_init(ACTOR* actorx, GAME* game); +static void aEMK_actor_save(ACTOR* actorx, GAME* game); + +ACTOR_PROFILE Ev_Miko_Profile = { + // clang-format off + mAc_PROFILE_EV_MIKO, + ACTOR_PART_NPC, + ACTOR_STATE_NO_MOVE_WHILE_CULLED, + SP_NPC_EV_MIKO, + ACTOR_OBJ_BANK_KEEP, + sizeof(EV_MIKO_ACTOR), + aEMK_actor_ct, + aEMK_actor_dt, + aEMK_actor_init, + mActor_NONE_PROC1, + aEMK_actor_save, + // clang-format on +}; + +static void aEMK_talk_request(ACTOR* actorx, GAME* game); +static int aEMK_talk_init(ACTOR* actorx, GAME* game); +static int aEMK_talk_end_chk(ACTOR* actorx, GAME* game); + +static void aEMK_setupAction(EV_MIKO_ACTOR* miko, GAME_PLAY* play, int action); + +static void aEMK_actor_ct(ACTOR* actorx, GAME* game) { + static aNPC_ct_data_c ct_data = { + // clang-format off + aEMK_actor_move, + aEMK_actor_draw, + aNPC_CT_SCHED_TYPE_STAND, + aEMK_talk_request, + aEMK_talk_init, + aEMK_talk_end_chk, + 0 + // clang-format on + }; + + if (NPC_CLIP->birth_check_proc(actorx, game) == TRUE) { + EV_MIKO_ACTOR* miko = (EV_MIKO_ACTOR*)actorx; + + NPC_CLIP->ct_proc(actorx, game, &ct_data); + miko->npc_class.condition_info.hide_flg = FALSE; + miko->npc_class.palActorIgnoreTimer = -1; + actorx->status_data.weight = MASSTYPE_IMMOVABLE; + miko->npc_class.collision.check_kind = aNPC_BG_CHECK_TYPE_NONE; + actorx->world.position.y = mCoBG_GetBgY_OnlyCenter_FromWpos2(actorx->world.position, 0.0f); + actorx->position_speed.y = 0.0f; + actorx->gravity = 0.0f; + actorx->max_velocity_y = 0.0f; + actorx->talk_distance = 78.0f; + + NPC_CLIP->set_dst_pos_proc((NPC_ACTOR*)miko, actorx->world.position.x + 10.0f, actorx->world.position.z + 10.0f); + } +} + +static void aEMK_actor_save(ACTOR* actorx, GAME* game) { + NPC_CLIP->save_proc(actorx, game); +} + +static void aEMK_actor_dt(ACTOR* actorx, GAME* game) { + NPC_CLIP->dt_proc(actorx, game); + mEv_actor_dying_message(mEv_EVENT_NEW_YEARS_DAY, actorx); +} + +static void aEMK_actor_init(ACTOR* actorx, GAME* game) { + NPC_CLIP->init_proc(actorx, game); +} + +static void aEMK_actor_draw(ACTOR* actorx, GAME* game) { + NPC_CLIP->draw_proc(actorx, game); +} + +#include "../src/actor/npc/event/ac_ev_miko_move.c_inc" diff --git a/src/actor/npc/event/ac_ev_miko_move.c_inc b/src/actor/npc/event/ac_ev_miko_move.c_inc new file mode 100644 index 00000000..1fff1b3d --- /dev/null +++ b/src/actor/npc/event/ac_ev_miko_move.c_inc @@ -0,0 +1,202 @@ +static int aEMK_set_request_act(EV_MIKO_ACTOR* miko, u8 prio, u8 idx, u8 type, u16 obj, s16 move_x, s16 move_z) { + int ret = FALSE; + + if (prio >= miko->npc_class.request.act_priority) { + u16 arg_data[aNPC_REQUEST_ARG_NUM]; + + bzero(arg_data, sizeof(arg_data)); + arg_data[0] = obj; + arg_data[2] = move_x; + arg_data[3] = move_z; + miko->npc_class.request.act_priority = prio; + miko->npc_class.request.act_idx = idx; + miko->npc_class.request.act_type = type; + mem_copy((u8*)miko->npc_class.request.act_args, (u8*)arg_data, sizeof(arg_data)); + ret = TRUE; + } + + return ret; +} + +static int aEMK_mail_free_space(void) { + if (Now_Private != NULL && mMl_chk_mail_free_space(Now_Private->mail, mPr_INVENTORY_MAIL_COUNT) != -1) { + return TRUE; + } + + return FALSE; +} + +static void aEMK_get_omikuji(Mail_c* mail, int idx) { + static u8 pf_string[mMsg_FREE_STRING_LEN]; + int header_back_start; + int load_idx; + static int table[] = { 0x2B1, 0x2A1, 0x2DA, 0x2CA }; + static int kekka_table[] = { 2, 3, 1, 0 }; + int i; + + for (i = 0; i < 4; i++) { + load_idx = table[i] + RANDOM(16); + mString_Load_StringFromRom(pf_string, sizeof(pf_string), load_idx); + mHandbill_Set_free_str(i, pf_string, sizeof(pf_string)); + } + + mString_Load_StringFromRom(pf_string, sizeof(pf_string), 0x2C1 + kekka_table[idx]); + mHandbill_Set_free_str(mHandbill_FREE_STR4, pf_string, sizeof(pf_string)); + load_idx = 0x072 + RANDOM(3); + mHandbill_Load_HandbillFromRom(mail->content.header, &header_back_start, mail->content.footer, mail->content.body, load_idx); + mail->content.font = mMl_FONT_RECV; + mail->content.header_back_start = header_back_start; + mail->content.mail_type = mMl_TYPE_OMIKUJI; + mMl_set_to_plname(mail, &Now_Private->player_ID); +} + +static void aEMK_talk_select(EV_MIKO_ACTOR* miko, GAME_PLAY* play) { + if (mMsg_CHECK_MAINNORMALCONTINUE()) { + switch (mChoice_GET_CHOSENUM()) { + case mChoice_CHOICE0: + if (!aEMK_mail_free_space()) { + mMsg_SET_CONTINUE_MSG_NUM(0x190F); + aEMK_setupAction(miko, play, aEMK_TALK_END_WAIT); + } else if (mSP_money_check(50)) { + aEMK_setupAction(miko, play, aEMK_TALK_OMIKUJI); + } else { + mMsg_SET_CONTINUE_MSG_NUM(0x190E); + aEMK_setupAction(miko, play, aEMK_TALK_END_WAIT); + } + + break; + case mChoice_CHOICE1: + aEMK_setupAction(miko, play, aEMK_TALK_END_WAIT); + break; + } + } +} + +static void aEMK_talk_omikuji(EV_MIKO_ACTOR* miko, GAME_PLAY* play) { + int order = mDemo_Get_OrderValue(mDemo_ORDER_NPC0, 9); + static u8 destiny[] = { mPr_DESTINY_NORMAL, mPr_DESTINY_BAD_LUCK, mPr_DESTINY_MONEY_LUCK, mPr_DESTINY_GOODS_LUCK }; + + if (mMsg_CHECK_MAINNORMALCONTINUE() && order == 1) { + mDemo_Set_OrderValue(mDemo_ORDER_NPC0, 9, 0); + mMsg_SET_CONTINUE_MSG_NUM(0x1911 + miko->omikuji); + aEMK_setupAction(miko, play, aEMK_TALK_GIVE); + Now_Private->destiny.type = destiny[miko->omikuji]; + mSP_get_sell_price(50); + } +} + +static void aEMK_talk_give(EV_MIKO_ACTOR* miko, GAME_PLAY* play) { + int order = mDemo_Get_OrderValue(mDemo_ORDER_NPC0, 9); + Mail_c mail; + int idx; + + if (order == 1) { + mDemo_Set_OrderValue(mDemo_ORDER_NPC0, 9, 0); + mDemo_Set_OrderValue(mDemo_ORDER_NPC0, 1, 2); + aNPC_DEMO_GIVE_ITEM(ITM_FORTUNE_SLIP, aHOI_REQUEST_PUTAWAY, FALSE); + aEMK_setupAction(miko, play, aEMK_TALK_END_WAIT); + mMl_clear_mail(&mail); + aEMK_get_omikuji(&mail, miko->omikuji); + + if (Now_Private != NULL) { + idx = mMl_chk_mail_free_space(Now_Private->mail, mPr_INVENTORY_MAIL_COUNT); + + if (idx != -1) { + mail.content.paper_type = (u8)ITM_PAPER25; + mMl_copy_mail(&Now_Private->mail[idx], &mail); + } + } + } +} + +static void aEMK_talk_omikuji_init(EV_MIKO_ACTOR* miko, GAME_PLAY* play) { + miko->omikuji = RANDOM(aEMK_OMIKUJI_NUM); +} + +typedef void (*aEMK_INIT_PROC)(EV_MIKO_ACTOR* miko, GAME_PLAY* play); + +static void aEMK_init_proc(EV_MIKO_ACTOR* miko, GAME_PLAY* play, int action) { + static aEMK_INIT_PROC init_proc[] = { + // clang-format off + (aEMK_INIT_PROC)none_proc1, + (aEMK_INIT_PROC)none_proc1, + aEMK_talk_omikuji_init, + (aEMK_INIT_PROC)none_proc1, + // clang-format on + }; + + (*init_proc[action])(miko, play); +} + +static void aEMK_setupAction(EV_MIKO_ACTOR* miko, GAME_PLAY* play, int action) { + static aEMK_TALK_PROC process[] = { + // clang-format off + (aEMK_TALK_PROC)none_proc1, + aEMK_talk_select, + aEMK_talk_omikuji, + aEMK_talk_give, + // clang-format on + }; + + miko->talk_action = action; + miko->talk_proc = process[action]; + aEMK_init_proc(miko, play, action); +} + +static void aEMK_set_talk_info(ACTOR* actorx) { + int msg_no; + EV_MIKO_ACTOR* miko = (EV_MIKO_ACTOR*)actorx; + + if (miko->in_front == TRUE) { + msg_no = 0x190C; + } else { + msg_no = 0x1916; + } + + mDemo_Set_msg_num(msg_no); +} + +static void aEMK_talk_request(ACTOR* actorx, GAME* game) { + EV_MIKO_ACTOR* miko = (EV_MIKO_ACTOR*)actorx; + + if (miko->npc_class.action.idx == aNPC_ACT_WAIT) { + mDemo_Request(mDemo_TYPE_TALK, actorx, aEMK_set_talk_info); + } +} + +static int aEMK_talk_init(ACTOR* actorx, GAME* game) { + EV_MIKO_ACTOR* miko = (EV_MIKO_ACTOR*)actorx; + GAME_PLAY* play = (GAME_PLAY*)game; + + aEMK_setupAction(miko, play, aEMK_TALK_SELECT); + mDemo_Set_ListenAble(); + return TRUE; +} + +static int aEMK_talk_end_chk(ACTOR* actorx, GAME* game) { + EV_MIKO_ACTOR* miko = (EV_MIKO_ACTOR*)actorx; + GAME_PLAY* play = (GAME_PLAY*)game; + int ret = FALSE; + + (*miko->talk_proc)(miko, play); + + if (!mDemo_Check(mDemo_TYPE_TALK, actorx)) { + ret = TRUE; + NPC_CLIP->set_dst_pos_proc((NPC_ACTOR*)miko, actorx->world.position.x + 10.0f, actorx->world.position.z + 10.0f); + aEMK_set_request_act(miko, 4, aNPC_ACT_TURN, aNPC_ACT_TYPE_TO_POINT, aNPC_ACT_OBJ_DEFAULT, actorx->world.position.x + 10.0f, actorx->world.position.z + 10.0f); + } + + return ret; +} + +static void aEMK_actor_move(ACTOR* actorx, GAME* game) { + EV_MIKO_ACTOR* miko = (EV_MIKO_ACTOR*)actorx; + + if (ABS((s16)(actorx->player_angle_y - actorx->shape_info.rotation.y)) < DEG2SHORT_ANGLE2(33.75f)) { + miko->in_front = TRUE; + } else { + miko->in_front = FALSE; + } + + NPC_CLIP->move_proc(actorx, game); +}