Animal Crossing
Nintendo's GameCube life simulation on the console's clock, read through the ACreTeam decompilation of its USA release.
On this page
The atlas reads Animal Crossing through the ACreTeam community’s decompilation, C that rebuilds the USA GameCube release. Names and comments quoted here are theirs. The Japanese Dōbutsu no Mori+ hasn’t been compared.
Turnips are the game’s fluctuating price. They sell for 70 to 130 bells on Sunday1, and the shop buys them at one price a day for the rest of the week2. Each week’s type follows from the last one’s3. A spike week sets one weekday to eight times the Sunday price4, and a falling week loses 5% to 20% a day5.
The town updates when it is loaded at or after the first 6:00 since its last update, and one update makes up for every morning missed: 5 weeds for each 6:00 passed6. Once a Sunday 6:00 has passed, the turnips in town, in pockets, and in houses spoil7. If the clock has been set back, the update takes another path8. It adds no weeds, and before the save’s date it spoils those turnips at once9. This is a time-travel-penalty.
Mechanics
| Mechanic | Kind | How |
|---|---|---|
| Price fluctuation | Weekly pattern | a Sunday price of 70 to 130 bells, then six weekday prices drawn ahead under one of three market types, a spike, a random walk, or a fall |
| Commodity trading | turnips bought on Sunday and sold to the shop on a weekday, before they spoil | |
| Time-travel penalty | Counted as time away | the town updates once per 6:00 passed: 5 weeds for each, with no cap, and turnips spoil once a Sunday 6:00 has passed |
| Time-travel penalty | Rollback check | a clock set back before the save's date spoils the turnips in town, in pockets, and in houses, and one set back before the save's time holds back the event mail |
| Time of day | External clock | the daily update compares the clock with the last update's time; the day turns over at 6:00, and one update makes up for every 6:00 missed |
Every claim about this game, by concept (12)
References
- ^In the USA GameCube release (
GAFE01_00, ACreTeam/ac-decomp),Kabu_decide_price_sundaysets the Sunday price to 100 × (1 + (r − 0.5) × 0.6), with r fromfqrandin [0, 1), stored in a 16-bit integer: a whole number of bells from 70 to 130 (computed with f32 rounding). 130 comes only from the generator's top value, 1 − 2⁻²³, where 1.29999995 × 100 rounds to 130.0; that is one 23-bit draw in 2²³. A decompiler's comment gives the range as [70, 129].CodeACreTeam/ac-decomprevisedHow we know
164#define TRADE_MARKET_SUNDAY_BASE_PRICE 100.0f ⋮ 169 * Prices will always been in the range of [70, 129] bells. ⋮ 172 f32 price = 1.0f + (fqrand() - 0.5f) * 0.6f; 173 Save_Set(kabu_price_schedule.daily_price[lbRTC_SUNDAY], price * TRADE_MARKET_SUNDAY_BASE_PRICE); /* [0.7, 1.3) * 100.0f = [70, 130) bells */
src/game/m_kabu_manager.c lines 164–174 at 09ca8e8 · upstream ↗ 17 __qrand_itemp = __qrand_idum >> 9 | 0x3F800000; ⋮ 19 return *(float*)&__qrand_itemp - 1.0f;
src/static/libc64/qrand.c lines 15–20 at 09ca8e8 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
- ^In the USA GameCube release of Animal Crossing (
GAFE01_00, the build ACreTeam/ac-decomp rebuilds), the turnip market keeps one price per weekday, a 7-entrydaily_pricetable indexed Sunday (0) to Saturday (6), with the market type and the date the table was set. The turnip seller (kabuPeddler) charges the Sunday entry per turnip, times 10, 50, or 100 for a bag. The shop buys atKabu_get_price, the entry for the clock's current weekday, times 10, 50, or 100 for a bag and 0 for a spoiled turnip. The price doesn't change within a day.CodeACreTeam/ac-decompHow we know
26 /* 0x00 */ u16 daily_price[lbRTC_WEEKDAYS_MAX]; 27 /* 0x0E */ u16 trade_market; 28 /* 0x10 */ lbRTC_time_c update_time;
include/m_kabu_manager.h lines 25–29 at 09ca8e8 · upstream ↗ 46 lbRTC_SUNDAY = lbRTC_WEEKDAYS_BEGIN, 47 lbRTC_MONDAY,
include/lb_rtc.h lines 45–54 at 09ca8e8 · upstream ↗ 205 return Save_Get(kabu_price_schedule.daily_price[Common_Get(time.rtc_time.weekday)]);src/game/m_kabu_manager.c lines 204–206 at 09ca8e8 · upstream ↗ 81 static int sell_sum[aEKPD_KABU_TYPE_NUM] = { 10, 50, 100, 0 }; ⋮ 93 price = Save_Get(kabu_price_schedule).daily_price[lbRTC_SUNDAY] * sell_sum[kabuPeddler->kabu_type];
src/actor/npc/event/ac_ev_kabuPeddler_move.c_inc lines 81–96 at 09ca8e8 · upstream ↗ 2986 static int kabu_sum[4] = { 10, 50, 100, 0 }; ⋮ 2999 value += Kabu_get_price() * kabu_sum[item - ITM_KABU_START] * counter;
src/actor/npc/ac_npc_shop_common.c lines 2985–2999 at 09ca8e8 · upstream ↗ 2780#define ITM_KABU_SPOILED (ITM_KABU_START + 3)include/m_name_table.h lines 2776–2780 at 09ca8e8 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^In the USA GameCube release (
GAFE01_00, ACreTeam/ac-decomp),Kabu_decide_trade_marketpicks the week's market type from last week's with onefqranddraw: after type A, A 50%, B 30%, C 20%; after type B, A 60%, B 20%, C 20%; after type C, A 60%, B 30%, C 10%. The tables hold the A and B odds and C takes the rest. The decompilers' comments call A a spike trend, B random, and C falling.CodeACreTeam/ac-decompHow we know
12 Kabu_TRADE_MARKET_TYPE_START, ⋮ 14 Kabu_TRADE_MARKET_TYPE_A = Kabu_TRADE_MARKET_TYPE_START, /* spike trend */ 15 Kabu_TRADE_MARKET_TYPE_B, /* random trend */ 16 Kabu_TRADE_MARKET_TYPE_C, /* falling trend */
include/m_kabu_manager.h lines 11–19 at 09ca8e8 · upstream ↗ 32 static f32 next_trade_market_typeA[TRADE_MARKET_ODDS_NUM] = { 33 0.5f, /* Type A (50%) */ 34 0.3f /* Type B (30%) */ ⋮ 46 0.3f /* Type B (30%) */ ⋮ 59 for (i = 0; i < TRADE_MARKET_ODDS_NUM; i++) { 60 if (chosen < *next_type_market) { 61 break; ⋮ 64 chosen -= *next_type_market; ⋮ 69 i = 0;
src/game/m_kabu_manager.c lines 31–73 at 09ca8e8 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^In the USA GameCube release (
GAFE01_00, ACreTeam/ac-decomp), a type A week (Kabu_decide_price_schedule_typeA) first builds a type B week, then sets one day, Monday to Friday with equal odds (fqrand× 5, truncated, plus Monday), to 8 × the Sunday price: 560 to 1,040 bells (computed; 1,040 only from a 130-bell Sunday, see turnip-sunday-price).CodeACreTeam/ac-decomprevisedHow we know
80#define TRADE_MARKET_A_SPIKE_DAYS 5 81#define TRADE_MARKET_A_SPIKE_DAY_BEGIN lbRTC_MONDAY 82#define TRADE_MARKET_A_SPIKE_MULTIPLIER 8.0f ⋮ 93 Kabu_decide_price_schedule_typeB(); /* start with a 'random' trend */ ⋮ 97 spike_day = fqrand() * TRADE_MARKET_A_SPIKE_DAYS; 98 spike_day += lbRTC_MONDAY;
src/game/m_kabu_manager.c lines 80–100 at 09ca8e8 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
- ^In the USA GameCube release (
GAFE01_00, ACreTeam/ac-decomp), a type C week (Kabu_decide_price_schedule_typeC) multiplies the price each day from Monday to Saturday by 0.8 + 0.14999998 × r, with r fromfqrandin [0, 1), so each day is 80% to 95% of the day before; a decompiler's comment says the constant stands for 0.15.CodeACreTeam/ac-decompHow we know
147#define TRADE_MARKET_C_ADJUST_MULT_SPREAD 0.14999998f /* mwcc doesn't generate the same */ //0.15f 148#define TRADE_MARKET_C_ADJUST_MULT_BASE 0.8f ⋮ 159 current_price *= (TRADE_MARKET_C_ADJUST_MULT_BASE + TRADE_MARKET_C_ADJUST_MULT_SPREAD * fqrand()); /* [80%, 95%) */
src/game/m_kabu_manager.c lines 147–162 at 09ca8e8 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^In the USA GameCube release (
GAFE01_00, ACreTeam/ac-decomp), the daily pass of the town update adds up to 5 weeds for each 6:00 passed since the last update (computed frommAGrw_GetGrassMax), with no cap on the days counted. Each weed goes on a random empty square, in a random town block that has one, that is plantable ground (mCoBG_PLANT4) and not marked off by the cancel map; weeds stop when no such square is left. The weed kinds cycle through a shuffled table of 12. Whileclear_grassis set (a decompilers' comment: "set by Wisp, removes all weeds"), the next update removes every weed, adds none, and clears the flag; the back-in-time pass adds none either.CodeACreTeam/ac-decompHow we know
2754 delta_days++; 2755 if (delta_days > 1 && now_time->hour < 6) { 2756 delta_days--; ⋮ 2759 return delta_days * mAGrw_GRASS_PER_DAY;
src/game/m_all_grow_ovl.c lines 2744–2760 at 09ca8e8 · upstream ↗ 19#define mAGrw_GRASS_TABLE_NUM 12 ⋮ 25#define mAGrw_GRASS_PER_DAY 5
include/m_all_grow_ovl.h lines 19–25 at 09ca8e8 · upstream ↗ 601 days += t1->day - 1; 602 days += total_days[over_leap][t1->month - 1];
src/lb_rtc.c lines 542–609 at 09ca8e8 · upstream ↗ 2778 if (((cancel[0] >> ut_x) & 1) != 1 && (items[0] == EMPTY_NO) && 2779 (mCoBG_Attribute2CheckPlant(col->data.unit_attribute, &wpos_temp) == mCoBG_PLANT4)) {
src/game/m_all_grow_ovl.c lines 2762–2792 at 09ca8e8 · upstream ↗ 2856 grass_max = mAGrw_GetGrassMax(now_time, grow_time); ⋮ 2876 for (i = 0; i < grass_max; i++) { 2877 if (candidate_blocks > 0) { ⋮ 2883 selected_block = RANDOM(candidate_blocks); ⋮ 2910 grass_table_idx++;
src/game/m_all_grow_ovl.c lines 2835–2918 at 09ca8e8 · upstream ↗ 3078 if (Save_Get(clear_grass)) { 3079 mAGrw_ClearGrass();
src/game/m_all_grow_ovl.c lines 3078–3080 at 09ca8e8 · upstream ↗ 2929 if (IS_ITEM_GRASS(items[0])) { 2930 items[0] = EMPTY_NO;
src/game/m_all_grow_ovl.c lines 2920–2936 at 09ca8e8 · upstream ↗ 3143 if (Save_Get(clear_grass) == FALSE) { 3144 mAGrw_SetGrass(now_time, &grow_time, l_cancel_ut); ⋮ 3155 Save_Set(clear_grass, FALSE);
src/game/m_all_grow_ovl.c lines 3143–3155 at 09ca8e8 · upstream ↗ 139 /* 0x021395 */ u8 clear_grass; /* set by Wisp, removes all weeds */include/m_common_data.h line 139 at 09ca8e8 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^In the USA GameCube release (
GAFE01_00, ACreTeam/ac-decomp), the daily pass of the town update turns turnips into spoiled turnips once the clock is at or past the first Sunday 6:00 at or after the pass's own 6:00 start; so a turnip bought after an update made on a Sunday at or after 6:00 spoils at the first update from 6:00 the next Sunday (computed). It spoils turnips on the ground in town and on the island's ground, in every player's pockets (and a visitor's), on every floor of every house, and in each house's gyroid storage (haniwa). The same pass first clears spoiled turnips lying on the ground in town and on the island.CodeACreTeam/ac-decomprevisedHow we know
323 if (temp.weekday != lbRTC_SUNDAY && temp.weekday < lbRTC_WEEK) { 324 days = lbRTC_WEEK - temp.weekday; ⋮ 329 if (lbRTC_IsOverTime(&temp, now) == lbRTC_OVER) { 330 spoil = TRUE;
src/game/m_all_grow_ovl.c lines 316–334 at 09ca8e8 · upstream ↗ 3129 spoil_kabu = mAGrw_CheckSpoilKabuTime(&grow_time, now_time); ⋮ 3132 mAGrw_GROW); 3133 mAGr_GrowIslandFgItem(now_time, &grow_time, flower_time, spoil_kabu, mAGrw_GROW); ⋮ 3140 mAGrw_SpoilAllPossession(spoil_kabu);
src/game/m_all_grow_ovl.c lines 3128–3140 at 09ca8e8 · upstream ↗ 1623 static mAGrw_GROW_PROC grow_proc[] = { &mAGrw_ClearSpoiledKabu, &mAGrw_SpoilKabu, ⋮ 1631 static mAGrw_GROW_PROC grow_island_proc[] = { &mAGrw_ClearSpoiledKabu, &mAGrw_SpoilKabu, &mAGrw_GrowPlant };
src/game/m_all_grow_ovl.c lines 1623–1631 at 09ca8e8 · upstream ↗ 770 if (grow_info->spoil_kabu == TRUE) { 771 *item = ITM_KABU_SPOILED; ⋮ 774 *item = ITM_KABU_SPOILED; ⋮ 786 if (*item == ITM_KABU_SPOILED) { 787 *item = EMPTY_NO;
src/game/m_all_grow_ovl.c lines 765–792 at 09ca8e8 · upstream ↗ 2520 for (i = 0; i < PLAYER_NUM; i++) { 2521 mAGrw_SpoilPossession(priv->inventory.pockets, mPr_POCKETS_SLOT_COUNT); ⋮ 2525 if (Common_Get(player_no) == mPr_FOREIGNER) { ⋮ 2532 mAGrw_SpoilPossession((&Save_Get(homes[i]).floors[j].layer_main)[k].items[0], UT_TOTAL_NUM); ⋮ 2538 mAGrw_SpoilKabu(&haniwa_item->item, NULL);
src/game/m_all_grow_ovl.c lines 2509–2543 at 09ca8e8 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
- ^In the USA GameCube release (
GAFE01_00, ACreTeam/ac-decomp), the town's daily update (mAGrw_RenewalFgItem_ovl) runs when the outdoor town scene is built and no event notice is showing, and compares the clock with the time of the last update. If the clock is earlier than that time, it runs a back-in-time pass (mAGrw_GROW_FIRST_FORCE; the decompilers' comment says "Used when you go back in time"). Otherwise it runs the daily pass only once the clock reaches the first 6:00 after the last update: 6:00 the same day if that update came before 6:00, or the next day's. Either pass then records the clock as the last update.CodeACreTeam/ac-decompHow we know
3075 if (Save_Get(scene_no) == SCENE_FG) { ⋮ 3086 if (Common_Get(event_notification_active) != TRUE) { ⋮ 3088 is_over_time = lbRTC_IsOverTime(last_grow_time, now_time); ⋮ 3090 if (is_grow_time_equal == TRUE || is_over_time == lbRTC_LESS) { ⋮ 3095 proc_type = mAGrw_GROW_FIRST_FORCE; ⋮ 3117 if (grow_time.hour >= 6) { 3118 lbRTC_Add_DD(&grow_time, 1); ⋮ 3126 grow_time.hour = 6; ⋮ 3128 if (lbRTC_IsOverTime(&grow_time, now_time) == lbRTC_OVER) {
src/game/m_all_grow_ovl.c lines 3075–3128 at 09ca8e8 · upstream ↗ 3110 lbRTC_TimeCopy(last_grow_time, now_time);src/game/m_all_grow_ovl.c line 3110 at 09ca8e8 · upstream ↗ 3150 lbRTC_TimeCopy(last_grow_time, now_time);src/game/m_all_grow_ovl.c line 3150 at 09ca8e8 · upstream ↗ 30 mAGrw_GROW_FIRST_FORCE, /* Used when you go back in time */include/m_all_grow_ovl.h lines 27–37 at 09ca8e8 · upstream ↗ 421 if (t1->year < t0->year) { 422 return lbRTC_LESS; ⋮ 435 return lbRTC_LESS; ⋮ 439 return lbRTC_LESS; ⋮ 443 return lbRTC_LESS; ⋮ 447 return lbRTC_LESS; ⋮ 451 return lbRTC_LESS; ⋮ 455 return lbRTC_OVER;
src/lb_rtc.c lines 420–456 at 09ca8e8 · upstream ↗ 1196extern void mFM_FieldInit(GAME_PLAY* play) { ⋮ 1220 mAGrw_RenewalFgItem(Common_GetPointer(time.rtc_time));
src/game/m_field_make.c lines 1196–1220 at 09ca8e8 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
- ^In the USA GameCube release (
GAFE01_00, ACreTeam/ac-decomp), when the town update finds the clock earlier than its last update (the back-in-time pass),mAGrw_ZuruSpoilKabuspoils the turnips on the ground in town and on the island's ground, in pockets, in houses, and in gyroid storage if a save exists and the clock's date is earlier than the date stored with the save. That pass adds no weeds.CodeACreTeam/ac-decomprevisedHow we know
3035 if (Save_Get(save_exist) == TRUE && 3036 lbRTC_IsEqualDate(now_time->year, now_time->month, now_time->day, Save_Get(save_check).time.year, 3037 Save_Get(save_check).time.month, Save_Get(save_check).time.day) < lbRTC_EQUAL) { 3038 mAGrw_ZuruSpoilKabuFieldFgItem(); 3039 mAGrw_ZuruSpoilKabuIslandFgItem(Save_GetPointer(island)); 3040 mAGrw_SpoilAllPossession(TRUE);
src/game/m_all_grow_ovl.c lines 3034–3042 at 09ca8e8 · upstream ↗ 3090 if (is_grow_time_equal == TRUE || is_over_time == lbRTC_LESS) { ⋮ 3109 mAGrw_ZuruSpoilKabu(now_time);
src/game/m_all_grow_ovl.c lines 3090–3111 at 09ca8e8 · upstream ↗ 2991 grow_info.spoil_kabu = TRUE;src/game/m_all_grow_ovl.c lines 2983–3007 at 09ca8e8 · upstream ↗ 229 lbRTC_TimeCopy(&save_check->time, &common_data.time.rtc_time);src/game/m_flashrom.c lines 223–230 at 09ca8e8 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Revised after a check by a second AI model, 2026-10-22.
- ^In the USA GameCube release (
GAFE01_00, ACreTeam/ac-decomp), when play starts with the clock earlier than the time stored with the save, the player-select code sets two save flags,cheated_flagandnpc_force_go_home(a decompiler's comment: the player "cheated"). Whilecheated_flagis set,mail_event_checksends no birthday or Christmas cards and schedules no Valentine's Day mail; it then callsmGH_animal_return_initifnpc_force_go_homeis set, and clears both flags.CodeACreTeam/ac-decompHow we know
1188 // If the current time is before the last save time, the player "cheated" so set the cheated flag 1189 if (lbRTC_IsOverTime(&Save_Get(save_check).time, Common_GetPointer(time.rtc_time)) == lbRTC_LESS) { 1190 Save_Set(cheated_flag, TRUE); 1191 Save_Set(npc_force_go_home, TRUE);
src/actor/npc/ac_npc_p_sel2_talk.c_inc lines 1188–1192 at 09ca8e8 · upstream ↗ 4468static int mail_event_check(void) { ⋮ 4483 if (Save_Get(cheated_flag) == FALSE && ⋮ 4498 mNpc_SendEventBirthdayCard2(&priv[i].player_ID, i); ⋮ 4508 mNpc_SendEventXmasCard(&priv[i].player_ID, i); ⋮ 4525 Save_Get(event_save_common).valentines_day_date = target_date.raw; ⋮ 4532 if (Save_Get(npc_force_go_home) == TRUE) { 4533 mGH_animal_return_init(); ⋮ 4536 Save_Set(cheated_flag, FALSE); 4537 Save_Set(npc_force_go_home, FALSE);
src/actor/ac_event_manager.c lines 4468–4539 at 09ca8e8 · upstream ↗ Checked against the evidence by its author, an AI model, 2026-10-22.
Checked by a second AI model, 2026-10-22.
Sources
Code
- ACreTeam contributors. 2026. “ac-decomp: a decompilation of Animal Crossing for the GameCube (ACreTeam/ac-decomp).” Commit 09ca8e8. ACreTeam/ac-decomp @ 09ca8e8 · licence, all uses