From 7c0de8f398f64469032eaff9fdded3ba1d06f9c3 Mon Sep 17 00:00:00 2001 From: Cuyler36 Date: Sat, 16 Sep 2023 22:56:56 -0400 Subject: [PATCH] Implement & link m_fishrecord.c --- config/rel_slices.yml | 5 + include/m_event_map_npc.h | 18 ++ include/m_fishrecord.h | 8 + include/m_mail.h | 2 + include/m_npc.h | 2 + include/m_shop.h | 1 + rel/m_fishrecord.c | 531 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 567 insertions(+) create mode 100644 include/m_event_map_npc.h create mode 100644 rel/m_fishrecord.c diff --git a/config/rel_slices.yml b/config/rel_slices.yml index 030e696e..9359525d 100644 --- a/config/rel_slices.yml +++ b/config/rel_slices.yml @@ -78,6 +78,11 @@ m_fbdemo_fade.c: .text: [0x803A12F8, 0x803A1508] .rodata: [0x80641FC0, 0x80641FD8] .data: [0x80653558, 0x806535A0] +m_fishrecord.c: + .text: [0x803AB698, 0x803AC5C8] + .rodata: [0x80642068, 0x80642080] + .data: [0x80654180, 0x80654188] + .bss: [0x81295BD0, 0x81295BD8] m_flashrom.c: .text: [0x803AC5C8, 0x803AF48C] .data: [0x80654188, 0x80654298] diff --git a/include/m_event_map_npc.h b/include/m_event_map_npc.h new file mode 100644 index 00000000..dea307a3 --- /dev/null +++ b/include/m_event_map_npc.h @@ -0,0 +1,18 @@ +#ifndef M_EVENT_MAP_NPC_H +#define M_EVENT_MAP_NPC_H + +#include "types.h" +#include "m_actor.h" +#include "m_npc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern int mEvMN_GetJointEventRandomNpc(mActor_name_t* selected_npc_id); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/m_fishrecord.h b/include/m_fishrecord.h index 92bc55c2..4b998e0b 100644 --- a/include/m_fishrecord.h +++ b/include/m_fishrecord.h @@ -17,6 +17,14 @@ typedef struct fishrecord_s { int size; } mFR_record_c; +enum { + mFR_SIZE_SMALL, + mFR_SIZE_MEDIUM, + mFR_SIZE_LARGE, + + mFR_SIZE_NUM +}; + extern void mEv_fishRecord_holder(PersonalID_c* winner_pid, u32* winning_size, lbRTC_ymd_c* contest_date); extern int mEv_fishday(lbRTC_ymd_c* dates, lbRTC_time_c* now_time); extern void mFR_fishmail(); diff --git a/include/m_mail.h b/include/m_mail.h index d631b452..08656634 100644 --- a/include/m_mail.h +++ b/include/m_mail.h @@ -53,6 +53,8 @@ enum { mMl_TYPE_SHOP_SALE_LEAFLET = 2, mMl_TYPE_BROKER_SALE_LEAFLET = 3, + mMl_TYPE_FISHING_CONTENST = 9, + mMl_TYPE_12 = 12 }; diff --git a/include/m_npc.h b/include/m_npc.h index 8db855ad..0dd5f101 100644 --- a/include/m_npc.h +++ b/include/m_npc.h @@ -230,6 +230,8 @@ extern int mNpc_SearchAnimalPersonalID(AnmPersonalID_c* id); extern void mNpc_GetActorWorldName(u8* buf, mActor_name_t id); extern u8 mNpc_CheckNormalMail_length(int* len, u8* body); extern mActor_name_t mNpc_GetNpcFurniture(AnmPersonalID_c* pid); +extern void mNpc_GetNpcWorldNameTableNo(u8* buf, mActor_name_t name_id); +extern void mNpc_GetRandomAnimalName(u8* buf); extern void mNpc_PrintRemoveInfo(gfxprint_t* gfxprint); extern void mNpc_PrintFriendship_fdebug(gfxprint_t* gfxprint); diff --git a/include/m_shop.h b/include/m_shop.h index ed137d36..98423cea 100644 --- a/include/m_shop.h +++ b/include/m_shop.h @@ -139,6 +139,7 @@ extern u32 mSP_ItemNo2ItemPrice(mActor_name_t item_no); extern int mSP_SearchItemCategoryPriority(mActor_name_t item_no, int category, int priority, GAME* unused); // not sure if it's actually a GAME* extern mActor_name_t mSP_RandomOneFossilSelect(int multi_fossil); extern mActor_name_t mSP_RandomHaniwaSelect(mActor_name_t* item_buf, int num); +extern mActor_name_t mSP_SelectFishginPresent(int player_no); extern void mItemDebug_ItemDebugMain(); extern void mItemDebug_ItemDebugDraw(gfxprint_t* gfxprint); diff --git a/rel/m_fishrecord.c b/rel/m_fishrecord.c new file mode 100644 index 00000000..cb5f81f5 --- /dev/null +++ b/rel/m_fishrecord.c @@ -0,0 +1,531 @@ +#include "m_fishrecord.h" + +#include "libultra/libultra.h" +#include "m_lib.h" +#include "m_event_map_npc.h" +#include "m_font.h" +#include "m_house.h" +#include "m_item_name.h" +#include "m_handbill.h" +#include "m_malloc.h" +#include "m_common_data.h" + +static void mFR_delete_record(mFR_record_c* record) { + mem_clear((u8*)record, sizeof(mFR_record_c), 0); +} + +static void mFR_delete_after_record(lbRTC_ymd_c* after) { + int i; + + for (i = 0; i < mFR_RECORD_NUM; i++) { + mFR_record_c* record = Save_Get(fishRecord) + i; + + int equal = lbRTC_IsEqualDate( + after->year, after->month, after->day, + record->time.year, record->time.month, record->time.day + ); + + if (equal == lbRTC_LESS) { + mFR_delete_record(record); + } + } +} + +static void mFR_delete_npc_record(lbRTC_ymd_c* date) { + int i; + + for (i = 0; i < mFR_RECORD_NUM; i++) { + mFR_record_c* record = Save_Get(fishRecord) + i; + + if ( + record->size != 0 && + ( + date == NULL || + lbRTC_IsEqualDate( date->year, date->month, date->day, record->time.year, record->time.month, record->time.day) != lbRTC_EQUAL + ) && + mPr_GetPrivateIdx(&record->pid) == -1 + ) { + mFR_delete_record(record); + } + } +} + +static mFR_record_c* mFR_get_record(lbRTC_ymd_c date) { + int i; + + for (i = 0; i < mFR_RECORD_NUM; i++) { + mFR_record_c* record = Save_Get(fishRecord) + i; + + int equal = lbRTC_IsEqualDate( + date.year, date.month, date.day, + record->time.year, record->time.month, record->time.day + ); + + if (equal == lbRTC_EQUAL) { + return record; + } + } + + return NULL; +} + +static mFR_record_c* mFR_get_free_record() { + int i; + + for (i = 0; i < mFR_RECORD_NUM; i++) { + mFR_record_c* record = Save_Get(fishRecord) + i; + + if (record->size == 0) { + return record; + } + } + + return NULL; +} + +static mFR_record_c* mFR_recycle_record() { + int i; + mFR_record_c* record; + mFR_record_c* recycle = Save_GetPointer(fishRecord[0]); + + for (i = 1; i < mFR_RECORD_NUM; i++) { + int equal; + + record = Save_Get(fishRecord) + i; + equal = lbRTC_IsEqualDate( + recycle->time.year, recycle->time.month, recycle->time.day, + record->time.year, record->time.month, record->time.day + ); + + if (equal == lbRTC_OVER) { + recycle = record; + } + } + + mFR_delete_record(recycle); + return recycle; +} + +static mFR_record_c* mFR_new_record(lbRTC_ymd_c date) { + mFR_record_c* new_record; + + mFR_delete_after_record(&date); + mFR_delete_npc_record(&date); + + new_record = mFR_get_free_record(); + if (new_record != NULL) { + return new_record; + } + else { + return mFR_recycle_record(); + } +} + +extern void mEv_fishRecord_set(PersonalID_c* pid, int size) { + lbRTC_ymd_c date; + lbRTC_time_c* rtc_time = Common_GetPointer(time.rtc_time); + mFR_record_c* record; + + mTM_set_renew_time(&date, rtc_time); + record = mFR_get_record(date); + + if (record == NULL) { + record = mFR_new_record(date); + } + + mPr_CopyPersonalID(&record->pid, pid); + record->size = size; + lbRTC_TimeCopy(&record->time, rtc_time); +} + +extern int mFR_fish_rndsize(int fish_size) { + f32 rng = fqrand(); + + switch(fish_size) { + case mFR_SIZE_LARGE: + return (50.0f + 20.0f * rng * rng) / 2.54f; + case mFR_SIZE_MEDIUM: + return (30.0f + 20.0f * rng) / 2.54f; + default: /* mFR_SIZE_SMALL */ + return (30.0f - 20.0f * rng * rng) / 2.54f; + } +} + +extern int mFR_make_NpcRecord(lbRTC_hour_t hour) { + int rand; + if (hour < 9) { + return (int)mFR_fish_rndsize(mFR_SIZE_SMALL); + } + else if (hour < 15) { + rand = RANDOM(2); + return (int)mFR_fish_rndsize(rand); /* 50/50 mFR_SIZE_SMALL, mFR_SIZE_MEDIUM */ + } + else { + rand = RANDOM(3); + return (int)mFR_fish_rndsize(rand); /* equal chance for all sizes */ + } +} + +static int mEv_fishRecord_local(PersonalID_c* pid, int* record_size, lbRTC_time_c* record_set_time, int player_record_size) { + static u8 l_name[] = { 0x98, 0xA6, 0x8F, 0xA1, 0x20 }; /* Untranslated JP, クニッチ */ + + u8 name[ANIMAL_NAME_LEN]; + mActor_name_t selected_npc; + lbRTC_time_c l_record_time; + + fqrand(); /* unused call to fqrand..? */ + lbRTC_TimeCopy(&l_record_time, record_set_time); + + /* Adjust time to 5:50 PM */ + l_record_time.hour = 17; + l_record_time.min = 50; + l_record_time.sec = 0; + + *record_size = 0; + + /* Roll potential new record size for every 30 minutes between the last record set time and 5:50PM */ + while (lbRTC_IsOverTime(&l_record_time, record_set_time) == lbRTC_LESS) { + /* Roll a potential new record size set by an NPC */ + int npc_record = mFR_make_NpcRecord(record_set_time->hour); + + if (npc_record > *record_size) { + *record_size = npc_record; + } + + lbRTC_Add_mm(record_set_time, 30); // add 30 minutes + } + + /* Adjust set time to 6:00PM */ + record_set_time->hour = 18; + record_set_time->min = 0; + record_set_time->sec = 0; + + if (*record_size <= player_record_size) { + return FALSE; + } + else { + mPr_ClearPersonalID(pid); + if (mEvMN_GetJointEventRandomNpc(&selected_npc) == TRUE) { + mNpc_GetNpcWorldNameTableNo(name, selected_npc); + } + else { + mNpc_GetRandomAnimalName(name); + } + + mem_copy(pid->player_name, name, ANIMAL_NAME_LEN); + mLd_ClearLandName(pid->land_name); + mem_copy(pid->land_name, l_name, sizeof(l_name)); + + return TRUE; + } +} + +static void mEv_fishRecord_holder1(PersonalID_c* pid, int* size, mFR_record_c* record) { + PersonalID_c record_id; + int record_size; + + + if (mEv_fishRecord_local(&record_id, &record_size, &record->time, record->size) == FALSE) { + /* Player wins */ + mPr_CopyPersonalID(pid, &Save_GetPointer(fishRecord[0])->pid); + *size = Save_GetPointer(fishRecord[0])->size; + } + else { + /* NPC wins */ + record->size = record_size; + mPr_CopyPersonalID(&record->pid, &record_id); + *size = record_size; + mPr_CopyPersonalID(pid, &record_id); + } +} + +static void mEv_fishRecord_holder2(PersonalID_c* pid, int* size) { + /* NPC only win */ + lbRTC_time_c time; + + lbRTC_TimeCopy(&time, Common_GetPointer(time.rtc_time)); + time.hour = 6; + time.min = 0; + time.sec = 0; + + mEv_fishRecord_local(pid, size, &time, 0); +} + +extern void mEv_fishRecord_holder(PersonalID_c* winning_pid, u32* winning_size, lbRTC_ymd_c* contest_date) { + int i; + + for (i = 0; i < mFR_RECORD_NUM; i++) { + mFR_record_c* record = Save_Get(fishRecord) + i; + + if ( + record->time.year == contest_date->year && + record->time.month == contest_date->month && + record->time.day == contest_date->day + ) { + mEv_fishRecord_holder1(winning_pid, (int*)winning_size, record); /* found an existing record for this fishing contest */ + return; + } + } + + /* No record found for this fishing contest so generate a fake one */ + mEv_fishRecord_holder2(winning_pid, (int*)winning_size); +} + +static lbRTC_ymd_c* mEv_fishday_day(lbRTC_year_t year, lbRTC_month_t month, int week) { + static lbRTC_ymd_c ymd; + + int weekday; + int day; + + weekday = lbRTC_Week(year, month, lbRTC_WEEK); + ymd.year = year; + ymd.month = month; + day = (week * lbRTC_WEEK) + lbRTC_WEEK - weekday; + + if (day > lbRTC_GetDaysByMonth(ymd.year, ymd.month)) { + return NULL; + } + else { + ymd.day = day; + return &ymd; + } +} + +extern int mEv_fishday(lbRTC_ymd_c* dates, lbRTC_time_c* time) { + lbRTC_time_c l_time; + lbRTC_ymd_c ymd; + int free_dates; + int i; + lbRTC_ymd_c* fishing_day_ymd_p; + + lbRTC_TimeCopy(&l_time, time); + lbRTC_Add_hh(&l_time, 6); + ymd.year = l_time.year; + ymd.month = l_time.month; + ymd.day = l_time.day; + free_dates = 4; + + if (ymd.month >= lbRTC_NOVEMBER) { + for (i = 4; i >= 0; i--) { + fishing_day_ymd_p = mEv_fishday_day(ymd.year, lbRTC_NOVEMBER, i); + + if (fishing_day_ymd_p != NULL && (ymd.month > lbRTC_NOVEMBER || ymd.day > fishing_day_ymd_p->day)) { + lbRTC_ymd_c* week_date = dates + free_dates; + + week_date->year = fishing_day_ymd_p->year; + week_date->month = fishing_day_ymd_p->month; + week_date->day = fishing_day_ymd_p->day; + + if (free_dates <= 0) { + return TRUE; + } + + free_dates--; + } + } + } + + if (ymd.month >= lbRTC_JUNE) { + for (i = 4; i >= 0; i--) { + fishing_day_ymd_p = mEv_fishday_day(ymd.year, lbRTC_JUNE, i); + + if (fishing_day_ymd_p != NULL && (ymd.month > lbRTC_JUNE || ymd.day > fishing_day_ymd_p->day)) { + lbRTC_ymd_c* week_date = dates + free_dates; + + week_date->year = fishing_day_ymd_p->year; + week_date->month = fishing_day_ymd_p->month; + week_date->day = fishing_day_ymd_p->day; + + if (free_dates <= 0) { + return TRUE; + } + + free_dates--; + } + } + } + + /* Go through previous year */ + lbRTC_Sub_YY(&l_time, 1); + ymd.year = l_time.year; + + for (i = 4; i >= 0; i--) { + fishing_day_ymd_p = mEv_fishday_day(ymd.year, lbRTC_NOVEMBER, i); + + if (fishing_day_ymd_p != NULL) { + lbRTC_ymd_c* week_date = dates + free_dates; + + week_date->year = fishing_day_ymd_p->year; + week_date->month = fishing_day_ymd_p->month; + week_date->day = fishing_day_ymd_p->day; + + if (free_dates <= 0) { + return TRUE; + } + + free_dates--; + } + } + + for (i = 4; i >= 0; i--) { + fishing_day_ymd_p = mEv_fishday_day(ymd.year, lbRTC_JUNE, i); + + if (fishing_day_ymd_p != NULL) { + lbRTC_ymd_c* week_date = dates + free_dates; + + week_date->year = fishing_day_ymd_p->year; + week_date->month = fishing_day_ymd_p->month; + week_date->day = fishing_day_ymd_p->day; + + if (free_dates <= 0) { + return TRUE; + } + + free_dates--; + } + } + + return FALSE; +} + +static void mFR_swap_record(mFR_record_c* r0, mFR_record_c* r1) { + mFR_record_c temp; + + bcopy(r0, &temp, sizeof(mFR_record_c)); + bcopy(r1, r0, sizeof(mFR_record_c)); + bcopy(&temp, r1, sizeof(mFR_record_c)); +} + +static void mFR_sort_record() { + int i; + int j; + mFR_record_c* records = Save_Get(fishRecord); + + for (i = 0; i < mFR_RECORD_NUM - 1; i++) { + for (j = i + 1; j < mFR_RECORD_NUM; j++) { + if ( + records[j].size != 0 && + (records[j].size == 0 || lbRTC_IsEqualDate( + records[i].time.year, records[i].time.month, records[i].time.day, + records[j].time.year, records[j].time.month, records[j].time.day + ) == lbRTC_OVER) + ) { + mFR_swap_record(&records[i], &records[j]); + } + } + } +} + +static int mFR_Fishmail_send_post(PersonalID_c* pid, Mail_c* mail) { + int arrange_idx = mHS_get_arrange_idx(mPr_GetPrivateIdx(pid)) & 3; + int free_mail_idx = mMl_chk_mail_free_space(Save_Get(homes[arrange_idx]).mailbox, HOME_MAILBOX_SIZE); + + if (free_mail_idx >= 0) { + mMl_copy_mail(Save_Get(homes[arrange_idx]).mailbox + free_mail_idx, mail); + return TRUE; + } + + return FALSE; +} + +static int mFR_Fishmail_send_postoffice(PersonalID_c* pid, Mail_c* mail) { + if (mPO_get_keep_mail_sum() < mPO_MAIL_STORAGE_SIZE) { + mPO_receipt_proc(mail, mPO_SENDTYPE_MAIL); + return TRUE; + } + + return FALSE; +} + +static void mFR_GetFishPresentMail(mFR_record_c* record, Mail_c* mail) { + mActor_name_t present; + u8 present_name[mIN_ITEM_NAME_LEN]; + int header_back_start; + + mMl_clear_mail(mail); + present = mSP_SelectFishginPresent(mPr_GetPrivateIdx(&record->pid)); + mIN_copy_name_str(present_name, present); + mHandbill_Set_free_str(mHandbill_FREE_STR0, present_name, mIN_ITEM_NAME_LEN); + mHandbill_Load_HandbillFromRom( + mail->content.header, &header_back_start, + mail->content.footer, + mail->content.body, + 0x23E + (((record->time.day - 1) / 7) & 3) + ); + + mail->content.header_back_start = header_back_start; + mail->content.font = mMl_FONT_0; + mail->content.mail_type = mMl_TYPE_FISHING_CONTENST; + mail->present = present; + mail->content.paper_type = 15; // deep sea paper + mPr_CopyPersonalID(&mail->header.recipient.personalID, &record->pid); + mail->header.recipient.type = mMl_NAME_TYPE_PLAYER; +} + +static void mFR_Fishmail_send() { + int i; + mFR_record_c* record; + lbRTC_time_c* rtc_time = Common_GetPointer(time.rtc_time); + Mail_c* mail; + + mail = (Mail_c*)zelda_malloc(sizeof(Mail_c)); + + if (mail != NULL) { + for (i = 0; i < mFR_RECORD_NUM; i++) { + record = Save_Get(fishRecord) + i; + + if ( + record->size > 0 && /* used record */ + mPr_GetPrivateIdx(&record->pid) != -1 && /* record holder isn't an NPC or from out of town */ + lbRTC_IsOverTime(&record->time, rtc_time) == lbRTC_OVER /* record happened in the past */ + ) { + mFR_GetFishPresentMail(record, mail); + + /* Try sending the mail to both the recipient's mailbox and the post office */ + if (mFR_Fishmail_send_post(&record->pid, mail) != FALSE) { + mFR_delete_record(record); + } + else if (mFR_Fishmail_send_postoffice(&record->pid, mail) != FALSE) { + mFR_delete_record(record); + } + } + } + + zelda_free(mail); + } +} + +static void mFR_fishRecord_last_holder() { + mFR_record_c* record; + int i; + lbRTC_time_c* rtc_time = Common_GetPointer(time.rtc_time); + PersonalID_c record_holder; + int record_size; + + for (i = 0; i < mFR_RECORD_NUM; i++) { + record = Save_Get(fishRecord) + i; + + if ( + record->size > 0 && + lbRTC_IsOverTime(&record->time, rtc_time) == lbRTC_OVER + ) { + int new_record = mEv_fishRecord_local(&record_holder, &record_size, &record->time, record->size); + + if (new_record) { + record->size = record_size; + mPr_CopyPersonalID(&record->pid, &record_holder); + } + } + } +} + +extern void mFR_fishmail() { + lbRTC_ymd_c now; + + mTM_set_renew_time(&now, Common_GetPointer(time.rtc_time)); + mFR_delete_after_record(&now); + mFR_fishRecord_last_holder(); + mFR_delete_npc_record(&now); + mFR_sort_record(); + mFR_Fishmail_send(); +}