diff --git a/config/rel_slices.yml b/config/rel_slices.yml index a3e9cd79..c710c500 100644 --- a/config/rel_slices.yml +++ b/config/rel_slices.yml @@ -533,6 +533,9 @@ ac_set_ovl_insect.c: ac_shop_design.c: .text: [0x8049C748, 0x8049D51C] .data: [0x8068E3C0, 0x8068E430] +ac_shop_level.c: + .text: [0x8049F40C, 0x8049FEC4] + .data: [0x8068E520, 0x8068E708] ac_sign.c: .text: [0x804A127C, 0x804A3328] .rodata: [0x80645D18, 0x80645D78] diff --git a/include/ac_shop_level.h b/include/ac_shop_level.h index 865f7aff..4896fd24 100644 --- a/include/ac_shop_level.h +++ b/include/ac_shop_level.h @@ -8,6 +8,14 @@ extern "C" { #endif +typedef struct shop_level_actor_s SHOP_LEVEL_ACTOR; + +struct shop_level_actor_s { + ACTOR actor_class; + int bx; + int bz; +}; + extern ACTOR_PROFILE Shop_Level_Profile; #ifdef __cplusplus @@ -15,4 +23,3 @@ extern ACTOR_PROFILE Shop_Level_Profile; #endif #endif - diff --git a/include/m_event.h b/include/m_event.h index 7dfe6908..988f3c25 100644 --- a/include/m_event.h +++ b/include/m_event.h @@ -591,6 +591,7 @@ extern int mEv_ArbeitPlayer(u32 player_no); extern u16 mEv_get_special_event_type(); extern void mEv_ClearEventSaveInfo(mEv_event_save_c* event_save_data); extern void mEv_EventON(u32 event_kind); +extern void mEv_EventOFF(u32 event_kind); extern int mEv_CheckGateway(); extern int mEv_check_schedule(int event); extern mEv_place_data_c* mEv_get_common_place(int type, u8 id); diff --git a/src/ac_shop_level.c b/src/ac_shop_level.c new file mode 100644 index 00000000..2fd802fc --- /dev/null +++ b/src/ac_shop_level.c @@ -0,0 +1,397 @@ +#include "ac_shop_level.h" + +#include "m_common_data.h" +#include "m_player_lib.h" +#include "m_house.h" +#include "m_handbill.h" +#include "m_string.h" + +static void Shop_Level_Actor_ct(ACTOR* actorx, GAME* game); +static void Shop_Level_Actor_dt(ACTOR* actorx, GAME* game); +static void Shop_Level_Actor_move(ACTOR* actorx, GAME* game); +static void Shop_Level_Actor_draw(ACTOR* actorx, GAME* game); + +// clang-format off +ACTOR_PROFILE Shop_Level_Profile = { + mAc_PROFILE_SHOP_LEVEL, + 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(SHOP_LEVEL_ACTOR), + &Shop_Level_Actor_ct, + &Shop_Level_Actor_dt, + &Shop_Level_Actor_move, + &Shop_Level_Actor_draw, + NULL, +}; +// clang-format on + +static void aSL_SetShopRenewalChirashi_Notice(int shop_level, lbRTC_time_c* time_p) { + static int renewal_chirashi_bunmen[] = { 0x0018, 0x0019, 0x001A, 0x001A }; + int header_back_start; + Mail_c mail; + Mail_ct_c* content = &mail.content; + u8 year_str[6]; + u8 month_str[9]; + u8 day_str[4]; + lbRTC_time_c renew_start_date = *time_p; + int i; + + lbRTC_Sub_DD(&renew_start_date, 1); + for (i = 0; i < PLAYER_NUM; i++) { + int player_no = mHS_get_pl_no_detail(i); + int free_idx = mMl_chk_mail_free_space(Save_Get(homes[i]).mailbox, HOME_MAILBOX_SIZE); + + if (free_idx >= 0 && player_no != -1 && mEv_ArbeitPlayer(player_no) == FALSE) { + int year_str_len; + + mString_Load_StringFromRom(month_str, sizeof(month_str), + mString_MONTH_START + (renew_start_date.month - 1)); + mHandbill_Set_free_str(mHandbill_FREE_STR0, month_str, sizeof(month_str)); + mString_Load_StringFromRom(day_str, sizeof(day_str), mString_DAY_START + (renew_start_date.day - 1)); + mHandbill_Set_free_str(mHandbill_FREE_STR1, day_str, sizeof(day_str)); + year_str_len = mString_Load_YearStringFromRom(year_str, renew_start_date.year); + mHandbill_Set_free_str(mHandbill_FREE_STR2, year_str, year_str_len); + mMl_clear_mail(&mail); + mHandbill_Load_HandbillFromRom(content->header, &header_back_start, content->footer, content->body, + renewal_chirashi_bunmen[shop_level & 3]); + content->header_back_start = header_back_start; + content->font = mMl_FONT_0; + content->paper_type = (u8)ITM_PAPER55; + content->mail_type = mMl_TYPE_SHOP_SALE_LEAFLET; + mPr_CopyPersonalID(&mail.header.recipient.personalID, &Save_Get(private_data[player_no]).player_ID); + mail.header.recipient.type = mMl_NAME_TYPE_PLAYER; + mMl_copy_mail(&Save_Get(homes[i]).mailbox[free_idx], &mail); + } + } +} + +static void aSL_SetRenewalChiraswhi_Notice(void) { + if (Save_Get(shop).shop_info.send_upgrade_notice == TRUE) { + if (mSP_GetShopLevel() < mSP_GetRealShopLevel()) { + lbRTC_time_c* rtc_time_p = Common_GetPointer(time.rtc_time); + lbRTC_time_c renew_time = Save_Get(shop).renewal_time; + + lbRTC_Sub_DD(&renew_time, 1); + if (lbRTC_IsOverTime(&renew_time, rtc_time_p) == lbRTC_OVER || + lbRTC_IsEqualTime(rtc_time_p, &renew_time, lbRTC_CHECK_YEARS | lbRTC_CHECK_MONTHS | lbRTC_CHECK_DAYS)) { + aSL_SetShopRenewalChirashi_Notice(mSP_GetShopLevel(), &Save_Get(shop).renewal_time); + Save_Get(shop).shop_info.send_upgrade_notice = FALSE; + } + } else { + aSL_SetShopRenewalChirashi_Notice((mSP_GetShopLevel() - 1) & 3, &Save_Get(shop).renewal_time); + Save_Get(shop).shop_info.send_upgrade_notice = FALSE; + } + } +} + +typedef struct shop_level_rewrite_offset_s { + int ofs_ux; + int ofs_uz; +} aSL_rewrite_ofs_c; + +// clang-format off +static aSL_rewrite_ofs_c aSL_zakka_rw[] = { + { -1, -1 }, + { 0, -1 }, + { -2, 0 }, + { -1, 0 }, + { 1, 0 }, + { -2, 1 }, + { -1, 1 }, + { 0, 1 }, + { 1, 1 }, + { -1, 2 }, + { 0, 2 }, +}; +// clang-format on + +// clang-format off +static aSL_rewrite_ofs_c aSL_conveni_rw[] = { + { -2, -2 }, + { -1, -2 }, + { -3, -1 }, + { -2, -1 }, + { -1, -1 }, + { 0, -1 }, + { -3, 0 }, + { -2, 0 }, + { -1, 0 }, + { 1, 0 }, + { -2, 1 }, + { -1, 1 }, + { 0, 1 }, + { 1, 1 }, + { -1, 2 }, + { 0, 2 }, +}; +// clang-format on + +// clang-format off +static aSL_rewrite_ofs_c aSL_super_rw[] = { + { -1, -3 }, + { 0, -3 }, + { -2, -2 }, + { -1, -2 }, + { 0, -2 }, + { 1, -2 }, + { -3, -1 }, + { -2, -1 }, + { -1, -1 }, + { 0, -1 }, + { 1, -1 }, + { 2, -1 }, + { -3, 0 }, + { -2, 0 }, + { -1, 0 }, + { 1, 0 }, + { 2, 0 }, + { -2, 1 }, + { -1, 1 }, + { 0, 1 }, + { 1, 1 }, + { -1, 2 }, + { 0, 2 }, +}; +// clang-format on + +static void aSL_GroundClean(s16 level, s16 next_level, mActor_name_t* fg_p, int bx, int bz, int ux, int uz) { + static aSL_rewrite_ofs_c* table[] = { + aSL_zakka_rw, + aSL_conveni_rw, + aSL_super_rw, + aSL_super_rw, + }; + static int rw_cnt[] = { + ARRAY_COUNT(aSL_zakka_rw), + ARRAY_COUNT(aSL_conveni_rw), + ARRAY_COUNT(aSL_super_rw), + ARRAY_COUNT(aSL_super_rw), + }; + + int i; + aSL_rewrite_ofs_c* rewrite_ofs_p; + u16* deposit_p; + + deposit_p = mFI_GetDepositP(bx, bz); + rewrite_ofs_p = table[next_level & 3]; + + for (i = 0; i < rw_cnt[next_level & 3]; i++) { + int rewrite_ux = (ux + rewrite_ofs_p[i].ofs_ux) & 0xF; + int rewrite_uz = (uz + rewrite_ofs_p[i].ofs_uz) & 0xF; + int rewrite_unit = rewrite_uz * UT_X_NUM + rewrite_ux; + + mPB_keep_item(fg_p[rewrite_unit]); + mFI_BlockDepositOFF(deposit_p, rewrite_ux, rewrite_uz); + fg_p[rewrite_unit] = RSV_NO; + } +} + +static int aSL_RewriteShopFg(ACTOR* actorx, GAME* game, s16 level, s16 next_level) { + SHOP_LEVEL_ACTOR* shop_level = (SHOP_LEVEL_ACTOR*)actorx; + int i; + int ux; + int uz; + mActor_name_t* fg_top_p; + mActor_name_t* fg_p; + + fg_top_p = mFI_BkNumtoUtFGTop(shop_level->bx, shop_level->bz); + fg_p = fg_top_p; + + if (fg_top_p != NULL) { + for (i = 0; i < UT_TOTAL_NUM; i++) { + /* @BUG - shouldn't this just be <= SHOP3? This goes one over which would include the Post Office */ + if (*fg_top_p >= SHOP0 && *fg_top_p <= (SHOP3 + 1)) { + ux = i & 0xF; + uz = (i >> 4) & 0xF; + *fg_top_p = SHOP0 + next_level; + aSL_GroundClean((s16)level, (s16)next_level, fg_p, shop_level->bx, shop_level->bz, ux, uz); + return TRUE; + } + + fg_top_p++; + } + } + + return FALSE; +} + +static void aSL_RenewShop(ACTOR* actorx, GAME* game) { + int last_scene = Common_Get(last_scene_no); + + if (last_scene == SCENE_SHOP0 || last_scene == SCENE_CONVENI || last_scene == SCENE_SUPER || + last_scene == SCENE_DEPART || last_scene == SCENE_DEPART_2) { + return; + } + + if (mFI_GET_TYPE(mFI_GetFieldId()) == mFI_FIELD_FG && Save_Get(scene_no) == SCENE_FG && + mEv_CheckEvent(mEv_SAVED_RENEWSHOP) == TRUE) { + lbRTC_time_c renew_time = Save_Get(shop).renewal_time; + lbRTC_time_c now_time = Common_Get(time.rtc_time); + + aSL_SetRenewalChiraswhi_Notice(); + if (lbRTC_IsOverTime(&renew_time, &now_time) == lbRTC_OVER || + lbRTC_IsEqualTime(&now_time, &renew_time, lbRTC_CHECK_YEARS | lbRTC_CHECK_MONTHS | lbRTC_CHECK_DAYS)) { + if (aSL_RewriteShopFg(actorx, game, mSP_GetShopLevel(), mSP_GetRealShopLevel()) && mSP_RenewShopLevel()) { + mEv_EventOFF(mEv_SAVED_RENEWSHOP); + mSP_NewExchangeDay(); + mSP_ExchangeLineUp_ZeldaMalloc(); + } + } + } +} + +static void Shop_Level_Actor_ct(ACTOR* actorx, GAME* game) { + SHOP_LEVEL_ACTOR* shop_level = (SHOP_LEVEL_ACTOR*)actorx; + GAME_PLAY* play = (GAME_PLAY*)game; + ACTOR* player_actor = GET_PLAYER_ACTOR_ACTOR(play); + xyz_t* pos_p; + int shop_block_flag; + + shop_level->bx = 0; + shop_level->bz = 0; + if (player_actor != NULL) { + pos_p = &player_actor->world.position; + } else { + pos_p = &actorx->world.position; + } + + /* Try to move shop level actor to where the player currently is */ + actorx->world.position = *pos_p; + shop_block_flag = mFI_BlockKind2BkNum(&shop_level->bx, &shop_level->bz, mRF_BLOCKKIND_SHOP); + if (Save_Get(scene_no) == SCENE_FG) { + mSP_SetShopRareFurnitureChirashi(Common_Get(player_no), Save_Get(shop).items, mSP_GOODS_COUNT, NULL); + Save_Get(shop).shop_info.not_loaded_before = FALSE; + } + + if (shop_block_flag) { + aSL_RenewShop(actorx, game); + } +} + +static void Shop_Level_Actor_dt(ACTOR* actorx, GAME* game) { + // nothing +} + +static void Shop_Level_Actor_draw(ACTOR* actorx, GAME* game) { + // nothing +} + +static void aSL_JudgeRenewShop(ACTOR* actorx, GAME* game) { + if (Save_Get(scene_no) == SCENE_FG && mEv_CheckEvent(mEv_SAVED_RENEWSHOP) == FALSE) { + if (mSP_GetShopLevel() < mSP_GetRealShopLevel()) { + lbRTC_time_c now_time = Common_Get(time.rtc_time); + lbRTC_time_c renewal_two_days = now_time; + lbRTC_time_c renewal_one_day; + u16 bargain_date; + + renewal_two_days.hour = mSP_GetShopOpenTime(); + renewal_two_days.min = 0; + renewal_two_days.sec = 0; + lbRTC_Add_DD(&renewal_two_days, 2); + renewal_one_day = now_time; + lbRTC_Add_DD(&renewal_one_day, 1); + bargain_date = mEv_get_bargain_day(); + if (bargain_date != 0) { + lbRTC_month_t bargain_month = (bargain_date >> 8) & 0xFF; + lbRTC_day_t bargain_day = bargain_date & 0xFF; + + /* No upgrading shop if a Nook sale event happens within 2 days */ + if ((now_time.month == bargain_month && now_time.day == bargain_day) || + (renewal_two_days.month == bargain_month && renewal_two_days.day == bargain_day) || + (renewal_one_day.month == bargain_month && renewal_one_day.day == bargain_day)) { + return; + } + } + + { + lbRTC_year_t now_year = now_time.year; + lbRTC_month_t now_month = now_time.month; + lbRTC_day_t max_day = lbRTC_GetDaysByMonth(now_year, now_time.month); + lbRTC_day_t now_day = now_time.day; + + /* No upgrading shop if lottery day is within 2 days */ + if (now_day == max_day || + (renewal_two_days.day == lbRTC_GetDaysByMonth(renewal_two_days.year, renewal_two_days.month)) || + (renewal_one_day.day == lbRTC_GetDaysByMonth(renewal_one_day.year, renewal_one_day.month))) { + return; + } + /* Don't upgrade shop on sale day */ + if ((now_month == lbRTC_NOVEMBER && + now_day == (lbRTC_Weekly_day(now_year, lbRTC_NOVEMBER, 4, lbRTC_THURSDAY) + 1)) || + (renewal_two_days.month == lbRTC_NOVEMBER && + renewal_two_days.day == + (lbRTC_Weekly_day(renewal_two_days.year, lbRTC_NOVEMBER, 4, lbRTC_THURSDAY) + 1)) || + (renewal_one_day.month == lbRTC_NOVEMBER && + renewal_one_day.day == + (lbRTC_Weekly_day(renewal_one_day.year, lbRTC_NOVEMBER, 4, lbRTC_THURSDAY) + 1))) { + return; + } + } + + mEv_EventON(mEv_SAVED_RENEWSHOP); + Save_Get(shop).renewal_time = renewal_two_days; + Save_Get(shop).shop_info.send_upgrade_notice = TRUE; + Save_Get(shop).shop_info.upgrading_today = TRUE; + } + } + + if (Save_Get(scene_no) == SCENE_FG && mEv_CheckEvent(mEv_SAVED_RENEWSHOP) == TRUE) { + lbRTC_time_c now_time = Common_Get(time.rtc_time); + lbRTC_time_c renewal_time = Save_Get(shop).renewal_time; + + lbRTC_Sub_DD(&renewal_time, 2); + if (lbRTC_IsOverTime(&now_time, &renewal_time) == lbRTC_OVER) { + mEv_EventOFF(mEv_SAVED_RENEWSHOP); + Save_Get(shop).shop_info.upgrading_today = FALSE; + Save_Get(shop).shop_info.send_upgrade_notice = FALSE; + return; + } + } + + aSL_SetRenewalChiraswhi_Notice(); + mSP_SetRenewalChiraswhi_AppoDay(); +} + +static void aSL_ExchangeShopGoodsInGame(ACTOR* actorx, GAME* game, lbRTC_hour_t hour) { + lbRTC_time_c now_time = Common_Get(time.rtc_time); + lbRTC_hour_t now_hour = now_time.hour; + + if (mEv_CheckEvent(mEv_SAVED_RENEWSHOP) == FALSE && Save_Get(scene_no) == SCENE_FG && now_hour >= hour && + mSP_CheckExchangeDay2()) { + if (mSP_CheckExchangeMonth()) { + mSP_LotteryLineUp_ZeldaMalloc(); + } + + mSP_NewExchangeDay(); + mSP_ExchangeLineUp_ZeldaMalloc(); + } +} + +static void aSL_ReportShopOpen2Event(void) { + int status = mSP_ShopOpen(); // @unused + + if (mSP_ShopOpen() == mSP_SHOP_STATUS_OPEN || mSP_ShopOpen() == mSP_SHOP_STATUS_OPENEVENT) { + mEv_EventON(mEv_DAILY_OPEN_SHOP); + } else { + mEv_EventOFF(mEv_DAILY_OPEN_SHOP); + } +} + +static void Shop_Level_Actor_move(ACTOR* actorx, GAME* game) { + GAME_PLAY* play = (GAME_PLAY*)game; + ACTOR* player_actor = GET_PLAYER_ACTOR_ACTOR(play); + xyz_t* pos_p; + + if (player_actor != NULL) { + pos_p = &player_actor->world.position; + } else { + pos_p = &actorx->world.position; + } + + actorx->world.position = *pos_p; + aSL_JudgeRenewShop(actorx, game); + aSL_ExchangeShopGoodsInGame(actorx, game, 6); + aSL_ReportShopOpen2Event(); +}