Merge pull request #32 from Cuyler36/m_mushroom

Implement & link m_mushroom.c
This commit is contained in:
Prakxo
2023-05-16 05:42:51 +02:00
committed by GitHub
9 changed files with 773 additions and 1 deletions
+5
View File
@@ -38,6 +38,11 @@ m_malloc.c:
#m_lib.c: #sqrtf statics
# .text: [0x803BAB0C, 0x803BB960]
# .rodata: [0x80642640, 0x80642680]
m_mushroom.c:
.text: [0x803C8090, 0x803C90A8]
.rodata: [0x80642950, 0x80642960]
.data: [0x8065A3F8, 0x8065A430]
.bss: [0x81298B40, 0x81298F60]
m_needlework.c:
.text: [0x803C98EC, 0x803C9F7C]
.data: [0x8065ABC0, 0x8065AE30]
+4 -1
View File
@@ -22,6 +22,7 @@
#include "m_museum_display.h"
#include "m_lib.h"
#include "m_field_assessment.h"
#include "m_mushroom.h"
#ifdef __cplusplus
extern "C" {
@@ -92,7 +93,9 @@ typedef struct Save_s {
/* 0x020F1B */ u8 npc_force_go_home; /* when set to 1, forces the 'm_go_home' code to activate */
/* 0x020F1C */ u16 deposit[FG_BLOCK_X_NUM * FG_BLOCK_Z_NUM][UT_Z_NUM]; /* flags for which items are buried around town */
/* 0x0212DC */ lbRTC_time_c last_grow_time; /* last time that a new villager moved into town */
/* 0x0212E4 */ u8 _tmp4[0x02137E - 0x0212E4];
/* 0x0212E4 */ u8 _tmp4[0x02131C - 0x0212E4];
/* 0x02131C */ mMsr_MushTime_c mushroom_time; /* last time mushroom season info was updated */
/* 0x021322 */ u8 _tmp21322[0x02137E - 0x021322];
/* 0x02137E */ lbRTC_time_c treasure_buried_time; /* last time treasure was actually buried */
/* 0x021386 */ lbRTC_time_c treasure_checked_time; /* last time check to bury treasure was executed */
/* 0x02138E */ u8 saved_rom_debug; /* flag to set save to 'debug rom' mode */
+14
View File
@@ -3,6 +3,7 @@
#include "types.h"
#include "libu64/gfxprint.h"
#include "m_time.h"
#ifdef __cplusplus
extern "C" {
@@ -107,9 +108,22 @@ enum week_type {
mEv_WEEKTYPE_SPECIAL
};
enum event_table {
mEv_EVENT_MUSHROOM_SEASON = 47
};
#define mEv_STATUS_ACTIVE (1 << 0) /* event is active */
#define mEv_STATUS_STOP (1 << 1) /* event is stopped */
#define mEv_STATUS_SHOW (1 << 2) /* event is shown */
#define mEv_STATUS_PLAYSOUND (1 << 3) /* event should play sound */
#define mEv_STATUS_RUN (1 << 4) /* event should run */
#define mEv_STATUS_ERROR (1 << 5) /* event is in error state */
#define mEv_STATUS_TALK (1 << 6) /* event requires talking to player */
extern int mEv_CheckFirstJob();
extern int mEv_CheckArbeit();
extern int mEv_CheckTitleDemo();
extern int mEv_check_status(int event, s16 status);
extern int mEv_weekday2day(lbRTC_month_t month, int week_type, lbRTC_weekday_t weekday);
+5
View File
@@ -3,6 +3,7 @@
#include "types.h"
#include "libu64/gfxprint.h"
#include "m_lib.h"
#ifdef __cplusplus
@@ -69,8 +70,12 @@ 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 int mFI_GetLineDeposit(u16* deposit, int ut_x);
extern void mFI_GetSpecialBlockNum(int* block_pos_tbl, u32* kind_list, int kind_num);
extern int mFI_SetTreasure(int* block_x, int* block_z, mActor_name_t item_no);
extern void mFI_SetFGUpData();
extern int mFI_ClearBlockItemRandom_common(mActor_name_t item_no, int count, mActor_name_t* fg_items, u16* deposit, int include_deposited);
extern void mFI_Wpos2BlockNum(int* block_x, int* block_z, xyz_t world_pos);
extern void mFI_PrintNowBGNum(gfxprint_t* gfxprint);
extern void mFI_PrintFgAttr(gfxprint_t* gfxprint);
+34
View File
@@ -0,0 +1,34 @@
#ifndef M_MUSHROOM_H
#define M_MUSHROOM_H
#include "types.h"
#include "m_lib.h"
#ifdef __cplusplus
extern "C" {
#endif
#define mMsr_ACTIVE_HOUR 8
#define mMsr_NUM_MUSHROOMS 5
/* sizeof(mMsr_Mushtime_c) == 6 */
typedef struct mushroom_time_s {
/* 0x00 */ u16 year:12;
/* 0x01 */ u16 month:4;
/* 0x02 */ u8 day:5;
/* 0x02 */ u8 pad0:3;
/* 0x03 */ u8 hour:5;
/* 0x03 */ u8 pad1:3;
/* 0x04 */ u8 hour_quarter:4;
/* 0x04 */ u8 active:1; /* probably a better name for this */
/* 0x04 */ u8 pad2:3;
} mMsr_MushTime_c;
extern void mMsr_FirstClearMushroom();
extern void mMsr_SetMushroom(xyz_t player_pos);
#ifdef __cplusplus
}
#endif
#endif
+4
View File
@@ -251,6 +251,10 @@ enum {
#define ITM_DUST2_OLD_TIRE 0x2510
#define ITM_PITFALL 0x2512
#define ITM_FOOD_START 0x2800
#define ITM_FOOD_MUSHROOM 0x2805
#define ITM_ENV_START 0x2900
#define ITM_SAPLING ITM_ENV_START
#define ITM_CEDAR_SAPLING 0x2901
+1
View File
@@ -14,6 +14,7 @@ extern "C" {
enum scene_table {
/* TODO: finish */
SCENE_FG = 0x07, /* outdoors/FG */
SCENE_ISLAND_COTTAGE = 0x2F,
/* TODO: finish */
};
+1
View File
@@ -74,6 +74,7 @@ extern const lbRTC_ymd_t mTM_rtcTime_ymd_clear_code;
extern const lbRTC_time_c mTM_rtcTime_default_code;
#define mTM_IsTimeCleared(time) (lbRTC_IsEqualTime((time), &mTM_rtcTime_clear_code, lbRTC_CHECK_ALL) == TRUE)
#define mTM_AreTimesEqual(t0, t1) (lbRTC_IsEqualTime(t0, t1, lbRTC_CHECK_ALL))
#ifdef __cplusplus
}
+705
View File
@@ -0,0 +1,705 @@
#include "m_mushroom.h"
#include "m_time.h"
#include "m_event.h"
#include "m_actor_type.h"
#include "m_field_make.h"
#include "m_field_info.h"
#include "m_scene_table.h"
#include "m_name_table.h"
#include "m_common_data.h"
#include "libultra/libultra.h"
#include "m_lib.h"
#include "libc64/qrand.h"
/* 'zero' time (uninitialized time value for mushroom saved time) */
static lbRTC_time_c l_mmsr_zeto_time = { 0, 0, 0, 0, 0, 0, 0 };
/**
* @brief Converts a saved time in mMsr_MushTime_c format into its lbRTC_time_c representation.
*
* @param dst The destination lbRTC_time_c
* @param src The source mMsr_MushTime_c
**/
static void mMsr_Mushtime2Rtc(lbRTC_time_c* dst, const mMsr_MushTime_c* src) {
dst->year = src->year;
dst->month = src->month;
dst->day = src->day;
dst->hour = src->hour;
dst->min = src->hour_quarter * 15;
dst->sec = 0;
dst->weekday = lbRTC_Week(dst->year, dst->month, dst->day);
}
/**
* @brief Converts time in lbRTC_time_c format into its mMsr_MushTime_c representation.
*
* @param dst The destination mMsr_MushTime_c
* @param src The source lbRTC_time_c
**/
static void mMsr_Rtc2MushTime(mMsr_MushTime_c* dst, const lbRTC_time_c* src) {
dst->year = src->year;
dst->month = src->month;
dst->day = src->day;
dst->hour = src->hour;
dst->hour_quarter = src->min / 15;
}
/**
* @brief Gets whether or not the mushroom season event is currently active.
*
* @return TRUE when event is active, FALSE otherwise
**/
static int mMsr_CheckMushroomDay() {
return mEv_check_status(mEv_EVENT_MUSHROOM_SEASON, mEv_STATUS_ACTIVE) == TRUE;
}
/**
* @brief Transforms a source lbRTC_time_c's minute member to the closest 15 minute margin in the future
*
* @param dst The destination 15-minute demarcated lbRTC_time_c
* @param src The source lbRTC_time_c
**/
static void mMsr_Set15Minites(lbRTC_time_c* dst, const lbRTC_time_c* src) {
static u8 min_15_table[4] = { 45, 30, 15, 0 };
int i;
lbRTC_TimeCopy(dst, src);
for (i = 0; i < 4; i++) {
if (dst->min >= min_15_table[i]) {
dst->min = min_15_table[i];
break;
}
}
}
/**
* @brief Gets the initial amount of mushrooms which should be removed.
*
* The amount removed is determined by how many 15-minute intervals
* have passed since 8 AM.
*
* @param rtc_time The current rtc time
* @param mush_time The current mushroom 15-minute demarcated time
*
* @return Number of mushrooms to initially remove
**/
static u32 mMsr_GetFirstClearMushroomNum(const lbRTC_time_c* rtc_time, const lbRTC_time_c* mush_time) {
u32 mushroom_num = 0;
lbRTC_time_c rtc_15_min;
lbRTC_time_c mush_15_min;
if (mTM_AreTimesEqual(rtc_time, mush_time) != TRUE) {
mMsr_Set15Minites(&rtc_15_min, rtc_time);
mMsr_Set15Minites(&mush_15_min, mush_time);
if (lbRTC_IsOverTime(&mush_15_min, &rtc_15_min) == lbRTC_OVER) {
mushroom_num = lbRTC_IntervalTime(&rtc_15_min, &mush_15_min);
}
else {
mushroom_num = lbRTC_IntervalTime(&mush_15_min, &rtc_15_min);
}
mushroom_num /= 15;
}
return mushroom_num;
}
/**
* @brief Gets the number of removeable mushrooms in an acre and tracks which locations they are at.
*
* Clearable mushrooms are mushrooms which are not buried.
*
* @param fg_block_items Pointer to the acre's item data
* @param save_deposit Pointer to the acre's buried item flags
* @param block_candidates Pointer to the table of saved mushroom locations
*
* @return Number of removeable mushrooms in the acre
**/
static int mMsr_GetBlockClearAbleMushroomNum(mActor_name_t* fg_block_items, u16* save_deposit, u16* block_candidates) {
int clearable = 0;
if (fg_block_items != NULL && save_deposit != NULL && block_candidates != NULL) {
int ut_z;
for (ut_z = 0; ut_z < UT_Z_NUM; ut_z++) {
int ut_x;
for (ut_x = 0; ut_x < UT_X_NUM; ut_x++) {
if (*fg_block_items == ITM_FOOD_MUSHROOM && mFI_GetLineDeposit(save_deposit, ut_x) == FALSE) {
clearable++;
*block_candidates |= 1 << ut_x;
}
fg_block_items++;
}
save_deposit++;
block_candidates++;
}
}
return clearable;
}
/**
* @brief Splits up an amount of cleared mushrooms between all acres in town.
*
* @param share_num Pointer to the list of mushrooms to remove for each acre
* @param candidate_num Pointer to the list of mushrooms present for removal in each acre
* @param candidate_block_num Total number of acres with clearable mushroom candidates
* @param total_clearable Total number of clearable mushrooms in town
* @param clear_num Amount of mushrooms to clear
**/
static void mMsr_SetShareNum(u8* share_num, u8* candidate_num, int candidate_block_num, int total_clearable, int clear_num) {
u8* share_num_p = share_num;
u8* candidate_num_p = candidate_num;
int clearable;
int i;
/* count clearable mushrooms and log which acres */
while(total_clearable != 0 && candidate_block_num != 0 && clear_num > candidate_block_num) {
share_num = share_num_p;
candidate_num = candidate_num_p;
clearable = 0;
for (i = 0; i < FG_BLOCK_TOTAL_NUM; i++) {
if (*candidate_num - *share_num > 0) {
clearable++;
(*share_num)++;
if (*candidate_num <= *share_num) {
candidate_block_num--; // acre doesn't have enough mushrooms in it, so remove it from the count
}
}
share_num++;
candidate_num++;
}
if (clearable == 0) {
break; // no clearable mushrooms left
}
clear_num -= clearable;
total_clearable -= clearable;
}
/* try to assign share for any cleared mushrooms left */
while (clear_num != 0 && candidate_block_num != 0 && total_clearable != 0) {
int selected_block;
share_num = share_num_p;
candidate_num = candidate_num_p;
selected_block = (int)(fqrand() * candidate_block_num); // randomly selected acre
for (i = 0; i < FG_BLOCK_TOTAL_NUM; i++) {
if (*candidate_num - *share_num > 0) {
if (selected_block == 0) {
clear_num--;
(*share_num)++;
total_clearable--;
if (*candidate_num <= *share_num) {
candidate_block_num--;
}
break;
}
selected_block--;
}
share_num++;
candidate_num++;
}
}
}
/**
* @brief Removes candidate mushrooms in an acre.
*
* @param fg_items Pointer to acre items
* @param candidates Pointer to mushroom removal candidate location bitfield for the acre
**/
static void mMsr_ClearBlockCandidateMushroom(mActor_name_t* fg_items, u16* candidates) {
int ut_z;
for (ut_z = 0; ut_z < UT_Z_NUM; ut_z++) {
int ut_x;
for (ut_x = 0; ut_x < UT_X_NUM; ut_x++) {
/* check if the 'candidate' bit is set for our x unit */
if ((((*candidates) >> ut_x) & 1) == 1) {
*fg_items = EMPTY_NO; // clear item
mFI_SetFGUpData();
}
fg_items++;
}
candidates++;
}
}
/**
* @brief Removes all candidate mushrooms from all acres
*
* @param fg Pointer to all acres' item data
* @param candidates Pointer to mushroom removal candidate location bitfields
* @param candidate_num Pointer to mushroom candidate number list
**/
static void mMsr_ClearCandidateMushroom(mFM_fg_c* fg, u16* candidates, u8* candidate_num) {
int i;
for (i = 0; i < FG_BLOCK_TOTAL_NUM; i++) {
if (*candidate_num != 0) {
mMsr_ClearBlockCandidateMushroom((mActor_name_t*)fg->items, candidates);
}
fg++;
candidates += UT_Z_NUM;
candidate_num++;
}
}
#pragma pool_data on
/**
* @brief Clears a set number of mushrooms around town while ignoring the acre the player is in.
*
* @param clear_num Number of mushrooms to clear
* @param block_x The X-acre the player is currently in
* @param block_z The Z-acre the player is currently in
**/
static void mMsr_ClearMushrooms(int clear_num, int block_x, int block_z) {
static u16 candidate[FG_BLOCK_TOTAL_NUM][UT_Z_NUM];
static u8 candidate_num[FG_BLOCK_TOTAL_NUM];
static u8 share_num[FG_BLOCK_TOTAL_NUM];
mFM_fg_c* fg_block;
int candidate_blocks = 0;
int total_candidate_num = 0;
int i;
fg_block = (mFM_fg_c*)Save_Get(fg);
bzero(candidate, FG_BLOCK_TOTAL_NUM * UT_Z_NUM * sizeof(u16));
bzero(candidate_num, FG_BLOCK_TOTAL_NUM * sizeof(u8));
bzero(share_num, FG_BLOCK_TOTAL_NUM * sizeof(u8));
for (i = 0; i < FG_BLOCK_TOTAL_NUM; i++) {
int clearable_num = mMsr_GetBlockClearAbleMushroomNum((mActor_name_t*)fg_block->items, Save_Get(deposit[i]), candidate[i]);
candidate_num[i] = clearable_num;
if (candidate_num[i] != 0) {
total_candidate_num += candidate_num[i];
candidate_blocks++;
}
fg_block++;
}
if ((block_x - 1) >= 0 && (block_x - 1) < FG_BLOCK_X_NUM && (block_z - 1) >= 0 && (block_z - 1) < FG_BLOCK_Z_NUM) {
u8 candidate_amt;
int bx = (block_z - 1) * FG_BLOCK_X_NUM + (block_x - 1);
candidate_amt = candidate_num[bx];
if (candidate_num[bx] != 0) {
candidate_num[bx] = 0;
total_candidate_num -= candidate_amt;
candidate_blocks--;
bzero(candidate[bx], UT_Z_NUM * sizeof(u16));
}
}
if (total_candidate_num > 0) {
if (total_candidate_num < clear_num) {
fg_block = (mFM_fg_c*)Save_Get(fg);
mMsr_ClearCandidateMushroom(fg_block, (u16*)candidate, candidate_num);
}
else {
mMsr_SetShareNum(share_num, candidate_num, candidate_blocks, total_candidate_num, clear_num);
fg_block = (mFM_fg_c*)Save_Get(fg);
for (i = 0; i < FG_BLOCK_TOTAL_NUM; i++) {
if (share_num[i] != 0) {
int cleared = mFI_ClearBlockItemRandom_common(ITM_FOOD_MUSHROOM, share_num[i], (mActor_name_t*)fg_block->items, Save_Get(deposit[i]), FALSE);
if (cleared > 0) {
mFI_SetFGUpData();
}
}
fg_block++;
}
}
}
}
#pragma pool_data reset
/**
* @brief Clears mushrooms upon first load.
*
* This funciton is necessary to remove the already missed mushrooms
* if there are any.
**/
extern void mMsr_FirstClearMushroom() {
int first_clear_num;
lbRTC_time_c mush_rtc_time;
mMsr_MushTime_c* mush_time = Save_GetPointer(mushroom_time);
const lbRTC_time_c* rtc_time = Common_GetPointer(time.rtc_time);
mMsr_Mushtime2Rtc(&mush_rtc_time, mush_time);
if (mTM_AreTimesEqual(&mush_rtc_time, &l_mmsr_zeto_time) == TRUE) {
lbRTC_TimeCopy(&mush_rtc_time, rtc_time);
}
first_clear_num = mMsr_GetFirstClearMushroomNum(rtc_time, &mush_rtc_time);
if (first_clear_num > 0) {
mMsr_ClearMushrooms(first_clear_num, 0, 0);
}
if (lbRTC_IsEqualTime(&mush_rtc_time, rtc_time, lbRTC_CHECK_DAYS | lbRTC_CHECK_MONTHS | lbRTC_CHECK_YEARS) == FALSE) {
mush_time->active = TRUE;
}
mMsr_Rtc2MushTime(mush_time, rtc_time);
}
/**
* @brief Gets number of mushrooms that should be placed
*
* @param mush_time Pointer to the next mushroom update time
* @param rtc_time Pointer to the current RTC time
* @param active Flag that force-controls whether mushrooms should be placed or not
*
**/
static int mMsr_GetMushroomNum(const lbRTC_time_c* mush_time, const lbRTC_time_c* rtc_time, u8 active) {
int mushroom_num = 0;
if (active == TRUE || lbRTC_IsEqualTime(rtc_time, mush_time, lbRTC_CHECK_DAYS | lbRTC_CHECK_MONTHS | lbRTC_CHECK_YEARS) == FALSE) {
if (rtc_time->hour == mMsr_ACTIVE_HOUR) {
mushroom_num = mMsr_NUM_MUSHROOMS - rtc_time->min / 15;
}
else if (rtc_time->hour == (mMsr_ACTIVE_HOUR + 1) && rtc_time->min < 15) {
mushroom_num = mMsr_NUM_MUSHROOMS - 4; // 1 mushroom until 9:15 am
}
}
return mushroom_num;
}
/**
* @brief Gets whether an item is a valid tree and if that tree has space for a mushroom around it.
*
* Valid trees are:
* - Trees (not ones with hidden objects)
* - Cedar trees (not ones with hidden objects)
* - Fruit trees that currently have fruit
* - Golden trees (including with golden shovel on them)
* - Player-grown money trees (all denominations)
*
* Spot check map (x = valid spot, T = tree):
* xTx
* xxx
*
* @param item Pointer to the item to check
* @param ut_x The X unit of the item
* @param ut_z The Z unit of the item
*
* @return TRUE when item is a valid tree with a valid spot, FALSE otherwise
**/
static int mMsr_CheckAroundTree(mActor_name_t* item, u8 ut_x, u8 ut_z) {
static int area_table[5] = {
-1, /* xT- 1 left of tree */
/* --- */
1, /* -Tx 1 right of tree */
/* --- */
15, /* -T- 1 below, 1 left of tree */
/* x-- */
16, /* -T- 1 below of tree */
/* -x- */
17 /* -T- 1 below, 1 right of tree */
/* --x */
};
int* area_p = area_table;
int res = FALSE;
if (ut_x < (UT_X_NUM - 1) && ut_z < (UT_Z_NUM - 1)) {
mActor_name_t item_no = *item;
if (item_no == TREE || item_no == TREE_APPLE_FRUIT || item_no == TREE_ORANGE_FRUIT ||
item_no == TREE_PEACH_FRUIT || item_no == TREE_PEAR_FRUIT ||
item_no == TREE_CHERRY_FRUIT || item_no == TREE_1000BELLS ||
item_no == TREE_10000BELLS || item_no == TREE_30000BELLS ||
item_no == TREE_100BELLS || item_no == TREE_PALM_FRUIT ||
item_no == CEDAR_TREE || item_no == GOLD_TREE || item_no == GOLD_TREE_SHOVEL
) {
int i;
for (i = 0; i < 5; i++) {
if (*(item + *area_p) == EMPTY_NO) {
res = TRUE;
break;
}
area_p++;
}
}
}
return res;
}
/**
* @brief Counts all the valid mushroom trees in an acre.
*
* @param item_p Pointer to the acre items
*
* @return Number of valid mushroom trees in the acre
**/
static u8 mMsr_GetBlockSetAbleMushroomTreeNum(mActor_name_t* item_p) {
u8 num = 0;
u8 ut_z;
for (ut_z = 0; ut_z < UT_Z_NUM; ut_z++) {
u8 ut_x;
for (ut_x = 0; ut_x < UT_X_NUM; ut_x++) {
if (mMsr_CheckAroundTree(item_p, ut_x, ut_z) == TRUE) {
num++;
}
item_p++;
}
}
return num;
}
/**
* @brief Sets a mushroom at a random valid spot under a tree.
*
* @param item_p Pointer to the tree item
**/
static void mMsr_SetMushroomAroundTree(mActor_name_t* item_p) {
static int area_table[5] = {
-1, /* xT- 1 left of tree */
/* --- */
1, /* -Tx 1 right of tree */
/* --- */
15, /* -T- 1 below, 1 left of tree */
/* x-- */
16, /* -T- 1 below of tree */
/* -x- */
17 /* -T- 1 below, 1 right of tree */
/* --x */
};
int empty_space_num = 0;
int i;
for (i = 0; i < 5; i++) {
if (*(item_p + area_table[i]) == EMPTY_NO) {
empty_space_num++;
}
}
if (empty_space_num > 0) {
int selected = (int)(fqrand() * empty_space_num);
for (i = 0; i < 5; i++) {
if (*(item_p + area_table[i]) == EMPTY_NO) {
if (selected <= 0) {
*(item_p + area_table[i]) = ITM_FOOD_MUSHROOM;
return;
}
selected--;
}
}
}
}
/**
* @brief Sets a mushroom randomly under N trees in an acre.
*
* @param fg_items Pointer to the acre items
* @param block_x Number of candidate trees in the acre
**/
static void mMsr_SetBlockMashroom(mActor_name_t* fg_items, u8 candidate_num) {
int selected_num = (int)(fqrand() * candidate_num);
u8 ut_z;
for (ut_z = 0; ut_z < UT_Z_NUM; ut_z++) {
u8 ut_x;
for (ut_x = 0; ut_x < UT_X_NUM; ut_x++) {
if (mMsr_CheckAroundTree(fg_items, ut_x, ut_z) == TRUE) {
if (selected_num <= 0) {
mMsr_SetMushroomAroundTree(fg_items);
ut_z = UT_Z_NUM; /* break out by exceeding first loop case... bruh */
break;
}
selected_num--;
}
fg_items++;
}
}
}
/**
* @brief Places all requested mushrooms in an acre column (A->F)
*
* @param fg_block_col Pointer to the town mFM_fg_c data
* @param candidate_num Pointer to the list containing number of candidate trees per acre
**/
static void mMsr_SetLineMushroom(mFM_fg_c* fg_block_col, u8* candidate_num) {
u8* t_cand_num = candidate_num;
int candidate_rows = 0;
int bz;
for (bz = 0; bz < FG_BLOCK_Z_NUM; bz++) {
if (*candidate_num != 0) {
candidate_rows++;
}
candidate_num += FG_BLOCK_X_NUM;
}
if (candidate_rows > 0) {
int selected_z = (int)(fqrand() * candidate_rows);
candidate_num = t_cand_num;
for (bz = 0; bz < FG_BLOCK_Z_NUM; bz++) {
if (*candidate_num != 0) {
if (selected_z <= 0) {
mMsr_SetBlockMashroom((mActor_name_t*)fg_block_col->items, *candidate_num);
break;
}
selected_z--;
}
/* Check next Z row */
candidate_num += FG_BLOCK_X_NUM;
fg_block_col += FG_BLOCK_X_NUM;
}
}
}
/**
* @brief Places N mushrooms around town, ignoring the acre the player is currently in.
*
* @param mushroom_num Number of mushrooms to place
* @param player_bx The X-acre the player is currently in
* @param player_bz The Z-acre the player is currently in
**/
static void mMsr_SetMushroomNum(int mushroom_num, int player_bx, int player_bz) {
static u8 candidate_num[FG_BLOCK_TOTAL_NUM];
mFM_fg_c* fg_block = (mFM_fg_c*)Save_Get(fg);
u8* candidate_p = candidate_num;
u8 possible_col_bitfield = 0;
int possible_cols = 0;
int bz;
int bx;
bzero(candidate_num, FG_BLOCK_TOTAL_NUM * sizeof(u8));
/* Check all acres for candidate mushroom locations */
for (bz = 0; bz < FG_BLOCK_Z_NUM; bz++) {
for (bx = 0; bx < FG_BLOCK_X_NUM; bx++) {
/* ensure we do not spawn a mushroom in the acre the player is currently in */
if (bx != player_bx - 1 && bz != player_bz - 1) {
*candidate_p = mMsr_GetBlockSetAbleMushroomTreeNum((mActor_name_t*)fg_block->items);
if (*candidate_p != 0 && ((possible_col_bitfield >> bx) & 1) == 0) {
possible_cols++;
possible_col_bitfield |= 1 << bx;
}
}
fg_block++;
candidate_p++;
}
}
if (possible_cols > 0) {
for (mushroom_num; mushroom_num != 0; mushroom_num--) {
int selected;
fg_block = (mFM_fg_c*)Save_Get(fg);
candidate_p = candidate_num;
/* if we've run out of X cols to place mushrooms in, look again */
if (possible_cols == 0) {
possible_col_bitfield = 0;
possible_cols = 0;
for (bx = 0; bx < FG_BLOCK_X_NUM; bx++) {
candidate_p = candidate_num + bx;
for (bz = 0; bz < FG_BLOCK_Z_NUM; bz++) {
if (*candidate_p != 0) {
possible_cols++;
possible_col_bitfield |= 1 << bx;
break;
}
candidate_p += FG_BLOCK_X_NUM; /* move onto next Z row */
}
}
candidate_p = candidate_num;
if (possible_cols == 0) {
break; /* no possible locations left */
}
}
/* randomly select one of the possible X columns */
selected = (int)(fqrand() * possible_cols);
for (bx = 0; bx < FG_BLOCK_X_NUM; bx++) {
u8 bit = possible_col_bitfield >> bx;
if ((bit & 1) == 1) {
if (selected <= 0) {
mMsr_SetLineMushroom(fg_block, candidate_p); /* set mushroom on our X column */
break;
}
selected--;
}
/* move onto next X column */
fg_block++;
candidate_p++;
}
}
}
}
/**
* @brief Places or removes mushrooms around town based on game state.
*
* For mushrooms to be placed/cleared:
* - Field data must be ready.
* - Scene must be outside in town.
* - Field type must be FG (outside).
* - 'MushroomDay' must be an active event.
* - Mushrooms are placed if mMsr_GetMushroomNum is non-zero, otherwise they're cleared.
*
* @param mushroom_num player_pos Current world position of the player
**/
extern void mMsr_SetMushroom(xyz_t player_pos) {
int player_bx, player_bz;
lbRTC_time_c mush_rtc;
mMsr_MushTime_c* mush_time = Save_GetPointer(mushroom_time);
const lbRTC_time_c* rtc_time = Common_GetPointer(time.rtc_time);
mMsr_Mushtime2Rtc(&mush_rtc, mush_time);
if (mTM_AreTimesEqual(&mush_rtc, &l_mmsr_zeto_time) == TRUE) {
/* mushroom time is not initialized, so initialize it to yesterday */
lbRTC_TimeCopy(&mush_rtc, rtc_time);
lbRTC_Sub_DD(&mush_rtc, 1);
}
mFI_Wpos2BlockNum(&player_bx, &player_bz, player_pos);
if (mFI_CheckFieldData() == TRUE) {
mActor_name_t field_id = mFI_GetFieldId();
if (Save_Get(scene_no) == SCENE_FG && mFI_GET_TYPE(field_id) == mFI_FIELDTYPE_FG && mMsr_CheckMushroomDay() == TRUE) {
int mushroom_num = mMsr_GetMushroomNum(&mush_rtc, rtc_time, mush_time->active);
if (mushroom_num > 0 && mushroom_num <= mMsr_NUM_MUSHROOMS) {
mMsr_SetMushroomNum(mushroom_num, player_bx, player_bz);
mMsr_Rtc2MushTime(mush_time, rtc_time);
mush_time->active = FALSE;
}
else {
int clear_num = mMsr_GetFirstClearMushroomNum(rtc_time, &mush_rtc);
if (clear_num > 0) {
mMsr_ClearMushrooms(clear_num, player_bx, player_bz);
if (mush_time->active != TRUE && lbRTC_IsEqualTime(&mush_rtc, rtc_time, lbRTC_CHECK_DAYS | lbRTC_CHECK_MONTHS | lbRTC_CHECK_YEARS) == FALSE) {
mush_time->active = TRUE;
}
mMsr_Rtc2MushTime(mush_time, rtc_time);
}
}
}
}
}