diff --git a/config/rel_slices.yml b/config/rel_slices.yml index a8b3638c..d7ae39e2 100644 --- a/config/rel_slices.yml +++ b/config/rel_slices.yml @@ -443,6 +443,10 @@ ac_psnowman.c: .text: [0x80484098, 0x80484694] .rodata: [0x80644C30, 0x80644C60] .data: [0x8068A458, 0x8068A480] +ac_quest_manager.c: + .text: [0x80485508, 0x80487A78] + .data: [0x8068A520, 0x8068ADF8] + .bss: [0x812FC920, 0x812FC938] ac_rope.c: .text: [0x804967A4, 0x80496AB8] .rodata: [0x80644DB0,0x80644DB8] diff --git a/include/ac_quest_manager.h b/include/ac_quest_manager.h index 10ffe4bb..e514e812 100644 --- a/include/ac_quest_manager.h +++ b/include/ac_quest_manager.h @@ -3,11 +3,264 @@ #include "types.h" #include "m_actor.h" +#include "ac_npc.h" +#include "m_submenu.h" +#include "m_choice.h" +#include "ac_quest_manager_clip.h" #ifdef __cplusplus extern "C" { #endif +#define aQMgr_REGIST_NUM 35 + +enum { + aQMgr_TALK_STATE_INIT, + aQMgr_TALK_STATE_SUB, + + aQMgr_TALK_STATE_NUM +}; + +enum { + aQMgr_TALK_SUB_STATE_WAIT, + aQMgr_TALK_SUB_STATE_MSG_DISAPPEAR_WAIT, + aQMgr_TALK_SUB_STATE_MSG_APPEAR_WAIT, + aQMgr_TALK_SUB_STATE_CHECK_BUTTON, + aQMgr_TALK_SUB_STATE_HAND_ITEM_WAIT, + aQMgr_TALK_SUB_STATE_NPC_HAND_WAIT, + aQMgr_TALK_SUB_STATE_ITEM_WAIT, + aQMgr_TALK_SUB_STATE_DEMO_ORDER_WAIT, + aQMgr_TALK_SUB_STATE_NO_WAIT, + aQMgr_TALK_SUB_STATE_NPC_HAND_WAIT_MSG_WAIT, + aQMgr_TALK_SUB_STATE_ITEM_WAIT_END, + aQMgr_TALK_SUB_STATE_ITEM_PLAYER_WAIT, + + aQMgr_TALK_SUB_STATE_NUM +}; + +enum { + aQMgr_TALK_COMMON_SET_TARGET, + aQMgr_TALK_COMMON_GET_ITEM_IDX, + aQMgr_TALK_COMMON_SET_MSG_NO, + aQMgr_TALK_COMMON_CLEAR_TALK_INFO, + aQMgr_TALK_COMMON_SET_CHOICE_STR, + aQMgr_TALK_COMMON_GET_SET_DATA, + aQMgr_TALK_COMMON_SET_NPC_TAKEOUT_ITEM, + aQMgr_TALK_COMMON_SET_NPC_TAKEOUT_REWARD, + aQMgr_TALK_COMMON_TALK_INIT_OVL, + aQMgr_TALK_COMMON_CHANGE_TALK_NORMAL, + aQMgr_TALK_COMMON_CHANGE_TALK_ISLAND, + + aQMgr_TALK_COMMON_NUM +}; + +enum { + aQMgr_TALK_KIND_QUEST, + aQMgr_TALK_KIND_FIRST_JOB, + aQMgr_TALK_KIND_NORMAL, + aQMgr_TALK_KIND_ISLAND, + + aQMgr_TALK_KIND_NUM +}; + +enum { + aQMgr_MSG_KIND_REQUEST_INIT, + aQMgr_MSG_KIND_REQUEST_END, + aQMgr_MSG_KIND_REQUEST_RECONF, // reconfirm? + aQMgr_MSG_KIND_REQUEST_REJECT, // rejected offer + aQMgr_MSG_KIND_COMPLETE_INIT, + aQMgr_MSG_KIND_COMPLETE_END, + aQMgr_MSG_KIND_FAILURE_INIT, + aQMgr_MSG_KIND_FAILURE_END, + aQMgr_MSG_KIND_FULL_ITEM, // pockets are full so can't give quests + aQMgr_MSG_KIND_AFTER_REWARD, + aQMgr_MSG_KIND_AFTER_REWARD_THANKS, + aQMgr_MSG_KIND_REWARD_FULL_ITEM, // pockets are full so can't give reward + aQMgr_MSG_KIND_REWARD_FULL_ITEM2, // pockets are still full + aQMgr_MSG_KIND_NONE, + + aQMgr_MSG_KIND_NUM = aQMgr_MSG_KIND_NONE +}; + +enum { + aQMgr_TIME_MORNING, + aQMgr_TIME_DAY, + aQMgr_TIME_EVENING, + aQMgr_TIME_NIGHT, + + aQMgr_TIME_NUM +}; + +enum { + aQMgr_QUEST_TARGET_RANDOM, + aQMgr_QUEST_TARGET_RANDOM_EXCLUDED, + aQMgr_QUEST_TARGET_ORIGINAL_TARGET, // same target as before? + aQMgr_QUEST_TARGET_FOREIGN, + aQMgr_QUEST_TARGET_LAST_REMOVE, + aQMgr_QUEST_TARGET_CLIENT, + + aQMgr_QUEST_TARGET_NUM +}; + +enum { + aQMgr_QUEST_ITEM_RANDOM, + aQMgr_QUEST_ITEM_FRUIT, + aQMgr_QUEST_ITEM_CLOTH, + aQMgr_QUEST_ITEM_FROM_DATA, + aQMgr_QUEST_ITEM_CURRENT_ITEM, + aQMgr_QUEST_ITEM_NONE, + + aQMgr_QUEST_ITEM_NUM +}; + +enum { + aQMgr_QUEST_REWARD_FTR, + aQMgr_QUEST_REWARD_STATIONERY, + aQMgr_QUEST_REWARD_CLOTH, + aQMgr_QUEST_REWARD_CARPET, + aQMgr_QUEST_REWARD_WALLPAPER, + aQMgr_QUEST_REWARD_MONEY, + aQMgr_QUEST_REWARD_WORN_CLOTH, + aQMgr_QUEST_REWARD_7, + + aQMgr_QUEST_REWARD_NUM +}; + +typedef struct quest_manager_actor QUEST_MANAGER_ACTOR; + +typedef union { + mQst_base_c base; + mQst_errand_c errand; + mQst_delivery_c delivery; + mQst_contest_c contest; +} aQMgr_quest_c; + +typedef struct quest_manager_order_s { + u16 type; + u16 value; +} aQMgr_order_c; + +typedef struct quest_manager_set_data_s { + u32 to_type:3; + u32 day_limit:6; + u32 last_step:4; + u32 handover_item:1; + u32 src_item_type:3; + mActor_name_t item; + u8 reward_percentages[aQMgr_QUEST_REWARD_NUM]; + u32 max_pay; + int msg_start[aQMgr_MSG_KIND_NUM]; +} aQMgr_set_data_c; + +typedef struct quest_manager_flower_work_data_s { + int exist_num; + int goal_num; + int remain_num; +} aQMgr_flower_data_c; + +typedef union { + aQMgr_flower_data_c flower; +} aQMgr_work_data_c; + +typedef struct quest_manager_target_s { + mQst_base_c quest_info; + AnmPersonalID_c* from_id; + AnmPersonalID_c* to_id; + int quest_inv_item_idx; + mActor_name_t quest_item; + int reward_kind; + mActor_name_t reward_item; + u32 pay; + lbRTC_time_c limit; + aQMgr_set_data_c* set_data_p; + int free_data_idx; + mQst_base_c* free_data_p; + u8 errand_type; + aQMgr_work_data_c work; +} aQMgr_target_c; + +typedef struct quest_manager_regist_s aQMgr_regist_c; + +typedef void (*aQMgr_CHECK_LIMIT_PROC)(QUEST_MANAGER_ACTOR*, aQMgr_regist_c*); +typedef int (*aQMgr_CHECK_FINISH_PROC)(mQst_base_c*, Animal_c*); + +struct quest_manager_regist_s { + aQMgr_CHECK_LIMIT_PROC check_limit_proc; + aQMgr_CHECK_FINISH_PROC check_finish_proc; + mQst_base_c* quest_info; + PersonalID_c* pid; + AnmPersonalID_c from_id; + AnmPersonalID_c to_id; + mActor_name_t item; + int animal_idx; +}; + +typedef struct quest_manager_normal_info_s { + Anmmem_c* memory; + Anmplmail_c* anmplmail; + u32 pay; + int player_num_items; // valid items to trade + int player_give_item_idx; // index to give item away + mActor_name_t player_items[4]; // items which the player has in their inventory which match 'player_num_items' + mActor_name_t selected_item; +} aQMgr_normal_info_c; + +typedef struct quest_manager_talk_wait_s { + int flags[3]; +} aQMgr_talk_wait_info_c; + +typedef struct quest_manager_choice_s { + int choice_ids[mChoice_CHOICE_NUM]; + int choice_num; + int talk_action; +} aQMgr_choice_c; + +typedef int (*aQMgr_TALK_COMMON_PROC)(QUEST_MANAGER_ACTOR*, int); +typedef void (*aQMgr_ACTOR_CLEAR_REGIST_PROC)(aQMgr_regist_c*, int); +typedef void (*aQMgr_ACTOR_REGIST_QUEST_PROC)(QUEST_MANAGER_ACTOR*, int*, aQMgr_quest_c*, int); +typedef int (*aQMgr_GET_TIME_KIND_PROC)(int); +typedef void (*aQMgr_TALK_INIT_PROC)(QUEST_MANAGER_ACTOR*); + +/* sizeof(struct quest_manager_actor) == 0xAA0 */ +struct quest_manager_actor { + /* 0x000 */ ACTOR actor_class; + /* 0x174 */ Submenu* submenu; + /* 0x178 */ ACTOR** client; + /* 0x17C */ Anmmem_c** memory; + /* 0x180 */ u8* mode; + /* 0x184 */ u8 talk_state; + /* 0x185 */ u8 sub_talk_state; + /* 0x186 */ u8 talk_step; + /* 0x188 */ aQMgr_choice_c choice; + /* 0x1A8 */ int msg_no; + /* 0x1AC */ int category_msg_no_start; + /* 0x1B0 */ int msg_category; + /* 0x1B4 */ aQMgr_order_c demo_order; + /* 0x1B8 */ aQMgr_talk_wait_info_c wait_info; + /* 0x1C4 */ mActor_name_t handover_item; + /* 0x1C8 */ aQMgr_target_c target; + /* 0x214 */ aQMgr_regist_c regist[aQMgr_REGIST_NUM]; + /* 0x930 */ int regist_idx; + /* 0x934 */ int regist_use_no; + /* 0x938 */ int _938; + /* 0x93C */ int _93C; + /* 0x940 */ aQMgr_Clip_c* clip; + /* 0x944 */ aQMgr_TALK_INIT_PROC talk_init_proc; + /* 0x948 */ aQMgr_TALK_COMMON_PROC talk_common_proc; + /* 0x94C */ aQMgr_ACTOR_CLEAR_REGIST_PROC clear_regist_proc; + /* 0x950 */ aQMgr_ACTOR_REGIST_QUEST_PROC regist_quest_proc; + /* 0x954 */ aQMgr_GET_TIME_KIND_PROC get_time_kind_proc; + /* 0x958 */ mActor_name_t cloth; + /* 0x95A */ u8 talk_type; + /* 0x95B */ u8 talk_change_type; + /* 0x95C */ u8 errand_next[5]; + /* 0x962 */ Mail_c mail; + /* 0xA8C */ Anmmem_c* mail_memory; + /* 0xA90 */ u8 last_strings[7]; + /* 0xA98 */ mActor_name_t give_item; + /* 0xA9C */ int _A9C; +}; + extern ACTOR_PROFILE Quest_Manager_Profile; #ifdef __cplusplus diff --git a/include/ac_quest_manager_clip.h b/include/ac_quest_manager_clip.h new file mode 100644 index 00000000..a1ff65b8 --- /dev/null +++ b/include/ac_quest_manager_clip.h @@ -0,0 +1,25 @@ +#ifndef AC_QUEST_MANAGER_CLIP_H +#define AC_QUEST_MANAGER_CLIP_H + +#include "types.h" +#include "ac_npc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*aQMgr_TALK_REQUEST_PROC)(ACTOR*); +typedef int (*aQMgr_TALK_START_PROC)(ACTOR*); +typedef int (*aQMgr_TALK_CHECK_PROC)(ACTOR*); + +typedef struct quest_manager_clip_s { + aQMgr_TALK_REQUEST_PROC talk_request_proc; + aQMgr_TALK_START_PROC talk_start_proc; + aQMgr_TALK_CHECK_PROC talk_check_proc; +} aQMgr_Clip_c; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/ac_quest_talk_fj_init.h b/include/ac_quest_talk_fj_init.h new file mode 100644 index 00000000..aba5e437 --- /dev/null +++ b/include/ac_quest_talk_fj_init.h @@ -0,0 +1,17 @@ +#ifndef AC_QUEST_TALK_FJ_INIT_H +#define AC_QUEST_TALK_FJ_INIT_H + +#include "types.h" +#include "ac_quest_manager.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern void aQMgr_talk_first_job_init(QUEST_MANAGER_ACTOR* manager); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/ac_quest_talk_greeting.h b/include/ac_quest_talk_greeting.h new file mode 100644 index 00000000..ab45d142 --- /dev/null +++ b/include/ac_quest_talk_greeting.h @@ -0,0 +1,17 @@ +#ifndef AC_QUEST_TALK_GREETING_H +#define AC_QUEST_TALK_GREETING_H + +#include "types.h" +#include "ac_quest_manager.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern int aQMgr_get_hello_msg_no(ACTOR* client, aQMgr_GET_TIME_KIND_PROC time_proc, mActor_name_t give_item); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/ac_quest_talk_init.h b/include/ac_quest_talk_init.h new file mode 100644 index 00000000..2be4ffab --- /dev/null +++ b/include/ac_quest_talk_init.h @@ -0,0 +1,17 @@ +#ifndef AC_QUEST_TALK_INIT_H +#define AC_QUEST_TALK_INIT_H + +#include "types.h" +#include "ac_quest_manager.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern void aQMgr_actor_move_talk_init(QUEST_MANAGER_ACTOR* manager); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/ac_quest_talk_island.h b/include/ac_quest_talk_island.h new file mode 100644 index 00000000..892a298e --- /dev/null +++ b/include/ac_quest_talk_island.h @@ -0,0 +1,17 @@ +#ifndef AC_QUEST_TALK_ISLAND_H +#define AC_QUEST_TALK_ISLAND_H + +#include "types.h" +#include "ac_quest_manager.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern void aQMgr_talk_island_init(QUEST_MANAGER_ACTOR* manager); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/ac_quest_talk_normal_init.h b/include/ac_quest_talk_normal_init.h new file mode 100644 index 00000000..65e0dbad --- /dev/null +++ b/include/ac_quest_talk_normal_init.h @@ -0,0 +1,17 @@ +#ifndef AC_QUEST_TALK_NORMAL_INIT_H +#define AC_QUEST_TALK_NORMAL_INIT_H + +#include "types.h" +#include "ac_quest_manager.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern void aQMgr_talk_normal_init(QUEST_MANAGER_ACTOR* manager); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/m_clip.h b/include/m_clip.h index 128300fe..5c19bd74 100644 --- a/include/m_clip.h +++ b/include/m_clip.h @@ -22,6 +22,7 @@ #include "ac_arrange_room.h" #include "ac_shop_umbrella.h" #include "ac_handOverItem.h" +#include "ac_quest_manager_clip.h" #ifdef __cplusplus extern "C" { @@ -39,7 +40,7 @@ typedef struct clip_s { /* 0x060 */ aSM_Clip_c* shop_manekin_clip; /* 0x064 */ void* _064; /* 0x068 */ CLIP_NONE_PROC _068; - /* 0x06C */ void* _06C; + /* 0x06C */ aQMgr_Clip_c* quest_manager_clip; /* 0x070 */ aSI_Clip_c* shop_indoor_clip; /* 0x074 */ bIT_Clip_c* bg_item_clip; /* 0x078 */ aWeather_Clip_c* weather_clip; diff --git a/include/m_lib.h b/include/m_lib.h index 97cf85b9..5632eb79 100644 --- a/include/m_lib.h +++ b/include/m_lib.h @@ -23,7 +23,7 @@ extern "C" { #define SQ(x) ((x)*(x)) #define CLAMP_MAX(x, min) ((min) < (x) ? (min) : (x)) -/* Percent of 360 deg */ +/* Float modulo operator */ #define MOD_F(a, m) (a - (int)((a) * (1.0f / (m))) * (m)) /* radians -> short angle */ diff --git a/include/m_name_table.h b/include/m_name_table.h index 74657af0..89a7743e 100644 --- a/include/m_name_table.h +++ b/include/m_name_table.h @@ -2414,7 +2414,7 @@ extern int mNT_check_unknown(mActor_name_t item_no); #define SP_NPC_EV_GROUNDHOG_4 (SP_NPC_START + 140) // D08C #define SP_NPC_TURKEY (SP_NPC_START + 141) // D08D #define SP_NPC_HEM (SP_NPC_START + 142) // D08E - +#define SP_NPC_EV_SUMMERCAMP_0 (SP_NPC_START + 143) // 0xD08F #define NPC_START 0xE000 // cats diff --git a/include/m_npc.h b/include/m_npc.h index 010e9396..9525b1de 100644 --- a/include/m_npc.h +++ b/include/m_npc.h @@ -35,6 +35,7 @@ extern "C" { #define mNpc_ISLAND_FTR_NUM 16 #define mNpc_EVENT_NPC_NUM 5 #define mNpc_MASK_NPC_NUM 3 +#define mNpc_ISLANDER_NO ANIMAL_NUM_MAX enum { mNpc_MOOD_0, @@ -333,7 +334,7 @@ extern void mNpc_CopyAnimalInfo(Animal_c* dst, Animal_c* src); extern int mNpc_SearchAnimalinfo(Animal_c* animal, mActor_name_t npc_id, int count); extern Animal_c* mNpc_GetAnimalInfoP(mActor_name_t npc_id); extern int mNpc_SearchAnimalPersonalID(AnmPersonalID_c* anm_pid); -extern AnmPersonalID_c* mNpc_GetOtherAnimalPersonalIDOtherBlock(AnmPersonalID_c* pids, int count, int bx, int bz, int check_flag); +extern AnmPersonalID_c* mNpc_GetOtherAnimalPersonalIDOtherBlock(AnmPersonalID_c* exclude_pids, int count, int bx, int bz, int check_flag); extern AnmPersonalID_c* mNpc_GetOtherAnimalPersonalID(AnmPersonalID_c* pids, int count); extern void mNpc_SetAnimalThisLand(Animal_c* animal, int count); extern int mNpc_GetSameLooksNum(u8 looks); diff --git a/include/m_player_lib.h b/include/m_player_lib.h index 2a6d528f..3264432c 100644 --- a/include/m_player_lib.h +++ b/include/m_player_lib.h @@ -55,6 +55,7 @@ extern void mPlib_Set_boat_angleZ(s16 angleZ); extern int mPlib_Get_end_player_demo_walk(); extern int mPlib_request_main_demo_geton_boat_type1(f32 goal_x, f32 goal_z, s16 angleY); extern int mPlib_request_main_demo_getoff_boat_standup_type1(const xyz_t* pos, s16 angleY); +extern int mPlib_check_player_actor_main_index_RecieveMove(GAME* game); extern mPlayer_change_data_from_submenu_c* mPlib_Get_change_data_from_submenu_p(); diff --git a/include/m_quest.h b/include/m_quest.h index 8780ea98..d798ddf7 100644 --- a/include/m_quest.h +++ b/include/m_quest.h @@ -37,6 +37,8 @@ extern "C" { #define mQst_LETTER_OKAY_LENGTH 17 #define mQst_LETTER_GOOD_LENGTH 49 +#define mQst_DELIVERY_KIND_NUM 4 + enum { mQst_QUEST_TYPE_DELIVERY, /* Deliver item quest */ mQst_QUEST_TYPE_ERRAND, /* Villager 'can I help' quests */ diff --git a/include/zurumode.h b/include/zurumode.h index 4b7f2605..f727f856 100644 --- a/include/zurumode.h +++ b/include/zurumode.h @@ -46,7 +46,7 @@ enum zurumode_stage { }; #define ZURUMODE_ENABLED() (zurumode_flag >= 1) -#define ZURUMODE2_ENALBED() (zurumode_flag >= 2) +#define ZURUMODE2_ENABLED() (zurumode_flag >= 2) #ifdef __cplusplus } diff --git a/src/ac_quest_contest.c_inc b/src/ac_quest_contest.c_inc new file mode 100644 index 00000000..6d70f3e9 --- /dev/null +++ b/src/ac_quest_contest.c_inc @@ -0,0 +1,180 @@ +static void aQMgr_actor_check_limit_and_clear_quest(mQst_contest_c* contest) { + if (mQst_CheckLimitOver(&contest->base) == TRUE) { + mQst_ClearContest(contest); + } +} + +static void aQMgr_contest_check_limit(QUEST_MANAGER_ACTOR* manager, aQMgr_regist_c* regist) { + aQMgr_actor_check_limit_and_clear_quest((mQst_contest_c*)regist->quest_info); +} + +static int aQMgr_actor_check_fin_fruit(mQst_base_c* quest_info, Animal_c* animal) { + int res = FALSE; + + if (quest_info->progress == 1 && aQMgr_actor_check_fin_item(quest_info, animal) == TRUE) { + res = TRUE; + } + + return res; +} + +static int aQMgr_actor_check_fin_soccer(mQst_base_c* quest_info, Animal_c* animal) { + int res = FALSE; + + if (quest_info->progress == 1) { + res = TRUE; + } + + return res; +} + +static void aQMgr_actor_contest_snowman_clear(QUEST_MANAGER_ACTOR* manager, aQMgr_regist_c* regist) { + lbRTC_time_c* rtc_time = Common_GetPointer(time.rtc_time); + + if (regist != NULL && regist->quest_info != NULL) { + mQst_base_c* quest_info = regist->quest_info; + + if ( + (rtc_time->month == lbRTC_FEBRUARY && rtc_time->day > 17) || + (rtc_time->month >= lbRTC_MARCH && rtc_time->month <= lbRTC_NOVEMBER) || + (rtc_time->month == lbRTC_DECEMBER && rtc_time->day < 25) || + (mQst_CheckLimitOver(quest_info) == TRUE) + ) { + mQst_ClearContest((mQst_contest_c*)quest_info); + } + } +} + +static int aQMgr_actor_check_fin_snowman(mQst_base_c* quest_info, Animal_c* animal) { + int res = FALSE; + + if (animal != NULL && quest_info != NULL) { + mQst_contest_c* contest = (mQst_contest_c*)quest_info; + + if (quest_info->progress == 1 && + mPr_NullCheckPersonalID(&contest->player_id) == FALSE && + mPr_CheckCmpPersonalID(&contest->player_id, &Common_Get(now_private)->player_ID) == TRUE) { + res = TRUE; + } + } + + return res; +} + +static void aQMgr_actor_contest_flower_clear(QUEST_MANAGER_ACTOR* manager, aQMgr_regist_c* regist) { + lbRTC_time_c* rtc_time = Common_GetPointer(time.rtc_time); + + if (regist != NULL && regist->quest_info != NULL) { + mQst_base_c* quest_info = regist->quest_info; + + if ( + (rtc_time->month == lbRTC_JANUARY) || + (rtc_time->month == lbRTC_FEBRUARY && rtc_time->day < 25) || + (rtc_time->month >= lbRTC_SEPTEMBER) || + (mQst_CheckLimitOver(quest_info) == TRUE) + ) { + mQst_ClearContest((mQst_contest_c*)quest_info); + } + } +} + +static int aQMgr_actor_check_flower(mQst_contest_c* contest, int bx, int bz) { + int res = FALSE; + int flower_num = mQst_GetFlowerSeedNum(bx, bz); + + if (contest->base.progress == 1) { + if (contest->info.flower_data.flowers_requested <= flower_num) { + if (mPr_NullCheckPersonalID(&contest->player_id) == TRUE || mPr_CheckCmpPersonalID(&contest->player_id, &Common_Get(now_private)->player_ID) == TRUE) { + res = TRUE; + } + } + else { + mPr_ClearPersonalID(&contest->player_id); + } + } + + return res; +} + +static int aQMgr_actor_check_fin_flower(mQst_base_c* quest_info, Animal_c* animal) { + mQst_contest_c* contest = (mQst_contest_c*)quest_info; + + return aQMgr_actor_check_flower(contest, animal->home_info.block_x, animal->home_info.block_z); +} + +static int aQMgr_actor_check_fin_quest_have_item1(mQst_contest_c* contest, u8 item1_category) { + int res = FALSE; + + if (contest->base.progress == 1 && mPr_NullCheckPersonalID(&contest->player_id) == TRUE && mPr_GetPossessionItemIdxItem1Category(Common_Get(now_private), item1_category) != -1) { + res = TRUE; + } + + return res; +} + +static int aQMgr_actor_check_fin_fish(mQst_base_c* quest_info, Animal_c* animal) { + mQst_contest_c* contest = (mQst_contest_c*)quest_info; + + return aQMgr_actor_check_fin_quest_have_item1(contest, ITEM1_CAT_FISH); +} + +static void aQMgr_actor_contest_insect_clear(QUEST_MANAGER_ACTOR* manager, aQMgr_regist_c* regist) { + lbRTC_time_c* rtc_time = Common_GetPointer(time.rtc_time); + + if (regist != NULL && regist->quest_info != NULL) { + mQst_base_c* quest_info = regist->quest_info; + + if ( + (rtc_time->month <= lbRTC_FEBRUARY) || + (rtc_time->month == lbRTC_NOVEMBER && rtc_time->day >= 29) || + (rtc_time->month == lbRTC_DECEMBER) || + (mQst_CheckLimitOver(quest_info) == TRUE) + ) { + mQst_ClearContest((mQst_contest_c*)quest_info); + } + } +} + +static int aQMgr_actor_check_fin_insect(mQst_base_c* quest_info, Animal_c* animal) { + mQst_contest_c* contest = (mQst_contest_c*)quest_info; + + return aQMgr_actor_check_fin_quest_have_item1(contest, ITEM1_CAT_INSECT); +} + +static int aQMgr_actor_check_fin_contest_letter(mQst_base_c* quest_info, Animal_c* animal) { + mQst_contest_c* contest = (mQst_contest_c*)quest_info; + int res = FALSE; + + if (contest->base.progress == 1) { + res = TRUE; + } + + return res; +} + +static void aQMgr_save_contest_flower(aQMgr_regist_c* regist) { + mQst_contest_c* contest = (mQst_contest_c*)regist->quest_info; + Anmhome_c* home = &Save_Get(animals[regist->animal_idx]).home_info; + + if (aQMgr_actor_check_flower(contest, home->block_x, home->block_z) == TRUE) { + mPr_ClearPersonalID(&contest->player_id); + mPr_CopyPersonalID(&contest->player_id, &Common_Get(now_private)->player_ID); + } +} + +typedef void (*aQMgr_CONTEST_SAVE_PROC)(aQMgr_regist_c*); + +static void aQMgr_save_contest(aQMgr_regist_c* regist) { + int contest_kind = regist->quest_info->quest_kind; + static aQMgr_CONTEST_SAVE_PROC save_proc[mQst_CONTEST_KIND_NUM] = { + (aQMgr_CONTEST_SAVE_PROC)&none_proc1, + (aQMgr_CONTEST_SAVE_PROC)&none_proc1, + (aQMgr_CONTEST_SAVE_PROC)&none_proc1, + &aQMgr_save_contest_flower, + (aQMgr_CONTEST_SAVE_PROC)&none_proc1, + (aQMgr_CONTEST_SAVE_PROC)&none_proc1, + (aQMgr_CONTEST_SAVE_PROC)&none_proc1 + }; + + (*save_proc[contest_kind])(regist); +} diff --git a/src/ac_quest_errand.c_inc b/src/ac_quest_errand.c_inc new file mode 100644 index 00000000..5f284397 --- /dev/null +++ b/src/ac_quest_errand.c_inc @@ -0,0 +1,85 @@ +static void aQMgr_move_own_errand_cloth(QUEST_MANAGER_ACTOR* manager, aQMgr_regist_c* regist) { + mActor_name_t player_cloth = Common_Get(now_private)->cloth.item; + mQst_errand_c* errand = (mQst_errand_c*)regist->quest_info; + mActor_name_t cloth = errand->item; + + switch (errand->base.progress) { + case 2: + if (cloth == player_cloth) { + errand->base.progress = 0; + } + + break; + + case 0: + if (cloth != player_cloth) { + errand->base.progress = 2; + } + + break; + } +} + +static void aQMgr_move_own_errand_seed(QUEST_MANAGER_ACTOR* manager, aQMgr_regist_c* regist) { + mActor_name_t* item_p = Common_Get(now_private)->inventory.pockets; + mQst_errand_c* errand = (mQst_errand_c*)regist->quest_info; + + if (errand->base.progress == 2) { + int i; + + for (i = 0; i < mPr_POCKETS_SLOT_COUNT; i++) { + if (ITEM_NAME_GET_TYPE(*item_p) == NAME_TYPE_ITEM1 && ITEM_NAME_GET_CAT(*item_p) == ITEM1_CAT_PLANT) { + break; + } + + item_p++; + } + + // Reset progress if no plants in inventory + if (i >= mPr_POCKETS_SLOT_COUNT) { + errand->base.progress = 0; + } + } +} + +static void aQMgr_move_own_errand_letter(QUEST_MANAGER_ACTOR* manager, aQMgr_regist_c* regist) { + mQst_errand_c* errand = (mQst_errand_c*)regist->quest_info; + PersonalID_c* pid = &Common_Get(now_private)->player_ID; + AnmPersonalID_c* to_id; + Animal_c* animal = Save_Get(animals); + Anmmem_c* memory; + + if (errand->base.progress == 2 || errand->base.progress == 3) { + int i; + + to_id = &errand->recipient; + for (i = 0; i < ANIMAL_NUM_MAX; i++) { + if (mNpc_CheckCmpAnimalPersonalID(to_id, &animal->id) == TRUE) { + int idx; + + memory = animal->memories; + idx = mNpc_GetAnimalMemoryIdx(pid, memory, ANIMAL_MEMORY_NUM); + + if (idx != -1) { + memory += idx; + if (memory->letter_info.exists == TRUE) { + errand->base.progress = 0; + break; + } + } + } + + animal++; + } + } +} + +static void aQMgr_move_own_errand_hello(QUEST_MANAGER_ACTOR* manager, aQMgr_regist_c* regist) { + mQst_errand_c* errand = (mQst_errand_c*)regist->quest_info; + + if (errand->base.progress == 2) { + if (mNpc_CheckFriendAllAnimal(&Common_Get(now_private)->player_ID) == TRUE && mSC_check_ArbeitPlayer()) { + errand->base.progress = 0; + } + } +} diff --git a/src/ac_quest_manager.c b/src/ac_quest_manager.c new file mode 100644 index 00000000..a3913f0e --- /dev/null +++ b/src/ac_quest_manager.c @@ -0,0 +1,1510 @@ +#include "ac_quest_manager.h" + +#include "m_common_data.h" +#include "m_msg.h" +#include "m_melody.h" +#include "ac_quest_talk_greeting.h" +#include "ac_quest_talk_init.h" +#include "ac_quest_talk_fj_init.h" +#include "ac_quest_talk_normal_init.h" +#include "ac_quest_talk_island.h" +#include "m_soncho.h" +#include "gfxalloc.h" +#include "m_player_lib.h" +#include "libultra/libultra.h" +#include "m_malloc.h" +#include "zurumode.h" + +enum { + aQMgr_MODE_NORMAL, + aQMgr_MODE_SELECT_TALK, + aQMgr_MODE_MOVE_TALK, + aQMgr_MODE_TALK_FIN, + aQMgr_MODE_TALK_START_KAMAKURA_HELLO, + aQMgr_MODE_TALK_START_KAMAKURA, + aQMgr_MODE_TALK_START_SUMMERCAMP_HELLO, + aQMgr_MODE_TALK_START_SUMMERCAMP, + + aQMgr_MODE_NUM +}; + +static ACTOR* l_client_p = NULL; +static u8 l_quest_manager_mode = aQMgr_MODE_NORMAL; +static Anmmem_c* l_quest_memory_p = NULL; +static QUEST_MANAGER_ACTOR* l_quest_actor_p = NULL; +static int l_quest_manager_hello = FALSE; +static int l_aQMgr_hand_start = FALSE; + +// clang-format off +static aQMgr_set_data_c l_set_delivery_data[] = { + { + aQMgr_QUEST_TARGET_RANDOM, + 2, + 0, + TRUE, + aQMgr_QUEST_ITEM_CLOTH, + EMPTY_NO, + 40, 0, 0, 0, 0, 30, 30, 0, + 200, + 0x0151, 0x024C, 0x0163, 0x025E, 0x0175, 0x0294, 0x0187, 0x02B8, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_FOREIGN, + 2, + 0, + TRUE, + aQMgr_QUEST_ITEM_RANDOM, + EMPTY_NO, + 40, 0, 0, 10, 10, 40, 0, 0, + 1000, + 0x0199, 0x024C, 0x01AB, 0x025E, 0x01BD, 0x0294, 0x01CF, 0x02CA, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_LAST_REMOVE, + 2, + 0, + TRUE, + aQMgr_QUEST_ITEM_RANDOM, + EMPTY_NO, + 20, 0, 0, 20, 20, 40, 0, 0, + 1000, + 0x0205, 0x024C, 0x0217, 0x025E, 0x10BF, 0x0294, 0x023A, 0x02CA, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_RANDOM, + 2, + 0, + TRUE, + aQMgr_QUEST_ITEM_RANDOM, + EMPTY_NO, + 40, 0, 40, 10, 10, 0, 0, 0, + 0, + 0x0A74, 0x024C, 0x0A86, 0x025E, 0x0A98, 0x0294, 0x0AAA, 0x02B8, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, +}; + +static aQMgr_set_data_c l_set_errand_data[] = { + { + aQMgr_QUEST_TARGET_RANDOM_EXCLUDED, + 2, + 4, + FALSE, + aQMgr_QUEST_ITEM_RANDOM, + EMPTY_NO, + 50, 0, 0, 0, 0, 0, 0, 0, + 500, + 0x038C, 0x024C, 0x03D4, 0x025E, 0x03F8, 0x0294, 0x2B73, 0x02CA, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_RANDOM_EXCLUDED, + 2, + 1, + FALSE, + aQMgr_QUEST_ITEM_CURRENT_ITEM, + EMPTY_NO, + 50, 0, 0, 0, 0, 0, 0, 0, + 500, + 0x03B0, 0x024C, 0x03E6, 0x025E, 0x03F8, 0x0294, 0x041C, 0x02CA, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_ORIGINAL_TARGET, + 2, + 0, + TRUE, + aQMgr_QUEST_ITEM_CURRENT_ITEM, + EMPTY_NO, + 50, 0, 0, 0, 0, 0, 0, 0, + 500, + 0x039E, 0x024C, 0x03C2, 0x025E, 0x03F8, 0x0294, 0x040A, 0x0452, 0x17B8, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_CLIENT, + 0, + 0, + FALSE, + aQMgr_QUEST_ITEM_CURRENT_ITEM, + EMPTY_NO, + 0, 0, 0, 0, 0, 0, 0, 100, + 0, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_CLIENT, + 0, + 0, + FALSE, + aQMgr_QUEST_ITEM_CURRENT_ITEM, + EMPTY_NO, + 0, 0, 0, 0, 0, 0, 0, 100, + 0, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_CLIENT, + 0, + 0, + FALSE, + aQMgr_QUEST_ITEM_CURRENT_ITEM, + EMPTY_NO, + 0, 0, 0, 0, 0, 0, 0, 100, + 0, + 0x0000, 0x0000, 0x0000, 0x0000, 0x08EF, 0x08F0, 0x0000, 0x0000, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_CLIENT, + 0, + 0, + FALSE, + aQMgr_QUEST_ITEM_CURRENT_ITEM, + EMPTY_NO, + 0, 0, 0, 0, 0, 0, 0, 100, + 0, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_CLIENT, + 0, + 0, + FALSE, + aQMgr_QUEST_ITEM_CURRENT_ITEM, + EMPTY_NO, + 0, 0, 0, 0, 0, 0, 0, 100, + 0, + 0x0000, 0x0000, 0x0000, 0x0000, 0x08FB, 0x08FC, 0x0000, 0x0000, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_CLIENT, + 0, + 0, + FALSE, + aQMgr_QUEST_ITEM_CURRENT_ITEM, + EMPTY_NO, + 0, 0, 0, 0, 0, 0, 0, 100, + 0, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0907, 0x0908, 0x0000, 0x0000, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_CLIENT, + 0, + 0, + FALSE, + aQMgr_QUEST_ITEM_CURRENT_ITEM, + EMPTY_NO, + 0, 0, 0, 0, 0, 0, 0, 100, + 0, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_CLIENT, + 0, + 0, + FALSE, + aQMgr_QUEST_ITEM_CURRENT_ITEM, + EMPTY_NO, + 0, 0, 0, 0, 0, 0, 0, 100, + 0, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_CLIENT, + 0, + 0, + FALSE, + aQMgr_QUEST_ITEM_CURRENT_ITEM, + EMPTY_NO, + 0, 0, 0, 0, 0, 0, 0, 100, + 0, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0907, 0x0908, 0x0000, 0x0000, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_CLIENT, + 0, + 0, + FALSE, + aQMgr_QUEST_ITEM_CURRENT_ITEM, + EMPTY_NO, + 0, 0, 0, 0, 0, 0, 0, 100, + 0, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_CLIENT, + 0, + 0, + FALSE, + aQMgr_QUEST_ITEM_CURRENT_ITEM, + EMPTY_NO, + 0, 0, 0, 0, 0, 0, 0, 100, + 0, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_CLIENT, + 0, + 0, + FALSE, + aQMgr_QUEST_ITEM_CURRENT_ITEM, + EMPTY_NO, + 0, 0, 0, 0, 0, 0, 0, 100, + 0, + 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, +}; + +static aQMgr_set_data_c l_set_contest_data[] = { + { + aQMgr_QUEST_TARGET_CLIENT, + 1, + 1, + FALSE, + aQMgr_QUEST_ITEM_FRUIT, + EMPTY_NO, + 0, 0, 0, 30, 30, 40, 0, 0, + 500, + 0x01E1, 0x0000, 0x01E1, 0x025E, 0x01F3, 0x117C, 0x0000, 0x02CA, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_CLIENT, + 1, + 2, + FALSE, + aQMgr_QUEST_ITEM_NONE, + EMPTY_NO, + 40, 0, 0, 30, 30, 0, 0, 0, + 0, + 0x0D79, 0x0000, 0x0D79, 0x025E, 0x0D91, 0x0DD9, 0x0000, 0x02CA, 0x0440, 0x0DFD, 0x0DD9, 0x0DEB, 0x0E0F + }, + { + aQMgr_QUEST_TARGET_CLIENT, + 1, + 1, + FALSE, + aQMgr_QUEST_ITEM_NONE, + EMPTY_NO, + 60, 0, 0, 20, 20, 0, 0, 0, + 0, + 0x0E33, 0x0000, 0x0E33, 0x025E, 0x0E45, 0x0E8D, 0x0000, 0x02CA, 0x0440, 0x0EB1, 0x0E8D, 0x0E9F, 0x0EC3 + }, + { + aQMgr_QUEST_TARGET_CLIENT, + 3, + 1, + FALSE, + aQMgr_QUEST_ITEM_NONE, + EMPTY_NO, + 60, 0, 0, 20, 20, 0, 0, 0, + 0, + 0x0FB5, 0x0000, 0x0FB5, 0x025E, 0x0FC7, 0x100F, 0x0000, 0x02CA, 0x0440, 0x1033, 0x100F, 0x1021, 0x1045 + }, + { + aQMgr_QUEST_TARGET_CLIENT, + 3, + 1, + FALSE, + aQMgr_QUEST_ITEM_NONE, + EMPTY_NO, + 80, 0, 0, 10, 10, 0, 0, 0, + 0, + 0x158C, 0x0000, 0x158C, 0x025E, 0x159E, 0x15B0, 0x0000, 0x02CA, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_CLIENT, + 3, + 1, + FALSE, + aQMgr_QUEST_ITEM_NONE, + EMPTY_NO, + 80, 0, 0, 10, 10, 0, 0, 0, + 0, + 0x160A, 0x0000, 0x160A, 0x025E, 0x161C, 0x162E, 0x0000, 0x02CA, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, + { + aQMgr_QUEST_TARGET_CLIENT, + 2, + 2, + FALSE, + aQMgr_QUEST_ITEM_NONE, + EMPTY_NO, + 80, 0, 0, 10, 10, 0, 0, 0, + 0, + 0x1AE1, 0x0000, 0x1AE1, 0x025E, 0x1B17, 0x0294, 0x0000, 0x02CA, 0x0440, 0x035E, 0x034C, 0x0370, 0x033A + }, +}; +//clang-format on + +static aQMgr_set_data_c* l_set_data[mQst_QUEST_TYPE_NUM] = { + l_set_delivery_data, + l_set_errand_data, + l_set_contest_data +}; + +static void aQMgr_move_own_errand_cloth(QUEST_MANAGER_ACTOR* manager, aQMgr_regist_c* regist); +static void aQMgr_move_own_errand_seed(QUEST_MANAGER_ACTOR* manager, aQMgr_regist_c* regist); +static void aQMgr_move_own_errand_letter(QUEST_MANAGER_ACTOR* manager, aQMgr_regist_c* regist); +static void aQMgr_move_own_errand_hello(QUEST_MANAGER_ACTOR* manager, aQMgr_regist_c* regist); + +static void aQMgr_contest_check_limit(QUEST_MANAGER_ACTOR* manager, aQMgr_regist_c* regist) ; +static int aQMgr_actor_check_fin_fruit(mQst_base_c* quest_info, Animal_c* animal); +static int aQMgr_actor_check_fin_soccer(mQst_base_c* quest_info, Animal_c* animal); +static void aQMgr_actor_contest_snowman_clear(QUEST_MANAGER_ACTOR* manager, aQMgr_regist_c* regist); +static int aQMgr_actor_check_fin_snowman(mQst_base_c* quest_info, Animal_c* animal); +static void aQMgr_actor_contest_flower_clear(QUEST_MANAGER_ACTOR* manager, aQMgr_regist_c* regist); +static int aQMgr_actor_check_flower(mQst_contest_c* contest, int bx, int bz); +static int aQMgr_actor_check_fin_flower(mQst_base_c* quest_info, Animal_c* animal); +static int aQMgr_actor_check_fin_fish(mQst_base_c* quest_info, Animal_c* animal); +static void aQMgr_actor_contest_insect_clear(QUEST_MANAGER_ACTOR* manager, aQMgr_regist_c* regist); +static int aQMgr_actor_check_fin_insect(mQst_base_c* quest_info, Animal_c* animal); +static int aQMgr_actor_check_fin_contest_letter(mQst_base_c* quest_info, Animal_c* animal); + +static void aQMgr_save_contest_flower(aQMgr_regist_c* regist); +static void aQMgr_save_contest(aQMgr_regist_c* regist); + +static aQMgr_CHECK_LIMIT_PROC l_errand_proc[mQst_ERRAND_NUM] = { + (aQMgr_CHECK_LIMIT_PROC)&none_proc1, + (aQMgr_CHECK_LIMIT_PROC)&none_proc1, + (aQMgr_CHECK_LIMIT_PROC)&none_proc1, + &aQMgr_move_own_errand_cloth, + &aQMgr_move_own_errand_seed, + (aQMgr_CHECK_LIMIT_PROC)&none_proc1, + &aQMgr_move_own_errand_letter, + (aQMgr_CHECK_LIMIT_PROC)&none_proc1, + (aQMgr_CHECK_LIMIT_PROC)&none_proc1, + (aQMgr_CHECK_LIMIT_PROC)&none_proc1, + &aQMgr_move_own_errand_letter, + (aQMgr_CHECK_LIMIT_PROC)&none_proc1, + &aQMgr_move_own_errand_hello, + (aQMgr_CHECK_LIMIT_PROC)&none_proc1, + (aQMgr_CHECK_LIMIT_PROC)&none_proc1 +}; + +static aQMgr_CHECK_LIMIT_PROC l_contest_proc[mQst_CONTEST_KIND_NUM] = { + &aQMgr_contest_check_limit, + &aQMgr_contest_check_limit, + &aQMgr_actor_contest_snowman_clear, + &aQMgr_actor_contest_flower_clear, + &aQMgr_contest_check_limit, + &aQMgr_actor_contest_insect_clear, + &aQMgr_contest_check_limit +}; + +static aQMgr_CHECK_FINISH_PROC l_contest_check[mQst_CONTEST_KIND_NUM] = { + &aQMgr_actor_check_fin_fruit, + &aQMgr_actor_check_fin_soccer, + &aQMgr_actor_check_fin_snowman, + &aQMgr_actor_check_fin_flower, + &aQMgr_actor_check_fin_fish, + &aQMgr_actor_check_fin_insect, + &aQMgr_actor_check_fin_contest_letter +}; + +static int l_proc_max[mQst_QUEST_TYPE_NUM] = { mQst_DELIVERY_KIND_NUM, mQst_ERRAND_NUM, mQst_CONTEST_KIND_NUM }; + +static void aQMgr_actor_set_memory(ACTOR* client) { + Animal_c* animal; + + if (client != NULL && client->part == ACTOR_PART_NPC) { + animal = ((NPC_ACTOR*)client)->npc_info.animal; + } + else { + animal = NULL; + } + + if (animal != NULL) { + Anmmem_c* memory = animal->memories; + int memory_idx = mNpc_GetAnimalMemoryIdx(&Common_Get(now_private)->player_ID, memory, ANIMAL_MEMORY_NUM); + + if (memory_idx != -1) { + memory += memory_idx; + l_quest_memory_p = memory; + } + else { + l_quest_memory_p = NULL; + } + } +} + +static void aQMgr_actor_set_hello_free_str(Animal_c* animal) { + int mem_idx = mNpc_GetAnimalMemoryIdx(&Common_Get(now_private)->player_ID, animal->memories, ANIMAL_MEMORY_NUM); + + if (mem_idx != -1 && mNpc_CheckIslandAnimal(animal) == FALSE) { + mLd_SetFreeStrLandMuraName(animal->memories[mem_idx].memuni.land.name, mMsg_FREE_STR6); + } + + if (mNpc_CheckIslandAnimal(animal) == FALSE) { + mLd_SetFreeStrLandMuraName(animal->anmuni.previous_land_name, mMsg_FREE_STR10); + } + + mLd_SetFreeStrLandMuraName(animal->id.land_name, mMsg_FREE_STR11); +} + +static int aQMgr_take_hello_msg_no(QUEST_MANAGER_ACTOR* manager) { + ACTOR* client = *manager->client; + Animal_c* animal = ((NPC_ACTOR*)client)->npc_info.animal; + PersonalID_c* pid = &Common_Get(now_private)->player_ID; + + if (animal != NULL) { + if (mNpc_GetAnimalMemoryIdx(pid, animal->memories, ANIMAL_MEMORY_NUM) == -1) { + l_quest_manager_hello = TRUE; + } + else { + l_quest_manager_hello = FALSE; + } + } + else { + l_quest_manager_hello = TRUE; + } + + return aQMgr_get_hello_msg_no(client, manager->get_time_kind_proc, manager->give_item); +} + +static void aQMgr_set_talk_info(ACTOR* client) { + QUEST_MANAGER_ACTOR* manager = l_quest_actor_p; + Animal_c* animal = ((NPC_ACTOR*)client)->npc_info.animal; + int msg_no; + + l_client_p = client; + msg_no = aQMgr_take_hello_msg_no(manager); + mDemo_Set_msg_num(msg_no); + + if (animal->mood == mNpc_FEEL_SLEEPY) { + animal->mood = mNpc_FEEL_NORMAL; + ((NPC_ACTOR*)client)->condition_info.feel_tim = 0; + } + + ((NPC_ACTOR*)client)->talk_info.feel = animal->mood; + aQMgr_actor_set_hello_free_str(animal); + if (animal != NULL) { + manager->cloth = animal->cloth; + } + + if (ZURUMODE2_ENABLED()) { + mNpc_SetTalkAnimalIdx_fdebug(&animal->id); + } +} + +static int aQMgr_actor_talk_request(ACTOR* client) { + int res = FALSE; + + if (client != NULL && l_quest_manager_mode == aQMgr_MODE_NORMAL) { + if (mDemo_Check(mDemo_TYPE_TALK, client) == FALSE) { + mDemo_Request(mDemo_TYPE_TALK, client, &aQMgr_set_talk_info); + } + else if (mDemo_Check_ListenAble() == FALSE) { + res = TRUE; + } + } + + return res; +} + +static int aQMgr_actor_talk_start(ACTOR* client) { + int res = FALSE; + + if (client != NULL && l_quest_manager_mode == aQMgr_MODE_NORMAL && mDemo_Check(mDemo_TYPE_TALK, client) == TRUE && + mDemo_Check_ListenAble() == FALSE && mDemo_Check_SpeakerAble() == TRUE && client == l_client_p) { + mDemo_Set_ListenAble(); + + if (client->npc_id == SP_NPC_EV_KAMAKURA_0) { + if (l_quest_manager_hello == TRUE) { + l_quest_manager_mode = aQMgr_MODE_TALK_START_KAMAKURA_HELLO; + } + else { + l_quest_manager_mode = aQMgr_MODE_TALK_START_KAMAKURA; + } + } + else if (client->npc_id == SP_NPC_EV_SUMMERCAMP_0) { + if (l_quest_manager_hello == TRUE) { + l_quest_manager_mode = aQMgr_MODE_TALK_START_SUMMERCAMP_HELLO; + Common_Set(npc_is_summercamper, TRUE); + } + else { + l_quest_manager_mode = aQMgr_MODE_TALK_START_SUMMERCAMP; + } + } + else { + l_quest_manager_mode = aQMgr_MODE_SELECT_TALK; + } + + aQMgr_actor_set_memory(client); + mMld_ActorMakeMelody(client); + res = TRUE; + } + + return res; +} + +static int aQMgr_actor_talk_check(ACTOR* client) { + int res = FALSE; + + if (client != NULL && l_client_p == client && l_quest_manager_mode == aQMgr_MODE_TALK_FIN && mDemo_Get_talk_actor() != client) { + l_client_p = NULL; + l_quest_manager_mode = aQMgr_MODE_NORMAL; + res = TRUE; + } + + return res; +} + +static int aQMgr_get_time_kind(int hour) { + int res = aQMgr_TIME_MORNING; + + if (hour >= 12 && hour < 17) { + res = aQMgr_TIME_DAY; + } + else if (hour >= 17 && hour < 24) { + res = aQMgr_TIME_EVENING; + } + else if (hour >= 0 && hour < 5) { + res = aQMgr_TIME_NIGHT; + } + + return res; +} + +static void aQMgr_clear_talk_init_ovl(QUEST_MANAGER_ACTOR* manager) { + manager->talk_init_proc = NULL; +} + +static int aQMgr_talk_common_talk_init_ovl(QUEST_MANAGER_ACTOR* manager) { + static aQMgr_TALK_INIT_PROC talk_init_table[aQMgr_TALK_KIND_NUM] = { + &aQMgr_actor_move_talk_init, + &aQMgr_talk_first_job_init, + &aQMgr_talk_normal_init, + &aQMgr_talk_island_init + }; + + int type = manager->talk_type; + + if (type < 0 || type >= aQMgr_TALK_KIND_NUM) { + type = aQMgr_TALK_KIND_QUEST; + } + + manager->talk_init_proc = talk_init_table[type]; + return TRUE; +} + +static void aQMgr_actor_change_talk_init_ovl(QUEST_MANAGER_ACTOR* manager) { + if (manager->talk_change_type < aQMgr_TALK_KIND_NUM) { + manager->talk_type = manager->talk_change_type; + if ((*manager->talk_common_proc)(manager, aQMgr_TALK_COMMON_TALK_INIT_OVL) == TRUE) { + manager->talk_change_type = aQMgr_TALK_KIND_NUM; + manager->sub_talk_state = aQMgr_TALK_SUB_STATE_NO_WAIT; + } + } +} + +static void aQMgr_actor_clear_regist(aQMgr_regist_c* regist, int count) { + for (count; count != 0; count--) { + regist->check_limit_proc = (aQMgr_CHECK_LIMIT_PROC)&none_proc1; + regist->check_finish_proc = NULL; + regist->quest_info = NULL; + regist->pid = NULL; + mNpc_ClearAnimalPersonalID(®ist->from_id); + mNpc_ClearAnimalPersonalID(®ist->to_id); + regist->animal_idx = -1; + regist++; + } +} + +static int aQMgr_actor_check_fin_item(mQst_base_c* quest_info, Animal_c* animal) { + mQst_contest_c* contest = (mQst_contest_c*)quest_info; + int res = FALSE; + + if (mPr_GetPossessionItemIdx(Common_Get(now_private), contest->requested_item) != -1) { + res = TRUE; + } + + return res; +} + +static int aQMgr_actor_free_regist(QUEST_MANAGER_ACTOR* manager, int start_idx) { + aQMgr_regist_c* regist = &manager->regist[start_idx]; + int idx = -1; + + if (manager->regist_use_no < aQMgr_REGIST_NUM) { + int i; + + for (i = start_idx; i < aQMgr_REGIST_NUM; i++) { + if (regist->quest_info == NULL) { + idx = i; + break; + } + + regist++; + } + } + + return idx; +} + +static void aQMgr_actor_regist_quest(QUEST_MANAGER_ACTOR* manager, int* idx, aQMgr_quest_c* quest, int animal_idx) { + mQst_base_c* quest_info = &quest->base; + u32 type = quest_info->quest_type; + + if (type < mQst_QUEST_TYPE_NUM) { + *idx = aQMgr_actor_free_regist(manager, *idx); + + if (*idx != -1) { + int kind = quest_info->quest_kind; + int max_kind = l_proc_max[type]; + + if (kind >= 0 && kind < max_kind) { + aQMgr_regist_c* regist = &manager->regist[*idx]; + AnmPersonalID_c* from_id; + AnmPersonalID_c* to_id; + Animal_c* animal; + + if (type == mQst_QUEST_TYPE_CONTEST) { + animal = &Save_Get(animals[animal_idx]); + regist->pid = &quest->contest.player_id; + regist->check_limit_proc = l_contest_proc[kind]; + regist->check_finish_proc = l_contest_check[kind]; + from_id = &animal->id; + to_id = &animal->id; + } + else { + if (type == mQst_QUEST_TYPE_ERRAND) { + regist->check_limit_proc = l_errand_proc[kind]; + } + + from_id = &quest->delivery.sender; + to_id = &quest->delivery.recipient; + regist->pid = &Common_Get(now_private)->player_ID; + } + + mNpc_CopyAnimalPersonalID(®ist->from_id, from_id); + mNpc_CopyAnimalPersonalID(®ist->to_id, to_id); + + regist->quest_info = quest_info; + + switch (type) { + case mQst_QUEST_TYPE_DELIVERY: + regist->item = Common_Get(now_private)->inventory.pockets[animal_idx]; + break; + case mQst_QUEST_TYPE_ERRAND: + regist->item = quest->errand.item; + break; + case mQst_QUEST_TYPE_CONTEST: + regist->item = quest->contest.requested_item; + break; + } + + regist->animal_idx = animal_idx; + manager->regist_use_no++; + } + } + } +} + +static void aQMgr_talk_clear_talk_order(aQMgr_order_c* demo_order) { + demo_order->type = mDemo_TYPE_NONE; + demo_order->value = 0; +} + +static int aQMgr_talk_common_clear_talk_info(QUEST_MANAGER_ACTOR* manager) { + aQMgr_target_c* target_p = &manager->target; + aQMgr_choice_c* choice_p = &manager->choice; + int i; + + *manager->memory = NULL; + manager->talk_step = 0; + manager->sub_talk_state = aQMgr_TALK_SUB_STATE_WAIT; + manager->handover_item = EMPTY_NO; + manager->msg_no = 0; + manager->category_msg_no_start = 0; + manager->msg_category = aQMgr_MSG_KIND_NONE; + + mQst_ClearQuestInfo(&target_p->quest_info); + target_p->from_id = NULL; + target_p->to_id = NULL; + target_p->quest_inv_item_idx = -1; + target_p->quest_item = EMPTY_NO; + target_p->reward_kind = -1; + target_p->reward_item = EMPTY_NO; + target_p->pay = 0; + target_p->set_data_p = NULL; + target_p->free_data_idx = -1; + target_p->free_data_p = NULL; + target_p->errand_type = mQst_ERRAND_TYPE_NONE; + bzero(&target_p->work, sizeof(target_p->work)); + + choice_p->choice_num = 0; + choice_p->talk_action = -1; + + for (i = 0; i < mChoice_CHOICE_NUM; i++) { + choice_p->choice_ids[i] = -1; + } + + manager->regist_idx = -1; + manager->talk_type = aQMgr_TALK_KIND_QUEST; + manager->talk_change_type = aQMgr_TALK_KIND_NUM; + aQMgr_talk_clear_talk_order(&manager->demo_order); + return TRUE; +} + +static int aQMgr_talk_common_get_item_idx(QUEST_MANAGER_ACTOR* manager) { + int regist_idx = manager->regist_idx; + aQMgr_regist_c* regist = &manager->regist[regist_idx]; + mQst_base_c* quest_info = regist->quest_info; + int idx = -1; + + if (quest_info != NULL) { + int type = quest_info->quest_type; + int kind = quest_info->quest_kind; + + if (type == mQst_QUEST_TYPE_DELIVERY) { + idx = regist->animal_idx; // reused here I guess + } + else if (type == mQst_QUEST_TYPE_ERRAND) { + idx = ((mQst_errand_c*)quest_info)->pockets_idx; + } + else if (type == mQst_QUEST_TYPE_CONTEST) { + mActor_name_t item = ((mQst_contest_c*)quest_info)->requested_item; + Private_c* priv = Common_Get(now_private); + + switch (kind) { + case mQst_CONTEST_KIND_FISH: + idx = mPr_GetPossessionItemIdxItem1Category(priv, ITEM1_CAT_FISH); + break; + case mQst_CONTEST_KIND_INSECT: + idx = mPr_GetPossessionItemIdxItem1Category(priv, ITEM1_CAT_INSECT); + break; + default: + idx = mPr_GetPossessionItemIdx(priv, item); + break; + } + } + } + + return idx; +} + +static int aQMgr_talk_common_set_choice_str(QUEST_MANAGER_ACTOR* manager) { + u8 str[mChoice_CHOICE_NUM][mChoice_CHOICE_STRING_LEN]; + u8* str_p_tbl[mChoice_CHOICE_NUM]; + aQMgr_choice_c* choice_p = &manager->choice; + int i; + + for (i = 0; i < mChoice_CHOICE_NUM; i++) { + str_p_tbl[i] = NULL; + } + + for (i = 0; i < choice_p->choice_num; i++) { + if (i >= mChoice_CHOICE_NUM) { + break; + } + + mChoice_Load_ChoseStringFromRom(mChoice_Get_base_window_p(), str[i], choice_p->choice_ids[i], NULL); + str_p_tbl[i] = str[i]; + } + + //clang-format off + mChoice_Set_choice_data( + mChoice_Get_base_window_p(), + str_p_tbl[0], mChoice_CHOICE_STRING_LEN, + str_p_tbl[1], mChoice_CHOICE_STRING_LEN, + str_p_tbl[2], mChoice_CHOICE_STRING_LEN, + str_p_tbl[3], mChoice_CHOICE_STRING_LEN, + NULL, mChoice_CHOICE_STRING_LEN, + NULL, mChoice_CHOICE_STRING_LEN + ); + //clang-format on + + return TRUE; +} + +static int aQMgr_talk_common_get_set_data_p(QUEST_MANAGER_ACTOR* manager) { + aQMgr_target_c* target = &manager->target; + mQst_base_c* quest_info = &target->quest_info; + aQMgr_set_data_c* set_data_p = NULL; + + if (quest_info != NULL) { + int type = quest_info->quest_type; + int kind = quest_info->quest_kind; + + set_data_p = &l_set_data[type][kind]; + } + + target->set_data_p = set_data_p; + return TRUE; +} + +static void aQMgr_actor_init_quest(QUEST_MANAGER_ACTOR* manager) { + mQst_delivery_c* delivery = Common_Get(now_private)->deliveries; + mQst_errand_c* errand = Common_Get(now_private)->errands; + Animal_c* animal = Save_Get(animals); + int free_idx = 0; + int i; + + for (i = 0; i < mPr_DELIVERY_QUEST_NUM; i++) { + if (free_idx == -1) { + break; + } + + aQMgr_actor_regist_quest(manager, &free_idx, (aQMgr_quest_c*)delivery, i); + delivery++; + } + + for (i = 0; i < mPr_ERRAND_QUEST_NUM; i++) { + if (free_idx == -1) { + break; + } + + aQMgr_actor_regist_quest(manager, &free_idx, (aQMgr_quest_c*)errand, i); + errand++; + } + + for (i = 0; i < ANIMAL_NUM_MAX; i++) { + if (free_idx == -1) { + break; + } + + aQMgr_actor_regist_quest(manager, &free_idx, (aQMgr_quest_c*)&animal->contest_quest, i); + animal++; + } +} + +static void aQMgr_actor_regist_quest_move(QUEST_MANAGER_ACTOR* manager) { + aQMgr_regist_c* regist = manager->regist; + int i; + + for (i = aQMgr_REGIST_NUM; i != 0; i--) { + if (regist->quest_info != NULL) { + if (regist->check_limit_proc != NULL) { + (*regist->check_limit_proc)(manager, regist); + } + } + + regist++; + } +} + +static void aQMgr_actor_move_wait(QUEST_MANAGER_ACTOR* manager) { + aQMgr_actor_init_quest(manager); + aQMgr_actor_regist_quest_move(manager); + (*manager->clear_regist_proc)(manager->regist, aQMgr_REGIST_NUM); + manager->regist_idx = -1; + manager->regist_use_no = 0; + mNpc_TalkInfoMove(); +} + +static void aQMgr_actor_move_talk_fin(QUEST_MANAGER_ACTOR* manager) { + aQMgr_actor_init_quest(manager); + aQMgr_actor_regist_quest_move(manager); + (*manager->clear_regist_proc)(manager->regist, aQMgr_REGIST_NUM); + manager->regist_idx = -1; + manager->regist_use_no = 0; +} + +static int aQMgr_actor_get_my_msg(int base_msg, int looks) { + int msg_no = base_msg + looks * 3; + msg_no += mQst_GetRandom(3); + + return msg_no; +} + +static int aQMgr_actor_decide_quest_message_no(aQMgr_target_c* target, int msg_type) { + aQMgr_set_data_c* set_data_p = target->set_data_p; + + return set_data_p->msg_start[msg_type]; +} + +static int aQMgr_talk_common_set_msg_no(QUEST_MANAGER_ACTOR* manager) { + int base_msg; + int msg_no; + + if (manager->msg_category != aQMgr_MSG_KIND_NONE) { + manager->category_msg_no_start = aQMgr_actor_decide_quest_message_no(&manager->target, manager->msg_category); + } + + base_msg = manager->category_msg_no_start; + if (base_msg != 15 && base_msg != 0) { + int looks = mNpc_GetNpcLooks(*manager->client); + + msg_no = aQMgr_actor_get_my_msg(base_msg, looks); + } + else { + msg_no = base_msg; + } + + manager->msg_no = msg_no; + manager->msg_category = aQMgr_MSG_KIND_NONE; + manager->category_msg_no_start = 0; + return TRUE; +} + +static int aQMgr_talk_common_regist_set_target(QUEST_MANAGER_ACTOR* manager) { + aQMgr_target_c* target = &manager->target; + aQMgr_regist_c* regist = &manager->regist[manager->regist_idx]; + mQst_base_c* quest_info = &target->quest_info; + mQst_base_c* src_info = regist->quest_info; + + quest_info->quest_type = src_info->quest_type; + quest_info->quest_kind = src_info->quest_kind; + + (*manager->talk_common_proc)(manager, aQMgr_TALK_COMMON_GET_SET_DATA); + + target->quest_item = regist->item; + target->to_id = ®ist->to_id; + target->from_id = ®ist->from_id; + return TRUE; +} + +static int aQMgr_talk_common_change_talk_normal(QUEST_MANAGER_ACTOR* manager) { + manager->talk_state = aQMgr_TALK_STATE_INIT; + manager->talk_change_type = aQMgr_TALK_KIND_NORMAL; + manager->talk_step = 0; + manager->choice.talk_action = -1; + mMsg_Set_LockContinue(mMsg_Get_base_window_p()); + return TRUE; +} + +static int aQMgr_talk_common_change_talk_island(QUEST_MANAGER_ACTOR* manager) { + manager->talk_state = aQMgr_TALK_STATE_INIT; + manager->talk_change_type = aQMgr_TALK_KIND_ISLAND; + manager->talk_step = 0; + manager->choice.talk_action = -1; + mMsg_Set_LockContinue(mMsg_Get_base_window_p()); + return TRUE; +} + +static int aQMgr_set_npc_putaway() { + int res = FALSE; + + if (Common_Get(clip).handOverItem_clip->request_mode == aHOI_REQUEST_TRANS_WAIT) { + mDemo_Set_OrderValue(mDemo_ORDER_NPC0, 3, 3); + res = TRUE; + } + + return res; +} + +static int aQMgr_talk_common_set_npc_takeout(mActor_name_t item) { + mDemo_Set_OrderValue(mDemo_ORDER_NPC0, 3, 2); + mDemo_Set_OrderValue(mDemo_ORDER_NPC1, 0, item); + mDemo_Set_OrderValue(mDemo_ORDER_NPC1, 1, 7); + mDemo_Set_OrderValue(mDemo_ORDER_NPC1, 2, 0); + return TRUE; +} + +static int aQMgr_talk_common_set_npc_takeout_item(QUEST_MANAGER_ACTOR* manager) { + aQMgr_talk_common_set_npc_takeout(manager->target.quest_item); + return TRUE; +} + +static int aQMgr_talk_common_set_npc_takeout_reward(QUEST_MANAGER_ACTOR* manager) { + aQMgr_talk_common_set_npc_takeout(manager->target.reward_item); + return TRUE; +} + +static int aQMgr_check_npc_hand_item() { + int res = FALSE; + + if (l_aQMgr_hand_start == FALSE && Common_Get(clip).handOverItem_clip->master_actor != NULL) { + l_aQMgr_hand_start = TRUE; + } + + if (l_aQMgr_hand_start && Common_Get(clip).handOverItem_clip->master_actor == NULL) { + mMsg_Unset_LockContinue(mMsg_Get_base_window_p()); + l_aQMgr_hand_start = FALSE; + res = TRUE; + } + + return res; +} + +typedef int (*aQMgr_COMMON_PROC)(QUEST_MANAGER_ACTOR*); + +static int aQMgr_talk_common_proc(QUEST_MANAGER_ACTOR* manager, int proc) { + static aQMgr_COMMON_PROC common_proc[aQMgr_TALK_COMMON_NUM] = { + &aQMgr_talk_common_regist_set_target, + &aQMgr_talk_common_get_item_idx, + &aQMgr_talk_common_set_msg_no, + &aQMgr_talk_common_clear_talk_info, + &aQMgr_talk_common_set_choice_str, + &aQMgr_talk_common_get_set_data_p, + &aQMgr_talk_common_set_npc_takeout_item, + &aQMgr_talk_common_set_npc_takeout_reward, + &aQMgr_talk_common_talk_init_ovl, + &aQMgr_talk_common_change_talk_normal, + &aQMgr_talk_common_change_talk_island + }; + + return (*common_proc[proc])(manager); +} + +#include "../src/ac_quest_errand.c_inc" +#include "../src/ac_quest_contest.c_inc" + +static void aQMgr_clear_talk_wait_info(aQMgr_talk_wait_info_c* wait_info) { + bzero(wait_info, sizeof(aQMgr_talk_wait_info_c)); +} + +static int aQMgr_actor_move_talk_sub_no_wait(QUEST_MANAGER_ACTOR* manager) { + return TRUE; +} + +static int aQMgr_actor_move_talk_sub_wait(QUEST_MANAGER_ACTOR* manager) { + int res = FALSE; + + if (mMsg_Check_MainNormalContinue(mMsg_Get_base_window_p()) == TRUE) { + if (manager->wait_info.flags[2] == FALSE) { + manager->wait_info.flags[2] = TRUE; + res = TRUE; + } + } + else { + manager->wait_info.flags[2] = FALSE; + } + + return res; +} + +static int aQMgr_actor_move_talk_sub_msg_disappear_wait(QUEST_MANAGER_ACTOR* manager) { + int res = FALSE; + + if (mMsg_Check_main_wait(mMsg_Get_base_window_p()) == TRUE) { + res = TRUE; + } + + return res; +} + +static int aQMgr_actor_move_talk_sub_msg_appear_wait(QUEST_MANAGER_ACTOR* manager) { + int res = FALSE; + + if (mMsg_Check_not_series_main_wait(mMsg_Get_base_window_p()) == TRUE) { + res = TRUE; + } + + return res; +} + +static int aQMgr_actor_move_talk_sub_check_button(QUEST_MANAGER_ACTOR* manager) { + int res = FALSE; + + if (mMsg_Check_MainNormal(mMsg_Get_base_window_p()) == FALSE) { + res = TRUE; + } + + return res; +} + +static int aQMgr_actor_move_talk_sub_hand_item_wait(QUEST_MANAGER_ACTOR* manager) { + Submenu* submenu = manager->submenu; + int res = FALSE; + + if (submenu->open_flag == FALSE) { + manager->handover_item = submenu->item_p->item; + res = TRUE; + + if (manager->mail_memory != NULL) { + mNpc_Mail2AnimalMail(&manager->mail_memory->letter, &manager->mail); + manager->mail_memory->letter_info.exists = TRUE; + } + } + + return res; +} + +static int aQMgr_actor_move_talk_sub_npc_hand_wait(QUEST_MANAGER_ACTOR* manager) { + return aQMgr_set_npc_putaway(); +} + +static int aQMgr_actor_move_talk_sub_item_wait(QUEST_MANAGER_ACTOR* manager) { + return aQMgr_check_npc_hand_item(); +} + +static int aQMgr_actor_move_talk_sub_demo_order_wait(QUEST_MANAGER_ACTOR* manager) { + aQMgr_order_c* order = &manager->demo_order; + int res = FALSE; + int i; + + for (i = 0; i < mDemo_ORDER_VALUE_MAX; i++) { + u16 value = mDemo_Get_OrderValue(mDemo_ORDER_QUEST, i); + + if (value != 0) { + order->type = i; + order->value = value; + mDemo_Set_OrderValue(mDemo_ORDER_QUEST, i, 0); + res = TRUE; + break; + } + } + + return res; +} + +static int aQMgr_actor_move_talk_sub_npc_hand_wait_msg_wait(QUEST_MANAGER_ACTOR* manager) { + int res = FALSE; + + if (manager->wait_info.flags[0] == FALSE) { + manager->wait_info.flags[0] = aQMgr_set_npc_putaway(); + mMsg_Set_LockContinue(mMsg_Get_base_window_p()); + } + + if (manager->wait_info.flags[1] == FALSE) { + if (mMsg_Check_MainNormalContinue(mMsg_Get_base_window_p()) == TRUE) { + manager->wait_info.flags[1] = TRUE; + } + } + + if (manager->wait_info.flags[0] == TRUE && manager->wait_info.flags[1] == TRUE) { + manager->wait_info.flags[0] = FALSE; + manager->wait_info.flags[1] = FALSE; + res = TRUE; + } + + return res; +} + +static int aQMgr_actor_move_talk_sub_item_wait_end(QUEST_MANAGER_ACTOR* manager) { + int res = FALSE; + + if (manager->wait_info.flags[0] == FALSE) { + manager->wait_info.flags[0] = aQMgr_set_npc_putaway(); + } + + if (manager->wait_info.flags[0] == TRUE) { + manager->wait_info.flags[1] = aQMgr_check_npc_hand_item(); + } + + if (manager->wait_info.flags[0] == TRUE && manager->wait_info.flags[1] == TRUE) { + manager->wait_info.flags[0] = FALSE; + manager->wait_info.flags[1] = FALSE; + res = TRUE; + } + + return res; +} + +static int aQMgr_actor_move_talk_sub_item_player_wait(QUEST_MANAGER_ACTOR* manager) { + int res = FALSE; + + if (manager->wait_info.flags[0] == FALSE) { + manager->wait_info.flags[0] = aQMgr_check_npc_hand_item(); + } + + mMsg_Set_LockContinue(mMsg_Get_base_window_p()); + if (manager->wait_info.flags[0] == TRUE && mPlib_check_player_actor_main_index_RecieveMove(gamePT) == FALSE) { + mMsg_Unset_LockContinue(mMsg_Get_base_window_p()); + manager->wait_info.flags[0] = FALSE; + res = TRUE; + } + + return res; +} + +static void aQMgr_select_talk(QUEST_MANAGER_ACTOR* manager) { + u32 event_id; + Animal_c* animal = ((NPC_ACTOR*)*manager->client)->npc_info.animal; + + event_id = mEv_SAVED_FJOPENQUEST_PLR0 + Common_Get(player_no); + aQMgr_actor_init_quest(manager); + aQMgr_clear_talk_wait_info(&manager->wait_info); + + if ((animal != NULL && mNpc_CheckIslandAnimal(animal) == TRUE) || (mLd_PlayerManKindCheck() == FALSE && mEv_CheckEvent(event_id) == TRUE)) { + manager->talk_type = aQMgr_TALK_KIND_QUEST; + } + else if (mEv_CheckFirstJob() == TRUE) { + manager->talk_type = aQMgr_TALK_KIND_FIRST_JOB; + } + else { + manager->talk_type = aQMgr_TALK_KIND_QUEST; + } + + *manager->mode = aQMgr_MODE_MOVE_TALK; + (*manager->talk_common_proc)(manager, aQMgr_TALK_COMMON_TALK_INIT_OVL); +} + +static int aQMgr_talk_init(QUEST_MANAGER_ACTOR* manager) { + int res = FALSE; + + if (manager->talk_init_proc != NULL) { + (*manager->talk_init_proc)(manager); + res = TRUE; + } + + return res; +} + +typedef int (*aQMgr_TALK_SUB_PROC)(QUEST_MANAGER_ACTOR*); + +static int aQMgr_talk_sub(QUEST_MANAGER_ACTOR* manager) { + int sub_mode = manager->sub_talk_state; + static aQMgr_TALK_SUB_PROC talk_sub_proc[] = { + &aQMgr_actor_move_talk_sub_wait, + &aQMgr_actor_move_talk_sub_msg_disappear_wait, + &aQMgr_actor_move_talk_sub_msg_appear_wait, + &aQMgr_actor_move_talk_sub_check_button, + &aQMgr_actor_move_talk_sub_hand_item_wait, + &aQMgr_actor_move_talk_sub_npc_hand_wait, + &aQMgr_actor_move_talk_sub_item_wait, + &aQMgr_actor_move_talk_sub_demo_order_wait, + &aQMgr_actor_move_talk_sub_no_wait, + &aQMgr_actor_move_talk_sub_npc_hand_wait_msg_wait, + &aQMgr_actor_move_talk_sub_item_wait_end, + &aQMgr_actor_move_talk_sub_item_player_wait + }; + + int res = (*talk_sub_proc[sub_mode])(manager); + + if (res == TRUE) { + manager->talk_state = aQMgr_TALK_STATE_INIT; + } + + return res; +} + +typedef int (*aQMgr_TALK_PROC)(QUEST_MANAGER_ACTOR*); + +static void aQMgr_move_talk(QUEST_MANAGER_ACTOR* manager) { + ACTOR* client = *manager->client; + + if (mDemo_Get_talk_actor() == client) { + static aQMgr_TALK_PROC talk_proc[aQMgr_TALK_STATE_NUM] = { + &aQMgr_talk_init, + &aQMgr_talk_sub + }; + int sub_res; + + aQMgr_actor_change_talk_init_ovl(manager); + sub_res = (*talk_proc[aQMgr_TALK_STATE_SUB])(manager); + mDemo_Set_OrderValue(mDemo_ORDER_QUEST, 0, 0); + + if (sub_res == TRUE) { + (*talk_proc[aQMgr_TALK_STATE_INIT])(manager); + } + + aQMgr_talk_clear_talk_order(&manager->demo_order); + } + + if (mDemo_CheckDemo() == FALSE || mDemo_Check(mDemo_TYPE_TALK, client) == FALSE) { + (*manager->talk_common_proc)(manager, aQMgr_TALK_COMMON_CLEAR_TALK_INFO); + *manager->mode = aQMgr_MODE_TALK_FIN; + aQMgr_clear_talk_init_ovl(manager); + + if (client != NULL && client->part == ACTOR_PART_NPC) { + Animal_c* animal = ((NPC_ACTOR*)client)->npc_info.animal; + + if (animal != NULL) { + Anmmem_c* memory; + int memory_idx = mNpc_GetAnimalMemoryIdx(&Common_Get(now_private)->player_ID, animal->memories, ANIMAL_MEMORY_NUM); + + if (memory_idx != -1) { + memory = animal->memories + memory_idx; + lbRTC_TimeCopy(&memory->last_speak_time, Common_GetPointer(time.rtc_time)); + + if (mNpc_CheckIslandAnimal(animal) == FALSE) { + mLd_CopyLandName(memory->memuni.land.name, Save_Get(land_info).name); + memory->memuni.land.id = Save_Get(land_info).id; + } + + memory->saved_town_tune = Save_Get(melody); + } + } + } + + { + Animal_c* animal = ((NPC_ACTOR*)*manager->client)->npc_info.animal; + int animal_idx = mNpc_SearchAnimalPersonalID(&animal->id); + + if (mNpc_CheckIslandAnimal(animal) == TRUE) { + mNpc_TalkEndMove(mNpc_ISLANDER_NO, animal->id.looks); + } + else { + mNpc_TalkEndMove(animal_idx, animal->id.looks); + } + } + } +} + +static void aQMgr_talk_start_kamakura_common(QUEST_MANAGER_ACTOR* manager, u8 step) { + aQMgr_clear_talk_wait_info(&manager->wait_info); + *manager->mode = aQMgr_MODE_MOVE_TALK; + manager->talk_step = step; + manager->talk_type = aQMgr_TALK_KIND_NORMAL; + (*manager->talk_common_proc)(manager, aQMgr_TALK_COMMON_TALK_INIT_OVL); + aQMgr_talk_init(manager); +} + +static void aQMgr_talk_start_kamakura_hello(QUEST_MANAGER_ACTOR* manager) { + aQMgr_talk_start_kamakura_common(manager, 8); +} + +static void aQMgr_talk_start_kamakura(QUEST_MANAGER_ACTOR* manager) { + aQMgr_talk_start_kamakura_common(manager, 9); +} + +static void aQMgr_talk_start_summercamp_hello(QUEST_MANAGER_ACTOR* manager) { + aQMgr_talk_start_kamakura_common(manager, 10); +} + +static void aQMgr_talk_start_summercamp(QUEST_MANAGER_ACTOR* manager) { + aQMgr_talk_start_kamakura_common(manager, 11); +} + +typedef void (*aQMgr_MOVE_PROC)(QUEST_MANAGER_ACTOR*); +static void aQMgr_actor_move_main(ACTOR* actorx, GAME* game) { + QUEST_MANAGER_ACTOR* manager = (QUEST_MANAGER_ACTOR*)actorx; + static aQMgr_MOVE_PROC move_proc[aQMgr_MODE_NUM] = { + &aQMgr_actor_move_wait, + &aQMgr_select_talk, + &aQMgr_move_talk, + &aQMgr_actor_move_talk_fin, + &aQMgr_talk_start_kamakura_hello, + &aQMgr_talk_start_kamakura, + &aQMgr_talk_start_summercamp_hello, + &aQMgr_talk_start_summercamp + }; + + u8 mode = *manager->mode; + + if (mEv_CheckTitleDemo() <= 0) { + (*move_proc[mode])(manager); + } + + { + GRAPH* graph = game->graph; + gfxprint_t gfxprint; + Gfx* gfx; + gfxprint_t* gfxprint_p = &gfxprint; + Gfx* gfx_work; + + if (ZURUMODE2_ENABLED()) { + gfxprint_init(gfxprint_p); + + OPEN_DISP(graph); + gfx = NOW_POLY_OPA_DISP; + gfx_work = gfxopen(gfx); + + gSPDisplayList(NEXT_OVERLAY_DISP, gfx_work); + gfxprint_open(gfxprint_p, gfx_work); + gfxprint_color(gfxprint_p, 245, 50, 50, 255); + gfxprint_locate8x8(gfxprint_p, 22, 3); + gfxprint_printf(gfxprint_p, "msg no :"); + gfxprint_locate8x8(gfxprint_p, 30, 3); + gfxprint_printf(gfxprint_p, "%5d", mFRm_get_msg_idx()); + + { + mEv_gst_common_c* ghost_common = (mEv_gst_common_c*)mEv_get_common_area(mEv_EVENT_GHOST, 55); + + if (ghost_common != NULL) { + int i; + + gfxprint_color(gfxprint_p, 45, 250, 50, 255); + + for (i = 0; i < mEv_GHOST_HITODAMA_NUM; i++) { + gfxprint_locate8x8(gfxprint_p, 30, 19 + i); + gfxprint_printf(gfxprint_p, "%3d,%3d", ghost_common->hitodama_block_data.block_x[i], ghost_common->hitodama_block_data.block_z[i]); + } + } + } + + gfx_work = gfxprint_close(gfxprint_p); + gSPEndDisplayList(gfx_work++); + gfxclose(gfx, gfx_work); + SET_POLY_OPA_DISP(gfx_work); + CLOSE_DISP(graph); + + gfxprint_cleanup(gfxprint_p); + } + } +} + +static void aQMgr_actor_ct(ACTOR* actorx, GAME* game) { + GAME_PLAY* play = (GAME_PLAY*)game; + QUEST_MANAGER_ACTOR* manager = (QUEST_MANAGER_ACTOR*)actorx; + + if (Common_Get(clip).quest_manager_clip == NULL) { + Common_Get(clip).quest_manager_clip = (aQMgr_Clip_c*)zelda_malloc(sizeof(aQMgr_Clip_c)); + } + + Common_Get(clip).quest_manager_clip->talk_request_proc = &aQMgr_actor_talk_request; + Common_Get(clip).quest_manager_clip->talk_start_proc = &aQMgr_actor_talk_start; + Common_Get(clip).quest_manager_clip->talk_check_proc = &aQMgr_actor_talk_check; + + l_client_p = NULL; + l_quest_manager_mode = aQMgr_MODE_NORMAL; + l_quest_memory_p = NULL; + l_quest_actor_p = NULL; + l_quest_manager_hello = FALSE; + + manager->client = &l_client_p; + manager->memory = &l_quest_memory_p; + manager->submenu = &play->submenu; + manager->mode = &l_quest_manager_mode; + manager->talk_state = aQMgr_TALK_STATE_INIT; + manager->sub_talk_state = aQMgr_TALK_SUB_STATE_WAIT; + + aQMgr_talk_common_clear_talk_info(manager); + aQMgr_actor_clear_regist(manager->regist, aQMgr_REGIST_NUM); + manager->regist_idx = -1; + manager->regist_use_no = 0; + aQMgr_actor_init_quest(manager); + manager->talk_common_proc = &aQMgr_talk_common_proc; + manager->clear_regist_proc = &aQMgr_actor_clear_regist; + manager->regist_quest_proc = &aQMgr_actor_regist_quest; + manager->get_time_kind_proc = &aQMgr_get_time_kind; + bzero(manager->errand_next, sizeof(manager->errand_next)); + manager->clip = NULL; + aQMgr_clear_talk_init_ovl(manager); + aQMgr_clear_talk_wait_info(&manager->wait_info); + l_aQMgr_hand_start = FALSE; + l_quest_actor_p = (QUEST_MANAGER_ACTOR*)actorx; // l_quest_actor_p is probably just ACTOR* + mem_clear(manager->last_strings, 7, 0xFF); + manager->give_item = EMPTY_NO; +} + +static void aQMgr_actor_dt(ACTOR* actorx, GAME* game) { + QUEST_MANAGER_ACTOR* manager = (QUEST_MANAGER_ACTOR*)actorx; + + if (Common_Get(clip).quest_manager_clip != NULL) { + zelda_free(Common_Get(clip).quest_manager_clip); + Common_Get(clip).quest_manager_clip = NULL; + } + + if (manager->clip != NULL) { + zelda_free(manager->clip); + } +} + +typedef void (*aQMgr_SAVE_PROC)(aQMgr_regist_c*); + +static void aQMgr_actor_save(ACTOR* actorx, GAME* game) { + QUEST_MANAGER_ACTOR* manager = (QUEST_MANAGER_ACTOR*)actorx; + aQMgr_regist_c* regist = manager->regist; + static aQMgr_SAVE_PROC save_proc[mQst_QUEST_TYPE_NUM] = { + (aQMgr_SAVE_PROC)&none_proc1, + (aQMgr_SAVE_PROC)&none_proc1, + &aQMgr_save_contest + }; + int i; + + aQMgr_actor_init_quest(manager); + for (i = 0; i < aQMgr_REGIST_NUM; i++) { + if (regist->quest_info != NULL) { + int type = regist->quest_info->quest_type; + + if (type >= 0 && type < mQst_QUEST_TYPE_NUM) { + (*save_proc[type])(regist); + } + } + + regist++; + } +} + +ACTOR_PROFILE Quest_Manager_Profile = { + mAc_PROFILE_QUEST_MANAGER, + ACTOR_PART_CONTROL, + ACTOR_STATE_CAN_MOVE_IN_DEMO_SCENES | ACTOR_STATE_NO_DRAW_WHILE_CULLED | ACTOR_STATE_NO_MOVE_WHILE_CULLED, + EMPTY_NO, + ACTOR_OBJ_BANK_KEEP, + sizeof(QUEST_MANAGER_ACTOR), + &aQMgr_actor_ct, + &aQMgr_actor_dt, + &aQMgr_actor_move_main, + mActor_NONE_PROC1, + &aQMgr_actor_save +}; diff --git a/src/c_keyframe.c b/src/c_keyframe.c index b467b89a..f894f228 100644 --- a/src/c_keyframe.c +++ b/src/c_keyframe.c @@ -229,7 +229,7 @@ static s16 cKF_KeyCalc(s16 start_idx, s16 n_frames, s16* data_src, f32 frame) { // Search through all frames now = 0; next = 1; - + while (TRUE) { if (key_p[next].frame > frame) { f32 delta_frame = key_p[next].frame - key_p[now].frame; @@ -237,15 +237,16 @@ static s16 cKF_KeyCalc(s16 start_idx, s16 n_frames, s16* data_src, f32 frame) { if (!(F32_IS_ZERO(delta_frame))) { f32 t = (frame - key_p[now].frame) / delta_frame; // progress towards the next frame f32 tension = delta_frame * (1.0f / 30.0f); - f32 calc = cKF_HermitCalc(t, tension, key_p[now].value, key_p[next].value, key_p[now].tangent, key_p[next].tangent); - int key = calc + 0.5; + f32 calc = cKF_HermitCalc(t, tension, key_p[now].value, key_p[next].value, key_p[now].tangent, + key_p[next].tangent); + int key = calc + 0.5; // Always round up return key; } else { return key_p[now].value; } } - + now++; next++; } @@ -271,7 +272,7 @@ extern void cKF_SkeletonInfo_subRotInterpolation(f32 t, s16* out, s16 rot1, s16 * Morphs current state towards a target state with a given step size. * * Adjusts 'now' values towards 'target' values based on 'step', performing this operation - * for three consecutive s16 values starting from 'now' and 'target'. + * for three consecutive s16 values (x->y->z) starting from 'now' and 'target'. * * @param now Pointer to the current values. * @param target Pointer to the target values to morph towards. @@ -527,7 +528,8 @@ extern int cKF_SkeletonInfo_R_play(cKF_SkeletonInfo_R_c* keyframe) { // Convert joint value to fixed-point format after scaling jointFlag >>= 1; // Shift flag for next component x -> y -> z - // Reduce the value by 10% and clamp to [0, 360) degrees converted back to binangle (s16) + // Reduce the value by 90% and clamp to [0, 360) degrees converted back to binangle (s16) + // This effectively limits any joint's maximum rotation to be in the range of [-36.8, 36.7] degrees adjustedJointValue = *jointValuePtr * 0.1f; *jointValuePtr++ = DEG2SHORT_ANGLE(MOD_F(adjustedJointValue, 360.0f)); } diff --git a/src/m_all_grow.c b/src/m_all_grow.c index 04dc4fcb..e6eeb124 100644 --- a/src/m_all_grow.c +++ b/src/m_all_grow.c @@ -288,7 +288,7 @@ extern void mAGrw_RenewalFgItem(lbRTC_time_c* time) { mAGrw_RenewalFgItem_ovl(time, &haniwa_scheduled); Save_Set(haniwa_scheduled, haniwa_scheduled); - if (ZURUMODE2_ENALBED()) { + if (ZURUMODE2_ENABLED()) { mAGrw_SetDebugData(); } } diff --git a/src/m_npc.c b/src/m_npc.c index 98342310..1fef4fa8 100644 --- a/src/m_npc.c +++ b/src/m_npc.c @@ -5185,13 +5185,13 @@ extern void mNpc_TalkInfoMove() { } } -extern void mNpc_TalkEndMove(int animal_idx, int feel) { - if (animal_idx >= 0 && animal_idx < ARRAY_COUNT(l_npc_talk_info) && feel >= 0 && feel < mNpc_FEEL_NUM) { +extern void mNpc_TalkEndMove(int animal_idx, int looks) { + if (animal_idx >= 0 && animal_idx < ARRAY_COUNT(l_npc_talk_info) && looks >= 0 && looks < mNpc_LOOKS_NUM) { mNpc_Talk_Info_c* talk_info = &l_npc_talk_info[animal_idx]; talk_info->timer = 1000; - if (mNpc_CountTalkNum(animal_idx, feel) == TRUE && mNpc_CheckOverImpatient(animal_idx, feel) == TRUE) { - mNpc_SetUnlockTimer(&talk_info->unlock_timer, &talk_info->reset_timer, feel); + if (mNpc_CountTalkNum(animal_idx, looks) == TRUE && mNpc_CheckOverImpatient(animal_idx, looks) == TRUE) { + mNpc_SetUnlockTimer(&talk_info->unlock_timer, &talk_info->reset_timer, looks); } } }