From 5f3a6f1f5b54ef5b73441861ba5def548e3c8cbb Mon Sep 17 00:00:00 2001 From: Cuyler36 Date: Sat, 29 Apr 2023 02:01:44 -0400 Subject: [PATCH] Implement and link m_cockroach, m_police_box --- config/rel_slices.yml | 6 + include/m_cockroach.h | 17 +++ include/m_common_data.h | 4 + include/m_event.h | 89 ++++++++++++ include/m_field_info.h | 34 +++++ include/m_home_h.h | 1 + include/m_name_table.h | 25 +++- include/m_police_box.h | 21 ++- include/m_scene_table.h | 25 ++++ include/m_shop.h | 84 ++++++++++- rel/m_cockroach.c | 310 +++++++++++++++++++++++++++++++++++++++ rel/m_police_box.c | 311 ++++++++++++++++++++++++++++++++++++++++ 12 files changed, 917 insertions(+), 10 deletions(-) create mode 100644 include/m_scene_table.h create mode 100644 rel/m_cockroach.c create mode 100644 rel/m_police_box.c diff --git a/config/rel_slices.yml b/config/rel_slices.yml index 39519842..745039a7 100644 --- a/config/rel_slices.yml +++ b/config/rel_slices.yml @@ -1,5 +1,7 @@ sys_vimgr.c: .text: [0x803703F8, 0x80370418] +m_cockroach.c: + .text: [0x80385430, 0x80385A80] m_debug_hayakawa.c: .text: [0x803965E4, 0x803973E8] .rodata: [0x80641D50, 0x80641D90] @@ -21,6 +23,10 @@ m_malloc.c: #m_lib.c: #sqrtf statics # .text: [0x803BAB0C, 0x803BB960] # .rodata: [0x80642640, 0x80642680] +m_police_box.c: + .text: [0x803DE8A0, 0x803DEE38] + .rodata: [0x806431C8, 0x806431D8] + .data: [0x8065BE98, 0x8065BEF0] m_room_type/mRmTp_FtrItemNo2FtrIdx.c: .text: [0x803E7878, 0x803E78BC] #m_time.c: # unlinked until function callers that reorder local static variables are implemented. diff --git a/include/m_cockroach.h b/include/m_cockroach.h index bcc21f71..2f0be817 100644 --- a/include/m_cockroach.h +++ b/include/m_cockroach.h @@ -8,8 +8,25 @@ extern "C" { #endif +#define mCkRh_MAX_NUM 10 /* maximum 'stored' in the house */ +#define mCkRh_INTERVAL_DAYS 6 /* number of days before roaches will spawn */ + +#define mCkRh_CAN_LOOK_GOKI_NUM 3 /* maximum that can spawn in the house at once */ + +extern void mCkRh_InitGokiSaveData_InitNewPlayer(); +extern void mCkRh_InitGokiSaveData_1Room(int home_no); extern void mCkRh_InitGokiSaveData_1Room_ByHomeData(mHm_hs_c* home); extern void mCkRh_InitGokiSaveData_IslandPlayerRoom(); +extern void mCkRh_InitGokiSaveData_AllRoom(); +extern void mCkRh_SetGoingOutCottageTime(int scene_id); +extern void mCkRh_SavePlayTime(int player_no); +extern void mCkRh_DecideNowGokiFamilyCount(int player_no); +extern int mCkRh_PlussGokiN_NowRoom(int count, int scene_no); +extern int mCkRh_MinusGokiN_NowRoom(int count, int scene_id); +extern int mCkRh_NowSceneGokiFamilyCount(); +extern void mCkRh_InitCanLookGokiCount(); +extern int mCkRh_CalcCanLookGokiCount(int count); +extern int mCkRh_GetCanLookGokiCount(); #ifdef __cplusplus } diff --git a/include/m_common_data.h b/include/m_common_data.h index a94661d8..fa9e52f1 100644 --- a/include/m_common_data.h +++ b/include/m_common_data.h @@ -94,6 +94,7 @@ typedef union save_u { u8 raw[0x26000]; /* Temp to force length */ } Save; +/* sizeof(common_data_t) == 0x2DC00 */ typedef struct common_data_s { /* 0x000000 */ Save save; /* 0x026000 */ u8 game_started; @@ -109,6 +110,9 @@ typedef struct common_data_s { /* 0x026144 */ u8 tmp0[0x23E8]; /* 0x02852C */ s16 money_power; /* 0x02852E */ s16 goods_power; + /* 0x028530 */ u8 tmp1[0x5680]; + /* 0x02DBB0 */ s16 can_look_goki_count; + /* 0x02DBB2 */ u8 tmp2[0x4E]; } common_data_t; extern common_data_t common_data; diff --git a/include/m_event.h b/include/m_event.h index e755fcaa..67d0f90a 100644 --- a/include/m_event.h +++ b/include/m_event.h @@ -8,7 +8,96 @@ extern "C" { #endif +/** + * Event type definition + * xxxyyyyy yyyyyyyy yyyyyyyy yyyyyyyy + * + * x: event type (e.g. special event, 'first job' (chores) event, holidays, ...) (0-7) + * y: sub-type (specific event) + **/ + +#define mEv_SUBTYPE_BITS 29 +#define mEv_TYPE_BITMASK (0b111 << mEv_SUBTYPE_BITS) +#define mEv_SUBTYPE_BITMASK ((1 << mEv_SUBTYPE_BITS) - 1) + +#define mEv_GET_TYPE(event) (((event) & mEv_TYPE_BITMASK) >> mEv_SUBTYPE_BITS) +#define mEv_SET_TYPE(t) (((t) << mEv_SUBTYPE_BITS) & mEv_TYPE_BITMASK) + +#define mEv_GET_SUBTYPE(event) ((event) & mEv_SUBTYPE_BITMASK) +#define mEv_SET_SUBTYPE(s) ((s) & mEv_SUBTYPE_BITMASK) + +#define mEv_SET(type, subtype) (mEv_SET_TYPE(type) | mEv_SET_TYPE(subtype)) + +enum event_type { + mEv_SPNPC_EVENT, /* special NPC events */ + mEv_SAVED_EVENT, /* events saved to data */ + mEv_TYPE2_EVENT, /* unused? */ + mEv_TYPE3_EVENT, /* unused? */ + mEv_TYPE4_EVENT, /* unused? */ + mEv_DAILY_EVENT, /* checked daily always? aSL_ReportShopOpen2Event has event 3 */ + mEv_SPECL_EVENT, /* ??? secondary special npc event data? */ + + mEv_EVENT_NUM +}; + +enum events { + mEv_SPNPC_SHOP = (int)mEv_SET(mEv_SPNPC_EVENT, 0), + mEv_SPNPC_DESIGNER, + mEv_SPNPC_BROKER, + mEv_SPNPC_ARTIST, + mEv_SPNPC_ARABIAN, + mEv_SPNPC_GYPSY, + + mEv_SAVED_RENEWSHOP = (int)mEv_SET(mEv_SAVED_EVENT, 0), /* renew shop */ + mEv_SAVED_UNK1, /* unused */ + + /* intro through chores */ + mEv_SAVED_FIRSTJOB_PLR0, + mEv_SAVED_FIRSTJOB_PLR1, + mEv_SAVED_FIRSTJOB_PLR2, + mEv_SAVED_FIRSTJOB_PLR3, + + /* selecting house */ + mEv_SAVED_FIRSTINTRO_PLR0, + mEv_SAVED_FIRSTINTRO_PLR1, + mEv_SAVED_FIRSTINTRO_PLR2, + mEv_SAVED_FIRSTINTRO_PLR3, + + /* wait for next day to talk about HRA */ + mEv_SAVED_HRAWAIT_PLR0, + mEv_SAVED_HRAWAIT_PLR1, + mEv_SAVED_HRAWAIT_PLR2, + mEv_SAVED_HRAWAIT_PLR3, + + /* Nook will talk about HRA when entering the shop */ + mEv_SAVED_HRATALK_PLR0, + mEv_SAVED_HRATALK_PLR1, + mEv_SAVED_HRATALK_PLR2, + mEv_SAVED_HRATALK_PLR3, + + /* Do a 'favor' for a villager during chores */ + mEv_SAVED_FJOPENQUEST_PLR0, + mEv_SAVED_FJOPENQUEST_PLR1, + mEv_SAVED_FJOPENQUEST_PLR2, + mEv_SAVED_FJOPENQUEST_PLR3, + + /* Player going to another town, set at train station */ + mEv_SAVED_GATEWAY_PLR0, + mEv_SAVED_GATEWAY_PLR1, + mEv_SAVED_GATEWAY_PLR2, + mEv_SAVED_GATEWAY_PLR3, + mEv_SAVED_GATEWAY_FRGN, /* Foreigner */ + + mEv_DAILY_0 = (int)mEv_SET(mEv_DAILY_EVENT, 0), /* unused */ + mEv_DAILY_1, /* unused */ + mEv_DAILY_2, /* unused */ + mEv_DAILY_OPEN_SHOP, /* shop opened */ + + mEv_SPECL_DESIGNER_COMPLETE = (int)mEv_SET(mEv_SPECL_EVENT, 0) +}; + extern int mEv_CheckFirstJob(); +extern int mEv_CheckArbeit(); extern void mEv_debug_print4f(gfxprint_t* gfxprint); extern void mEv_sp_debug_print4f(gfxprint_t* gfxprint); diff --git a/include/m_field_info.h b/include/m_field_info.h index 6698a26d..3a99c081 100644 --- a/include/m_field_info.h +++ b/include/m_field_info.h @@ -9,6 +9,37 @@ extern "C" { #endif +enum field_type { + mFI_FIELDTYPE_FG, + mFI_FIELDTYPE_1, + mFI_FIELDTYPE_2, + mFI_FIELDTYPE_ROOM, + mFI_FIELDTYPE_NPC_ROOM, + mFI_FIELDTYPE_DEMO, + mFI_FIELDTYPE_PLAYER_ROOM, + + mFI_FIELDTYPE_NUM +}; + +#define mFI_TO_FIELD_ID(type, index) ((type << 12) | index) +#define mFI_GET_TYPE (field_id) (field_id & 0xF000) + +enum field_room { + /* TODO: others */ + + mFI_FIELD_PLAYER0_ROOM = mFI_TO_FIELD_ID(mFI_FIELDTYPE_PLAYER_ROOM, 0), + mFI_FIELD_PLAYER1_ROOM, + mFI_FIELD_PLAYER2_ROOM, + mFI_FIELD_PLAYER3_ROOM, + + /* TODO: others */ +}; + +#define mFI_GET_PLAYER_ROOM_NO(field_id) (((field_id)-mFI_FIELD_PLAYER0_ROOM) & 3) +#define mFI_IS_PLAYER_ROOM(field_id) \ + ((field_id) == mFI_FIELD_PLAYER0_ROOM || (field_id) == mFI_FIELD_PLAYER1_ROOM || \ + (field_id) == mFI_FIELD_PLAYER2_ROOM || (field_id) == mFI_FIELD_PLAYER3_ROOM) + /* Not sure about these other than the island one */ enum { mFI_CLIMATE_0, @@ -20,7 +51,10 @@ enum { mFI_CLIMATE_NUM }; +extern mActor_name_t mFI_GetFieldId(); extern int mFI_GetClimate(); +extern mActor_name_t* mFI_BkNumtoUtFGTop(int block_x, int block_z); +extern void mFI_ClearDeposit(int block_x, int block_z); extern void mFI_PrintNowBGNum(gfxprint_t* gfxprint); extern void mFI_PrintFgAttr(gfxprint_t* gfxprint); diff --git a/include/m_home_h.h b/include/m_home_h.h index b6c1c20f..4c9cd676 100644 --- a/include/m_home_h.h +++ b/include/m_home_h.h @@ -85,6 +85,7 @@ typedef struct home_wall_floor_s { typedef struct home_goki_s { /* 0x00 */ lbRTC_time_c time; /* last time updated */ /* 0x08 */ u8 num; /* number of cockroaches in the house */ + /* 0x09 */ u8 pad; /* unused outside of being initalized to 0 */ } mHm_goki_c; /* sizeof(mHm_lyr_c) == 0x228 */ diff --git a/include/m_name_table.h b/include/m_name_table.h index 82731d27..b55d7382 100644 --- a/include/m_name_table.h +++ b/include/m_name_table.h @@ -37,8 +37,10 @@ enum { #define ITEM_IS_FTR(n) \ (ITEM_NAME_GET_TYPE(n) == NAME_TYPE_FTR0 || ITEM_NAME_GET_TYPE(n) == NAME_TYPE_FTR1) -#define GET_NAME_ITEM0_CATEGORY(f) (((f)&0x800) >> 11) -#define GET_NAME_ITEM1_CATEGORY(f) (((f)&0xF00) >> 8) +#define ITEM_IS_ITEM1(n) (ITEM_NAME_GET_TYPE(n) == NAME_TYPE_ITEM1) + +#define GET_NAME_ITEM0_CATEGORY(f) (((f) & 0x0800) >> 11) +#define GET_NAME_ITEM1_CATEGORY(f) (((f) & 0x0F00) >> 8) #define EMPTY_NO 0x0000 @@ -47,6 +49,25 @@ enum { #define FTR_TAPEDECK 0x1E58 +#define ITM_TOOL_START 0x2200 +#define ITM_NET ITM_TOOL_START +#define ITM_AXE 0x2201 +#define ITM_SHOVEL 0x2202 +#define ITM_ROD 0x2203 + +#define ITM_ENV_START 0x2900 +#define ITM_SAPLING ITM_ENV_START +#define ITM_CEDAR_SAPLING 0x2901 +#define ITM_WHITE_PANSY_BAG 0x2902 +#define ITM_PURPLE_PANSY_BAG 0x2903 +#define ITM_YELLOW_PANSY_BAG 0x2904 +#define ITM_WHITE_COSMOS_BAG 0x2905 +#define ITM_PINK_COSMOS_BAG 0x2906 +#define ITM_BLUE_COSMOS_BAG 0x2907 +#define ITM_RED_TULIP_BAG 0x2908 +#define ITM_WHITE_TULIP_BAG 0x2909 +#define ITEM_YELLOW_TULIP_BAG 0x290A + #define ITM_COLLEGERULE 0x2B00 #define FTR_ORANGEBOX 0x30F8 diff --git a/include/m_police_box.h b/include/m_police_box.h index b93c4567..1f1455cc 100644 --- a/include/m_police_box.h +++ b/include/m_police_box.h @@ -3,18 +3,37 @@ #include "types.h" #include "m_actor_type.h" +#include "game.h" #ifdef __cplusplus extern "C" { #endif -#define mPB_POLICE_BOX_ITEM_STORAGE_COUNT 20 +#define mPB_POLICE_BOX_ITEM_STORAGE_COUNT 20 /* total storage space */ +#define mPB_MAX_GROW_SIZE 5 /* Maximum number of items to trigger generating random new items */ + +/* Possible new items to add to lost and found */ +enum police_box_category { + mPB_CATEGORY_GOODS, + mPB_CATEGORY_ITEM, + mPB_CATEGORY_FLOWER, + mPB_CATEGORY_UMBRELLA, + + mPB_CATEGORY_NUM +}; /* sizeof(PoliceBox_c) == 0x28 */ typedef struct police_box_s { /* 0x00 */ mActor_name_t keep_items[mPB_POLICE_BOX_ITEM_STORAGE_COUNT]; } PoliceBox_c; +extern void mPB_copy_itemBuf(mActor_name_t* item_buf); +extern int mPB_get_keep_item_sum(); +extern void mPB_keep_item(mActor_name_t item_no); +extern void mPB_keep_all_item_in_block(int blk_x, int blk_z); +extern void mPB_force_set_keep_item(); +extern void mPB_police_box_init(GAME* game); + #ifdef __cplusplus } #endif diff --git a/include/m_scene_table.h b/include/m_scene_table.h new file mode 100644 index 00000000..1323a84d --- /dev/null +++ b/include/m_scene_table.h @@ -0,0 +1,25 @@ +#ifndef M_SCENE_TABLE_H +#define M_SCENE_TABLE_H + +#include "types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + TODO: is this right? I assume so based on file names but + there may be a better place for this +*/ + +enum scene_table { + /* TODO: finish */ + SCENE_ISLAND_COTTAGE = 0x2F, + /* TODO: finish */ +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/m_shop.h b/include/m_shop.h index 8de62b9d..2260e201 100644 --- a/include/m_shop.h +++ b/include/m_shop.h @@ -6,6 +6,7 @@ #include "lb_rtc.h" #include "m_personal_id.h" #include "m_actor_type.h" +#include "game.h" #ifdef __cplusplus extern "C" { @@ -15,14 +16,81 @@ extern "C" { #define mSP_GOODS_COUNT 39 #define mSP_LOTTERY_ITEM_COUNT 3 +/* item list groups */ enum { - mSP_LIST_FURNITURE, - mSP_LIST_PAPER, - mSP_LIST_CLOTH, - mSP_LIST_CARPET, - mSP_LIST_WALLPAPER, + mSP_LIST_A, /* A priority list */ + mSP_LIST_B, /* B priority list */ + mSP_LIST_C, /* C priority list */ + mSP_LIST_EVENT, /* event list */ + mSP_LIST_TRAIN, /* */ + mSP_LIST_LOTTERY, /* lottery list */ + mSP_LIST_HALLOWEEN, /* halloween theme list */ + mSP_LIST_PRESENT, /* */ + mSP_LIST_CHRISTMAS, + mSP_LIST_SNOW, + mSP_LIST_HALLOWEEN2, + mSP_LIST_JONASON, + mSP_LIST_POSTOFFICE, + mSP_LIST_NINTENDO64, + mSP_LIST_SPECIALPRESENT, + mSP_LIST_ISLAND, + mSP_LIST_HOMEPAGE, + mSP_LIST_EVENTPRESENTCHUMON, + mSP_LIST_KAMAKURA, + mSP_LIST_ISLANDFAMICOM, + mSP_LIST_HARVEST, + mSP_LIST_MARIO, + mSP_LIST_TENT, + mSP_LIST_DUMMY23, + mSP_LIST_DUMMY24, + mSP_LIST_DUMMY25, + mSP_LIST_DUMMY26, + mSP_LIST_DUMMY27, + mSP_LIST_DUMMY28, + mSP_LIST_DUMMY29, - mSP_LIST_MAX + mSP_LIST_NUM +}; + +/* item List types */ +enum { + mSP_LISTTYPE_COMMON, /* common ABC priority list */ + mSP_LISTTYPE_UNCOMMON, /* uncommon ABC priority list */ + mSP_LISTTYPE_RARE, /* rare ABC priority list */ + mSP_LISTTYPE_EVENT, /* event list */ + mSP_LISTTYPE_TRAIN, /* */ + mSP_LISTTYPE_LOTTERY, /* lottery list */ + mSP_LISTTYPE_HALLOWEEN, /* halloween theme list */ + mSP_LISTTYPE_PRESENT, /* */ + mSP_LISTTYPE_ABC, /* ABC */ + mSP_LISTTYPE_CHRISTMAS, + mSP_LISTTYPE_SNOW, + mSP_LISTTYPE_HALLOWEEN2, + mSP_LISTTYPE_JONASON, + mSP_LISTTYPE_POSTOFFICE, + mSP_LISTTYPE_NINTENDO64, + mSP_LISTTYPE_SPECIALPRESENT, + mSP_LISTTYPE_ISLAND, + mSP_LISTTYPE_HOMEPAGE, + mSP_LISTTYPE_EVENTPRESENTCHUMON, + mSP_LISTTYPE_KAMAKURA, + mSP_LISTTYPE_ISLANDFAMICOM, + mSP_LISTTYPE_HARVEST, + mSP_LISTTYPE_MARIO, + mSP_LISTTYPE_TENT, + + mSP_LISTTYPE_NUM +}; + +/* item list kinds */ +enum { + mSP_KIND_FURNITURE, + mSP_KIND_PAPER, + mSP_KIND_CLOTH, + mSP_KIND_CARPET, + mSP_KIND_WALLPAPER, + + mSP_KIND_MAX }; /* sizeof(mSP_goods_priority_list_c) == 1 */ @@ -35,7 +103,7 @@ typedef struct shop_goods_priority_list_s { /* sizeof(Shop_c) == 0x140 */ typedef struct shop_s { - /* 0x000 */ mSP_goods_priority_list_c priority_lists[mSP_LIST_MAX]; /* ABC list rarity (known internally as priority) */ + /* 0x000 */ mSP_goods_priority_list_c priority_lists[mSP_KIND_MAX]; /* ABC list rarity (known internally as priority) */ /* 0x006 */ PersonalID_c unused_ids[mSP_PERSONAL_ID_COUNT]; /* unused personal ids */ /* 0x0CE */ mActor_name_t items[mSP_GOODS_COUNT]; /* standard shop items */ /* 0x11C */ mActor_name_t rare_item; /* spotlight rare item taken from rare furniture ABC list */ @@ -56,6 +124,8 @@ typedef struct shop_s { } Shop_c; extern void mSP_PrintNowShopSalesSum(gfxprint_t* gfxprint); +extern void mSP_SelectRandomItem_New(GAME* unused, mActor_name_t* item_tbl, int item_tbl_count, mActor_name_t* goods_exist_tbl, int goods_exist_tbl_count, int category, int list_type, int get_uncollected_item); +extern void mSP_RandomUmbSelect(mActor_name_t* item_buf, int item_buf_count); extern void mItemDebug_ItemDebugMain(); extern void mItemDebug_ItemDebugDraw(gfxprint_t* gfxprint); diff --git a/rel/m_cockroach.c b/rel/m_cockroach.c new file mode 100644 index 00000000..9146b4dd --- /dev/null +++ b/rel/m_cockroach.c @@ -0,0 +1,310 @@ +#include "m_cockroach.h" + +#include "m_home.h" +#include "m_house.h" +#include "m_private.h" +#include "m_field_info.h" +#include "m_scene_table.h" +#include "m_common_data.h" + +/** + * @brief Clamps the input cockroach count between [0, mCkRh_MAX_NUM]. + * + * @param count The new cockroach count + * @return The clamped cockroach count + **/ +static int mCkRh_GokiFamilyCount2Good(int count) { + if (count < 0) { + return 0; + } + + return (count <= mCkRh_MAX_NUM ? count : mCkRh_MAX_NUM); +} + +/** + * @brief Initializes all house cockroach save data for unclaimed houses + * when a new Player is created. + **/ +extern void mCkRh_InitGokiSaveData_InitNewPlayer() { + int i; + + for (i = 0; i < PLAYER_NUM; i++) { + if (mHS_get_pl_no_detail(i) == -1) { + mCkRh_InitGokiSaveData_1Room(i); + } + } +} + +/** + * @brief Initializes a single house's cockroach data by house index. + * + * @param home_no The house index whose cockroach data will be initialized + **/ +extern void mCkRh_InitGokiSaveData_1Room(int home_no) { + mCkRh_InitGokiSaveData_1Room_ByHomeData(Save_GetPointer(homes[home_no])); +} + +/** + * @brief Initializes a single house's cockroach data. + * + * @param home The mHm_hs_c house whose cockroach data wil be initialized. + **/ +extern void mCkRh_InitGokiSaveData_1Room_ByHomeData(mHm_hs_c* home) { + home->goki.num = 0; + home->goki.pad = 0; + + home->goki.time = Common_Get(time.rtc_time); +} + +/** + * @brief Initializes island cottage cockroach data. + **/ +extern void mCkRh_InitGokiSaveData_IslandPlayerRoom() { + const lbRTC_time_c* rtc_time = Common_GetPointer(time.rtc_time); + + Save_Set(island.cottage.goki.num, 0); + Save_Set(island.cottage.goki.pad, 0); + Save_Set(island.cottage.goki.time.year, rtc_time->year); + Save_Set(island.cottage.goki.time.month, rtc_time->month); + Save_Set(island.cottage.goki.time.day, rtc_time->day); +} + +/** + * @brief Initializes all houses' cockroach data even if a player owns it. + **/ +extern void mCkRh_InitGokiSaveData_AllRoom() { + int i; + + for (i = 0; i < PLAYER_NUM; i++) { + mCkRh_InitGokiSaveData_1Room(i); + } +} + +/** + * @brief Updates the cockroach 'last entered' time for the island cottage. + * + * @param scene_id The current scene id + **/ +extern void mCkRh_SetGoingOutCottageTime(int scene_id) { + if (scene_id == SCENE_ISLAND_COTTAGE) { + Save_Set(island.cottage.goki.time.year, Common_Get(time.rtc_time.year)); + Save_Set(island.cottage.goki.time.month, Common_Get(time.rtc_time.month)); + Save_Set(island.cottage.goki.time.day, Common_Get(time.rtc_time.day)); + } +} + +/** + * @brief Updates the cockroach 'last entered' time for a player's house. + * + * @param player_no The index of the player whose cockroach data will be updated + **/ +extern void mCkRh_SavePlayTime(int player_no) { + if (player_no < PLAYER_NUM) { + int home_no = mHS_get_arrange_idx(player_no); + Save_Set(homes[home_no & 3].goki.time, Common_Get(time.rtc_time)); + } +} + +/** + * @brief Returns the inverval in days between the current time and + * the last time that the player's cockroach 'last enter time'. + * + * @param player_no The index of the player whose 'days gap' will be calculated + * @return When a foriegner, 0, otherwise day interval + **/ +static int mCkRh_DaysGapCompareWithSaveTime(int player_no) { + int homeid; + lbRTC_time_c goki_time; + int interval; + + if (player_no < PLAYER_NUM) { + homeid = mHS_get_arrange_idx(player_no) & 3; + goki_time.year = Save_Get(homes[homeid].goki.time.year); + goki_time.month = Save_Get(homes[homeid].goki.time.month); + goki_time.day = Save_Get(homes[homeid].goki.time.day); + goki_time.weekday = lbRTC_Week(goki_time.year, goki_time.month, goki_time.day); + goki_time.hour = 1; + goki_time.min = 1; + goki_time.sec = 1; + + interval = lbRTC_GetIntervalDays(&goki_time, Common_GetPointer(time.rtc_time)); + } + else { + interval = 0; + } + + return interval; +} + +/** + * @brief Gets the day interval between current time and the island cottage + * cockroach last enter time. + * + * @return days between current time and last island cottage enter time + **/ +static int mCkRh_DaysGapCompareWithCottageSaveTime() { + lbRTC_time_c goki_time; + + goki_time.year = Save_Get(island.cottage.goki.time.year); + goki_time.month = Save_Get(island.cottage.goki.time.month); + goki_time.day = Save_Get(island.cottage.goki.time.day); + goki_time.weekday = lbRTC_Week(goki_time.year, goki_time.month, goki_time.day); + goki_time.hour = 1; + goki_time.min = 1; + goki_time.sec = 1; + + return lbRTC_GetIntervalDays(&goki_time, Common_GetPointer(time.rtc_time)); +} + +/** + * @brief Determines and updates the saved cockroach number for a player. + * + * The minimum day interval between the current time and the cockroach's + * 'last entered time' is 7 days. The amount you get scales linearly, + * starting with 1 new cockroach on day 7, 2 new on day 8, ... + * + * The maximum number of cockroaches is clamped to mCkRh_MAX_NUM which stock + * is 10 cockroaches. + * + * @param player_no The inex of the player whose cockroach data will be updated + **/ +extern void mCkRh_DecideNowGokiFamilyCount(int player_no) { + int count; + int day_gap; + int home_no; + int goki_num; + + /* player must live in town */ + if (player_no < PLAYER_NUM) { + day_gap = mCkRh_DaysGapCompareWithSaveTime(player_no); + home_no = mHS_get_arrange_idx(player_no) & 3; + if (day_gap > mCkRh_INTERVAL_DAYS) { + goki_num = Save_Get(homes[home_no].goki.num); + count = goki_num > 0 ? day_gap : day_gap - mCkRh_INTERVAL_DAYS; + Save_Set(homes[home_no].goki.num, mCkRh_GokiFamilyCount2Good(count + goki_num)); + } + } + + day_gap = mCkRh_DaysGapCompareWithCottageSaveTime(); + if (day_gap > mCkRh_INTERVAL_DAYS) { + u8* goki_num = Save_GetPointer(island.cottage.goki.num); + count = (int)*goki_num > 0 ? day_gap : day_gap - mCkRh_INTERVAL_DAYS; + *goki_num = mCkRh_GokiFamilyCount2Good(count + (int)*goki_num); + } +} + + +/** + * @brief Adds a specific amount of cockroaches to the current player's house. + * + * @param count The number of cockroaches to add (total clamped to mCkRh_MAX_NUM) + * @return TRUE/FALSE cockroach data was updated + **/ +extern int mCkRh_PlussGokiN_NowRoom(int count, int scene_no) { + mActor_name_t fieldid = mFI_GetFieldId(); + + if (mFI_IS_PLAYER_ROOM(fieldid)) { + int player_no = Common_Get(player_no); + int house_field_id = mFI_GET_PLAYER_ROOM_NO(fieldid); + int home_id = mHS_get_arrange_idx(player_no) & 3; + if ((player_no < PLAYER_NUM) && (house_field_id == home_id)) { + Save_Set(homes[home_id].goki.num, mCkRh_GokiFamilyCount2Good(count + Save_Get(homes[home_id].goki.num))); + return TRUE; + } + } + else if (scene_no == SCENE_ISLAND_COTTAGE) { + u8* goki_num = Save_GetPointer(island.cottage.goki.num); + *goki_num = mCkRh_GokiFamilyCount2Good(count + *goki_num); + } + + return FALSE; +} + +/** + * @brief Removes a specific amount of cockroaches to the current player's house. + * + * @param count The number of cockroaches to remove (minimum clamped to 0) + * @return TRUE/FALSE cockroach data was updated + **/ +extern int mCkRh_MinusGokiN_NowRoom(int count, int scene_id) { + mActor_name_t field_id = mFI_GetFieldId(); + if (mFI_IS_PLAYER_ROOM(field_id)) { + int player_no = Common_Get(player_no); + int house_field_id = mFI_GET_PLAYER_ROOM_NO(field_id); + int home_no = mHS_get_arrange_idx(player_no) & 3; + if (player_no < PLAYER_NUM && house_field_id == home_no) { + Save_Set(homes[home_no].goki.num, mCkRh_GokiFamilyCount2Good(Save_Get(homes[home_no].goki.num) - count)); + return TRUE; + } + } + else if (scene_id == SCENE_ISLAND_COTTAGE) { + u8* goki_num = Save_GetPointer(island.cottage.goki.num); + *goki_num = mCkRh_GokiFamilyCount2Good(*goki_num - count); + } + + return FALSE; +} + +/** + * @brief Gets the cockroach count for the current scene id. + * + * @return + * - in player house: house cockroach count + * - in island octtage: island cottage cockroach count + * - elsewhere: 0 + **/ +extern int mCkRh_NowSceneGokiFamilyCount() { + mActor_name_t fieldid = mFI_GetFieldId(); + if (mFI_IS_PLAYER_ROOM(fieldid)) { + return Save_Get(homes[mFI_GET_PLAYER_ROOM_NO(fieldid)].goki.num); + } + else if (Save_Get(scene_no) == SCENE_ISLAND_COTTAGE) { + return Save_Get(island.cottage.goki.num); + } + + return 0; +} + +/** + * @brief Initializes the 'can_look_goki_count' variable to 0. + * + * This variable controls how many cockroaches are currently + * visible to the player in a room. + **/ +extern void mCkRh_InitCanLookGokiCount() { + Common_Set(can_look_goki_count, 0); +} + +/** + * @brief Changes the 'can_look_goki_count' variable by 'count'. + * + * 'can_look_goki_count' is clamped between + * + * @param count The number of visible cockroaches to add/remove + * @return TRUE/FALSE were number of visible cockroaches updated? + **/ +extern int mCkRh_CalcCanLookGokiCount(int count) { + count += Common_Get(can_look_goki_count); + + if (count < 0) { + Common_Set(can_look_goki_count, 0); + return FALSE; + } + + if (count > mCkRh_CAN_LOOK_GOKI_NUM) { + Common_Set(can_look_goki_count, mCkRh_CAN_LOOK_GOKI_NUM); + return FALSE; + } + + Common_Set(can_look_goki_count, count); + return TRUE; +} + +/** + * @brief Retrieves the 'can_look_gooki_count' value. + * + * @return number of cockroaches currently visible to the player + **/ +extern int mCkRh_GetCanLookGokiCount() { + return Common_Get(can_look_goki_count); +} diff --git a/rel/m_police_box.c b/rel/m_police_box.c new file mode 100644 index 00000000..df72e1a9 --- /dev/null +++ b/rel/m_police_box.c @@ -0,0 +1,311 @@ +#include "m_police_box.h" + +#include "game.h" +#include "m_actor_type.h" +#include "libc64/qrand.h" +#include "m_common_data.h" +#include "m_name_table.h" +#include "m_field_info.h" +#include "m_field_make.h" +#include "libultra/libultra.h" +#include "m_lib.h" +#include "m_shop.h" + +/** + * @brief Copies an array of items to the lost and found. + * + * item_buf must have a length of at least mPB_POLICE_BOX_ITEM_STORAGE_COUNT. + * + * @param item_buf The array of items to copy + **/ +extern void mPB_copy_itemBuf(mActor_name_t* item_buf) { + mActor_name_t* keep_item; + int count; + int i; + + keep_item = Save_Get(police_box.keep_items); + count = 0; + + /* copy over the items */ + for (i = 0; i < mPB_POLICE_BOX_ITEM_STORAGE_COUNT; i++) { + if (*item_buf != EMPTY_NO) { + *keep_item = *item_buf; + count++; + keep_item++; + } + + item_buf++; + } + + /* clear out any unset items if necessary */ + for (i = count; i < mPB_POLICE_BOX_ITEM_STORAGE_COUNT; i++) { + *keep_item++ = EMPTY_NO; + } +} + +/** + * @brief Gets the number of used slots in the lost and found. + * + * @return count of used slots + **/ +extern int mPB_get_keep_item_sum() { + int i; + mActor_name_t* keep_item = Save_Get(police_box.keep_items); + int sum = 0; + + for (i = 0; i < mPB_POLICE_BOX_ITEM_STORAGE_COUNT; i++) { + if (*keep_item != EMPTY_NO) { + sum++; + } + keep_item++; + } + + return sum; +} + +/** + * @brief Adds an item to the lost and found. + * + * The item must be of type 'ITEM1' or 'FTR0/FTR1'. + * If the lost and found is full, the first item in it + * will be deleted and all subsequent items will be shifted + * over by one. + * + * @param item_no The item to add + **/ +extern void mPB_keep_item(mActor_name_t item_no) { + if (ITEM_IS_ITEM1(item_no) || ITEM_IS_FTR(item_no)) { + int keep_item_sum = mPB_get_keep_item_sum(); + if (keep_item_sum >= mPB_POLICE_BOX_ITEM_STORAGE_COUNT) { + /* delete the first item and move the rest down one slot */ + mActor_name_t* keep_item = Save_Get(police_box.keep_items); + int i; + + for (i = 0; i < mPB_POLICE_BOX_ITEM_STORAGE_COUNT - 1; i++) { + keep_item[0] = keep_item[1]; + keep_item++; + } + + keep_item_sum = mPB_POLICE_BOX_ITEM_STORAGE_COUNT - 1; + } + + Save_Set(police_box.keep_items[keep_item_sum], item_no); + } +} + +/** + * @brief Transfers all items of type 'ITEM1' and 'FTR0/FTR1' to the lost and found. + * + * If the number of items to add exceeds the size of the lost and found, + * the items kept will be randomly selected by using rng to overwrite + * previously saved items from the acre. + * + * Additionally, items already in the lost in found will be deleted to + * make space for the new items if necessary. + * + * @param blk_x The x acre (column) + * @param blk_z The z acre (row) + **/ +extern void mPB_keep_all_item_in_block(int blk_x, int blk_z) { + int count; + mActor_name_t new_items[mPB_POLICE_BOX_ITEM_STORAGE_COUNT]; + int keep_item_sum; + mActor_name_t item; + mActor_name_t* block_items; + int i; + + keep_item_sum = mPB_get_keep_item_sum(); + block_items = mFI_BkNumtoUtFGTop(blk_x, blk_z); + count = 0; + + bzero(new_items, mPB_POLICE_BOX_ITEM_STORAGE_COUNT * sizeof(mActor_name_t)); + + for (i = 0; i < UT_X_NUM * UT_Z_NUM; i++) { + int item_type; + item = *block_items; + + /* only accept items in the 0x1XXX, 2XXX, and 3XXX range */ + item_type = ITEM_NAME_GET_TYPE(item); + if (item_type == NAME_TYPE_FTR0 || item_type == NAME_TYPE_ITEM1 || item_type == NAME_TYPE_FTR1) { + if (count < mPB_POLICE_BOX_ITEM_STORAGE_COUNT) { + /* space is still available, so directly add it */ + new_items[count++] = item; + } + else { + /* randomly overwrite one of the items in the lost and found */ + new_items[(int)(fqrand() * mPB_POLICE_BOX_ITEM_STORAGE_COUNT)] = item; + } + + *block_items = EMPTY_NO; + } + block_items++; + } + + if (count > 0) { + int total_sum = count + keep_item_sum; + + if (total_sum <= mPB_POLICE_BOX_ITEM_STORAGE_COUNT) { + mem_copy((u8*)(Save_Get(police_box.keep_items) + keep_item_sum), (u8*)new_items, count * sizeof(mActor_name_t)); + } + else { + mActor_name_t* dst = Save_Get(police_box.keep_items); + int save_count = total_sum - mPB_POLICE_BOX_ITEM_STORAGE_COUNT; + int keep_sum = keep_item_sum - save_count; + + /* move over saved items already in lost and found */ + for (i = 0; i < keep_sum; i++) { + dst[i] = dst[i + save_count]; + } + + /* copy newly added items */ + mem_copy((u8*)(Save_Get(police_box.keep_items) + keep_sum), (u8*)new_items, count * sizeof(mActor_name_t)); + } + } + + /* clear buried item flags in the cleared acre */ + mFI_ClearDeposit(blk_x, blk_z); +} + +/* select random item function definition */ +typedef mActor_name_t (*mPB_get_force_set_proc)(); + +/** + * @brief Selects a random 'goods' item to add to the lost and found/ + * + * @return The randomly selected item + **/ +static mActor_name_t mPB_get_force_set_item_goods() { + static int category_table[mSP_KIND_MAX] = { + mSP_KIND_FURNITURE, + mSP_KIND_PAPER, + mSP_KIND_CLOTH, + mSP_KIND_CARPET, + mSP_KIND_WALLPAPER + }; + + static int prob_table[mSP_KIND_MAX] = { + 35, /* furniture 0-35 (36%) */ + 58, /* stationery 36-58 (23%) */ + 88, /* clothing 59-88 (30%) */ + 94, /* carpets 89-94 (6%) */ + 100 /* wallpaper 95-99 (5%) (fqrand is [min, max) so 100 is not possible) */ + }; + + + int category = mSP_KIND_MAX; + mActor_name_t item = EMPTY_NO; + int roll = (int)(fqrand() * 100.0f); + int i; + + for (i = 0; i < mSP_KIND_MAX; i++) { + if (roll <= prob_table[i]) { + category = i; + break; + } + } + + mSP_SelectRandomItem_New(NULL, &item, 1, NULL, 0, category_table[category], mSP_LISTTYPE_COMMON, FALSE); + return item; +} + +/** + * @brief Selects a random tool or sapling to add to the lost and found. + * + * @return The randomly selected item + **/ +static mActor_name_t mPB_get_force_set_item_item() { + static mActor_name_t category_table[6] = { + ITM_NET, ITM_AXE, ITM_SHOVEL, ITM_ROD, ITM_SAPLING, ITM_CEDAR_SAPLING + }; + + return category_table[(int)(fqrand() * 6.0f)]; +} + +/** + * @brief Selects a random flower bag to add to the lost and found. + * + * @return The randomly selected flower bag + **/ +static mActor_name_t mPB_get_force_set_item_flower() { + return ITM_WHITE_PANSY_BAG + (int)(fqrand() * 8.0f); +} + +/** + * @brief Selects a random umbrella to add to the lost and found. + * + * @return The randomly selected umbrella + **/ +static mActor_name_t mPB_get_force_set_item_umbrella() { + mActor_name_t umbrella; + mSP_RandomUmbSelect(&umbrella, 1); + + return umbrella; +} + +/** + * @brief Selects a random item from a random category to add to the lost and found. + * + * @return The randomly selected item + **/ +static mActor_name_t mPB_get_force_set_item() { + static mPB_get_force_set_proc force_proc[mPB_CATEGORY_NUM] = { + &mPB_get_force_set_item_goods, + &mPB_get_force_set_item_item, + &mPB_get_force_set_item_flower, + &mPB_get_force_set_item_umbrella + }; + + static int prob_table[mPB_CATEGORY_NUM] = { + 85, /* 0-85 (86%) 'goods' */ + 90, /* 86-90 (5%) 'item' */ + 95, /* 91-95 (5%) 'flower' */ + 100 /* 96-100 (4%) 'umbrella' (again, fqrand is [min, max) therefore 100 is never possible) */ + }; + + int proc = mPB_CATEGORY_NUM; + int roll = (int)(fqrand() * 100.0f); + int i; + + for (i = 0; i < mPB_CATEGORY_NUM; i++) { + if (roll <= prob_table[i]) { + proc = i; + break; + } + } + + return (*force_proc[proc])(); +} + +/** + * @brief Adds a random item to the lost and found if grow space is available. + **/ +extern void mPB_force_set_keep_item() { + /* if lost and found item count is less-than-equal-to max grow size & 50-50 roll */ + if (mPB_get_keep_item_sum() <= mPB_MAX_GROW_SIZE && (int)qrand() >= 0) { + mPB_keep_item(mPB_get_force_set_item()); + } +} + +/** + * @brief Initializes the police station lost and found. + * + * The lost and found will always start with one random common + * priority list furniture and two random common priority list + * shirts. + * + * @param game The GAME pointer to pass to mSP_SelectRandomItem_New, goes unused + **/ +extern void mPB_police_box_init(GAME* game) { + mActor_name_t* keep_items = Save_Get(police_box.keep_items); + int i; + + /* generate one random ABC prio furniture and two random ABC prio shirts */ + mSP_SelectRandomItem_New(game, keep_items + 0, 1, NULL, 0, mSP_KIND_FURNITURE, mSP_LISTTYPE_ABC, FALSE); + mSP_SelectRandomItem_New(game, keep_items + 1, 2, NULL, 0, mSP_KIND_CLOTH, mSP_LISTTYPE_ABC, FALSE); + + /* clear the rest of the lost and found */ + keep_items += 3; + for (i = 0; i < mPB_POLICE_BOX_ITEM_STORAGE_COUNT - 3; i++) { + *keep_items++ = EMPTY_NO; + } +}