diff --git a/include/config/overworld.h b/include/config/overworld.h index cebbe5b34b..8be52abfb5 100644 --- a/include/config/overworld.h +++ b/include/config/overworld.h @@ -74,6 +74,13 @@ #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. +// Time-based wild Pokemon encounters - Allows you to create additional encounter groups in wild_encounters.json (or in porymap). +// The groups are processed by base label's suffix. For example, "gRoute101" (no suffix) contains daytime encounters, and "gRoute101_Night" contains nighttime encounters. +// Not every group needs to be defined for every map - you could for example omit them inside caves and it would fall back to the unsuffixed daytime encounter group. +#define OW_TIME_BASED_WILD_ENCOUNTERS FALSE // Enables the system. If disabled, all suffixes are ignored and the first encounter group found for a map will be used for wild encounters. +#define OW_ENABLE_MORNING_WILD_ENCOUNTERS FALSE // If true, allows definition of morning encounter groups such as "gRoute101_Morning". Otherwise, morning is considered to be day for encounters. No effect if OW_TIME_BASED_WILD_ENCOUNTERS is false. +#define OW_ENABLE_EVENING_WILD_ENCOUNTERS FALSE // If true, allows definition of evening encounter groups such as "gRoute101_Evening". Otherwise, evening is considered to be night for encounters. No effect if OW_TIME_BASED_WILD_ENCOUNTERS is false. + // Overworld flags // To use the following features in scripting, replace the 0s with the flag ID you're assigning it to. // Eg: Replace with FLAG_UNUSED_0x264 so you can use that flag to toggle the feature. diff --git a/include/wild_encounter.h b/include/wild_encounter.h index 63289f081c..fb0f5ab00e 100644 --- a/include/wild_encounter.h +++ b/include/wild_encounter.h @@ -20,6 +20,7 @@ struct WildPokemonHeader { u8 mapGroup; u8 mapNum; + u8 timeOfDay; const struct WildPokemonInfo *landMonsInfo; const struct WildPokemonInfo *waterMonsInfo; const struct WildPokemonInfo *rockSmashMonsInfo; diff --git a/src/data/wild_encounters.json.txt b/src/data/wild_encounters.json.txt index bf848249f0..55dd20cd52 100755 --- a/src/data/wild_encounters.json.txt +++ b/src/data/wild_encounters.json.txt @@ -72,6 +72,7 @@ const struct WildPokemonHeader {{ wild_encounter_group.label }}[] = { .mapGroup = {% if wild_encounter_group.for_maps %}MAP_GROUP({{ removePrefix(encounter.map, "MAP_") }}){% else %}0{% endif %}, .mapNum = {% if wild_encounter_group.for_maps %}MAP_NUM({{ removePrefix(encounter.map, "MAP_") }}){% else %}{{ loop.index1 }}{% endif %}, + .timeOfDay = {% if stringEndsWith(encounter.base_label, "_Night") or stringContains(encounter.base_label, "_Night_") %}TIME_NIGHT{% else if stringEndsWith(encounter.base_label, "_Morning") or stringContains(encounter.base_label, "_Morning_") %}TIME_MORNING{% else if stringEndsWith(encounter.base_label, "_Evening") or stringContains(encounter.base_label, "_Evening_") %}TIME_EVENING{% else %}TIME_DAY{% endif %}, .landMonsInfo = {% if existsIn(encounter, "land_mons") %}&{{ encounter.base_label }}_LandMonsInfo{% else %}NULL{% endif %}, .waterMonsInfo = {% if existsIn(encounter, "water_mons") %}&{{ encounter.base_label }}_WaterMonsInfo{% else %}NULL{% endif %}, .rockSmashMonsInfo = {% if existsIn(encounter, "rock_smash_mons") %}&{{ encounter.base_label }}_RockSmashMonsInfo{% else %}NULL{% endif %}, @@ -81,6 +82,7 @@ const struct WildPokemonHeader {{ wild_encounter_group.label }}[] = { .mapGroup = MAP_GROUP(UNDEFINED), .mapNum = MAP_NUM(UNDEFINED), + .timeOfDay = TIME_DAY, .landMonsInfo = NULL, .waterMonsInfo = NULL, .rockSmashMonsInfo = NULL, diff --git a/src/wild_encounter.c b/src/wild_encounter.c index 033eec56da..3cfdfb1191 100644 --- a/src/wild_encounter.c +++ b/src/wild_encounter.c @@ -11,6 +11,7 @@ #include "pokeblock.h" #include "battle_setup.h" #include "roamer.h" +#include "rtc.h" #include "tv.h" #include "link.h" #include "script.h" @@ -354,9 +355,32 @@ static u8 ChooseWildMonLevel(const struct WildPokemon *wildPokemon, u8 wildMonIn } } +static bool8 IsExactTimeOfDayMatchForWildEncounters(u8 currentTimeOfDay, u8 encounterTimeOfDay) +{ + switch (currentTimeOfDay) + { + case TIME_MORNING: + if (OW_ENABLE_MORNING_WILD_ENCOUNTERS) + return encounterTimeOfDay == TIME_MORNING; + // fallthrough + case TIME_DAY: + return encounterTimeOfDay == TIME_DAY; + case TIME_EVENING: + if (OW_ENABLE_EVENING_WILD_ENCOUNTERS) + return encounterTimeOfDay == TIME_EVENING; + // fallthrough + case TIME_NIGHT: + return encounterTimeOfDay == TIME_NIGHT; + } + return FALSE; +} + static u16 GetCurrentMapWildMonHeaderId(void) { u16 i; + u8 currentTimeOfDay = GetTimeOfDay(); + u16 dayId = HEADER_NONE; + u16 nightId = HEADER_NONE; for (i = 0; ; i++) { @@ -375,13 +399,30 @@ static u16 GetCurrentMapWildMonHeaderId(void) alteringCaveId = 0; i += alteringCaveId; + return i; // Altering cave is not affected by time-of-day encounters. } - return i; + if (OW_TIME_BASED_WILD_ENCOUNTERS) + { + if (IsExactTimeOfDayMatchForWildEncounters(currentTimeOfDay, gWildMonHeaders[i].timeOfDay)) + return i; + else if (gWildMonHeaders[i].timeOfDay == TIME_DAY && dayId == HEADER_NONE) + dayId = i; + else if (gWildMonHeaders[i].timeOfDay == TIME_NIGHT && nightId == HEADER_NONE) + nightId = i; + } + else + return i; } } - return HEADER_NONE; + if (!OW_TIME_BASED_WILD_ENCOUNTERS) + return HEADER_NONE; + + // Exact match for time of day was not found. We fall back to other encounter groups for this map + if (currentTimeOfDay == TIME_EVENING && OW_ENABLE_EVENING_WILD_ENCOUNTERS && nightId != HEADER_NONE) + return nightId; + return dayId; } u8 PickWildMonNature(void) diff --git a/tools/jsonproc/jsonproc.cpp b/tools/jsonproc/jsonproc.cpp index 9ef23cd43e..5e661e40d3 100755 --- a/tools/jsonproc/jsonproc.cpp +++ b/tools/jsonproc/jsonproc.cpp @@ -118,6 +118,27 @@ int main(int argc, char *argv[]) return str; }); + env.add_callback("stringContains", 2, [](Arguments& args) { + string str = args.at(0)->get(); + string check = args.at(1)->get(); + + return str.find(check) != std::string::npos; + }); + + env.add_callback("stringStartsWith", 2, [](Arguments& args) { + string str = args.at(0)->get(); + string check = args.at(1)->get(); + + return str.find(check) == 0; + }); + + env.add_callback("stringEndsWith", 2, [](Arguments& args) { + string str = args.at(0)->get(); + string check = args.at(1)->get(); + + return str.rfind(check) == (str.length() - check.length()); + }); + try { env.write_with_json_file(templateFilepath, jsonfilepath, outputFilepath);