|
|
|
@@ -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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|