From 26f28103ec406209c61eb114de5266f8a177031e Mon Sep 17 00:00:00 2001 From: khbsd Date: Wed, 9 Apr 2025 02:49:09 -0500 Subject: [PATCH] Add Time-Based Encounters (#6454) --- Makefile | 9 + include/config/overworld.h | 10 +- include/pokedex_area_screen.h | 12 +- include/rtc.h | 22 +- include/wild_encounter.h | 33 +- include/window.h | 1 + json_data_rules.mk | 6 - .../add_time_based_encounters.py | 97 +++ src/dexnav.c | 59 +- src/match_call.c | 11 +- src/pokedex.c | 33 +- src/pokedex_area_screen.c | 267 ++++++- src/pokedex_plus_hgss.c | 32 +- src/rtc.c | 19 +- src/wild_encounter.c | 206 ++++-- src/window.c | 14 + .../wild_encounters_to_header.py | 673 ++++++++++++++++++ 17 files changed, 1391 insertions(+), 113 deletions(-) create mode 100644 migration_scripts/add_time_based_encounters.py create mode 100644 tools/wild_encounters/wild_encounters_to_header.py diff --git a/Makefile b/Makefile index de030e096f..a2c41d957f 100644 --- a/Makefile +++ b/Makefile @@ -171,6 +171,15 @@ LEARNSET_HELPERS_DATA_DIR := $(LEARNSET_HELPERS_DIR)/porymoves_files LEARNSET_HELPERS_BUILD_DIR := $(LEARNSET_HELPERS_DIR)/build ALL_LEARNABLES_JSON := $(LEARNSET_HELPERS_BUILD_DIR)/all_learnables.json +# wild_encounters.h is generated by a Python script +WILD_ENCOUNTERS_TOOL_DIR := $(TOOLS_DIR)/wild_encounters +AUTO_GEN_TARGETS += $(DATA_SRC_SUBDIR)/wild_encounters.h + +$(DATA_SRC_SUBDIR)/wild_encounters.h: $(DATA_SRC_SUBDIR)/wild_encounters.json $(WILD_ENCOUNTERS_TOOL_DIR)/wild_encounters_to_header.py $(INCLUDE_DIRS)/config/overworld.h + python3 $(WILD_ENCOUNTERS_TOOL_DIR)/wild_encounters_to_header.py > $@ + +$(C_BUILDDIR)/wild_encounter.o: c_dep += $(DATA_SRC_SUBDIR)/wild_encounters.h + PERL := perl SHA1 := $(shell { command -v sha1sum || command -v shasum; } 2>/dev/null) -c diff --git a/include/config/overworld.h b/include/config/overworld.h index 820c4a5d9b..90aa546d48 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -103,9 +103,13 @@ #define GEN_8_PLA GEN_LATEST + 2 //Time -#define OW_TIMES_OF_DAY GEN_LATEST // Different generations have the times of day change at different times. -#define OW_USE_FAKE_RTC FALSE // When TRUE, seconds on the in-game clock will only advance once every 60 playTimeVBlanks (every 60 frames). -#define OW_ALTERED_TIME_RATIO GEN_LATEST // In GEN_8_PLA, the time in game moves forward 60 seconds for every second in the RTC. In GEN_9, it is 20 seconds. This has no effect if OW_USE_FAKE_RTC is FALSE. +#define OW_TIMES_OF_DAY GEN_LATEST // Different generations have the times of day change at different times. +#define OW_USE_FAKE_RTC FALSE // When TRUE, seconds on the in-game clock will only advance once every 60 playTimeVBlanks (every 60 frames). +#define OW_ALTERED_TIME_RATIO GEN_LATEST // In GEN_8_PLA, the time in game moves forward 60 seconds for every second in the RTC. In GEN_9, it is 20 seconds. This has no effect if OW_USE_FAKE_RTC is FALSE. +#define OW_TIME_OF_DAY_ENCOUNTERS FALSE // If TRUE, will allow the user to define and use different encounter tables based on the time of day. +#define OW_TIME_OF_DAY_DISABLE_FALLBACK FALSE // If TRUE, if the encounter table for a specific map and time is empty, the area will have no encounters instead of falling back to the vanilla map and time. +#define OW_TIME_OF_DAY_DEFAULT TIME_MORNING // Should be set to whatever is the first value in the TimeOfDay enum in rtc.h +#define OW_TIME_OF_DAY_FALLBACK OW_TIME_OF_DAY_DEFAULT // The time of day that encounter tables fall back to. Defaults to whatever OW_TIME_OF_DAY_FALLBACK is set to. // Overworld flags // To use the following features in scripting, replace the 0s with the flag ID you're assigning it to. diff --git a/include/pokedex_area_screen.h b/include/pokedex_area_screen.h index 86dfaa6c5b..1127d780f2 100755 --- a/include/pokedex_area_screen.h +++ b/include/pokedex_area_screen.h @@ -1,6 +1,16 @@ #ifndef GUARD_POKEDEX_AREA_SCREEN_H #define GUARD_POKEDEX_AREA_SCREEN_H -void ShowPokedexAreaScreen(u16, u8 *); +#include "rtc.h" + +extern u8 gAreaTimeOfDay; + +enum PokedexAreaScreenState +{ + DEX_SHOW_AREA_SCREEN, + DEX_UPDATE_AREA_SCREEN +}; + +void DisplayPokedexAreaScreen(u16 species, u8 *screenSwitchState, enum TimeOfDay timeOfDay, enum PokedexAreaScreenState areaState); #endif // GUARD_POKEDEX_AREA_SCREEN_H diff --git a/include/rtc.h b/include/rtc.h index bf8ff9aa93..ba69286716 100644 --- a/include/rtc.h +++ b/include/rtc.h @@ -1,7 +1,9 @@ #ifndef GUARD_RTC_UTIL_H #define GUARD_RTC_UTIL_H +#include "global.h" #include "siirtc.h" +#include "config/overworld.h" #define RTC_INIT_ERROR 0x0001 #define RTC_INIT_WARNING 0x0002 @@ -83,10 +85,17 @@ #define NIGHT_HOUR_END 6 #endif -#define TIME_MORNING 0 -#define TIME_DAY 1 -#define TIME_EVENING 2 -#define TIME_NIGHT 3 +// TIMES_OF_DAY_COUNT must be last +enum TimeOfDay +{ + TIME_MORNING, + TIME_DAY, + TIME_EVENING, + TIME_NIGHT, + TIMES_OF_DAY_COUNT, +}; + +STATIC_ASSERT(OW_TIME_OF_DAY_DEFAULT == 0, TimeOfDayDefaultMustBeFirstElementInTimeOfDayEnum) extern struct Time gLocalTime; @@ -112,12 +121,15 @@ void FormatHexDate(u8 *dest, s32 year, s32 month, s32 day); void RtcCalcTimeDifference(struct SiiRtcInfo *rtc, struct Time *result, struct Time *t); void RtcCalcLocalTime(void); bool8 IsBetweenHours(s32 hours, s32 begin, s32 end); -u8 GetTimeOfDay(void); +enum TimeOfDay GetTimeOfDay(void); +enum TimeOfDay GetTimeOfDayForDex(void); void RtcInitLocalTimeOffset(s32 hour, s32 minute); void RtcCalcLocalTimeOffset(s32 days, s32 hours, s32 minutes, s32 seconds); void CalcTimeDifference(struct Time *result, struct Time *t1, struct Time *t2); u32 RtcGetMinuteCount(void); u32 RtcGetLocalDayCount(void); void FormatDecimalTimeWithoutSeconds(u8 *dest, s8 hour, s8 minute, bool32 is24Hour); +enum TimeOfDay TryIncrementTimeOfDay(enum TimeOfDay timeOfDay); +enum TimeOfDay TryDecrementTimeOfDay(enum TimeOfDay timeOfDay); #endif // GUARD_RTC_UTIL_H diff --git a/include/wild_encounter.h b/include/wild_encounter.h index c7abb72aa1..0edf396164 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -1,8 +1,19 @@ #ifndef GUARD_WILD_ENCOUNTER_H #define GUARD_WILD_ENCOUNTER_H +#include "rtc.h" #include "constants/wild_encounter.h" +#define HEADER_NONE 0xFFFF + +enum WildPokemonArea { + WILD_AREA_LAND, + WILD_AREA_WATER, + WILD_AREA_ROCKS, + WILD_AREA_FISHING, + WILD_AREA_HIDDEN +}; + struct WildPokemon { u8 minLevel; @@ -16,17 +27,28 @@ struct WildPokemonInfo const struct WildPokemon *wildPokemon; }; +struct WildEncounterTypes +{ + const struct WildPokemonInfo *landMonsInfo; + const struct WildPokemonInfo *waterMonsInfo; + const struct WildPokemonInfo *rockSmashMonsInfo; + const struct WildPokemonInfo *fishingMonsInfo; + const struct WildPokemonInfo *hiddenMonsInfo; +}; + struct WildPokemonHeader { u8 mapGroup; u8 mapNum; - const struct WildPokemonInfo *landMonsInfo; - const struct WildPokemonInfo *waterMonsInfo; - const struct WildPokemonInfo *rockSmashMonsInfo; - const struct WildPokemonInfo *hiddenMonsInfo; - const struct WildPokemonInfo *fishingMonsInfo; + +#if OW_TIME_OF_DAY_ENCOUNTERS + const struct WildEncounterTypes encounterTypes[TIMES_OF_DAY_COUNT]; +#else + const struct WildEncounterTypes encounterTypes[1]; +#endif }; + extern const struct WildPokemonHeader gWildMonHeaders[]; extern bool8 gIsFishingEncounter; extern bool8 gIsSurfingEncounter; @@ -50,5 +72,6 @@ u8 ChooseWildMonIndex_Land(void); u8 ChooseWildMonIndex_WaterRock(void); u8 ChooseHiddenMonIndex(void); bool32 MapHasNoEncounterData(void); +enum TimeOfDay GetTimeOfDayForEncounters(u32 headerId, enum WildPokemonArea area); #endif // GUARD_WILD_ENCOUNTER_H diff --git a/include/window.h b/include/window.h index 2fd0bddb20..2dd67b123f 100644 --- a/include/window.h +++ b/include/window.h @@ -52,6 +52,7 @@ bool32 InitWindows(const struct WindowTemplate *templates); u32 AddWindow(const struct WindowTemplate *template); int AddWindowWithoutTileMap(const struct WindowTemplate *template); void RemoveWindow(u32 windowId); +void RemoveAllWindowsOnBg(u32 bgId); void FreeAllWindowBuffers(void); void CopyWindowToVram(u32 windowId, u32 mode); void CopyWindowRectToVram(u32 windowId, u32 mode, u32 x, u32 y, u32 w, u32 h); diff --git a/json_data_rules.mk b/json_data_rules.mk index aa9e5b3a25..1969496b0e 100755 --- a/json_data_rules.mk +++ b/json_data_rules.mk @@ -1,12 +1,6 @@ # JSON files are run through jsonproc, which is a tool that converts JSON data to an output file # based on an Inja template. https://github.com/pantor/inja -AUTO_GEN_TARGETS += $(DATA_SRC_SUBDIR)/wild_encounters.h -$(DATA_SRC_SUBDIR)/wild_encounters.h: $(DATA_SRC_SUBDIR)/wild_encounters.json $(DATA_SRC_SUBDIR)/wild_encounters.json.txt - $(JSONPROC) $^ $@ - -$(C_BUILDDIR)/wild_encounter.o: c_dep += $(DATA_SRC_SUBDIR)/wild_encounters.h - AUTO_GEN_TARGETS += $(DATA_SRC_SUBDIR)/region_map/region_map_entries.h $(DATA_SRC_SUBDIR)/region_map/region_map_entries.h: $(DATA_SRC_SUBDIR)/region_map/region_map_sections.json $(DATA_SRC_SUBDIR)/region_map/region_map_sections.json.txt $(JSONPROC) $^ $@ diff --git a/migration_scripts/add_time_based_encounters.py b/migration_scripts/add_time_based_encounters.py new file mode 100644 index 0000000000..c26d86019e --- /dev/null +++ b/migration_scripts/add_time_based_encounters.py @@ -0,0 +1,97 @@ +import json +import sys +import os + +""" +- you can change/add to these if you're adding seasons/days of the week, etc +- if you're just adding times of the day, make sure they are in the same order +as the `TimeOfDay` enum in include/rtc.h. +- you don't need to add an entry for `TIMES_OF_DAY_COUNT` +""" +ENCOUNTER_GROUP_SUFFIX = [ + "Morning", + "Day", + "Evening", + "Night" +] + +ARGS = [ + "--copy", +] + +""" +- make sure this number is the same as `OW_TIME_OF_DAY_DEFAULT` in config/overworld.h. +- by default in config/overworld.h it is set to `TIME_MORNING`, which is 0 in the +`TimeOfDay` enum in include/rtc.h +""" +OW_TIME_OF_DAY_DEFAULT = 0 + + +def GetWildEncounterFile(): + if not os.path.exists("Makefile"): + print("Please run this script from the project's root folder.") + quit() + + wFile = open("src/data/wild_encounters.json") + wData = json.load(wFile) + + wBackupData = json.dumps(wData, indent=2) + wBackupFile = open("src/data/wild_encounters.json.bak", mode="w", encoding="utf-8") + wBackupFile.write(wBackupData) + + global COPY_FULL_ENCOUNTER + COPY_FULL_ENCOUNTER = False + + for arg in ARGS: + if len(sys.argv) > 1: + if arg in sys.argv[1:3]: + if arg == ARGS[0]: + COPY_FULL_ENCOUNTER = True + + j = 0 + for group in wData["wild_encounter_groups"]: + wEncounters = wData["wild_encounter_groups"][j]["encounters"] + editMap = True + + wEncounters_New = list() + for map in wEncounters: + for suffix in ENCOUNTER_GROUP_SUFFIX: + tempSuffix = "_" + suffix + if tempSuffix in map["base_label"]: + editMap = False + break + else: + editMap = True + + if editMap: + k = 0 + for suffix in ENCOUNTER_GROUP_SUFFIX: + tempDict = dict() + if k == OW_TIME_OF_DAY_DEFAULT or COPY_FULL_ENCOUNTER: + tempDict = map.copy() + + tempMapLabel = "" + if "map" in map: + tempMapLabel = map["map"] + tempDict["map"] = tempMapLabel + + tempLabel = map["base_label"] + "_" + suffix + tempDict["base_label"] = tempLabel + wEncounters_New.append(tempDict) + if map["base_label"] in wEncounters_New: + wEncounters_New[map["base_label"]].pop() + + print(tempLabel + " added") + k += 1 + else: + wEncounters_New.append(map.copy()) + + wData["wild_encounter_groups"][j]["encounters"] = wEncounters_New + j += 1 + + wNewData = json.dumps(wData, indent=2) + wNewFile = open("src/data/wild_encounters.json", mode="w", encoding="utf-8") + wNewFile.write(wNewData) + + +GetWildEncounterFile() diff --git a/src/dexnav.c b/src/dexnav.c index 1e3db82d4b..52836bad75 100644 --- a/src/dexnav.c +++ b/src/dexnav.c @@ -36,6 +36,7 @@ #include "pokemon_summary_screen.h" #include "random.h" #include "region_map.h" +#include "rtc.h" #include "scanline_effect.h" #include "script.h" #include "script_pokemon_util.h" @@ -1515,10 +1516,8 @@ static u8 DexNavGeneratePotential(u8 searchLevel) static u8 GetEncounterLevelFromMapData(u16 species, u8 environment) { - u16 headerId = GetCurrentMapWildMonHeaderId(); - const struct WildPokemonInfo *landMonsInfo = gWildMonHeaders[headerId].landMonsInfo; - const struct WildPokemonInfo *waterMonsInfo = gWildMonHeaders[headerId].waterMonsInfo; - const struct WildPokemonInfo *hiddenMonsInfo = gWildMonHeaders[headerId].hiddenMonsInfo; + u32 headerId = GetCurrentMapWildMonHeaderId(); + enum TimeOfDay timeOfDay; u8 min = 100; u8 max = 0; u8 i; @@ -1526,6 +1525,9 @@ static u8 GetEncounterLevelFromMapData(u16 species, u8 environment) switch (environment) { case ENCOUNTER_TYPE_LAND: // grass + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + const struct WildPokemonInfo *landMonsInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; + if (landMonsInfo == NULL) return MON_LEVEL_NONEXISTENT; //Hidden pokemon should only appear on walkable tiles or surf tiles @@ -1539,6 +1541,9 @@ static u8 GetEncounterLevelFromMapData(u16 species, u8 environment) } break; case ENCOUNTER_TYPE_WATER: //water + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); + const struct WildPokemonInfo *waterMonsInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; + if (waterMonsInfo == NULL) return MON_LEVEL_NONEXISTENT; //Hidden pokemon should only appear on walkable tiles or surf tiles @@ -1552,6 +1557,9 @@ static u8 GetEncounterLevelFromMapData(u16 species, u8 environment) } break; case ENCOUNTER_TYPE_HIDDEN: + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_HIDDEN); + const struct WildPokemonInfo *hiddenMonsInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].hiddenMonsInfo; + if (hiddenMonsInfo == NULL) return MON_LEVEL_NONEXISTENT; @@ -1724,11 +1732,13 @@ static void CreateNoDataIcon(s16 x, s16 y) CreateSprite(&sNoDataIconTemplate, x, y, 0); } -static bool8 CapturedAllLandMons(u16 headerId) +static bool8 CapturedAllLandMons(u32 headerId) { u16 i, species; int count = 0; - const struct WildPokemonInfo* landMonsInfo = gWildMonHeaders[headerId].landMonsInfo; + enum TimeOfDay timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + + const struct WildPokemonInfo* landMonsInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; if (landMonsInfo != NULL) { @@ -1756,12 +1766,14 @@ static bool8 CapturedAllLandMons(u16 headerId) } //Checks if all Pokemon that can be encountered while surfing have been capture -static bool8 CapturedAllWaterMons(u16 headerId) +static bool8 CapturedAllWaterMons(u32 headerId) { u32 i; u16 species; u8 count = 0; - const struct WildPokemonInfo* waterMonsInfo = gWildMonHeaders[headerId].waterMonsInfo; + enum TimeOfDay timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); + + const struct WildPokemonInfo* waterMonsInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; if (waterMonsInfo != NULL) { @@ -1787,13 +1799,15 @@ static bool8 CapturedAllWaterMons(u16 headerId) return FALSE; } -static bool8 CapturedAllHiddenMons(u16 headerId) +static bool8 CapturedAllHiddenMons(u32 headerId) { u32 i; u16 species; u8 count = 0; - const struct WildPokemonInfo* hiddenMonsInfo = gWildMonHeaders[headerId].hiddenMonsInfo; + enum TimeOfDay timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_HIDDEN); + const struct WildPokemonInfo* hiddenMonsInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].hiddenMonsInfo; + if (hiddenMonsInfo != NULL) { for (i = 0; i < HIDDEN_WILD_COUNT; ++i) @@ -1820,7 +1834,7 @@ static bool8 CapturedAllHiddenMons(u16 headerId) static void DexNavLoadCapturedAllSymbols(void) { - u16 headerId = GetCurrentMapWildMonHeaderId(); + u32 headerId = GetCurrentMapWildMonHeaderId(); LoadCompressedSpriteSheetUsingHeap(&sCapturedAllPokemonSpriteSheet); @@ -1936,11 +1950,16 @@ static void DexNavLoadEncounterData(void) u8 hiddenIndex = 0; u16 species; u32 i; - u16 headerId = GetCurrentMapWildMonHeaderId(); - const struct WildPokemonInfo* landMonsInfo = gWildMonHeaders[headerId].landMonsInfo; - const struct WildPokemonInfo* waterMonsInfo = gWildMonHeaders[headerId].waterMonsInfo; - const struct WildPokemonInfo* hiddenMonsInfo = gWildMonHeaders[headerId].hiddenMonsInfo; + u32 headerId = GetCurrentMapWildMonHeaderId(); + enum TimeOfDay timeOfDay; + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + const struct WildPokemonInfo* landMonsInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); + const struct WildPokemonInfo* waterMonsInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_HIDDEN); + const struct WildPokemonInfo* hiddenMonsInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].hiddenMonsInfo; + // nop struct data memset(sDexNavUiDataPtr->landSpecies, 0, sizeof(sDexNavUiDataPtr->landSpecies)); memset(sDexNavUiDataPtr->waterSpecies, 0, sizeof(sDexNavUiDataPtr->waterSpecies)); @@ -2509,12 +2528,14 @@ bool8 TryFindHiddenPokemon(void) if ((*stepPtr) == 0 && (Random() % 100 < HIDDEN_MON_SEARCH_RATE)) { // hidden pokemon - u16 headerId = GetCurrentMapWildMonHeaderId(); + u32 headerId = GetCurrentMapWildMonHeaderId(); u8 index; u16 species; u8 environment; u8 taskId; - const struct WildPokemonInfo* hiddenMonsInfo = gWildMonHeaders[headerId].hiddenMonsInfo; + enum TimeOfDay timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_HIDDEN); + + const struct WildPokemonInfo* hiddenMonsInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].hiddenMonsInfo; bool8 isHiddenMon = FALSE; // while you can still technically find hidden pokemon if there are not hidden-only pokemon on a map, @@ -2539,7 +2560,7 @@ bool8 TryFindHiddenPokemon(void) } else { - species = gWildMonHeaders[headerId].landMonsInfo->wildPokemon[ChooseWildMonIndex_Land()].species; + species = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo->wildPokemon[ChooseWildMonIndex_Land()].species; environment = ENCOUNTER_TYPE_LAND; } break; @@ -2557,7 +2578,7 @@ bool8 TryFindHiddenPokemon(void) } else { - species = gWildMonHeaders[headerId].waterMonsInfo->wildPokemon[ChooseWildMonIndex_WaterRock()].species; + species = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo->wildPokemon[ChooseWildMonIndex_WaterRock()].species; environment = ENCOUNTER_TYPE_WATER; } diff --git a/src/match_call.c b/src/match_call.c index ea2ab8ed46..4d937381b5 100644 --- a/src/match_call.c +++ b/src/match_call.c @@ -1752,6 +1752,7 @@ static void PopulateSpeciesFromTrainerLocation(int matchCallId, u8 *destStr) int numSpecies; u8 slot; int i = 0; + enum TimeOfDay timeOfDay; if (gWildMonHeaders[i].mapGroup != MAP_GROUP(UNDEFINED)) // ??? This check is nonsense. { @@ -1766,18 +1767,20 @@ static void PopulateSpeciesFromTrainerLocation(int matchCallId, u8 *destStr) if (gWildMonHeaders[i].mapGroup != MAP_GROUP(UNDEFINED)) { + timeOfDay = GetTimeOfDayForEncounters(i, WILD_AREA_LAND); numSpecies = 0; - if (gWildMonHeaders[i].landMonsInfo) + if (gWildMonHeaders[i].encounterTypes[timeOfDay].landMonsInfo) { slot = GetLandEncounterSlot(); - species[numSpecies] = gWildMonHeaders[i].landMonsInfo->wildPokemon[slot].species; + species[numSpecies] = gWildMonHeaders[i].encounterTypes[timeOfDay].landMonsInfo->wildPokemon[slot].species; numSpecies++; } - if (gWildMonHeaders[i].waterMonsInfo) + timeOfDay = GetTimeOfDayForEncounters(i, WILD_AREA_WATER); + if (gWildMonHeaders[i].encounterTypes[timeOfDay].waterMonsInfo) { slot = GetWaterEncounterSlot(); - species[numSpecies] = gWildMonHeaders[i].waterMonsInfo->wildPokemon[slot].species; + species[numSpecies] = gWildMonHeaders[i].encounterTypes[timeOfDay].waterMonsInfo->wildPokemon[slot].species; numSpecies++; } diff --git a/src/pokedex.c b/src/pokedex.c index 141ae3cdd2..31d60c55bc 100644 --- a/src/pokedex.c +++ b/src/pokedex.c @@ -17,6 +17,7 @@ #include "pokedex_area_screen.h" #include "pokedex_cry_screen.h" #include "pokedex_plus_hgss.h" +#include "rtc.h" #include "scanline_effect.h" #include "sound.h" #include "sprite.h" @@ -255,7 +256,8 @@ static void Task_HandleInfoScreenInput(u8); static void Task_SwitchScreensFromInfoScreen(u8); static void Task_LoadInfoScreenWaitForFade(u8); static void Task_ExitInfoScreen(u8); -static void Task_LoadAreaScreen(u8); +static void Task_LoadAreaScreen(u8 taskId); +static void Task_ReloadAreaScreen(u8 taskId); static void Task_WaitForAreaScreenInput(u8 taskId); static void Task_SwitchScreensFromAreaScreen(u8); static void Task_LoadCryScreen(u8); @@ -3245,6 +3247,7 @@ static u8 LoadInfoScreen(struct PokedexListItem *item, u8 monSpriteId) u8 taskId; sPokedexListItem = item; + gAreaTimeOfDay = GetTimeOfDayForDex(); taskId = CreateTask(Task_LoadInfoScreen, 0); gTasks[taskId].tScrolling = FALSE; gTasks[taskId].tMonSpriteDone = TRUE; // Already has sprite from list view @@ -3552,7 +3555,7 @@ static void Task_LoadAreaScreen(u8 taskId) gMain.state++; break; case 2: - ShowPokedexAreaScreen(NationalPokedexNumToSpecies(sPokedexListItem->dexNum), &sPokedexView->screenSwitchState); + DisplayPokedexAreaScreen(NationalPokedexNumToSpecies(sPokedexListItem->dexNum), &sPokedexView->screenSwitchState, gAreaTimeOfDay, DEX_SHOW_AREA_SCREEN); SetVBlankCallback(gPokedexVBlankCB); sPokedexView->screenSwitchState = 0; gMain.state = 0; @@ -3561,6 +3564,29 @@ static void Task_LoadAreaScreen(u8 taskId) } } + +static void Task_ReloadAreaScreen(u8 taskId) +{ + switch (gMain.state) + { + case 0: + default: + sPokedexView->currentPage = PAGE_AREA; + gMain.state = 1; + break; + case 1: + LoadPokedexBgPalette(sPokedexView->isSearchResults); + SetGpuReg(REG_OFFSET_BG1CNT, BGCNT_PRIORITY(0) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(13) | BGCNT_16COLOR | BGCNT_TXT256x256); + gMain.state++; + break; + case 2: + DisplayPokedexAreaScreen(NationalPokedexNumToSpecies(sPokedexListItem->dexNum), &sPokedexView->screenSwitchState, gAreaTimeOfDay, DEX_UPDATE_AREA_SCREEN); + gMain.state = 0; + gTasks[taskId].func = Task_WaitForAreaScreenInput; + break; + } +} + static void Task_WaitForAreaScreenInput(u8 taskId) { // See Task_HandlePokedexAreaScreenInput() in pokedex_area_screen.c @@ -3581,6 +3607,9 @@ static void Task_SwitchScreensFromAreaScreen(u8 taskId) case 2: gTasks[taskId].func = Task_LoadCryScreen; break; + case 3: + gTasks[taskId].func = Task_ReloadAreaScreen; + break; } } } diff --git a/src/pokedex_area_screen.c b/src/pokedex_area_screen.c index dd45335afb..93afc0af7a 100644 --- a/src/pokedex_area_screen.c +++ b/src/pokedex_area_screen.c @@ -3,6 +3,7 @@ #include "event_data.h" #include "gpu_regs.h" #include "graphics.h" +#include "international_string_util.h" #include "main.h" #include "malloc.h" #include "menu.h" @@ -12,11 +13,15 @@ #include "pokedex_area_screen.h" #include "region_map.h" #include "roamer.h" +#include "rtc.h" #include "sound.h" #include "string_util.h" +#include "text.h" +#include "text_window.h" #include "trig.h" #include "pokedex_area_region_map.h" #include "wild_encounter.h" +#include "window.h" #include "constants/region_map_sections.h" #include "constants/rgb.h" #include "constants/songs.h" @@ -55,6 +60,15 @@ #define MAX_AREA_HIGHLIGHTS 64 // Maximum number of rectangular route highlights #define MAX_AREA_MARKERS 32 // Maximum number of circular spot highlights +#define LABEL_WINDOW_BG 1 +#define NUM_LABEL_WINDOWS 2 + +enum PokedexAreaLabels +{ + DEX_AREA_LABEL_TIME_OF_DAY, + DEX_AREA_LABEL_AREA_UNKNOWN +}; + struct OverworldArea { u8 mapGroup; @@ -90,28 +104,41 @@ struct /*0xF70*/ u8 charBuffer[64]; /*0xFB0*/ struct Sprite * areaUnknownSprites[3]; /*0xFBC*/ u8 areaUnknownGraphicsBuffer[0x600]; + /*0xFC0*/ u8 areaScreenLabelIds[NUM_LABEL_WINDOWS]; + /*0xFC8*/ u8 areaState; } static EWRAM_DATA *sPokedexAreaScreen = NULL; +EWRAM_DATA u8 gAreaTimeOfDay = 0; + static void FindMapsWithMon(u16); static void BuildAreaGlowTilemap(void); static void SetAreaHasMon(u16, u16); static void SetSpecialMapHasMon(u16, u16); static u16 GetRegionMapSectionId(u8, u8); -static bool8 MapHasSpecies(const struct WildPokemonHeader *, u16); +static bool8 MapHasSpecies(const struct WildEncounterTypes *, u16); static bool8 MonListHasSpecies(const struct WildPokemonInfo *, u16, u16); static void DoAreaGlow(void); -static void Task_ShowPokedexAreaScreen(u8); +static void Task_ShowPokedexAreaScreen(u8 taskId); +static void Task_UpdatePokedexAreaScreen(u8 taskId); static void CreateAreaMarkerSprites(void); static void LoadAreaUnknownGraphics(void); static void CreateAreaUnknownSprites(void); static void Task_HandlePokedexAreaScreenInput(u8); static void ResetPokedexAreaMapBg(void); static void DestroyAreaScreenSprites(void); -static void LoadHGSSScreenSelectBarSubmenu(void); +static void AddTimeOfDayLabels(void); +static void ShowEncounterInfoLabel(void); +static void ShowAreaUnknownLabel(void); +static void PrintAreaLabelText(const u8 *text, enum PokedexAreaLabels labelId, int textXPos); +static void ClearAreaWindowLabel(enum PokedexAreaLabels labelId); + +bool32 ShouldShowAreaUnknownLabel(void); static const u32 sAreaGlow_Pal[] = INCBIN_U32("graphics/pokedex/area_glow.gbapal"); static const u32 sAreaGlow_Gfx[] = INCBIN_U32("graphics/pokedex/area_glow.4bpp.lz"); + static const u32 sPokedexPlusHGSS_ScreenSelectBarSubmenu_Tilemap[] = INCBIN_U32("graphics/pokedex/hgss/SelectBar.bin.lz"); +static void LoadHGSSScreenSelectBarSubmenu(void); static const u16 sSpeciesHiddenFromAreaScreen[] = { SPECIES_WYNAUT }; @@ -205,6 +232,32 @@ static const struct SpriteTemplate sAreaUnknownSpriteTemplate = .callback = SpriteCallbackDummy }; +static const u8 sFontColor_AreaInfo[3] = {TEXT_COLOR_TRANSPARENT, TEXT_COLOR_WHITE, 5}; +static const struct WindowTemplate sTimeOfDayWindowLabelTemplates[] = +{ + [DEX_AREA_LABEL_TIME_OF_DAY] = + { + .bg = LABEL_WINDOW_BG, + .tilemapLeft = 22, + .tilemapTop = 18, + .width = 8, + .height = 2, + .paletteNum = 0, + .baseBlock = 0x16C + }, + + [DEX_AREA_LABEL_AREA_UNKNOWN] = + { + .bg = LABEL_WINDOW_BG, + .tilemapLeft = 12, + .tilemapTop = 18, + .width = 10, + .height = 2, + .paletteNum = 0, + .baseBlock = 0x240 + } +}; + static void ResetDrawAreaGlowState(void) { sPokedexAreaScreen->drawAreaGlowState = 0; @@ -287,7 +340,7 @@ static void FindMapsWithMon(u16 species) // Add regular species to the area map for (i = 0; gWildMonHeaders[i].mapGroup != MAP_GROUP(UNDEFINED); i++) { - if (MapHasSpecies(&gWildMonHeaders[i], species)) + if (MapHasSpecies(&gWildMonHeaders[i].encounterTypes[gAreaTimeOfDay], species)) { switch (gWildMonHeaders[i].mapGroup) { @@ -373,10 +426,13 @@ static u16 GetRegionMapSectionId(u8 mapGroup, u8 mapNum) return Overworld_GetMapHeaderByGroupAndId(mapGroup, mapNum)->regionMapSectionId; } -static bool8 MapHasSpecies(const struct WildPokemonHeader *info, u16 species) +static bool8 MapHasSpecies(const struct WildEncounterTypes *info, u16 species) { + u32 headerId = GetCurrentMapWildMonHeaderId(); + u8 currentMapGroup = gWildMonHeaders[headerId].mapGroup; + u8 currentMapNum = gWildMonHeaders[headerId].mapNum; // If this is a header for Altering Cave, skip it if it's not the current Altering Cave encounter set - if (GetRegionMapSectionId(info->mapGroup, info->mapNum) == MAPSEC_ALTERING_CAVE) + if (GetRegionMapSectionId(currentMapGroup, currentMapNum) == MAPSEC_ALTERING_CAVE) { sPokedexAreaScreen->alteringCaveCounter++; if (sPokedexAreaScreen->alteringCaveCounter != sPokedexAreaScreen->alteringCaveId + 1) @@ -576,17 +632,98 @@ static void DoAreaGlow(void) } } +static const u8 *GetTimeOfDayTextWithButton(enum TimeOfDay timeOfDay) +{ + static const u8 gText_Morning[] = _("{DPAD_UPDOWN} MORNING"); + static const u8 gText_Day[] = _("{DPAD_UPDOWN} DAY"); + static const u8 gText_Evening[] = _("{DPAD_UPDOWN} EVENING"); + static const u8 gText_Night[] = _("{DPAD_UPDOWN} NIGHT"); + + switch (gAreaTimeOfDay) + { + case TIME_MORNING: + return gText_Morning; + case TIME_EVENING: + return gText_Evening; + case TIME_NIGHT: + return gText_Night; + case TIME_DAY: + default: + return gText_Day; + } +} + +static void AddTimeOfDayLabels(void) +{ + u32 i; + + // clear the background before adding any more windows + RemoveAllWindowsOnBg(LABEL_WINDOW_BG); + + for (i = 0; i < NUM_LABEL_WINDOWS; i ++) + { + sPokedexAreaScreen->areaScreenLabelIds[i] = AddWindow(&sTimeOfDayWindowLabelTemplates[i]); + FillWindowPixelBuffer(sPokedexAreaScreen->areaScreenLabelIds[i], PIXEL_FILL(0)); + } +} + +static void ShowEncounterInfoLabel(void) +{ + const u8 *gText_TimeOfDay = GetTimeOfDayTextWithButton(gAreaTimeOfDay); + int stringXPos = GetStringCenterAlignXOffset(FONT_NORMAL, gText_TimeOfDay, 64); + + PrintAreaLabelText(gText_TimeOfDay, DEX_AREA_LABEL_TIME_OF_DAY, stringXPos); +} + +static void ShowAreaUnknownLabel(void) +{ + static const u8 gText_AreaUnknown[] = _("AREA UNKNOWN"); + int stringXPos = GetStringCenterAlignXOffset(FONT_NORMAL, gText_AreaUnknown, 80); + + PrintAreaLabelText(gText_AreaUnknown, DEX_AREA_LABEL_AREA_UNKNOWN, stringXPos); +} + +static void ClearAreaWindowLabel(enum PokedexAreaLabels labelId) +{ + FillWindowPixelBuffer(sPokedexAreaScreen->areaScreenLabelIds[labelId], PIXEL_FILL(0)); + ClearWindowTilemap(sPokedexAreaScreen->areaScreenLabelIds[labelId]); + ScheduleBgCopyTilemapToVram(0); +} + +static void PrintAreaLabelText(const u8 *text, enum PokedexAreaLabels labelId, int textXPos) +{ + ClearAreaWindowLabel(labelId); + + PutWindowTilemap(sPokedexAreaScreen->areaScreenLabelIds[labelId]); + FillWindowPixelBuffer(sPokedexAreaScreen->areaScreenLabelIds[labelId], PIXEL_FILL(7)); + + AddTextPrinterParameterized4(sPokedexAreaScreen->areaScreenLabelIds[labelId], FONT_NORMAL, textXPos, 0, 0, 0, sFontColor_AreaInfo, TEXT_SKIP_DRAW, text); + CopyWindowToVram(sPokedexAreaScreen->areaScreenLabelIds[labelId], COPYWIN_FULL); +} + +bool32 ShouldShowAreaUnknownLabel(void) +{ + return !sPokedexAreaScreen->numOverworldAreas && !sPokedexAreaScreen->numSpecialAreas; +} + #define tState data[0] -void ShowPokedexAreaScreen(u16 species, u8 *screenSwitchState) +void DisplayPokedexAreaScreen(u16 species, u8 *screenSwitchState, enum TimeOfDay timeOfDay, enum PokedexAreaScreenState areaState) { u8 taskId; sPokedexAreaScreen = AllocZeroed(sizeof(*sPokedexAreaScreen)); sPokedexAreaScreen->species = species; sPokedexAreaScreen->screenSwitchState = screenSwitchState; + sPokedexAreaScreen->areaState = areaState; + gAreaTimeOfDay = timeOfDay; screenSwitchState[0] = 0; - taskId = CreateTask(Task_ShowPokedexAreaScreen, 0); + + if (sPokedexAreaScreen->areaState == DEX_UPDATE_AREA_SCREEN) + taskId = CreateTask(Task_UpdatePokedexAreaScreen, 0); + else + taskId = CreateTask(Task_ShowPokedexAreaScreen, 0); + gTasks[taskId].tState = 0; } @@ -627,20 +764,29 @@ static void Task_ShowPokedexAreaScreen(u8 taskId) CreateAreaMarkerSprites(); break; case 7: - LoadAreaUnknownGraphics(); + if(!OW_TIME_OF_DAY_ENCOUNTERS) + LoadAreaUnknownGraphics(); break; case 8: - CreateAreaUnknownSprites(); + if(!OW_TIME_OF_DAY_ENCOUNTERS) + CreateAreaUnknownSprites(); break; case 9: BeginNormalPaletteFade(PALETTES_ALL & ~(0x14), 0, 16, 0, RGB_BLACK); break; case 10: - if (POKEDEX_PLUS_HGSS) - LoadHGSSScreenSelectBarSubmenu(); - SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_BG0 | BLDCNT_EFFECT_BLEND | BLDCNT_TGT2_BG0 | BLDCNT_TGT2_ALL); StartAreaGlow(); + if (OW_TIME_OF_DAY_ENCOUNTERS) + { + AddTimeOfDayLabels(); + ShowEncounterInfoLabel(); + if (ShouldShowAreaUnknownLabel()) + ShowAreaUnknownLabel(); + DoScheduledBgTilemapCopiesToVram(); + } + if (POKEDEX_PLUS_HGSS) + LoadHGSSScreenSelectBarSubmenu(); ShowBg(2); ShowBg(3); // TryShowPokedexAreaMap will have done this already SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON); @@ -654,6 +800,58 @@ static void Task_ShowPokedexAreaScreen(u8 taskId) gTasks[taskId].tState++; } +static void Task_UpdatePokedexAreaScreen(u8 taskId) +{ + switch (gTasks[taskId].tState) + { + case 0: + ClearAreaWindowLabel(DEX_AREA_LABEL_TIME_OF_DAY); + ClearAreaWindowLabel(DEX_AREA_LABEL_AREA_UNKNOWN); + ResetSpriteData(); + FreeAllSpritePalettes(); + ResetDrawAreaGlowState(); + HideBg(2); + HideBg(0); + break; + case 1: + SetBgAttribute(3, BG_ATTR_CHARBASEINDEX, 3); + LoadPokedexAreaMapGfx(&sPokedexAreaMapTemplate); + PokedexAreaMapChangeBgY(-8); + StringFill(sPokedexAreaScreen->charBuffer, CHAR_SPACE, 16); + break; + case 2: + if (TryShowPokedexAreaMap() == TRUE) + return; + break; + case 3: + if (DrawAreaGlow()) + return; + break; + case 4: + ShowRegionMapForPokedexAreaScreen(&sPokedexAreaScreen->regionMap); + CreateRegionMapPlayerIcon(1, 1); + PokedexAreaScreen_UpdateRegionMapVariablesAndVideoRegs(0, -8); + CreateAreaMarkerSprites(); + break; + case 5: + SetGpuReg(REG_OFFSET_BLDCNT, BLDCNT_TGT1_BG0 | BLDCNT_EFFECT_BLEND | BLDCNT_TGT2_BG0 | BLDCNT_TGT2_ALL); + StartAreaGlow(); + AddTimeOfDayLabels(); + ShowEncounterInfoLabel(); + if (ShouldShowAreaUnknownLabel()) + ShowAreaUnknownLabel(); + ShowBg(2); + SetGpuRegBits(REG_OFFSET_DISPCNT, DISPCNT_OBJ_ON); + break; + case 6: + gTasks[taskId].func = Task_HandlePokedexAreaScreenInput; + gTasks[taskId].tState = 0; + return; + } + + gTasks[taskId].tState++; +} + static void Task_HandlePokedexAreaScreenInput(u8 taskId) { DoAreaGlow(); @@ -687,16 +885,42 @@ static void Task_HandlePokedexAreaScreenInput(u8 taskId) gTasks[taskId].data[1] = 2; PlaySE(SE_DEX_PAGE); } + else if (JOY_NEW(DPAD_UP) && OW_TIME_OF_DAY_ENCOUNTERS == TRUE) + { + gTasks[taskId].data[1] = 3; + gAreaTimeOfDay = TryDecrementTimeOfDay(gAreaTimeOfDay); + sPokedexAreaScreen->areaState = DEX_UPDATE_AREA_SCREEN; + PlaySE(SE_DEX_PAGE); + } + else if (JOY_NEW(DPAD_DOWN) && OW_TIME_OF_DAY_ENCOUNTERS == TRUE) + { + gTasks[taskId].data[1] = 3; + gAreaTimeOfDay = TryIncrementTimeOfDay(gAreaTimeOfDay); + sPokedexAreaScreen->areaState = DEX_UPDATE_AREA_SCREEN; + PlaySE(SE_DEX_PAGE); + } else + { + // screen needs to fade if its doing anything except updating the area screen + sPokedexAreaScreen->areaState = DEX_SHOW_AREA_SCREEN; return; + } break; case 2: - BeginNormalPaletteFade(PALETTES_ALL & ~(0x14), 0, 0, 16, RGB_BLACK); + if (sPokedexAreaScreen->areaState != DEX_UPDATE_AREA_SCREEN) + BeginNormalPaletteFade(PALETTES_ALL & ~(0x14), 0, 0, 16, RGB_BLACK); break; case 3: if (gPaletteFade.active) return; DestroyAreaScreenSprites(); + if (OW_TIME_OF_DAY_ENCOUNTERS) + { + ClearAreaWindowLabel(DEX_AREA_LABEL_TIME_OF_DAY); + ClearAreaWindowLabel(DEX_AREA_LABEL_AREA_UNKNOWN); + RemoveAllWindowsOnBg(LABEL_WINDOW_BG); + } + sPokedexAreaScreen->screenSwitchState[0] = gTasks[taskId].data[1]; ResetPokedexAreaMapBg(); DestroyTask(taskId); @@ -755,13 +979,16 @@ static void DestroyAreaScreenSprites(void) for (i = 0; i < sPokedexAreaScreen->numAreaMarkerSprites; i++) DestroySprite(sPokedexAreaScreen->areaMarkerSprites[i]); - // Destroy "Area Unknown" sprites - FreeSpriteTilesByTag(TAG_AREA_UNKNOWN); - FreeSpritePaletteByTag(TAG_AREA_UNKNOWN); - for (i = 0; i < ARRAY_COUNT(sPokedexAreaScreen->areaUnknownSprites); i++) + if (!OW_TIME_OF_DAY_ENCOUNTERS) { - if (sPokedexAreaScreen->areaUnknownSprites[i]) - DestroySprite(sPokedexAreaScreen->areaUnknownSprites[i]); + // Destroy "Area Unknown" sprites + FreeSpriteTilesByTag(TAG_AREA_UNKNOWN); + FreeSpritePaletteByTag(TAG_AREA_UNKNOWN); + for (i = 0; i < ARRAY_COUNT(sPokedexAreaScreen->areaUnknownSprites); i++) + { + if (sPokedexAreaScreen->areaUnknownSprites[i]) + DestroySprite(sPokedexAreaScreen->areaUnknownSprites[i]); + } } } diff --git a/src/pokedex_plus_hgss.c b/src/pokedex_plus_hgss.c index d3e270d645..999bfd0b70 100644 --- a/src/pokedex_plus_hgss.c +++ b/src/pokedex_plus_hgss.c @@ -28,6 +28,7 @@ #include "region_map.h" #include "pokemon.h" #include "reset_rtc_screen.h" +#include "rtc.h" #include "scanline_effect.h" #include "shop.h" #include "sound.h" @@ -526,7 +527,8 @@ static void Task_HandleInfoScreenInput(u8); static void Task_SwitchScreensFromInfoScreen(u8); static void Task_LoadInfoScreenWaitForFade(u8); static void Task_ExitInfoScreen(u8); -static void Task_LoadAreaScreen(u8); +static void Task_LoadAreaScreen(u8 taskId); +static void Task_ReloadAreaScreen(u8 taskId); static void Task_WaitForAreaScreenInput(u8 taskId); static void Task_SwitchScreensFromAreaScreen(u8); static void Task_LoadCryScreen(u8); @@ -3736,6 +3738,7 @@ static u8 LoadInfoScreen(struct PokedexListItem *item, u8 monSpriteId) u8 taskId; sPokedexListItem = item; + gAreaTimeOfDay = GetTimeOfDayForDex(); taskId = CreateTask(Task_LoadInfoScreen, 0); gTasks[taskId].tScrolling = FALSE; gTasks[taskId].tMonSpriteDone = TRUE; // Already has sprite from list view @@ -3994,7 +3997,7 @@ static void Task_LoadAreaScreen(u8 taskId) gMain.state++; break; case 2: - ShowPokedexAreaScreen(NationalPokedexNumToSpeciesHGSS(sPokedexListItem->dexNum), &sPokedexView->screenSwitchState); + DisplayPokedexAreaScreen(NationalPokedexNumToSpeciesHGSS(sPokedexListItem->dexNum), &sPokedexView->screenSwitchState, gAreaTimeOfDay, DEX_SHOW_AREA_SCREEN); SetVBlankCallback(gPokedexVBlankCB); sPokedexView->screenSwitchState = 0; gMain.state = 0; @@ -4003,6 +4006,28 @@ static void Task_LoadAreaScreen(u8 taskId) } } +static void Task_ReloadAreaScreen(u8 taskId) +{ + switch (gMain.state) + { + case 0: + default: + sPokedexView->currentPage = PAGE_AREA; + gMain.state = 1; + break; + case 1: + LoadPokedexBgPalette(sPokedexView->isSearchResults); + SetGpuReg(REG_OFFSET_BG1CNT, BGCNT_PRIORITY(0) | BGCNT_CHARBASE(0) | BGCNT_SCREENBASE(13) | BGCNT_16COLOR | BGCNT_TXT256x256); + gMain.state++; + break; + case 2: + DisplayPokedexAreaScreen(NationalPokedexNumToSpeciesHGSS(sPokedexListItem->dexNum), &sPokedexView->screenSwitchState, gAreaTimeOfDay, DEX_UPDATE_AREA_SCREEN); + gMain.state = 0; + gTasks[taskId].func = Task_WaitForAreaScreenInput; + break; + } +} + static void Task_WaitForAreaScreenInput(u8 taskId) { // See Task_HandlePokedexAreaScreenInput() in pokedex_area_screen.c @@ -4026,6 +4051,9 @@ static void Task_SwitchScreensFromAreaScreen(u8 taskId) else gTasks[taskId].func = Task_LoadStatsScreen; break; + case 3: + gTasks[taskId].func = Task_ReloadAreaScreen; + break; } } } diff --git a/src/rtc.c b/src/rtc.c index e5a8b2d1df..eb7272c8c0 100644 --- a/src/rtc.c +++ b/src/rtc.c @@ -1,4 +1,6 @@ #include "global.h" +#include "battle_pike.h" +#include "battle_pyramid.h" #include "rtc.h" #include "string_util.h" #include "strings.h" @@ -325,7 +327,7 @@ bool8 IsBetweenHours(s32 hours, s32 begin, s32 end) return hours >= begin && hours < end; } -u8 GetTimeOfDay(void) +enum TimeOfDay GetTimeOfDay(void) { RtcCalcLocalTime(); if (IsBetweenHours(gLocalTime.hours, MORNING_HOUR_BEGIN, MORNING_HOUR_END)) @@ -337,6 +339,11 @@ u8 GetTimeOfDay(void) return TIME_DAY; } +enum TimeOfDay GetTimeOfDayForDex(void) +{ + return OW_TIME_OF_DAY_ENCOUNTERS ? GetTimeOfDay() : OW_TIME_OF_DAY_DEFAULT; +} + void RtcInitLocalTimeOffset(s32 hour, s32 minute) { RtcCalcLocalTimeOffset(0, hour, minute, 0); @@ -418,3 +425,13 @@ void FormatDecimalTimeWithoutSeconds(u8 *txtPtr, s8 hour, s8 minute, bool32 is24 *txtPtr++ = EOS; *txtPtr = EOS; } + +enum TimeOfDay TryIncrementTimeOfDay(enum TimeOfDay timeOfDay) +{ + return timeOfDay == TIME_NIGHT ? TIME_MORNING : timeOfDay + 1; +} + +enum TimeOfDay TryDecrementTimeOfDay(enum TimeOfDay timeOfDay) +{ + return timeOfDay == TIME_MORNING ? TIME_NIGHT : timeOfDay - 1; +} diff --git a/src/wild_encounter.c b/src/wild_encounter.c index bd00e58fcd..3a3a7d8dc7 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -37,25 +37,16 @@ extern const u8 EventScript_SprayWoreOff[]; #define NUM_FISHING_SPOTS_3 149 #define NUM_FISHING_SPOTS (NUM_FISHING_SPOTS_1 + NUM_FISHING_SPOTS_2 + NUM_FISHING_SPOTS_3) -enum { - WILD_AREA_LAND, - WILD_AREA_WATER, - WILD_AREA_ROCKS, - WILD_AREA_FISHING, -}; - #define WILD_CHECK_REPEL (1 << 0) #define WILD_CHECK_KEEN_EYE (1 << 1) -#define HEADER_NONE 0xFFFF - static u16 FeebasRandom(void); static void FeebasSeedRng(u16 seed); static void UpdateChainFishingStreak(); static bool8 IsWildLevelAllowedByRepel(u8 level); static void ApplyFluteEncounterRateMod(u32 *encRate); static void ApplyCleanseTagEncounterRateMod(u32 *encRate); -static u8 GetMaxLevelOfSpeciesInWildTable(const struct WildPokemon *wildMon, u16 species, u8 area); +static u8 GetMaxLevelOfSpeciesInWildTable(const struct WildPokemon *wildMon, u16 species, enum WildPokemonArea area); #ifdef BUGFIX static bool8 TryGetAbilityInfluencedWildMonIndex(const struct WildPokemon *wildMon, u8 type, u16 ability, u8 *monIndex, u32 size); #else @@ -305,7 +296,7 @@ static u8 ChooseWildMonIndex_Fishing(u8 rod) return wildMonIndex; } -static u8 ChooseWildMonLevel(const struct WildPokemon *wildPokemon, u8 wildMonIndex, u8 area) +static u8 ChooseWildMonLevel(const struct WildPokemon *wildPokemon, u8 wildMonIndex, enum WildPokemonArea area) { u8 min; u8 max; @@ -384,6 +375,87 @@ u16 GetCurrentMapWildMonHeaderId(void) return HEADER_NONE; } +enum TimeOfDay GetTimeOfDayForEncounters(u32 headerId, enum WildPokemonArea area) +{ + const struct WildPokemonInfo *wildMonInfo; + enum TimeOfDay timeOfDay = GetTimeOfDay(); + + if (!OW_TIME_OF_DAY_ENCOUNTERS) + return OW_TIME_OF_DAY_DEFAULT; + + if (InBattlePike()) + { + switch (area) + { + default: + case WILD_AREA_LAND: + wildMonInfo = gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; + break; + case WILD_AREA_WATER: + wildMonInfo = gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; + break; + case WILD_AREA_ROCKS: + wildMonInfo = gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].rockSmashMonsInfo; + break; + case WILD_AREA_FISHING: + wildMonInfo = gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].fishingMonsInfo; + break; + case WILD_AREA_HIDDEN: + wildMonInfo = gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].hiddenMonsInfo; + break; + } + } + else if (InBattlePyramid()) + { + switch (area) + { + default: + case WILD_AREA_LAND: + wildMonInfo = gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; + break; + case WILD_AREA_WATER: + wildMonInfo = gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; + break; + case WILD_AREA_ROCKS: + wildMonInfo = gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].rockSmashMonsInfo; + break; + case WILD_AREA_FISHING: + wildMonInfo = gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].fishingMonsInfo; + break; + case WILD_AREA_HIDDEN: + wildMonInfo = gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].hiddenMonsInfo; + break; + } + } + else + { + switch (area) + { + default: + case WILD_AREA_LAND: + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; + break; + case WILD_AREA_WATER: + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; + break; + case WILD_AREA_ROCKS: + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].rockSmashMonsInfo; + break; + case WILD_AREA_FISHING: + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].fishingMonsInfo; + break; + case WILD_AREA_HIDDEN: + wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].hiddenMonsInfo; + break; + } + } + + if (wildMonInfo == NULL && !OW_TIME_OF_DAY_DISABLE_FALLBACK) + return OW_TIME_OF_DAY_FALLBACK; + else + return timeOfDay; +} + u8 PickWildMonNature(void) { u8 i; @@ -459,7 +531,7 @@ void CreateWildMon(u16 species, u8 level) #define TRY_GET_ABILITY_INFLUENCED_WILD_MON_INDEX(wildPokemon, type, ability, ptr, count) TryGetAbilityInfluencedWildMonIndex(wildPokemon, type, ability, ptr) #endif -static bool8 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, u8 area, u8 flags) +static bool8 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, enum WildPokemonArea area, u8 flags) { u8 wildMonIndex = 0; u8 level; @@ -501,6 +573,10 @@ static bool8 TryGenerateWildMon(const struct WildPokemonInfo *wildMonInfo, u8 ar case WILD_AREA_ROCKS: wildMonIndex = ChooseWildMonIndex_WaterRock(); break; + default: + case WILD_AREA_FISHING: + case WILD_AREA_HIDDEN: + break; } level = ChooseWildMonLevel(wildMonInfo->wildPokemon, wildMonIndex, area); @@ -621,7 +697,8 @@ static bool8 AreLegendariesInSootopolisPreventingEncounters(void) bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior) { - u16 headerId; + u32 headerId; + enum TimeOfDay timeOfDay; struct Roamer *roamer; if (sWildEncountersDisabled == TRUE) @@ -633,11 +710,13 @@ bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior) if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS) { headerId = GetBattlePikeWildMonHeaderId(); + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + if (prevMetatileBehavior != curMetatileBehavior && !AllowWildCheckOnNewMetatile()) return FALSE; - else if (WildEncounterCheck(gBattlePikeWildMonHeaders[headerId].landMonsInfo->encounterRate, FALSE) != TRUE) + else if (WildEncounterCheck(gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo->encounterRate, FALSE) != TRUE) return FALSE; - else if (TryGenerateWildMon(gBattlePikeWildMonHeaders[headerId].landMonsInfo, WILD_AREA_LAND, WILD_CHECK_KEEN_EYE) != TRUE) + else if (TryGenerateWildMon(gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, WILD_CHECK_KEEN_EYE) != TRUE) return FALSE; else if (!TryGenerateBattlePikeWildMon(TRUE)) return FALSE; @@ -648,11 +727,13 @@ bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior) if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) { headerId = gSaveBlock2Ptr->frontier.curChallengeBattleNum; + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + if (prevMetatileBehavior != curMetatileBehavior && !AllowWildCheckOnNewMetatile()) return FALSE; - else if (WildEncounterCheck(gBattlePyramidWildMonHeaders[headerId].landMonsInfo->encounterRate, FALSE) != TRUE) + else if (WildEncounterCheck(gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo->encounterRate, FALSE) != TRUE) return FALSE; - else if (TryGenerateWildMon(gBattlePyramidWildMonHeaders[headerId].landMonsInfo, WILD_AREA_LAND, WILD_CHECK_KEEN_EYE) != TRUE) + else if (TryGenerateWildMon(gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, WILD_CHECK_KEEN_EYE) != TRUE) return FALSE; GenerateBattlePyramidWildMon(); @@ -664,11 +745,13 @@ bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior) { if (MetatileBehavior_IsLandWildEncounter(curMetatileBehavior) == TRUE) { - if (gWildMonHeaders[headerId].landMonsInfo == NULL) + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + + if (gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo == NULL) return FALSE; else if (prevMetatileBehavior != curMetatileBehavior && !AllowWildCheckOnNewMetatile()) return FALSE; - else if (WildEncounterCheck(gWildMonHeaders[headerId].landMonsInfo->encounterRate, FALSE) != TRUE) + else if (WildEncounterCheck(gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo->encounterRate, FALSE) != TRUE) return FALSE; if (TryStartRoamerEncounter()) @@ -689,12 +772,12 @@ bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior) } // try a regular wild land encounter - if (TryGenerateWildMon(gWildMonHeaders[headerId].landMonsInfo, WILD_AREA_LAND, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE) == TRUE) + if (TryGenerateWildMon(gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE) == TRUE) { if (TryDoDoubleWildBattle()) { struct Pokemon mon1 = gEnemyParty[0]; - TryGenerateWildMon(gWildMonHeaders[headerId].landMonsInfo, WILD_AREA_LAND, WILD_CHECK_KEEN_EYE); + TryGenerateWildMon(gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, WILD_CHECK_KEEN_EYE); gEnemyParty[1] = mon1; BattleSetup_StartDoubleWildBattle(); } @@ -711,13 +794,15 @@ bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior) else if (MetatileBehavior_IsWaterWildEncounter(curMetatileBehavior) == TRUE || (TestPlayerAvatarFlags(PLAYER_AVATAR_FLAG_SURFING) && MetatileBehavior_IsBridgeOverWater(curMetatileBehavior) == TRUE)) { + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); + if (AreLegendariesInSootopolisPreventingEncounters() == TRUE) return FALSE; - else if (gWildMonHeaders[headerId].waterMonsInfo == NULL) + else if (gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo == NULL) return FALSE; else if (prevMetatileBehavior != curMetatileBehavior && !AllowWildCheckOnNewMetatile()) return FALSE; - else if (WildEncounterCheck(gWildMonHeaders[headerId].waterMonsInfo->encounterRate, FALSE) != TRUE) + else if (WildEncounterCheck(gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo->encounterRate, FALSE) != TRUE) return FALSE; if (TryStartRoamerEncounter()) @@ -731,13 +816,13 @@ bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior) } else // try a regular surfing encounter { - if (TryGenerateWildMon(gWildMonHeaders[headerId].waterMonsInfo, WILD_AREA_WATER, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE) == TRUE) + if (TryGenerateWildMon(gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo, WILD_AREA_WATER, WILD_CHECK_REPEL | WILD_CHECK_KEEN_EYE) == TRUE) { gIsSurfingEncounter = TRUE; if (TryDoDoubleWildBattle()) { struct Pokemon mon1 = gEnemyParty[0]; - TryGenerateWildMon(gWildMonHeaders[headerId].waterMonsInfo, WILD_AREA_WATER, WILD_CHECK_KEEN_EYE); + TryGenerateWildMon(gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo, WILD_AREA_WATER, WILD_CHECK_KEEN_EYE); gEnemyParty[1] = mon1; BattleSetup_StartDoubleWildBattle(); } @@ -758,11 +843,14 @@ bool8 StandardWildEncounter(u16 curMetatileBehavior, u16 prevMetatileBehavior) void RockSmashWildEncounter(void) { - u16 headerId = GetCurrentMapWildMonHeaderId(); + u32 headerId = GetCurrentMapWildMonHeaderId(); + enum TimeOfDay timeOfDay; if (headerId != HEADER_NONE) { - const struct WildPokemonInfo *wildPokemonInfo = gWildMonHeaders[headerId].rockSmashMonsInfo; + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_ROCKS); + + const struct WildPokemonInfo *wildPokemonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].rockSmashMonsInfo; if (wildPokemonInfo == NULL) { @@ -788,7 +876,8 @@ void RockSmashWildEncounter(void) bool8 SweetScentWildEncounter(void) { s16 x, y; - u16 headerId; + u32 headerId; + enum TimeOfDay timeOfDay; PlayerGetDestCoords(&x, &y); headerId = GetCurrentMapWildMonHeaderId(); @@ -797,7 +886,9 @@ bool8 SweetScentWildEncounter(void) if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PIKE_ROOM_WILD_MONS) { headerId = GetBattlePikeWildMonHeaderId(); - if (TryGenerateWildMon(gBattlePikeWildMonHeaders[headerId].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + + if (TryGenerateWildMon(gBattlePikeWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) return FALSE; TryGenerateBattlePikeWildMon(FALSE); @@ -807,7 +898,9 @@ bool8 SweetScentWildEncounter(void) if (gMapHeader.mapLayoutId == LAYOUT_BATTLE_FRONTIER_BATTLE_PYRAMID_FLOOR) { headerId = gSaveBlock2Ptr->frontier.curChallengeBattleNum; - if (TryGenerateWildMon(gBattlePyramidWildMonHeaders[headerId].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + + if (TryGenerateWildMon(gBattlePyramidWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) return FALSE; GenerateBattlePyramidWildMon(); @@ -819,7 +912,9 @@ bool8 SweetScentWildEncounter(void) { if (MetatileBehavior_IsLandWildEncounter(MapGridGetMetatileBehaviorAt(x, y)) == TRUE) { - if (gWildMonHeaders[headerId].landMonsInfo == NULL) + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + + if (gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo == NULL) return FALSE; if (TryStartRoamerEncounter()) @@ -831,16 +926,18 @@ bool8 SweetScentWildEncounter(void) if (DoMassOutbreakEncounterTest() == TRUE) SetUpMassOutbreakEncounter(0); else - TryGenerateWildMon(gWildMonHeaders[headerId].landMonsInfo, WILD_AREA_LAND, 0); + TryGenerateWildMon(gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0); BattleSetup_StartWildBattle(); return TRUE; } else if (MetatileBehavior_IsWaterWildEncounter(MapGridGetMetatileBehaviorAt(x, y)) == TRUE) { + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); + if (AreLegendariesInSootopolisPreventingEncounters() == TRUE) return FALSE; - if (gWildMonHeaders[headerId].waterMonsInfo == NULL) + if (gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo == NULL) return FALSE; if (TryStartRoamerEncounter()) @@ -849,7 +946,7 @@ bool8 SweetScentWildEncounter(void) return TRUE; } - TryGenerateWildMon(gWildMonHeaders[headerId].waterMonsInfo, WILD_AREA_WATER, 0); + TryGenerateWildMon(gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo, WILD_AREA_WATER, 0); BattleSetup_StartWildBattle(); return TRUE; } @@ -860,9 +957,10 @@ bool8 SweetScentWildEncounter(void) bool8 DoesCurrentMapHaveFishingMons(void) { - u16 headerId = GetCurrentMapWildMonHeaderId(); + u32 headerId = GetCurrentMapWildMonHeaderId(); + enum TimeOfDay timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_FISHING); - if (headerId != HEADER_NONE && gWildMonHeaders[headerId].fishingMonsInfo != NULL) + if (headerId != HEADER_NONE && gWildMonHeaders[headerId].encounterTypes[timeOfDay].fishingMonsInfo != NULL) return TRUE; else return FALSE; @@ -887,6 +985,8 @@ static void UpdateChainFishingStreak() void FishingWildEncounter(u8 rod) { u16 species; + u32 headerId; + enum TimeOfDay timeOfDay; gIsFishingEncounter = TRUE; if (CheckFeebas() == TRUE) @@ -898,7 +998,9 @@ void FishingWildEncounter(u8 rod) } else { - species = GenerateFishingWildMon(gWildMonHeaders[GetCurrentMapWildMonHeaderId()].fishingMonsInfo, rod); + headerId = GetCurrentMapWildMonHeaderId(); + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_FISHING); + species = GenerateFishingWildMon(gWildMonHeaders[headerId].encounterTypes[timeOfDay].fishingMonsInfo, rod); } IncrementGameStat(GAME_STAT_FISHING_ENCOUNTERS); @@ -908,7 +1010,8 @@ void FishingWildEncounter(u8 rod) u16 GetLocalWildMon(bool8 *isWaterMon) { - u16 headerId; + u32 headerId; + enum TimeOfDay timeOfDay; const struct WildPokemonInfo *landMonsInfo; const struct WildPokemonInfo *waterMonsInfo; @@ -916,8 +1019,13 @@ u16 GetLocalWildMon(bool8 *isWaterMon) headerId = GetCurrentMapWildMonHeaderId(); if (headerId == HEADER_NONE) return SPECIES_NONE; - landMonsInfo = gWildMonHeaders[headerId].landMonsInfo; - waterMonsInfo = gWildMonHeaders[headerId].waterMonsInfo; + + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + landMonsInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; + + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); + waterMonsInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; + // Neither if (landMonsInfo == NULL && waterMonsInfo == NULL) return SPECIES_NONE; @@ -944,11 +1052,14 @@ u16 GetLocalWildMon(bool8 *isWaterMon) u16 GetLocalWaterMon(void) { - u16 headerId = GetCurrentMapWildMonHeaderId(); + u32 headerId = GetCurrentMapWildMonHeaderId(); + enum TimeOfDay timeOfDay; if (headerId != HEADER_NONE) { - const struct WildPokemonInfo *waterMonsInfo = gWildMonHeaders[headerId].waterMonsInfo; + timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_WATER); + + const struct WildPokemonInfo *waterMonsInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; if (waterMonsInfo) return waterMonsInfo->wildPokemon[ChooseWildMonIndex_WaterRock()].species; @@ -1053,7 +1164,7 @@ static bool8 TryGetRandomWildMonIndexByType(const struct WildPokemon *wildMon, u #include "data.h" -static u8 GetMaxLevelOfSpeciesInWildTable(const struct WildPokemon *wildMon, u16 species, u8 area) +static u8 GetMaxLevelOfSpeciesInWildTable(const struct WildPokemon *wildMon, u16 species, enum WildPokemonArea area) { u8 i, maxLevel = 0, numMon = 0; @@ -1068,6 +1179,9 @@ static u8 GetMaxLevelOfSpeciesInWildTable(const struct WildPokemon *wildMon, u16 case WILD_AREA_ROCKS: numMon = ROCK_WILD_COUNT; break; + default: + case WILD_AREA_FISHING: + case WILD_AREA_HIDDEN: } for (i = 0; i < numMon; i++) @@ -1127,8 +1241,10 @@ bool8 TryDoDoubleWildBattle(void) bool8 StandardWildEncounter_Debug(void) { - u16 headerId = GetCurrentMapWildMonHeaderId(); - if (TryGenerateWildMon(gWildMonHeaders[headerId].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) + u32 headerId = GetCurrentMapWildMonHeaderId(); + enum TimeOfDay timeOfDay = GetTimeOfDayForEncounters(headerId, WILD_AREA_LAND); + + if (TryGenerateWildMon(gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo, WILD_AREA_LAND, 0) != TRUE) return FALSE; DoStandardWildBattle_Debug(); diff --git a/src/window.c b/src/window.c index ec998083a0..850b83d4fc 100644 --- a/src/window.c +++ b/src/window.c @@ -240,6 +240,20 @@ void RemoveWindow(u32 windowId) } } +void RemoveAllWindowsOnBg(u32 bgId) +{ + u32 i; + + if (bgId > NUM_BACKGROUNDS) + return; + + for (i = 0; i < WINDOWS_MAX; i++) + { + if (gWindows[i].window.bg == bgId) + RemoveWindow(i); + } +} + void FreeAllWindowBuffers(void) { int i; diff --git a/tools/wild_encounters/wild_encounters_to_header.py b/tools/wild_encounters/wild_encounters_to_header.py new file mode 100644 index 0000000000..0ea958de9c --- /dev/null +++ b/tools/wild_encounters/wild_encounters_to_header.py @@ -0,0 +1,673 @@ +import json +import re +import os + + +IS_ENABLED = False + +# C string vars +define = "#define" +ENCOUNTER_CHANCE = "ENCOUNTER_CHANCE" +SLOT = "SLOT" +TOTAL = "TOTAL" +NULL = "NULL" +UNDEFINED = "UNDEFINED" +MAP_UNDEFINED = "MAP_UNDEFINED" + +# encounter group header types, filled out programmatically +MON_HEADERS = [] + +# mon encounter group types +LAND_MONS = "land_mons" +LAND_MONS_LABEL = "LandMons" +LAND_MONS_INDEX = 0 +WATER_MONS = "water_mons" +WATER_MONS_LABEL = "WaterMons" +WATER_MONS_INDEX = 1 +ROCK_SMASH_MONS = "rock_smash_mons" +ROCK_SMASH_MONS_LABEL = "RockSmashMons" +ROCK_SMASH_MONS_INDEX = 2 +FISHING_MONS = "fishing_mons" +FISHING_MONS_LABEL = "FishingMons" +FISHING_MONS_INDEX = 3 +HIDDEN_MONS = "hidden_mons" +HIDDEN_MONS_LABEL = "HiddenMons" +HIDDEN_MONS_INDEX = 4 +MONS_INFO_TOTAL = HIDDEN_MONS_INDEX + 1 + +# fishing encounter data +GOOD_ROD = "good_rod" +GOOD_ROD_FIRST_INDEX = 2 +GOOD_ROD_LAST_INDEX = 4 +OLD_ROD = "old_rod" +OLD_ROD_FIRST_INDEX = 0 +OLD_ROD_LAST_INDEX = 1 +SUPER_ROD = "super_rod" +SUPER_ROD_FIRST_INDEX = 5 +SUPER_ROD_LAST_INDEX = 9 + +# time of day encounter data +TIME_DEFAULT = "OW_TIME_OF_DAY_DEFAULT" +TIME_DEFAULT_LABEL = "" +TIME_DEFAULT_INDEX = 0 +TIME_MORNING = "time_morning" +TIME_MORNING_LABEL = "Morning" +TIME_MORNING_INDEX = 0 +TIME_DAY = "time_day" +TIME_DAY_LABEL = "Day" +TIME_DAY_INDEX = 1 +TIME_EVENING = "time_evening" +TIME_EVENING_LABEL = "Evening" +TIME_EVENING_INDEX = 2 +TIME_NIGHT = "time_night" +TIME_NIGHT_LABEL = "Night" +TIME_NIGHT_INDEX = 3 +TOTAL_TIME_STAGES = TIME_NIGHT_INDEX + 1 + +# struct building blocks +baseStruct = "const struct WildPokemon" +structLabel = "" +structMonType = "" +structTime = "" +structMap = "" + +structInfo = "Info" +structHeader = "Header" +structArrayAssign = "[] =" + +baseStructLabel = "" +baseStructContent = [] +infoStructString = "" +infoStructRate = 0 +infoStructContent = [] +headerStructLabel = "" +headerStructContent = {} +headerStructTable = {} +headerIndex = 0 + +# map header data variables +hLabel = "" +hForMaps = True +headersArray = [headerIndex] + +# headersArrayItems +landMonsInfo = "" +waterMonsInfo = "" +rockSmashMonsInfo = "" +fishingMonsInfo = "" + +# encounter rate variables +eLandMons = [] +eWaterMons = [] +eRockSmashMons = [] +eFishingMons = [] + + +# debug output control +printEncounterHeaders = True +printEncounterRateMacros = True +printEncounterStructsInfoString = True +printEncounterStructs = True + + +def ImportWildEncounterFile(): + # make sure we're in the right directory before anything else + if not os.path.exists("Makefile"): + print("Please run this script from the project's root folder.") + quit() + + global MON_HEADERS + global landMonsInfo + global waterMonsInfo + global rockSmashMonsInfo + global fishingMonsInfo + global hiddenMonsInfo + global structLabel + global structMonType + global structTime + global structMap + global baseStructLabel + global baseStructContent + global infoStructString + global infoStructRate + global headerStructLabel + global headerStructContent + global hLabel + global headersArray + global eLandMons + global eWaterMons + global eRockSmashMons + global eFishingMons + global encounterTotalCount + global encounterCount + global headerIndex + global tabStr + tabStr = " " + + global IS_ENABLED + IS_ENABLED = IsConfigEnabled() + + wFile = open("src/data/wild_encounters.json") + wData = json.load(wFile) + + encounterTotalCount = [] + encounterCount = [] + groupCount = 0 + while groupCount < len(wData["wild_encounter_groups"]): + encounterTotalCount.append(0) + encounterCount.append(0) + groupCount += 1 + + for data in wData["wild_encounter_groups"]: + wEncounters = wData["wild_encounter_groups"][headerIndex]["encounters"] + headerSuffix = structHeader + "s" + + if data["label"]: + hLabel = wData["wild_encounter_groups"][headerIndex]["label"] + if headerSuffix in hLabel: + hLabel = hLabel[:len(hLabel) - len(headerSuffix)] + MON_HEADERS.append(hLabel) + + if data["for_maps"]: + hForMaps = wData["wild_encounter_groups"][headerIndex]["for_maps"] + + # for the encounter rate macros, so we don't worry about hidden mons here + if headerIndex == 0: + wFields = wData["wild_encounter_groups"][headerIndex]["fields"] + for field in wFields: + if field["type"] == LAND_MONS: + eLandMons = field["encounter_rates"] + elif field["type"] == WATER_MONS: + eWaterMons = field["encounter_rates"] + elif field["type"] == ROCK_SMASH_MONS: + eRockSmashMons = field["encounter_rates"] + elif field["type"] == FISHING_MONS: + eFishingMons = field["encounter_rates"] + eFishingMons.append(field["groups"]) + + PrintGeneratedWarningText() + + print('#include "rtc.h"') + print("\n") + + PrintEncounterRateMacros() + print("\n") + + for encounter in wEncounters: + if "map" in encounter: + structMap = encounter["map"] + else: + structMap = encounter["base_label"] + structLabel = encounter["base_label"] + + if encounterTotalCount[headerIndex] != len(wEncounters): + encounterTotalCount[headerIndex] = len(wEncounters) + encounterCount[headerIndex] += 1 + headersArray = [] + + if not IS_ENABLED: + structTime = TIME_DEFAULT_LABEL + elif TIME_MORNING_LABEL in structLabel: + structTime = TIME_MORNING_LABEL + elif TIME_DAY_LABEL in structLabel: + structTime = TIME_DAY_LABEL + elif TIME_EVENING_LABEL in structLabel: + structTime = TIME_EVENING_LABEL + elif TIME_NIGHT_LABEL in structLabel: + structTime = TIME_NIGHT_LABEL + else: + structTime = TIME_MORNING_LABEL + structLabel = structLabel + "_Morning" + + landMonsInfo = "" + waterMonsInfo = "" + rockSmashMonsInfo = "" + fishingMonsInfo = "" + hiddenMonsInfo = "" + for areaTable in encounter: + if LAND_MONS in areaTable: + structMonType = LAND_MONS_LABEL + landMonsInfo = f"{structLabel}_{structMonType}{structInfo}" + elif WATER_MONS in areaTable: + structMonType = WATER_MONS_LABEL + waterMonsInfo = f"{structLabel}_{structMonType}{structInfo}" + elif ROCK_SMASH_MONS in areaTable: + structMonType = ROCK_SMASH_MONS_LABEL + rockSmashMonsInfo = f"{structLabel}_{structMonType}{structInfo}" + elif FISHING_MONS in areaTable: + structMonType = FISHING_MONS_LABEL + fishingMonsInfo = f"{structLabel}_{structMonType}{structInfo}" + elif HIDDEN_MONS in areaTable: + structMonType = HIDDEN_MONS_LABEL + hiddenMonsInfo = f"{structLabel}_{structMonType}{structInfo}" + else: + structMonType = "" + continue + + baseStructContent = [] + for group in encounter[areaTable]: + if "mons" in group: + for mon in encounter[areaTable][group]: + baseStructContent.append(list(mon.values())) + + if "encounter_rate" in group: + infoStructRate = encounter[areaTable][group] + + baseStructLabel = f"{baseStruct} {structLabel}_{structMonType}{structArrayAssign}" + if printEncounterStructs: + print() + print(baseStructLabel) + print("{") + PrintStructContent(baseStructContent) + print("};") + + if printEncounterStructsInfoString: + infoStructString = f"{baseStruct}{structInfo} {structLabel}_{structMonType}{structInfo} = {{ {infoStructRate}, {structLabel}_{structMonType} }};" + print(infoStructString) + + AssembleMonHeaderContent() + headerIndex += 1 + PrintWildMonHeadersContent() + + +def PrintStructContent(contentList): + for monList in contentList: + print(f"{tabStr}{{ {monList[0]}, {monList[1]}, {monList[2]} }},") + return + + +def GetStructLabelWithoutTime(label): + labelLength = len(label) + timeLength = 0 + + if not IS_ENABLED: + return label + elif TIME_MORNING_LABEL in label: + timeLength = len(TIME_MORNING_LABEL) + elif TIME_DAY_LABEL in label: + timeLength = len(TIME_DAY_LABEL) + elif TIME_EVENING_LABEL in label: + timeLength = len(TIME_EVENING_LABEL) + elif TIME_NIGHT_LABEL in label: + timeLength = len(TIME_NIGHT_LABEL) + return label[:(labelLength - (timeLength + 1))] + + +def AssembleMonHeaderContent(): + global structLabel + + SetupMonInfoVars() + + tempHeaderLabel = GetWildMonHeadersLabel() + tempHeaderTimeIndex = GetTimeIndexFromString(structTime) + structLabelNoTime = GetStructLabelWithoutTime(structLabel) + + if tempHeaderLabel not in headerStructTable: + headerStructTable[tempHeaderLabel] = {} + headerStructTable[tempHeaderLabel]["groupNum"] = headerIndex + + if structLabelNoTime not in headerStructTable[tempHeaderLabel]: + headerStructTable[tempHeaderLabel][structLabelNoTime] = {} + headerStructTable[tempHeaderLabel][structLabelNoTime]["headerType"] = GetWildMonHeadersLabel() + headerStructTable[tempHeaderLabel][structLabelNoTime]["mapGroup"] = structMap + headerStructTable[tempHeaderLabel][structLabelNoTime]["mapNum"] = structMap + headerStructTable[tempHeaderLabel][structLabelNoTime]["encounterTotalCount"] = encounterTotalCount[headerIndex] + headerStructTable[tempHeaderLabel][structLabelNoTime]["encounter_types"] = [] + + timeStart = TIME_DEFAULT_INDEX + timeEnd = TIME_NIGHT_INDEX if IS_ENABLED else TIME_DEFAULT_INDEX + while timeStart <= timeEnd: + headerStructTable[tempHeaderLabel][structLabelNoTime]["encounter_types"].append([]) + timeStart += 1 + + headerStructTable[tempHeaderLabel][structLabelNoTime]["encounter_types"][tempHeaderTimeIndex].append(landMonsInfo) + headerStructTable[tempHeaderLabel][structLabelNoTime]["encounter_types"][tempHeaderTimeIndex].append(waterMonsInfo) + headerStructTable[tempHeaderLabel][structLabelNoTime]["encounter_types"][tempHeaderTimeIndex].append(rockSmashMonsInfo) + headerStructTable[tempHeaderLabel][structLabelNoTime]["encounter_types"][tempHeaderTimeIndex].append(fishingMonsInfo) + headerStructTable[tempHeaderLabel][structLabelNoTime]["encounter_types"][tempHeaderTimeIndex].append(hiddenMonsInfo) + + +def SetupMonInfoVars(): + global landMonsInfo + global waterMonsInfo + global rockSmashMonsInfo + global fishingMonsInfo + global hiddenMonsInfo + + if landMonsInfo == "": + landMonsInfo = NULL + else: + landMonsInfo = f"&{landMonsInfo}" + + if waterMonsInfo == "": + waterMonsInfo = NULL + else: + waterMonsInfo = f"&{waterMonsInfo}" + + if rockSmashMonsInfo == "": + rockSmashMonsInfo = NULL + else: + rockSmashMonsInfo = f"&{rockSmashMonsInfo}" + + if fishingMonsInfo == "": + fishingMonsInfo = NULL + else: + fishingMonsInfo = f"&{fishingMonsInfo}" + + if hiddenMonsInfo == "": + hiddenMonsInfo = NULL + else: + hiddenMonsInfo = f"&{hiddenMonsInfo}" + + +def PrintWildMonHeadersContent(): + global tabStr + + groupCount = 0 + for group in headerStructTable: + labelCount = 0 + for label in headerStructTable[group]: + if label != "groupNum": + if labelCount == 0: + PrintEncounterHeaders("\n") + PrintEncounterHeaders(headerStructTable[group][label]["headerType"]) + + PrintEncounterHeaders(tabStr + "{") + + for stat in headerStructTable[group][label]: + mapData = headerStructTable[group][label][stat] + + if stat == "mapGroup": + PrintEncounterHeaders(f"{TabStr(2)}.mapGroup = {GetMapGroupEnum(mapData)},") + elif stat == "mapNum": + PrintEncounterHeaders(f"{TabStr(2)}.mapNum = {GetMapGroupEnum(mapData, labelCount + 1)},") + + if type(headerStructTable[group][label][stat]) == list: + PrintEncounterHeaders(f"{TabStr(2)}.encounterTypes =") + PrintEncounterHeaders(TabStr(2) + "{") + + infoCount = 0 + for monInfo in headerStructTable[group][label][stat]: + PrintEncounterHeaders(f"{TabStr(3)}[{GetTimeStrFromIndex(infoCount)}] = ") + + infoIndex = 0 + while infoIndex <= MONS_INFO_TOTAL - 1: + if infoIndex == 0: + PrintEncounterHeaders(TabStr(3) + "{") + + if len(monInfo) == 0: + PrintEncounterHeaders(f"{TabStr(4)}{GetIMonInfoStringFromIndex(infoIndex)} = NULL,") + else: + PrintEncounterHeaders(f"{TabStr(4)}{GetIMonInfoStringFromIndex(infoIndex)} = {monInfo[infoIndex]},") + + if infoIndex == MONS_INFO_TOTAL - 1: + PrintEncounterHeaders(TabStr(3) + "},") + + infoIndex += 1 + infoCount += 1 + PrintEncounterHeaders(TabStr(2) + "},") + PrintEncounterHeaders(tabStr + "},") + + if labelCount + 1 == headerStructTable[group][label]["encounterTotalCount"]: + PrintEncounterHeaders(tabStr + "{") + PrintEncounterHeaders(f"{TabStr(2)}.mapGroup = {GetMapGroupEnum(MAP_UNDEFINED)},") + PrintEncounterHeaders(f"{TabStr(2)}.mapNum = {GetMapGroupEnum(MAP_UNDEFINED, labelCount + 1)},") + + timeEnd = TIME_NIGHT_INDEX if IS_ENABLED else TIME_DEFAULT_INDEX + nullCount = 0 + while nullCount <= timeEnd: + if nullCount == 0: + PrintEncounterHeaders(f"{TabStr(2)}.encounterTypes =") + PrintEncounterHeaders(TabStr(2)+ "{") + + PrintEncounterHeaders(f"{TabStr(3)}[{GetTimeStrFromIndex(nullCount)}] = ") + + nullIndex = 0 + while nullIndex <= MONS_INFO_TOTAL - 1: + if nullIndex == 0: + PrintEncounterHeaders(TabStr(3) + "{") + + PrintEncounterHeaders(f"{TabStr(4)}{GetIMonInfoStringFromIndex(nullIndex)} = NULL,") + + if nullIndex == MONS_INFO_TOTAL - 1: + PrintEncounterHeaders(TabStr(3) + "},") + + nullIndex += 1 + nullCount += 1 + PrintEncounterHeaders(TabStr(2) + "},") + PrintEncounterHeaders(tabStr + "},") + labelCount += 1 + groupCount += 1 + PrintEncounterHeaders("};") + + +def GetWildMonHeadersLabel(): + return f"{baseStruct}{structHeader} {MON_HEADERS[headerIndex]}{structHeader}s{structArrayAssign}" + "\n{" + + +def PrintEncounterHeaders(content): + if printEncounterHeaders: + print(content) + + +def PrintEncounterRateMacros(): + if not printEncounterRateMacros: + return + + rateCount = 0 + for percent in eLandMons: + if rateCount == 0: + print(f"{define} {ENCOUNTER_CHANCE}_{LAND_MONS.upper()}_{SLOT}_{rateCount} {percent}") + else: + print( + f"{define} {ENCOUNTER_CHANCE}_{LAND_MONS.upper()}_{SLOT}_{rateCount} {ENCOUNTER_CHANCE}_{LAND_MONS.upper()}_{SLOT}_{rateCount - 1} + {percent}" + ) + + if rateCount + 1 == len(eLandMons): + print( + f"{define} {ENCOUNTER_CHANCE}_{LAND_MONS.upper()}_{TOTAL} ({ENCOUNTER_CHANCE}_{LAND_MONS.upper()}_{SLOT}_{rateCount})" + ) + rateCount += 1 + + rateCount = 0 + for percent in eWaterMons: + if rateCount == 0: + print(f"{define} {ENCOUNTER_CHANCE}_{WATER_MONS.upper()}_{SLOT}_{rateCount} {percent}") + else: + print( + f"{define} {ENCOUNTER_CHANCE}_{WATER_MONS.upper()}_{SLOT}_{rateCount} {ENCOUNTER_CHANCE}_{WATER_MONS.upper()}_{SLOT}_{rateCount - 1} + {percent}" + ) + + if rateCount + 1 == len(eWaterMons): + print( + f"{define} {ENCOUNTER_CHANCE}_{WATER_MONS.upper()}_{TOTAL} ({ENCOUNTER_CHANCE}_{WATER_MONS.upper()}_{SLOT}_{rateCount})" + ) + rateCount += 1 + + rateCount = 0 + for percent in eRockSmashMons: + if rateCount == 0: + print(f"{define} {ENCOUNTER_CHANCE}_{ROCK_SMASH_MONS.upper()}_{SLOT}_{rateCount} {percent}") + else: + print( + f"{define} {ENCOUNTER_CHANCE}_{ROCK_SMASH_MONS.upper()}_{SLOT}_{rateCount} {ENCOUNTER_CHANCE}_{ROCK_SMASH_MONS.upper()}_{SLOT}_{rateCount - 1} + {percent}" + ) + + if rateCount + 1 == len(eRockSmashMons): + print( + f"{define} {ENCOUNTER_CHANCE}_{ROCK_SMASH_MONS.upper()}_{TOTAL} ({ENCOUNTER_CHANCE}_{ROCK_SMASH_MONS.upper()}_{SLOT}_{rateCount})" + ) + rateCount += 1 + + for rodRate in eFishingMons[-1]: + for rodPercentIndex in eFishingMons[-1][rodRate]: + if rodPercentIndex == OLD_ROD_FIRST_INDEX or rodPercentIndex == GOOD_ROD_FIRST_INDEX or rodPercentIndex == SUPER_ROD_FIRST_INDEX: + print( + f"{define} {ENCOUNTER_CHANCE}_{FISHING_MONS.upper()}_{rodRate.upper()}_{SLOT}_{rodPercentIndex} {eFishingMons[rodPercentIndex]}" + ) + else: + print( + f"{define} {ENCOUNTER_CHANCE}_{FISHING_MONS.upper()}_{rodRate.upper()}_{SLOT}_{rodPercentIndex} {ENCOUNTER_CHANCE}_{FISHING_MONS.upper()}_{rodRate.upper()}_{SLOT}_{rodPercentIndex - 1} + {eFishingMons[rodPercentIndex]}" + ) + + if rodPercentIndex == OLD_ROD_LAST_INDEX or rodPercentIndex == GOOD_ROD_LAST_INDEX or rodPercentIndex == SUPER_ROD_LAST_INDEX: + print( + f"{define} {ENCOUNTER_CHANCE}_{FISHING_MONS.upper()}_{rodRate.upper()}_{TOTAL} ({ENCOUNTER_CHANCE}_{FISHING_MONS.upper()}_{rodRate.upper()}_{SLOT}_{rodPercentIndex})" + ) + + +def GetTimeStrFromIndex(index): + if not IS_ENABLED: + return TIME_DEFAULT + elif index == TIME_MORNING_INDEX: + return TIME_MORNING.upper() + elif index == TIME_DAY_INDEX: + return TIME_DAY.upper() + elif index == TIME_EVENING_INDEX: + return TIME_EVENING.upper() + elif index == TIME_NIGHT_INDEX: + return TIME_NIGHT.upper() + return index + + +def GetTimeIndexFromString(string): + if not IS_ENABLED: + return TIME_DEFAULT_INDEX + elif string.lower() == TIME_MORNING or string == TIME_MORNING_LABEL: + return TIME_MORNING_INDEX + elif string.lower() == TIME_DAY or string == TIME_DAY_LABEL: + return TIME_DAY_INDEX + elif string.lower() == TIME_EVENING or string == TIME_EVENING_LABEL: + return TIME_EVENING_INDEX + elif string.lower() == TIME_NIGHT or string == TIME_NIGHT_LABEL: + return TIME_NIGHT_INDEX + return string + + +def GetIMonInfoStringFromIndex(index): + if index == LAND_MONS_INDEX: + return ".landMonsInfo" + elif index == WATER_MONS_INDEX: + return ".waterMonsInfo" + elif index == ROCK_SMASH_MONS_INDEX: + return ".rockSmashMonsInfo" + elif index == FISHING_MONS_INDEX: + return ".fishingMonsInfo" + elif index == HIDDEN_MONS_INDEX: + return ".hiddenMonsInfo" + return index + + +def GetMapGroupEnum(string, index = 0): + if "MAP_" in string and index == 0: + return "MAP_GROUP(" + string[4:len(string)] + ")" + elif "MAP_" in string and index != 0: + return "MAP_NUM(" + string[4:len(string)] + ")" + return index + + +""" +get copied lhea :^ ) +- next two functions copied almost verbatim from @lhearachel's python scripts in tools/learnset_helpers +""" +def PrintGeneratedWarningText(): + print("//") + print("// DO NOT MODIFY THIS FILE! It is auto-generated by tools/wild_encounters/wild_encounters_to_header.py") + print("//") + print("\n") + + +def IsConfigEnabled(): + CONFIG_ENABLED_PAT = re.compile(r"#define OW_TIME_OF_DAY_ENCOUNTERS\s+(?P[^ ]*)") + + with open("./include/config/overworld.h", "r") as overworld_config_file: + config_overworld = overworld_config_file.read() + config_setting = CONFIG_ENABLED_PAT.search(config_overworld) + return config_setting is not None and config_setting.group("cfg_val") in ("TRUE", "1") + + +def TabStr(amount): + global tabStr + return tabStr * amount + + +ImportWildEncounterFile() + + +""" +!!!! EXAMPLE OUTPUT !!!! +- when OW_TIME_OF DAY_ENCOUNTERS is FALSE in configoverworld.h + +#define ENCOUNTER_CHANCE_LAND_MONS_SLOT_0 20 +#define ENCOUNTER_CHANCE_LAND_MONS_SLOT_1 ENCOUNTER_CHANCE_LAND_MONS_SLOT_0 + 20 +#define ENCOUNTER_CHANCE_LAND_MONS_SLOT_2 ENCOUNTER_CHANCE_LAND_MONS_SLOT_1 + 10 +#define ENCOUNTER_CHANCE_LAND_MONS_SLOT_3 ENCOUNTER_CHANCE_LAND_MONS_SLOT_2 + 10 +#define ENCOUNTER_CHANCE_LAND_MONS_SLOT_4 ENCOUNTER_CHANCE_LAND_MONS_SLOT_3 + 10 +#define ENCOUNTER_CHANCE_LAND_MONS_SLOT_5 ENCOUNTER_CHANCE_LAND_MONS_SLOT_4 + 10 +#define ENCOUNTER_CHANCE_LAND_MONS_SLOT_6 ENCOUNTER_CHANCE_LAND_MONS_SLOT_5 + 5 +#define ENCOUNTER_CHANCE_LAND_MONS_SLOT_7 ENCOUNTER_CHANCE_LAND_MONS_SLOT_6 + 5 +#define ENCOUNTER_CHANCE_LAND_MONS_SLOT_8 ENCOUNTER_CHANCE_LAND_MONS_SLOT_7 + 4 +#define ENCOUNTER_CHANCE_LAND_MONS_SLOT_9 ENCOUNTER_CHANCE_LAND_MONS_SLOT_8 + 4 +#define ENCOUNTER_CHANCE_LAND_MONS_SLOT_10 ENCOUNTER_CHANCE_LAND_MONS_SLOT_9 + 1 +#define ENCOUNTER_CHANCE_LAND_MONS_SLOT_11 ENCOUNTER_CHANCE_LAND_MONS_SLOT_10 + 1 +#define ENCOUNTER_CHANCE_LAND_MONS_TOTAL (ENCOUNTER_CHANCE_LAND_MONS_SLOT_11) +#define ENCOUNTER_CHANCE_WATER_MONS_SLOT_0 60 +#define ENCOUNTER_CHANCE_WATER_MONS_SLOT_1 ENCOUNTER_CHANCE_WATER_MONS_SLOT_0 + 30 +#define ENCOUNTER_CHANCE_WATER_MONS_SLOT_2 ENCOUNTER_CHANCE_WATER_MONS_SLOT_1 + 5 +#define ENCOUNTER_CHANCE_WATER_MONS_SLOT_3 ENCOUNTER_CHANCE_WATER_MONS_SLOT_2 + 4 +#define ENCOUNTER_CHANCE_WATER_MONS_SLOT_4 ENCOUNTER_CHANCE_WATER_MONS_SLOT_3 + 1 +#define ENCOUNTER_CHANCE_WATER_MONS_TOTAL (ENCOUNTER_CHANCE_WATER_MONS_SLOT_4) +#define ENCOUNTER_CHANCE_ROCK_SMASH_MONS_SLOT_0 60 +#define ENCOUNTER_CHANCE_ROCK_SMASH_MONS_SLOT_1 ENCOUNTER_CHANCE_ROCK_SMASH_MONS_SLOT_0 + 30 +#define ENCOUNTER_CHANCE_ROCK_SMASH_MONS_SLOT_2 ENCOUNTER_CHANCE_ROCK_SMASH_MONS_SLOT_1 + 5 +#define ENCOUNTER_CHANCE_ROCK_SMASH_MONS_SLOT_3 ENCOUNTER_CHANCE_ROCK_SMASH_MONS_SLOT_2 + 4 +#define ENCOUNTER_CHANCE_ROCK_SMASH_MONS_SLOT_4 ENCOUNTER_CHANCE_ROCK_SMASH_MONS_SLOT_3 + 1 +#define ENCOUNTER_CHANCE_ROCK_SMASH_MONS_TOTAL (ENCOUNTER_CHANCE_ROCK_SMASH_MONS_SLOT_4) +#define ENCOUNTER_CHANCE_FISHING_MONS_GOOD_ROD_SLOT_2 60 +#define ENCOUNTER_CHANCE_FISHING_MONS_GOOD_ROD_SLOT_3 ENCOUNTER_CHANCE_FISHING_MONS_GOOD_ROD_SLOT_2 + 20 +#define ENCOUNTER_CHANCE_FISHING_MONS_GOOD_ROD_SLOT_4 ENCOUNTER_CHANCE_FISHING_MONS_GOOD_ROD_SLOT_3 + 20 +#define ENCOUNTER_CHANCE_FISHING_MONS_GOOD_ROD_TOTAL (ENCOUNTER_CHANCE_FISHING_MONS_GOOD_ROD_SLOT_4) +#define ENCOUNTER_CHANCE_FISHING_MONS_OLD_ROD_SLOT_0 70 +#define ENCOUNTER_CHANCE_FISHING_MONS_OLD_ROD_SLOT_1 ENCOUNTER_CHANCE_FISHING_MONS_OLD_ROD_SLOT_0 + 30 +#define ENCOUNTER_CHANCE_FISHING_MONS_OLD_ROD_TOTAL (ENCOUNTER_CHANCE_FISHING_MONS_OLD_ROD_SLOT_1) +#define ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_5 40 +#define ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_6 ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_5 + 40 +#define ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_7 ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_6 + 15 +#define ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_8 ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_7 + 4 +#define ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_9 ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_8 + 1 +#define ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_TOTAL (ENCOUNTER_CHANCE_FISHING_MONS_SUPER_ROD_SLOT_9) + +const struct WildPokemon gRoute101_LandMons_Day[] = +{ + { 2, 2, SPECIES_WURMPLE }, + { 2, 2, SPECIES_POOCHYENA }, + { 2, 2, SPECIES_WURMPLE }, + { 3, 3, SPECIES_WURMPLE }, + { 3, 3, SPECIES_POOCHYENA }, + { 3, 3, SPECIES_POOCHYENA }, + { 3, 3, SPECIES_WURMPLE }, + { 3, 3, SPECIES_POOCHYENA }, + { 2, 2, SPECIES_ZIGZAGOON }, + { 2, 2, SPECIES_ZIGZAGOON }, + { 3, 3, SPECIES_ZIGZAGOON }, + { 3, 3, SPECIES_ZIGZAGOON }, +}; + +const struct WildPokemonInfo gRoute101_Day_LandMonsInfo= { 20, gRoute101_Day_LandMons }; +const struct WildPokemonHeader gWildMonHeaders[] = +{ + { + .mapGroup = MAP(ROUTE101), + .mapNum = MAP_NUM(ROUTE101), + .encounterTypes = + [OW_TIME_OF_DAY_DEFAULT] = + { + .landMonsInfo = &gRoute101_LandMonsInfo, + .waterMonsInfo = NULL, + .rockSmashMonsInfo = NULL, + .fishingMonsInfo = NULL, + .hiddenMonsInfo = NULL, + } + }, +} +"""