From a50058847b30660e626c0b5d8aeeed7d85ffcf8e Mon Sep 17 00:00:00 2001 From: Cuyler36 Date: Fri, 28 Jul 2023 21:53:12 -0400 Subject: [PATCH] Implement & link m_npc_walk.c --- config/rel_slices.yml | 5 + include/m_field_info.h | 2 +- include/m_field_make.h | 5 +- include/m_npc.h | 2 + include/m_npc_walk.h | 53 ++- rel/m_npc_walk.c | 728 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 788 insertions(+), 7 deletions(-) create mode 100644 rel/m_npc_walk.c diff --git a/config/rel_slices.yml b/config/rel_slices.yml index 4f7139a6..fb3b57de 100644 --- a/config/rel_slices.yml +++ b/config/rel_slices.yml @@ -107,6 +107,11 @@ m_needlework.c: m_npc_schedule.c: .text: [0x803D7570, 0x803D7890] .data: [0x8065B638, 0x8065B7F0] +m_npc_walk.c: + .text: [0x803D7890, 0x803D88E8] + .rodata: [0x806429F0, 0x80642A00] + .data: [0x8065B7F0, 0x8065B9A0] + .bss: [0x8129CC48, 0x8129CC98] m_olib.c: .text: [0x803D88E8, 0x803D8A34] .rodata: [0x80642A00, 0x80642A10] diff --git a/include/m_field_info.h b/include/m_field_info.h index 5db7729f..e35f80fb 100644 --- a/include/m_field_info.h +++ b/include/m_field_info.h @@ -137,7 +137,7 @@ extern int mFI_GetBlockUtNum2FG(mActor_name_t* fg_item, int block_x, int block_z extern void mFI_UtNum2CenterWpos(xyz_t* wpos, int ut_x, int ut_z); extern u8 mFI_GetBlockXMax(); extern u8 mFI_GetBlockZMax(); -extern u8 mFI_BkNum2BlockType(); +extern u8 mFI_BkNum2BlockType(int block_x, int block_z); extern mFI_sound_source_info_c* mFI_GetSoundSourcePBlockNum(int block_x,int block_z); extern int mFI_Wpos2UtNum(int* ut_x, int* ut_z, xyz_t wpos); extern void mFI_ClearFieldData(); diff --git a/include/m_field_make.h b/include/m_field_make.h index 6f8b19d7..d5acc9e6 100644 --- a/include/m_field_make.h +++ b/include/m_field_make.h @@ -18,8 +18,9 @@ extern "C" { #define FG_BLOCK_Z_NUM (BLOCK_Z_NUM - 4) /* 6 */ #define FG_BLOCK_TOTAL_NUM (FG_BLOCK_X_NUM * FG_BLOCK_Z_NUM) -#define UT_X_NUM 16 /* Spaces per block (acre) in x direction */ -#define UT_Z_NUM 16 /* Spaces per block (acre) in z direction */ +#define UT_BASE_NUM 16 +#define UT_X_NUM UT_BASE_NUM /* Spaces per block (acre) in x direction */ +#define UT_Z_NUM UT_BASE_NUM /* Spaces per block (acre) in z direction */ #define UT_TOTAL_NUM (UT_X_NUM * UT_Z_NUM) #define IDX_2_UT_X(idx) ((idx) & (UT_X_NUM - 1)) diff --git a/include/m_npc.h b/include/m_npc.h index 1418b6bf..367186b1 100644 --- a/include/m_npc.h +++ b/include/m_npc.h @@ -210,6 +210,8 @@ extern void mNpc_SendMailtoNpc(Mail_c* mail); extern void mNpc_SetNpcinfo(ACTOR* actor, s8 npc_info_idx); extern void mNpc_InitNpcAllInfo(int malloc_flag); extern void mNpc_SetRemoveAnimalNo(u8* remove_animal_no, Animal_c* animals, int remove_no); +extern void mNpc_ClearAnimalPersonalID(AnmPersonalID_c* id); +extern int mNpc_CheckCmpAnimalPersonalID(AnmPersonalID_c* id0, AnmPersonalID_c* id1); extern void mNpc_PrintRemoveInfo(gfxprint_t* gfxprint); extern void mNpc_PrintFriendship_fdebug(gfxprint_t* gfxprint); diff --git a/include/m_npc_walk.h b/include/m_npc_walk.h index 332b2264..15197ec1 100644 --- a/include/m_npc_walk.h +++ b/include/m_npc_walk.h @@ -21,7 +21,7 @@ typedef struct goal_data_s { typedef struct goal_data_table_s { mNpcW_GoalData_c* data_p; int count; -} mNpc_GoalDataTable_c; +} mNpcW_GoalDataTable_c; enum { mNpcW_GOAL_SHRINE, /* Shrine/Wishing Well acre */ @@ -32,15 +32,50 @@ enum { mNpcW_GOAL_NUM }; +enum { + mNpcW_GOAL_BLOCK_SHRINE, + mNpcW_GOAL_BLOCK_HOME, + + mNpcW_GOAL_BLOCK_NUM +}; + +/* TODO: figure out remaining enums */ +enum { + mNpcW_APPEAR_STATUS_0, + mNpcW_APPEAR_STATUS_1, + + mNpcW_APPEAR_STATUS_NUM +}; + +enum { + mNpcW_APPEAR_WAY_UP, + mNpcW_APPEAR_WAY_DOWN, + mNpcW_APPEAR_WAY_LEFT, + mNpcW_APPEAR_WAY_RIGHT, + + mNpcW_APPEAR_WAY_NUM +}; + +/* TODO: figure out remaining enums */ +enum { + mNpcW_INFO_STATUS_0, + mNpcW_INFO_STATUS_WALKING, + mNpcW_INFO_STATUS_2, + mNpcW_INFO_STATUS_3, + mNpcW_INFO_STATUS_4, + + mNpcW_INFO_STATUS_NUM +}; + typedef struct npc_walk_appear_s { - u8 state; - u8 target_direction; + u8 status; + u8 way; } mNpcW_appear_c; typedef struct npc_walk_information_s { AnmPersonalID_c id; int idx; - u8 state; + u8 status; u8 goal_type; u8 goal_block_x; u8 goal_block_z; @@ -54,6 +89,16 @@ typedef struct npc_walk_s { u8 info_max; } mNpc_walk_c; +extern void mNpcW_ClearNpcWalkInfo(mNpcW_info_c* info, int num); +extern int mNpcW_GetNpcWalkInfoIdx(mNpcW_info_c* info, int num, AnmPersonalID_c* anm_id); +extern int mNpcW_ChangeNpcWalk(mNpc_walk_c* walk, mNpcW_info_c* info); +extern void mNpcW_SetGoalBlock(mNpcW_info_c* info); +extern void mNpcW_InitNpcWalk(mNpc_walk_c* walk); +extern int mNpcW_GetAppearStatusWay(u8* status, u8* way, Animal_c* animal); +extern int* mNpcW_GetArriveStayCountP(int idx); +extern int mNpcW_GetWalkInfoStatusGoalAnimalIdx(int* status, int* goal, int idx); +extern int mNpcW_GetNearGate(int* target_ut_x, int* target_ut_z, int block_x, int block_z, int ut_x, int ut_z); + #ifdef __cplusplus } #endif diff --git a/rel/m_npc_walk.c b/rel/m_npc_walk.c new file mode 100644 index 00000000..c225c5f9 --- /dev/null +++ b/rel/m_npc_walk.c @@ -0,0 +1,728 @@ +#include "m_npc_walk.h" + +#include "libultra/libultra.h" +#include "m_random_field.h" +#include "m_common_data.h" + +static int l_arrive_stay_count[ANIMAL_NUM_MAX]; /* TODO: swap with l_goal_block when bss ordering is fixed */ +static int l_goal_block[mNpcW_GOAL_BLOCK_NUM][2]; + +#define mNpcW_TIME_2_SEC(hour, min, sec) ((hour) * 3600 + (min) * 60 + (sec)) +#define mNpcW_MAKE_SCHEDULE_TABLE(goal_data) { goal_data, ARRAY_SIZE(goal_data, mNpcW_GoalData_c) } + +static u8 l_girl_time_12[] = { mNpcW_GOAL_SHRINE, mNpcW_GOAL_HOME, mNpcW_GOAL_ALONE }; +static u8 l_girl_time_18_30[] = { mNpcW_GOAL_SHRINE, mNpcW_GOAL_HOME }; + +static mNpcW_GoalData_c l_girl_goal_data[] = { + { NULL, 0, mNpcW_TIME_2_SEC(6, 0, 0) }, + { l_girl_time_12, ARRAY_SIZE(l_girl_time_12, u8), mNpcW_TIME_2_SEC(12, 0, 0) }, + { NULL, 0, mNpcW_TIME_2_SEC(13, 0, 0) }, + { l_girl_time_18_30, ARRAY_SIZE(l_girl_time_18_30, u8), mNpcW_TIME_2_SEC(18, 30, 0) }, + { NULL, 0, mNpcW_TIME_2_SEC(24, 0, 0) } +}; + +static mNpcW_GoalDataTable_c l_girl_goal_table = mNpcW_MAKE_SCHEDULE_TABLE(l_girl_goal_data); + +static u8 l_kogirl_time_ed[] = { mNpcW_GOAL_SHRINE, mNpcW_GOAL_HOME }; + +static mNpcW_GoalData_c l_kogirl_goal_data[] = { + { l_kogirl_time_ed, ARRAY_SIZE(l_kogirl_time_ed, u8), mNpcW_TIME_2_SEC(24, 0, 0) } +}; + +static mNpcW_GoalDataTable_c l_kogirl_goal_table = mNpcW_MAKE_SCHEDULE_TABLE(l_kogirl_goal_data); + +static u8 l_boy_time_12[] = { mNpcW_GOAL_ALONE }; +static u8 l_boy_time_19_30[] = { mNpcW_GOAL_SHRINE, mNpcW_GOAL_HOME }; + +static mNpcW_GoalData_c l_boy_goal_data[] = { + { NULL, 0, mNpcW_TIME_2_SEC(9, 0, 0) }, + { l_boy_time_12, ARRAY_SIZE(l_boy_time_12, u8), mNpcW_TIME_2_SEC(12, 0, 0) }, + { NULL, 0, mNpcW_TIME_2_SEC(14, 0, 0) }, + { l_boy_time_19_30, ARRAY_SIZE(l_boy_time_19_30, u8), mNpcW_TIME_2_SEC(19, 30, 0) }, + { NULL, 0, mNpcW_TIME_2_SEC(24, 0, 0) } +}; + +static mNpcW_GoalDataTable_c l_boy_goal_table = mNpcW_MAKE_SCHEDULE_TABLE(l_boy_goal_data); + +static u8 l_sports_man_time_12[] = { mNpcW_GOAL_SHRINE, mNpcW_GOAL_HOME, mNpcW_GOAL_ALONE, mNpcW_GOAL_ALONE }; +static u8 l_sports_man_time_23[] = { mNpcW_GOAL_SHRINE, mNpcW_GOAL_SHRINE, mNpcW_GOAL_HOME, mNpcW_GOAL_HOME, mNpcW_GOAL_ALONE }; + +static mNpcW_GoalData_c l_sports_man_goal_data[] = { + { NULL, 0, mNpcW_TIME_2_SEC(6, 30, 0) }, + { l_sports_man_time_12, ARRAY_SIZE(l_sports_man_time_12, u8), mNpcW_TIME_2_SEC(12, 0, 0) }, + { NULL, 0, mNpcW_TIME_2_SEC(12, 30, 0) }, + { l_sports_man_time_23, ARRAY_SIZE(l_sports_man_time_23, u8), mNpcW_TIME_2_SEC(23, 0, 0) }, + { NULL, 0, mNpcW_TIME_2_SEC(24, 0, 0) } +}; + +static mNpcW_GoalDataTable_c l_sports_man_goal_table = mNpcW_MAKE_SCHEDULE_TABLE(l_sports_man_goal_data); + +static u8 l_grim_man_time_ed[] = { mNpcW_GOAL_SHRINE, mNpcW_GOAL_SHRINE, mNpcW_GOAL_SHRINE, mNpcW_GOAL_ALONE, mNpcW_GOAL_ALONE, mNpcW_GOAL_ALONE, mNpcW_GOAL_ALONE, mNpcW_GOAL_ALONE, mNpcW_GOAL_ALONE, mNpcW_GOAL_ALONE }; + +static mNpcW_GoalData_c l_grim_man_goal_data[] = { + { l_grim_man_time_ed, ARRAY_SIZE(l_grim_man_time_ed, u8), mNpcW_TIME_2_SEC(24, 0, 0) } +}; + +static mNpcW_GoalDataTable_c l_grim_man_goal_table = mNpcW_MAKE_SCHEDULE_TABLE(l_grim_man_goal_data); + +static u8 l_naniwa_lady_time_1_30[] = { mNpcW_GOAL_SHRINE, mNpcW_GOAL_SHRINE, mNpcW_GOAL_SHRINE, mNpcW_GOAL_SHRINE, mNpcW_GOAL_SHRINE, mNpcW_GOAL_SHRINE, mNpcW_GOAL_SHRINE, mNpcW_GOAL_ALONE, mNpcW_GOAL_ALONE, mNpcW_GOAL_ALONE }; +static u8 l_naniwa_lady_time_13[] = { mNpcW_GOAL_HOME }; +static u8 l_naniwa_lady_time_21[] = { mNpcW_GOAL_SHRINE, mNpcW_GOAL_SHRINE, mNpcW_GOAL_HOME, mNpcW_GOAL_HOME, mNpcW_GOAL_ALONE }; + +static mNpcW_GoalData_c l_naniwa_lady_goal_data[] = { + { l_naniwa_lady_time_1_30, ARRAY_SIZE(l_naniwa_lady_time_1_30, u8), mNpcW_TIME_2_SEC(1, 30, 0) }, + { NULL, 0, mNpcW_TIME_2_SEC(10, 0, 0) }, + { l_naniwa_lady_time_13, ARRAY_SIZE(l_naniwa_lady_time_13, u8), mNpcW_TIME_2_SEC(13, 0, 0) }, + { NULL, 0, mNpcW_TIME_2_SEC(14, 0, 0) }, + { l_naniwa_lady_time_21, ARRAY_SIZE(l_naniwa_lady_time_21, u8), mNpcW_TIME_2_SEC(21, 0, 0) }, + { NULL, 0, mNpcW_TIME_2_SEC(22, 0, 0) }, + { l_naniwa_lady_time_1_30, ARRAY_SIZE(l_naniwa_lady_time_1_30, u8), mNpcW_TIME_2_SEC(24, 0, 0) } +}; + +static mNpcW_GoalDataTable_c l_naniwa_lady_goal_table = mNpcW_MAKE_SCHEDULE_TABLE(l_naniwa_lady_goal_data); + +static mNpcW_GoalDataTable_c* l_looks_goal_table[mNpc_LOOKS_NUM] = { + &l_girl_goal_table, + &l_kogirl_goal_table, + &l_boy_goal_table, + &l_sports_man_goal_table, + &l_grim_man_goal_table, + &l_naniwa_lady_goal_table +}; + +static mNpcW_GoalData_c* mNpcW_GetGoalDataInfo(int looks, int now_sec) { + int goal_num = l_looks_goal_table[looks]->count; + mNpcW_GoalData_c* goal_data_save = l_looks_goal_table[looks]->data_p; + mNpcW_GoalData_c* goal_data = goal_data_save; + int i; + + for (i = 0; i < goal_num; i++) { + if (goal_data->end_time > now_sec) { + break; + } + + goal_data++; + } + + if (i != goal_num) { + return goal_data; + } + + return &goal_data_save[goal_num - 1]; +} + +static void mNpcW_ClearNpcWalkAppear(mNpcW_appear_c* appear) { + appear->status = mNpcW_APPEAR_STATUS_NUM; + appear->way = mNpcW_APPEAR_WAY_NUM; +} + +extern void mNpcW_ClearNpcWalkInfo(mNpcW_info_c* info, int num) { + for (num; num != 0 && info != NULL; num--) { + bzero(info, sizeof(mNpcW_info_c)); + mNpc_ClearAnimalPersonalID(&info->id); + info->idx = -1; + info->status = mNpcW_INFO_STATUS_NUM; + info->goal_type = mNpcW_GOAL_NUM; + mNpcW_ClearNpcWalkAppear(&info->appear_info); + + info++; + } +} + +static void mNpcW_ClearNpcWalk(mNpc_walk_c* walk_info) { + bzero(walk_info, sizeof(mNpc_walk_c)); + mNpcW_ClearNpcWalkInfo(walk_info->info, mNpcW_MAX); +} + +static int mNpcW_CheckFreeNpcWalkInfo(mNpcW_info_c* info) { + int res = FALSE; + + if (info != NULL) { + res = mNpc_CheckFreeAnimalPersonalID(&info->id); + } + + return res; +} + +static int mNpcW_GetFreeNpcWalkInfoIdx(mNpcW_info_c* info, int num) { + int idx = -1; + int i; + + for (i = 0; i < num; i++) { + if (mNpcW_CheckFreeNpcWalkInfo(info) == TRUE) { + idx = i; + break; + } + + info++; + } + + return idx; +} + +extern int mNpcW_GetNpcWalkInfoIdx(mNpcW_info_c* info, int num, AnmPersonalID_c* anm_id) { + int idx = -1; + int i; + + if (anm_id != NULL && mNpc_CheckFreeAnimalPersonalID(anm_id) == FALSE) { + for (i = 0; i < num; i++) { + if (mNpc_CheckCmpAnimalPersonalID(anm_id, &info->id) == TRUE) { + idx = i; + break; + } + + info++; + } + } + + return idx; +} + +static int mNpcW_DecideNpc(Animal_c* animal, u16 used) { + int idx; + u16 exist; + u16 possible_goal; + int unused; + int now_sec; + int i; + + now_sec = Common_Get(time.now_sec); + exist = 0; + possible_goal = 0; + unused = 0; + idx = -1; + + for (i = 0; i < ANIMAL_NUM_MAX; i++) { + if (mNpc_CheckFreeAnimalPersonalID(&animal->id) == FALSE) { + mNpcW_GoalData_c* goal_data; + + exist |= (1 << i); + goal_data = mNpcW_GetGoalDataInfo(animal->id.looks, now_sec); + + if (goal_data != NULL) { + if (goal_data->count != 0) { + possible_goal |= (1 << i); + + if (((used >> i) & 1) == FALSE) { + unused++; + } + } + } + } + + animal++; + } + + if (unused > 0) { + int selected_num; + u32 selection_field = possible_goal & (exist & ~used); /* clear the possible bitfield to only include unused existing villagers */ + + selected_num = RANDOM(unused) + 1; + + while (selected_num != 0) { + if ((selection_field & 1) == TRUE) { + selected_num--; + } + + selection_field >>= 1; + idx++; + } + } + + return idx; +} + +static void mNpcW_SetNpcWalkInfo(mNpcW_info_c* info, Animal_c* animal, int idx) { + int now_sec = Common_Get(time.now_sec); + mNpcW_GoalData_c* goal_data; + + mNpc_CopyAnimalPersonalID(&info->id, &animal->id); + info->idx = idx; + goal_data = mNpcW_GetGoalDataInfo(animal->id.looks, now_sec); + + if (goal_data != NULL && goal_data->count != 0) { + info->goal_type = goal_data->types[RANDOM(goal_data->count)]; + } + else { + info->goal_type = mNpcW_GOAL_MY_HOME; /* Default to acre the villager's house is in */ + } + + info->status = mNpcW_INFO_STATUS_WALKING; +} + +extern int mNpcW_ChangeNpcWalk(mNpc_walk_c* walk, mNpcW_info_c* info) { + mNpcW_info_c* walk_info = walk->info; + int free_idx; + Animal_c* animals; + int idx = -1; + + mNpcW_ClearNpcWalkInfo(info, 1); + free_idx = mNpcW_GetFreeNpcWalkInfoIdx(walk_info, mNpcW_MAX); + + if (free_idx != -1) { + mNpcW_info_c* free_info = &walk_info[free_idx]; + + mNpcW_ClearNpcWalkInfo(free_info, 1); + animals = Save_Get(animals); + idx = mNpcW_DecideNpc(animals, walk->used_idx_bitfield); + + if (idx == -1) { + int i; + + walk->used_idx_bitfield = 0; + for (i = 0; i < mNpcW_MAX; i++) { + int animal_idx = walk_info[i].idx; + + if (animal_idx >= 0 && animal_idx < ANIMAL_NUM_MAX) { + walk->used_idx_bitfield |= (1 << animal_idx); + } + } + + idx = mNpcW_DecideNpc(animals, walk->used_idx_bitfield); + } + + if (idx != -1) { + mNpcW_SetNpcWalkInfo(free_info, Save_GetPointer(animals[idx]), idx); + walk->used_idx_bitfield |= (1 << idx); + mNpcW_SetGoalBlock(free_info); + } + } + + return idx; +} + +static int mNpcW_GetAloneBlock(u8* goal_block_x, u8* goal_block_z) { + u8 block_field[FG_BLOCK_Z_NUM + 2]; + Animal_c* animal = Save_Get(animals); + mNpc_NpcList_c* npclist = Common_Get(npclist); + u8 animal_count = 0; + int selected; + int bz; + int bx; + int i; + int z; + + // 0 1 2 3 4 5 6 + // Q - - - - - - - + // A - - - - - - - + // B - - - - - - - + // C - - - - - - - + // D - - - x - - - + // E - - - - - - - + // F - - - - - - - + // Z - - - - - - - + + /* Default goal block is D-3 */ + *goal_block_x = 3; + *goal_block_z = 4; + + bzero(block_field, FG_BLOCK_Z_NUM + 2); + /* Log all acres which currently have a villager in them */ + for (i = 0; i < ANIMAL_NUM_MAX; i++) { + if (mNpc_CheckFreeAnimalPersonalID(&animal->id) == FALSE) { + int block_x; + int block_z; + + if (mFI_Wpos2BlockNum(&block_x, &block_z, npclist->position) == TRUE) { + animal_count++; + block_field[block_z] |= (1 << block_x); + } + } + + animal++; + npclist++; + } + + /* Select a random empty acre to go to */ + z = 1; + selected = RANDOM(FG_BLOCK_TOTAL_NUM - animal_count); + /* Find the random acre and set output */ + for (bz = 1; bz < BLOCK_Z_NUM - 3; bz++) { + for (bx = 1; bx < BLOCK_X_NUM - 1; bx++) { + if ((((block_field[z]) >> bx) & 1) == 0) { + if (selected <= 0) { + *goal_block_x = bx; + *goal_block_z = bz; + return TRUE; + } + else { + selected--; + } + } + } + + z++; + } + + return FALSE; +} + +static void mNpcW_GetBlockXZNumExceptHome(int* goal_block_x, int* goal_block_z, Animal_c* animal) { + u8 possible_x = 0b11111111; + u8 possible_z = 0b11111111; + + int x_num = 0; + int z_num = 0; + + int selected_x; + int selected_z; + + int i; + + for (i = 0; i < ANIMAL_NUM_MAX; i++) { + if (mNpc_CheckFreeAnimalPersonalID(&animal->id) == FALSE) { + u8 home_block_x = animal->home_info.block_x - 1; + u8 home_block_z = animal->home_info.block_z - 1; + + if (((possible_x >> home_block_x) & 1) == 1) { + x_num++; + possible_x &= ~(1 << home_block_x); + } + + if (((possible_z >> home_block_z) & 1) == 1) { + z_num++; + possible_z &= ~(1 << home_block_z); + } + } + + animal++; + } + + selected_x = RANDOM(FG_BLOCK_X_NUM - x_num); + selected_z = RANDOM(FG_BLOCK_Z_NUM - z_num); + + for (i = 0; i < FG_BLOCK_X_NUM; i++) { + if (((possible_x >> i) & 1) == 1) { + if (selected_x <= 0) { + break; + } + else { + selected_x--; + } + } + } + + *goal_block_x = i + 1; + + for (i = 0; i < FG_BLOCK_Z_NUM; i++) { + if (((possible_z >> i) & 1) == 1) { + if (selected_z <= 0) { + break; + } + else { + selected_z--; + } + } + } + + *goal_block_z = i + 1; + + if (*goal_block_x <= 0 || *goal_block_x >= BLOCK_X_NUM || *goal_block_z <= 0 || *goal_block_z >= (BLOCK_Z_NUM - 2)) { + *goal_block_x = 4; + *goal_block_z = 3; + } +} + +static int mNpcW_CheckDiffBlockWalkNpcHome(int block_x, int block_z, mNpcW_info_c* info) { + int res = FALSE; + Anmhome_c* home; + int i; + + for (i = 0; i < mNpcW_MAX; i++) { + if (info->idx != -1) { + home = Save_GetPointer(animals[info->idx].home_info); + + if (home->block_x == block_x && home->block_z == block_z) { + break; + } + } + + info++; + } + + if (i == mNpcW_MAX) { + res = TRUE; + } + + return res; +} + +static void mNpcW_SetHomeBlockSource(mNpc_walk_c* walk, Animal_c* animal) { + Animal_c* animal_p = animal; + u16 diff_animal_field; + int diff_animal_num; + int i; + + l_goal_block[mNpcW_GOAL_BLOCK_HOME][0] = 0; + l_goal_block[mNpcW_GOAL_BLOCK_HOME][1] = 0; + + animal = animal_p; + diff_animal_field = 0; + diff_animal_num = 0; + + for (i = 0; i < ANIMAL_NUM_MAX; i++) { + if (mNpc_CheckFreeAnimalPersonalID(&animal->id) == FALSE) { + Anmhome_c* home = &animal->home_info; + + if (home->block_x != 0xFF && home->block_z != 0xFF) { + if (mNpcW_CheckDiffBlockWalkNpcHome(home->block_x, home->block_z, walk->info) == TRUE) { + diff_animal_num++; + diff_animal_field |= (1 << i); + } + } + } + + animal++; + } + + if (diff_animal_num > 0) { + int selected = RANDOM(diff_animal_num); + + animal = animal_p; + + for (i = 0; i < ANIMAL_NUM_MAX; i++) { + if (((diff_animal_field >> i) & 1) == 1) { + if (selected == 0) { + l_goal_block[mNpcW_GOAL_BLOCK_HOME][0] = animal->home_info.block_x; + l_goal_block[mNpcW_GOAL_BLOCK_HOME][1] = animal->home_info.block_z; + break; + } + else { + selected--; + } + } + + animal++; + } + } + + if (l_goal_block[mNpcW_GOAL_BLOCK_HOME][0] == 0 || l_goal_block[mNpcW_GOAL_BLOCK_HOME][1] == 0) { + mNpcW_GetBlockXZNumExceptHome(&l_goal_block[mNpcW_GOAL_BLOCK_HOME][0], &l_goal_block[mNpcW_GOAL_BLOCK_HOME][1], animal_p); + } + + if ( + l_goal_block[mNpcW_GOAL_BLOCK_HOME][0] <= 0 || l_goal_block[mNpcW_GOAL_BLOCK_HOME][0] >= BLOCK_X_NUM || + l_goal_block[mNpcW_GOAL_BLOCK_HOME][1] <= 0 || l_goal_block[mNpcW_GOAL_BLOCK_HOME][1] >= (BLOCK_Z_NUM - 2) + ) { + l_goal_block[mNpcW_GOAL_BLOCK_HOME][0] = 4; + l_goal_block[mNpcW_GOAL_BLOCK_HOME][1] = 3; + } +} + +static void mNpcW_InitGoalBlockSource(mNpc_walk_c* walk, Animal_c* animal) { + int shrine_x; + int shrine_z; + + if (mFI_BlockKind2BkNum(&shrine_x, &shrine_z, mRF_BLOCKKIND_SHRINE) == FALSE) { + shrine_x = 4; + shrine_z = 3; + } + + l_goal_block[mNpcW_GOAL_BLOCK_SHRINE][0] = shrine_x; + l_goal_block[mNpcW_GOAL_BLOCK_SHRINE][1] = shrine_z; + + mNpcW_SetHomeBlockSource(walk, animal); +} + +extern void mNpcW_SetGoalBlock(mNpcW_info_c* info) { + switch (info->goal_type) { + case mNpcW_GOAL_SHRINE: + { + info->goal_block_x = l_goal_block[mNpcW_GOAL_BLOCK_SHRINE][0]; + info->goal_block_z = l_goal_block[mNpcW_GOAL_BLOCK_SHRINE][1]; + break; + } + + case mNpcW_GOAL_HOME: + { + info->goal_block_x = l_goal_block[mNpcW_GOAL_BLOCK_HOME][0]; + info->goal_block_z = l_goal_block[mNpcW_GOAL_BLOCK_HOME][1]; + break; + } + + case mNpcW_GOAL_ALONE: + { + mNpcW_GetAloneBlock(&info->goal_block_x, &info->goal_block_z); + break; + } + + case mNpcW_GOAL_MY_HOME: + { + int idx = info->idx; + + if (idx == -1) { + idx = 0; + } + + info->goal_block_x = Save_Get(animals[idx]).home_info.block_x; + info->goal_block_z = Save_Get(animals[idx]).home_info.block_z; + break; + } + + default: + { + info->goal_block_x = 4; + info->goal_block_z = 3; + break; + } + } + + if (info->goal_block_x <= 0 || info->goal_block_x >= BLOCK_X_NUM || info->goal_block_z <= 0 || info->goal_block_z >= (BLOCK_Z_NUM - 2)) { + info->goal_block_x = 4; + info->goal_block_z = 3; + } +} + +extern void mNpcW_InitNpcWalk(mNpc_walk_c* walk) { + Animal_c* animal; + int npc_max; + mNpcW_info_c* info = walk->info; + int idx; + int i; + + mNpcW_ClearNpcWalk(walk); + bzero(l_goal_block, sizeof(l_goal_block)); + bzero(l_arrive_stay_count, sizeof(l_arrive_stay_count)); + + animal = Save_Get(animals); + npc_max = mNpcW_GET_WALK_NUM(Save_Get(now_npc_max)); + + for (i = 0; i < npc_max; i++) { + idx = mNpcW_DecideNpc(animal, walk->used_idx_bitfield); + if (idx == -1) { + break; + } + + mNpcW_SetNpcWalkInfo(info, Save_GetPointer(animals[idx]), idx); + walk->used_idx_bitfield |= (1 << idx); + info++; + } + + mNpcW_InitGoalBlockSource(walk, animal); +} + +extern int mNpcW_GetAppearStatusWay(u8* status, u8* way, Animal_c* animal) { + mNpc_walk_c* walk = Common_GetPointer(npc_walk); + int info_idx; + int res = FALSE; + + *status = mNpcW_APPEAR_STATUS_NUM; + + info_idx = mNpcW_GetNpcWalkInfoIdx(walk->info, mNpcW_MAX, &animal->id); + + if (info_idx >= 0) { + *status = walk->info[info_idx].appear_info.status; + *way = walk->info[info_idx].appear_info.way; + res = TRUE; + } + else { + *status = mNpcW_APPEAR_STATUS_0; + *way = mNpcW_APPEAR_WAY_NUM; + } + + return res; +} + +extern int* mNpcW_GetArriveStayCountP(int idx) { + if (idx >= 0 && idx < ANIMAL_NUM_MAX) { + return &l_arrive_stay_count[idx]; + } + + return NULL; +} + +extern int mNpcW_GetWalkInfoStatusGoalAnimalIdx(int* status, int* goal, int idx) { + int res; + + *status = mNpcW_INFO_STATUS_NUM; + *goal = mNpcW_GOAL_NUM; + res = FALSE; + + if (idx >= 0 && idx < ANIMAL_NUM_MAX) { + mNpc_walk_c* walk = Common_GetPointer(npc_walk); + int info_idx = mNpcW_GetNpcWalkInfoIdx(walk->info, mNpcW_MAX, &Save_Get(animals[idx]).id); + + if (info_idx >= 0) { + res = TRUE; + *status = walk->info[info_idx].status; + *goal = walk->info[info_idx].goal_type; + } + } + + return res; +} + +extern int mNpcW_GetNearGate(int* target_ut_x, int* target_ut_z, int block_x, int block_z, int ut_x, int ut_z) { + u8 block_type; + int t_ut_x; + int t_ut_z; + mRF_gate_c* gate; + int gate_count; + int x; + int z; + int i; + int direction; + int res = FALSE; + + *target_ut_x = UT_X_NUM; + *target_ut_z = UT_Z_NUM; + + if (ut_x >= 0 && ut_x < UT_X_NUM && ut_z >= 0 && ut_z < UT_Z_NUM) { + block_type = mFI_BkNum2BlockType(block_x, block_z); + t_ut_x = ut_x < (UT_X_NUM / 2) ? ut_x : UT_X_NUM - ut_x; + t_ut_z = ut_z < (UT_Z_NUM / 2) ? ut_z : UT_Z_NUM - ut_z; + + + if (t_ut_x < t_ut_z) { + if (ut_x < (UT_X_NUM / 2)) { + direction = mRF_DIRECT_WEST; + } + else { + direction = mRF_DIRECT_EAST; + } + } + else { + if (ut_z < (UT_Z_NUM / 2)) { + direction = mRF_DIRECT_NORTH; + } + else { + direction = mRF_DIRECT_SOUTH; + } + } + + gate = mRF_BlockTypeDirect2GateData(&gate_count, block_type, direction); + if (gate != NULL && gate_count > 0) { + + + for (i = 0; i < gate_count; i++) { + int difference; + x = gate->ut1 & 0xF; + z = gate->ut1 >> 4; + + /* BUG: this is likely meant to be 'if (direction == mRF_DIRECT_WEST || direction == mRF_DIRECT_EAST)' */ + #ifndef BUGFIXES + if (direction == mRF_DIRECT_WEST || direction == mRF_DIRECT_WEST) { + #else + if (direction == mRF_DIRECT_WEST || direction == mRF_DIRECT_EAST) { + #endif + difference = ut_z - z; + difference = ABS(difference); + } + else { + difference = ut_x - x; + difference = ABS(difference); + } + + if (difference < UT_BASE_NUM) { + *target_ut_x = x; + *target_ut_z = z; + res = TRUE; + } + + gate++; + } + } + } + + return res; +}