update: time-based encounters system tuneup and @cawtds' header script (#8158)

This commit is contained in:
khbsd 2025-11-08 05:25:09 +00:00 committed by GitHub
parent 232ddb04f4
commit 82a63a09fe
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 349 additions and 753 deletions

View File

@ -1,12 +1,32 @@
GAME_VERSION ?= EMERALD
TITLE ?= POKEMON EMER
GAME_CODE ?= BPEE
BUILD_NAME ?= emerald
MAP_VERSION ?= emerald
ifeq (firered,$(MAKECMDGOALS))
GAME_VERSION := FIRERED
TITLE := POKEMON FIRE
GAME_CODE := BPRE
BUILD_NAME := firered
MAP_VERSION := firered
else
ifeq (leafgreen,$(MAKECMDGOALS))
GAME_VERSION := LEAFGREEN
TITLE := POKEMON LEAF
GAME_CODE := BPGE
BUILD_NAME := leafgreen
MAP_VERSION := firered
endif
endif
# GBA rom header # GBA rom header
TITLE := POKEMON EMER
GAME_CODE := BPEE
MAKER_CODE := 01 MAKER_CODE := 01
REVISION := 0 REVISION := 0
KEEP_TEMPS ?= 0 KEEP_TEMPS ?= 0
# `File name`.gba # `File name`.gba
FILE_NAME := pokeemerald FILE_NAME := poke$(BUILD_NAME)
BUILD_DIR := build BUILD_DIR := build
# Compares the ROM to a checksum of the original - only makes sense using when non-modern # Compares the ROM to a checksum of the original - only makes sense using when non-modern
@ -75,10 +95,10 @@ ifeq ($(RELEASE),1)
endif endif
ROM_NAME := $(FILE_NAME).gba ROM_NAME := $(FILE_NAME).gba
OBJ_DIR_NAME := $(BUILD_DIR)/modern OBJ_DIR_NAME := $(BUILD_DIR)/$(BUILD_NAME)
OBJ_DIR_NAME_TEST := $(BUILD_DIR)/modern-test OBJ_DIR_NAME_TEST := $(BUILD_DIR)/$(BUILD_NAME)-test
OBJ_DIR_NAME_DEBUG := $(BUILD_DIR)/modern-debug OBJ_DIR_NAME_DEBUG := $(BUILD_DIR)/$(BUILD_NAME)-debug
OBJ_DIR_NAME_RELEASE := $(BUILD_DIR)/modern-release OBJ_DIR_NAME_RELEASE := $(BUILD_DIR)/$(BUILD_NAME)-release
ELF_NAME := $(ROM_NAME:.gba=.elf) ELF_NAME := $(ROM_NAME:.gba=.elf)
MAP_NAME := $(ROM_NAME:.gba=.map) MAP_NAME := $(ROM_NAME:.gba=.map)
@ -124,7 +144,7 @@ TEST_BUILDDIR = $(OBJ_DIR)/$(TEST_SUBDIR)
SHELL := bash -o pipefail SHELL := bash -o pipefail
# Set flags for tools # Set flags for tools
ASFLAGS := -mcpu=arm7tdmi -march=armv4t -meabi=5 --defsym MODERN=1 ASFLAGS := -mcpu=arm7tdmi -march=armv4t -meabi=5 --defsym MODERN=1 --defsym $(GAME_VERSION)=1
INCLUDE_DIRS := include INCLUDE_DIRS := include
INCLUDE_CPP_ARGS := $(INCLUDE_DIRS:%=-iquote %) INCLUDE_CPP_ARGS := $(INCLUDE_DIRS:%=-iquote %)
@ -135,7 +155,7 @@ O_LEVEL ?= g
else else
O_LEVEL ?= 2 O_LEVEL ?= 2
endif endif
CPPFLAGS := $(INCLUDE_CPP_ARGS) -Wno-trigraphs -DMODERN=1 -DTESTING=$(TEST) -std=gnu17 CPPFLAGS := $(INCLUDE_CPP_ARGS) -Wno-trigraphs -DMODERN=1 -DTESTING=$(TEST) -D$(GAME_VERSION) -std=gnu17
ifeq ($(RELEASE),1) ifeq ($(RELEASE),1)
override CPPFLAGS += -DRELEASE override CPPFLAGS += -DRELEASE
ifeq ($(USE_LTO_ON_RELEASE),1) ifeq ($(USE_LTO_ON_RELEASE),1)
@ -221,7 +241,7 @@ MISC_TOOL_DIR := $(TOOLS_DIR)/misc
AUTO_GEN_TARGETS += $(INCLUDE_DIRS)/constants/script_commands.h AUTO_GEN_TARGETS += $(INCLUDE_DIRS)/constants/script_commands.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 $(INCLUDE_DIRS)/config/dexnav.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 $(INCLUDE_DIRS)/config/dexnav.h
python3 $(WILD_ENCOUNTERS_TOOL_DIR)/wild_encounters_to_header.py > $@ python3 $(WILD_ENCOUNTERS_TOOL_DIR)/wild_encounters_to_header.py
$(INCLUDE_DIRS)/constants/script_commands.h: $(MISC_TOOL_DIR)/make_scr_cmd_constants.py $(DATA_ASM_SUBDIR)/script_cmd_table.inc $(INCLUDE_DIRS)/constants/script_commands.h: $(MISC_TOOL_DIR)/make_scr_cmd_constants.py $(DATA_ASM_SUBDIR)/script_cmd_table.inc
python3 $(MISC_TOOL_DIR)/make_scr_cmd_constants.py python3 $(MISC_TOOL_DIR)/make_scr_cmd_constants.py
@ -270,7 +290,7 @@ ifeq ($(SETUP_PREREQS),1)
$(error Errors occurred while building tools. See error messages above for more details) $(error Errors occurred while building tools. See error messages above for more details)
endif endif
# Oh and also generate mapjson sources before we use `SCANINC`. # Oh and also generate mapjson sources before we use `SCANINC`.
$(foreach line, $(shell $(MAKE) generated | sed "s/ /__SPACE__/g"), $(info $(subst __SPACE__, ,$(line)))) $(foreach line, $(shell $(MAKE) MAP_VERSION=$(MAP_VERSION) generated | sed "s/ /__SPACE__/g"), $(info $(subst __SPACE__, ,$(line))))
ifneq ($(.SHELLSTATUS),0) ifneq ($(.SHELLSTATUS),0)
$(error Errors occurred while generating map-related sources. See error messages above for more details) $(error Errors occurred while generating map-related sources. See error messages above for more details)
endif endif
@ -480,7 +500,7 @@ ifneq ($(NODEP),1)
endif endif
$(C_BUILDDIR)/%.o: $(C_SUBDIR)/%.s $(C_BUILDDIR)/%.o: $(C_SUBDIR)/%.s
$(PREPROC) $< charmap.txt | $(CPP) $(INCLUDE_SCANINC_ARGS) - | $(PREPROC) -ie $< charmap.txt | $(AS) $(ASFLAGS) -o $@ $(PREPROC) $< charmap.txt | $(CPP) $(CPPFLAGS) $(INCLUDE_SCANINC_ARGS) - | $(PREPROC) -ie $< charmap.txt | $(AS) $(ASFLAGS) -o $@
$(C_BUILDDIR)/%.d: $(C_SUBDIR)/%.s $(C_BUILDDIR)/%.d: $(C_SUBDIR)/%.s
$(SCANINC) -M $@ $(INCLUDE_SCANINC_ARGS) -I "" $< $(SCANINC) -M $@ $(INCLUDE_SCANINC_ARGS) -I "" $<
@ -490,7 +510,7 @@ ifneq ($(NODEP),1)
endif endif
$(DATA_ASM_BUILDDIR)/%.o: $(DATA_ASM_SUBDIR)/%.s $(DATA_ASM_BUILDDIR)/%.o: $(DATA_ASM_SUBDIR)/%.s
$(PREPROC) $< charmap.txt | $(CPP) $(INCLUDE_SCANINC_ARGS) - | $(PREPROC) -ie $< charmap.txt | $(AS) $(ASFLAGS) -o $@ $(PREPROC) $< charmap.txt | $(CPP) $(CPPFLAGS) $(INCLUDE_SCANINC_ARGS) - | $(PREPROC) -ie $< charmap.txt | $(AS) $(ASFLAGS) -o $@
$(DATA_ASM_BUILDDIR)/%.d: $(DATA_ASM_SUBDIR)/%.s $(DATA_ASM_BUILDDIR)/%.d: $(DATA_ASM_SUBDIR)/%.s
$(SCANINC) -M $@ $(INCLUDE_SCANINC_ARGS) -I "" $< $(SCANINC) -M $@ $(INCLUDE_SCANINC_ARGS) -I "" $<
@ -549,6 +569,9 @@ $(ROM): $(ELF)
$(OBJCOPY) -O binary $< $@ $(OBJCOPY) -O binary $< $@
$(FIX) $@ -p --silent $(FIX) $@ -p --silent
emerald: all
firered: all
leafgreen: all
# Symbol file (`make syms`) # Symbol file (`make syms`)
$(SYM): $(ELF) $(SYM): $(ELF)
$(OBJDUMP) -t $< | sort -u | grep -E "^0[2389]" | $(PERL) -p -e 's/^(\w{8}) (\w).{6} \S+\t(\w{8}) (\S+)$$/\1 \2 \3 \4/g' > $@ $(OBJDUMP) -t $< | sort -u | grep -E "^0[2389]" | $(PERL) -p -e 's/^(\w{8}) (\w).{6} \S+\t(\w{8}) (\S+)$$/\1 \2 \3 \4/g' > $@

View File

@ -86,13 +86,13 @@
#define GEN_8_PLA GEN_LATEST + 2 #define GEN_8_PLA GEN_LATEST + 2
#define TIME_DEBUG GEN_LATEST + 3 #define TIME_DEBUG GEN_LATEST + 3
//Time // Time
#define OW_TIMES_OF_DAY GEN_LATEST // Different generations have the times of day change at different times. #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_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. TIME_DEBUG is 1:1, and meant for debugging purposes. This has no effect if OW_USE_FAKE_RTC is FALSE. #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. TIME_DEBUG is 1:1, and meant for debugging purposes. 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_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_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_FALLBACK TIME_MORNING // The time of day that encounter tables fall back to. #define OW_TIME_OF_DAY_FALLBACK TIME_MORNING // The time of day that encounter tables fall back to. If you set OW_TIMES_OF_DAY to GEN_3, change this to TIME_DAY or you won't have any encounters!
// Lighting // Lighting
#define OW_SHADOW_INTENSITY 4 // Ranges from 0 to 16, where 0 is fully transparent and 16 is black. #define OW_SHADOW_INTENSITY 4 // Ranges from 0 to 16, where 0 is fully transparent and 16 is black.

View File

@ -81,7 +81,7 @@
#define NIGHT_HOUR_END 6 #define NIGHT_HOUR_END 6
#endif #endif
// TIMES_OF_DAY_COUNT must be last // TIMES_OF_DAY_COUNT must be last or things will break
enum TimeOfDay enum TimeOfDay
{ {
TIME_MORNING, TIME_MORNING,
@ -91,6 +91,10 @@ enum TimeOfDay
TIMES_OF_DAY_COUNT, TIMES_OF_DAY_COUNT,
}; };
// for incrementing/decrementing
#define TIME_FIRST 0
#define TIME_LAST (TIMES_OF_DAY_COUNT - 1)
#define TIME_OF_DAY_DEFAULT 0 #define TIME_OF_DAY_DEFAULT 0
#endif // GUARD_CONSTANTS_RTC_H #endif // GUARD_CONSTANTS_RTC_H

View File

@ -43,6 +43,7 @@ u16 GetFullYear(void);
enum Month GetMonth(void); enum Month GetMonth(void);
u8 GetDay(void); u8 GetDay(void);
enum Weekday GetDayOfWeek(void); enum Weekday GetDayOfWeek(void);
enum TimeOfDay GenConfigTimeOfDay(enum TimeOfDay timeOfDay);
enum TimeOfDay TryIncrementTimeOfDay(enum TimeOfDay timeOfDay); enum TimeOfDay TryIncrementTimeOfDay(enum TimeOfDay timeOfDay);
enum TimeOfDay TryDecrementTimeOfDay(enum TimeOfDay timeOfDay); enum TimeOfDay TryDecrementTimeOfDay(enum TimeOfDay timeOfDay);

View File

@ -7,7 +7,7 @@ try:
print("Please run this script from the project's root folder.") print("Please run this script from the project's root folder.")
quit() quit()
sys.path.append("./tools/wild_encounters/") sys.path.append("./tools/wild_encounters/")
from wild_encounters_to_header import TimeOfDay, SetupUserTimeEnum from wild_encounters_to_header import Config
except ImportError: except ImportError:
print("Could not import the file tools/wild_encounters/wild_encounters_to_header.py") print("Could not import the file tools/wild_encounters/wild_encounters_to_header.py")
quit() quit()
@ -24,11 +24,13 @@ def GetWildEncounterFile():
print("Please run this script from the project's root folder.") print("Please run this script from the project's root folder.")
quit() quit()
timeOfDay = SetupUserTimeEnum(TimeOfDay())
wFile = open("src/data/wild_encounters.json") wFile = open("src/data/wild_encounters.json")
wData = json.load(wFile) wData = json.load(wFile)
config = Config('include/config/overworld.h', 'include/constants/rtc.h', wData)
timeOfDay = config.times_of_day
wBackupData = json.dumps(wData, indent=2) wBackupData = json.dumps(wData, indent=2)
wBackupFile = open("src/data/wild_encounters.json.bak", mode="w", encoding="utf-8") wBackupFile = open("src/data/wild_encounters.json.bak", mode="w", encoding="utf-8")
wBackupFile.write(wBackupData) wBackupFile.write(wBackupData)
@ -49,7 +51,7 @@ def GetWildEncounterFile():
wEncounters_New = list() wEncounters_New = list()
for map in wEncounters: for map in wEncounters:
for suffix in timeOfDay.fvals: for suffix in timeOfDay.values():
tempSuffix = "_" + suffix tempSuffix = "_" + suffix
if tempSuffix in map["base_label"]: if tempSuffix in map["base_label"]:
editMap = False editMap = False
@ -59,7 +61,7 @@ def GetWildEncounterFile():
if editMap: if editMap:
k = 0 k = 0
for suffix in timeOfDay.fvals: for suffix in timeOfDay.values():
tempDict = dict() tempDict = dict()
if k == TIME_OF_DAY_DEFAULT or COPY_FULL_ENCOUNTER: if k == TIME_OF_DAY_DEFAULT or COPY_FULL_ENCOUNTER:
tempDict = map.copy() tempDict = map.copy()

View File

@ -334,7 +334,8 @@ enum TimeOfDay GetTimeOfDay(void)
enum TimeOfDay GetTimeOfDayForDex(void) enum TimeOfDay GetTimeOfDayForDex(void)
{ {
return OW_TIME_OF_DAY_ENCOUNTERS ? GetTimeOfDay() : TIME_OF_DAY_DEFAULT; enum TimeOfDay timeOfDay = OW_TIME_OF_DAY_ENCOUNTERS ? GetTimeOfDay() : TIME_OF_DAY_DEFAULT;
return GenConfigTimeOfDay(timeOfDay);
} }
void RtcInitLocalTimeOffset(s32 hour, s32 minute) void RtcInitLocalTimeOffset(s32 hour, s32 minute)
@ -456,12 +457,24 @@ enum Weekday GetDayOfWeek(void)
return dateTime.dayOfWeek; return dateTime.dayOfWeek;
} }
enum TimeOfDay GenConfigTimeOfDay(enum TimeOfDay timeOfDay)
{
if ((((timeOfDay == TIME_MORNING || timeOfDay == TIME_EVENING) && OW_TIMES_OF_DAY == GEN_3)
|| (timeOfDay == TIME_EVENING && OW_TIMES_OF_DAY == GEN_4))
&& timeOfDay < TIME_LAST)
timeOfDay++;
return timeOfDay;
}
enum TimeOfDay TryIncrementTimeOfDay(enum TimeOfDay timeOfDay) enum TimeOfDay TryIncrementTimeOfDay(enum TimeOfDay timeOfDay)
{ {
return timeOfDay == TIME_NIGHT ? TIME_MORNING : timeOfDay + 1; timeOfDay = timeOfDay == TIME_LAST ? TIME_FIRST : timeOfDay + 1;
return GenConfigTimeOfDay(timeOfDay);
} }
enum TimeOfDay TryDecrementTimeOfDay(enum TimeOfDay timeOfDay) enum TimeOfDay TryDecrementTimeOfDay(enum TimeOfDay timeOfDay)
{ {
return timeOfDay == TIME_MORNING ? TIME_NIGHT : timeOfDay - 1; timeOfDay = timeOfDay == TIME_FIRST ? TIME_LAST : timeOfDay - 1;
return GenConfigTimeOfDay(timeOfDay);
} }

View File

@ -415,36 +415,32 @@ enum TimeOfDay GetTimeOfDayForEncounters(u32 headerId, enum WildPokemonArea area
return TIME_OF_DAY_DEFAULT; return TIME_OF_DAY_DEFAULT;
if (InBattlePike() || CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE) if (InBattlePike() || CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE)
{
return OW_TIME_OF_DAY_FALLBACK; return OW_TIME_OF_DAY_FALLBACK;
}
else switch (area)
{ {
switch (area) default:
{ case WILD_AREA_LAND:
default: wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo;
case WILD_AREA_LAND: break;
wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].landMonsInfo; case WILD_AREA_WATER:
break; wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo;
case WILD_AREA_WATER: break;
wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].waterMonsInfo; case WILD_AREA_ROCKS:
break; wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].rockSmashMonsInfo;
case WILD_AREA_ROCKS: break;
wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].rockSmashMonsInfo; case WILD_AREA_FISHING:
break; wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].fishingMonsInfo;
case WILD_AREA_FISHING: break;
wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].fishingMonsInfo; case WILD_AREA_HIDDEN:
break; wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].hiddenMonsInfo;
case WILD_AREA_HIDDEN: break;
wildMonInfo = gWildMonHeaders[headerId].encounterTypes[timeOfDay].hiddenMonsInfo;
break;
}
} }
if (wildMonInfo == NULL && !OW_TIME_OF_DAY_DISABLE_FALLBACK) if (wildMonInfo == NULL && !OW_TIME_OF_DAY_DISABLE_FALLBACK)
return OW_TIME_OF_DAY_FALLBACK; return OW_TIME_OF_DAY_FALLBACK;
else else
return timeOfDay; return GenConfigTimeOfDay(timeOfDay);
} }
u8 PickWildMonNature(void) u8 PickWildMonNature(void)

View File

@ -1,720 +1,277 @@
import json import json
import re import re
import os
class Config:
IS_ENABLED = False def __init__(self, config_file_name, rtc_constants_file_name, encounters_json_data):
DEXNAV_ENABLED = False self.times_of_day = None
self.mon_types = None
# C string vars self.time_encounters = None
define = "#define" self.disable_time_fallback = None
ENCOUNTER_CHANCE = "ENCOUNTER_CHANCE" self.time_fallback = None
SLOT = "SLOT"
TOTAL = "TOTAL" self.ParseTimeEnum(rtc_constants_file_name)
NULL = "NULL" if self.times_of_day == None:
UNDEFINED = "UNDEFINED" raise Exception(f"Failed to parse 'enum TimeOfDay' in '{rtc_constants_file_name}'")
MAP_UNDEFINED = "MAP_UNDEFINED"
self.ParseMonTypes(encounters_json_data)
# encounter group header types, filled out programmatically if self.mon_types == None:
MON_HEADERS = [] raise Exception("No fields defined in 'wild_encounters.json'")
# mon encounter group types with open(config_file_name, 'r') as config_file:
fieldData = [] lines = config_file.readlines()
fieldInfoStrings = [] for line in lines:
fieldStrings = [] self.ParseTimeConfig(line)
# time of day encounter data if self.time_encounters == None:
TIME_DEFAULT = "" raise Exception("OW_TIME_OF_DAY_ENCOUNTERS not defined.")
TIME_DEFAULT_LABEL = "TIME_OF_DAY_DEFAULT" if self.disable_time_fallback == None:
TIME_DEFAULT_INDEX = 0 raise Exception("OW_TIME_OF_DAY_DISABLE_FALLBACK not defined.")
TIMES_OF_DAY_COUNT = TIME_DEFAULT_INDEX + 1 if self.time_fallback == None:
raise Exception("OW_TIME_OF_DAY_FALLBACK not defined.")
# struct building blocks
baseStruct = "const struct WildPokemon" def ParseTimeEnum(self, rtc_constants_file_name):
structLabel = "" with open(rtc_constants_file_name, 'r') as rtc_constants_file:
structMonType = "" DEFAULT_TIME_PAT = re.compile(r"enum\s+TimeOfDay\s*\{(?P<rtc_val>[\s*\w+,\=\d*]+)\s*\}\s*\;")
structTime = "" file = rtc_constants_file.read()
structMap = ""
m = DEFAULT_TIME_PAT.search(file)
structInfo = "Info" if m:
structHeader = "Header" txt = m.group('rtc_val')
structArrayAssign = "[] =" values = re.findall(r'TIME_\w+', txt)
self.times_of_day = {}
baseStructLabel = "" for value in values:
baseStructContent = [] self.times_of_day[value] = value.title().replace("Time_", "").replace("_", "")
infoStructString = ""
infoStructRate = 0 def ParseMonTypes(self, encounters_json_data):
infoStructContent = [] for group in encounters_json_data["wild_encounter_groups"]:
headerStructLabel = "" if "fields" in group:
headerStructContent = {} for field in group["fields"]:
headerStructTable = {} field_type = field["type"]
headerIndex = 0 if not self.mon_types:
self.mon_types = []
# map header data variables self.mon_types.append(field_type)
hLabel = ""
hForMaps = True def ParseTimeConfig(self, line):
headersArray = [headerIndex] m = re.search(r'#define OW_TIME_OF_DAY_ENCOUNTERS\s+(\w+)', line)
if m:
# debug output control self.time_encounters = m.group(1) == "TRUE"
mainSwitch = True
printWarningAndInclude = mainSwitch m = re.search(r'#define OW_TIME_OF_DAY_DISABLE_FALLBACK\s+(\w+)', line)
printEncounterHeaders = mainSwitch if m:
printEncounterRateMacros = mainSwitch self.disable_time_fallback = m.group(1) == "TRUE"
printEncounterStructsInfoString = mainSwitch
printEncounterStructs = mainSwitch m = re.search(r'#define OW_TIME_OF_DAY_FALLBACK\s+(\w+)', line)
if m:
self.time_fallback = m.group(1)
class TimeOfDay():
def __init__(self):
self.vals = [] class WildEncounterAssembler:
self.lvals = [] def __init__(self, output_file, json_data, config):
self.fvals = [] self.output_file = output_file
self.count = 0 self.json_data = json_data
self.config = config
def __len__(self):
return self.count def WriteLine(self, line="", indents = 0):
self.output_file.write(4 * indents * " " + line + "\n")
# for debugging purposes
def __str__(self): def WriteHeader(self):
return str([self.vals, self.lvals, self.fvals, self.count]) self.WriteLine("//")
self.WriteLine("// DO NOT MODIFY THIS FILE! It is auto-generated by tools/wild_encounters/wild_encounters_to_header.py")
def add(self, val): self.WriteLine("//")
self.vals.append(val) self.output_file.write("\n\n")
self.lvals.append(val.lower())
self.fvals.append(GetTimeLabelFromString(val).capitalize()) def WriteMacro(self, macro, value):
self.count += 1 self.output_file.write("#define " + macro + " " + value + "\n")
def indexOf(self, val): def WriteMacros(self):
tempArr = [self.vals, self.lvals, self.fvals] wild_encounter_groups = self.json_data["wild_encounter_groups"]
for wild_encounter_group in wild_encounter_groups:
for tvals in tempArr: if "fields" in wild_encounter_group:
i = 0 fields = wild_encounter_group["fields"]
for time in tvals: for field in fields:
if val in time: field_type = field["type"]
return i macro_base = "ENCOUNTER_CHANCE_" + field_type.upper()
previous_group = None
i += 1 previous_macro = None
# return -1 here so it returns a consistent type and can be checked against < 0 encounter_rates = field["encounter_rates"]
return -1
group_name_mapping = len(encounter_rates) * [""]
if "groups" in field:
def ImportWildEncounterFile(): groups = field["groups"]
# make sure we're in the right directory before anything else for group_name, indices in groups.items():
if not os.path.exists("Makefile"): for index in indices:
print("Please run this script from the project's root folder.") group_name_mapping[index] = "_" + group_name.upper()
quit()
for idx, rate in enumerate(encounter_rates):
global MON_HEADERS macro_name = macro_base + group_name_mapping[idx] + "_SLOT_" + str(idx)
macro_value = str(rate)
global TIME_OF_DAY if previous_group == group_name_mapping[idx]:
TIME_OF_DAY = SetupUserTimeEnum(TimeOfDay()) macro_value = "(" + previous_macro + " + " + macro_value + ")"
elif idx > 0:
global IS_ENABLED macro_total_name = macro_base + group_name_mapping[idx - 1] + "_TOTAL"
global TIMES_OF_DAY_COUNT self.WriteMacro(macro_total_name, "(" + previous_macro + ")")
if IsConfigEnabled(): self.WriteMacro(macro_name, macro_value)
IS_ENABLED = True previous_group = group_name_mapping[idx]
TIMES_OF_DAY_COUNT = len(TIME_OF_DAY) previous_macro = macro_name
if idx == len(encounter_rates) - 1:
global DEXNAV_ENABLED macro_total_name = macro_base + group_name_mapping[idx] + "_TOTAL"
DEXNAV_ENABLED = IsDexnavEnabled() self.WriteMacro(macro_total_name, "(" + previous_macro + ")")
macro_total_name = macro_base + group_name_mapping[-1] + "_TOTAL"
global fieldInfoStrings self.WriteLine()
global fieldStrings
global structLabel def WriteMonInfos(self, name, mons, encounter_rate):
global structMonType info_name = name + "Info"
global structTime self.WriteLine(f"const struct WildPokemon {name}[] =")
global structMap self.WriteLine("{")
global baseStructLabel for mon in mons:
global baseStructContent species = mon["species"]
global infoStructString min_level = 2 if "min_level" not in mon else mon["min_level"]
global infoStructRate max_level = 100 if "max_level" not in mon else mon["max_level"]
global headerStructLabel self.WriteLine(f"{{ {min_level}, {max_level}, {species} }},", 1)
global headerStructContent
global hLabel self.WriteLine("};")
global headersArray self.WriteLine()
global encounterTotalCount self.WriteLine(f"const struct WildPokemonInfo {info_name} = {{ {encounter_rate}, {name} }};")
global encounterCount self.WriteLine()
global headerIndex
global fieldData def WriteTerminator(self):
global tabStr self.WriteLine("{", 1)
tabStr = " " self.WriteLine(".mapGroup = MAP_GROUP(MAP_UNDEFINED),", 2)
self.WriteLine(".mapNum = MAP_NUM(MAP_UNDEFINED),", 2)
wFile = open("src/data/wild_encounters.json") self.WriteLine(".encounterTypes =", 2)
wData = json.load(wFile) self.WriteLine("{", 2)
for time in self.config.times_of_day:
encounterTotalCount = [] if not self.config.time_encounters and time != self.config.time_fallback:
encounterCount = [] continue
groupCount = 0 self.WriteLine(f"[{time}] =", 3)
while groupCount < len(wData["wild_encounter_groups"]): self.WriteLine("{", 3)
encounterTotalCount.append(0) for mon_type in self.config.mon_types:
encounterCount.append(0) member_name = mon_type.title().replace("_", "")
groupCount += 1 member_name = member_name[0].lower() + member_name[1:] + "Info"
self.WriteLine(f".{member_name} = NULL,", 4)
for data in wData["wild_encounter_groups"]: self.WriteLine("},", 3)
wEncounters = wData["wild_encounter_groups"][headerIndex]["encounters"] self.WriteLine("},", 2)
headerSuffix = structHeader + "s" self.WriteLine("},", 1)
if data["label"]: def WritePokemonHeaders(self, headers):
hLabel = wData["wild_encounter_groups"][headerIndex]["label"] label = headers["label"]
if headerSuffix in hLabel: self.WriteLine(f"const struct WildPokemonHeader {label}[] =")
hLabel = hLabel[:len(hLabel) - len(headerSuffix)] self.WriteLine("{")
MON_HEADERS.append(hLabel) for shared_label in headers["data"]:
self.WriteLine()
if data["for_maps"]: map_data = headers["data"][shared_label]
hForMaps = wData["wild_encounter_groups"][headerIndex]["for_maps"] encounter_data = map_data
map_group = map_data["mapGroup"]
if headerIndex == 0: map_num = map_data["mapNum"]
wFields = wData["wild_encounter_groups"][headerIndex]["fields"] version = "EMERALD"
fieldCounter = 0 if "FireRed" in shared_label:
for field in wFields: version = "FIRERED"
if not CheckFieldDataDupes(field["type"]): elif "LeafGreen" in shared_label:
AddFieldData(fieldCounter, field["type"], field["encounter_rates"]) version = "LEAFGREEN"
if "groups" in field: self.WriteLine(f"#ifdef {version}")
fieldData[fieldCounter]["groups"] = field["groups"]
self.WriteLine("{", 1)
""" self.WriteLine(f".mapGroup = {map_group},", 2)
hidden mons need a special bit of logic since they're not in the vanilla self.WriteLine(f".mapNum = {map_num},", 2)
wild_encounters.json file, but the code expects them to be there self.WriteLine(".encounterTypes =", 2)
""" self.WriteLine("{", 2)
hidden_mons = "hidden_mons" for time in self.config.times_of_day:
if (fieldCounter == len(wFields) - 1) and not CheckFieldDataDupes(hidden_mons): if not self.config.time_encounters and time != self.config.time_fallback:
hidden_dummy_rates = [1, 0]
AddFieldData(fieldCounter + 1, hidden_mons, hidden_dummy_rates)
fieldCounter += 1
if printWarningAndInclude:
PrintGeneratedWarningText()
print('#include "rtc.h"')
print("\n")
PrintEncounterRateMacros()
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 = []
structTime = TIME_DEFAULT_INDEX
if IS_ENABLED:
timeCounter = 0
while timeCounter < TIMES_OF_DAY_COUNT:
tempfTime = f"_{TIME_OF_DAY.fvals[timeCounter]}"
tempTime = TIME_OF_DAY.vals[timeCounter]
if tempfTime in structLabel or tempTime in structLabel:
structTime = timeCounter
timeCounter += 1
fieldCounter = 0
fieldInfoStrings = []
while fieldCounter < len(fieldData):
fieldInfoStrings.append("")
fieldStrings.append("")
fieldCounter += 1
fieldCounter = 0
while fieldCounter < len(fieldData):
for areaTable in encounter:
if fieldData[fieldCounter]["name"] in areaTable:
structMonType = fieldData[fieldCounter]["pascalName"]
if f"_{TIME_OF_DAY.fvals[structTime]}" in structLabel:
fieldInfoStrings[fieldCounter] = f"{structLabel}_{structMonType}{structInfo}"
fieldStrings[fieldCounter] = f"{structLabel}_{structMonType}"
else:
fieldInfoStrings[fieldCounter] = f"{structLabel}_{TIME_OF_DAY.fvals[structTime]}_{structMonType}{structInfo}"
fieldStrings[fieldCounter] = f"{structLabel}_{TIME_OF_DAY.fvals[structTime]}_{structMonType}"
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} {fieldStrings[fieldCounter]}{structArrayAssign}"
if printEncounterStructs:
print()
print(baseStructLabel)
print("{")
PrintStructContent(baseStructContent)
print("};")
if printEncounterStructsInfoString:
infoStructString = f"{baseStruct}{structInfo} {fieldInfoStrings[fieldCounter]} = {{ {infoStructRate}, {fieldStrings[fieldCounter]} }};"
print(infoStructString)
fieldCounter += 1
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
timeCounter = 0
while timeCounter < TIMES_OF_DAY_COUNT:
tempTime = TIME_OF_DAY.fvals[timeCounter]
if tempTime in label:
timeLength = len(tempTime)
return label[:(labelLength - (timeLength + 1))]
timeCounter += 1
return label
def GetStructTimeWithoutLabel(label):
if not IS_ENABLED:
return TIME_DEFAULT_INDEX
timeCounter = 0
while timeCounter < TIMES_OF_DAY_COUNT:
tempTime = f"_{TIME_OF_DAY.fvals[timeCounter]}"
if tempTime in label:
return timeCounter
timeCounter += 1
return TIME_DEFAULT_INDEX
def AssembleMonHeaderContent():
SetupMonInfoVars()
tempHeaderLabel = GetWildMonHeadersLabel()
tempHeaderTimeIndex = GetStructTimeWithoutLabel(structLabel)
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"] = []
timeCounter = 0
while timeCounter < TIMES_OF_DAY_COUNT:
headerStructTable[tempHeaderLabel][structLabelNoTime]["encounter_types"].append([])
timeCounter += 1
fieldCounter = 0
while fieldCounter < len(fieldData):
headerStructTable[tempHeaderLabel][structLabelNoTime]["encounter_types"][tempHeaderTimeIndex].append(fieldInfoStrings[fieldCounter])
fieldCounter += 1
def SetupMonInfoVars():
i = 0
while i < len(fieldData):
fieldData[i]["infoStringBase"] = "." + fieldData[i]["snakeName"] + structInfo
if CheckEmpty(fieldInfoStrings[i]):
fieldInfoStrings[i] = NULL
else:
fieldInfoStrings[i] = "&" + fieldInfoStrings[i]
i += 1
def PrintWildMonHeadersContent():
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) + "{")
timeCounter = 0
while timeCounter < TIMES_OF_DAY_COUNT:
monInfo = headerStructTable[group][label][stat][timeCounter]
PrintEncounterHeaders(f"{TabStr(3)}[{TIME_OF_DAY.vals[timeCounter]}] = ")
infoIndex = 0
while infoIndex < len(fieldData):
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 == len(fieldData) - 1:
PrintEncounterHeaders(TabStr(3) + "},")
infoIndex += 1
timeCounter += 1
PrintEncounterHeaders(TabStr(2) + "},")
PrintEncounterHeaders(tabStr + "},")
labelCount += 1
PrintEncounterHeaders(tabStr + "{")
PrintEncounterHeaders(f"{TabStr(2)}.mapGroup = {GetMapGroupEnum(MAP_UNDEFINED)},")
PrintEncounterHeaders(f"{TabStr(2)}.mapNum = {GetMapGroupEnum(MAP_UNDEFINED, labelCount + 1)},")
nullCount = 0
while nullCount < TIMES_OF_DAY_COUNT:
if nullCount == 0:
PrintEncounterHeaders(f"{TabStr(2)}.encounterTypes =")
PrintEncounterHeaders(TabStr(2)+ "{")
PrintEncounterHeaders(f"{TabStr(3)}[{TIME_OF_DAY.vals[nullCount]}] = ")
nullIndex = 0
while nullIndex <= len(fieldData) - 1:
if nullIndex == 0:
PrintEncounterHeaders(TabStr(3) + "{")
PrintEncounterHeaders(f"{TabStr(4)}{GetIMonInfoStringFromIndex(nullIndex)} = NULL,")
if nullIndex == len(fieldData) - 1:
PrintEncounterHeaders(TabStr(3) + "},")
nullIndex += 1
nullCount += 1
PrintEncounterHeaders(TabStr(2) + "},")
PrintEncounterHeaders(tabStr + "},")
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
fieldCounter = 0
while fieldCounter < len(fieldData):
tempName = fieldData[fieldCounter]["name"].upper()
if "groups" not in fieldData[fieldCounter]:
rateCount = 0
if fieldData[fieldCounter]["encounter_rates"]:
for percent in fieldData[fieldCounter]["encounter_rates"]:
if not DEXNAV_ENABLED and tempName == "HIDDEN_MONS":
break
if rateCount == 0:
print(f"{define} {ENCOUNTER_CHANCE}_{tempName}_{SLOT}_{rateCount} {percent}")
else:
print(
f"{define} {ENCOUNTER_CHANCE}_{tempName}_{SLOT}_{rateCount} {ENCOUNTER_CHANCE}_{tempName}_{SLOT}_{rateCount - 1} + {percent}"
)
if rateCount + 1 == len(fieldData[fieldCounter]["encounter_rates"]):
print(
f"{define} {ENCOUNTER_CHANCE}_{tempName}_{TOTAL} ({ENCOUNTER_CHANCE}_{tempName}_{SLOT}_{rateCount})"
)
rateCount += 1
else:
rates = fieldData[fieldCounter]["encounter_rates"]
groups = fieldData[fieldCounter]["groups"]
for method in groups:
method_indices = groups[method]
if not method_indices:
continue continue
self.WriteLine(f"[{time}] =", 3)
self.WriteLine("{", 3)
for mon_type in self.config.mon_types:
member_name = mon_type.title().replace("_", "")
member_name = member_name[0].lower() + member_name[1:] + "Info"
value = "NULL"
if time in encounter_data and mon_type in encounter_data[time]:
value = encounter_data[time][mon_type]
if value != "NULL":
value = "&" + value
self.WriteLine(f".{member_name} = {value},", 4)
for i, methodPercentIndex in enumerate(method_indices): self.WriteLine("},", 3)
if methodPercentIndex < 0 or methodPercentIndex >= len(rates):
print(f"#error Invalid fishing encounter rate index {methodPercentIndex} for {method.upper()}") self.WriteLine("},", 2)
self.WriteLine("},", 1)
self.WriteLine(f"#endif")
self.WriteTerminator()
self.WriteLine("};")
def WriteEncounters(self):
wild_encounter_groups = self.json_data["wild_encounter_groups"]
for wild_encounter_group in wild_encounter_groups:
headers = {}
headers["label"] = wild_encounter_group["label"]
headers["data"] = {}
for_maps = False
map_num_counter = 1
if "for_maps" in wild_encounter_group:
for_maps = wild_encounter_group["for_maps"]
encounters = wild_encounter_group["encounters"]
for map_encounters in encounters:
map_group = "0"
map_num = str(map_num_counter)
if for_maps:
map_name = map_encounters["map"]
map_group = f"MAP_GROUP({map_name})"
map_num = f"MAP_NUM({map_name})"
map_num_counter += 1
base_label = map_encounters["base_label"]
shared_label = base_label
time = self.config.time_fallback
for time_ident in self.config.times_of_day:
if self.config.times_of_day[time_ident] in base_label:
time = time_ident
shared_label = shared_label.replace('_' + self.config.times_of_day[time_ident], '')
if shared_label not in headers["data"]:
headers["data"][shared_label] = {}
if time not in headers["data"][shared_label]:
headers["data"][shared_label][time] = {}
headers["data"][shared_label]["mapGroup"] = map_group
headers["data"][shared_label]["mapNum"] = map_num
version = "EMERALD"
if "FireRed" in shared_label:
version = "FIRERED"
elif "LeafGreen" in shared_label:
version = "LEAFGREEN"
self.WriteLine(f"#ifdef {version}")
for mon_type in self.config.mon_types:
if mon_type not in map_encounters:
headers["data"][shared_label][mon_type] = "NULL"
continue continue
rate_value = rates[methodPercentIndex] mons_entry = map_encounters[mon_type]
if i == 0: encounter_rate = mons_entry["encounter_rate"]
print(f"{define} {ENCOUNTER_CHANCE}_{tempName}_{method.upper()}_{SLOT}_{methodPercentIndex} {rate_value}") mons = mons_entry["mons"]
else:
previous_method_index = method_indices[i - 1]
print(f"{define} {ENCOUNTER_CHANCE}_{tempName}_{method.upper()}_{SLOT}_{methodPercentIndex} {ENCOUNTER_CHANCE}_{tempName}_{method.upper()}_{SLOT}_{previous_method_index} + {rate_value}")
if i == len(method_indices) - 1: mon_array_name = base_label + "_" + mon_type.title().replace("_", "")
print(f"{define} {ENCOUNTER_CHANCE}_{tempName}_{method.upper()}_{TOTAL} ({ENCOUNTER_CHANCE}_{tempName}_{method.upper()}_{SLOT}_{methodPercentIndex})") self.WriteMonInfos(mon_array_name, mons, encounter_rate)
headers["data"][shared_label][time][mon_type] = mon_array_name + "Info"
self.WriteLine(f"#endif")
fieldCounter += 1 self.WritePokemonHeaders(headers)
print()
def GetTimeLabelFromString(string): def ConvertToHeaderFile(json_data):
time = "TIME" with open('src/data/wild_encounters.h', 'w') as output_file:
time_ = "TIME_" config = Config('include/config/overworld.h', 'include/constants/rtc.h', json_data)
assembler = WildEncounterAssembler(output_file, json_data, config)
if string == "TIMES_OF_DAY_COUNT": assembler.WriteHeader()
return string assembler.WriteMacros()
assembler.WriteEncounters()
if time_ in string.upper():
return string[len(time_):len(string)]
elif time in string.upper():
return string[len(time):len(string)]
return string
def GetIMonInfoStringFromIndex(index):
return fieldData[index]["infoStringBase"]
def GetMapGroupEnum(string, index = 0):
if "MAP_" in string and index == 0:
return "MAP_GROUP(" + string + ")"
elif "MAP_" in string and index != 0:
return "MAP_NUM(" + string + ")"
return index
"""
get copied lhea :^ )
- next four 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<cfg_val>[^ ]*)")
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 IsDexnavEnabled():
CONFIG_ENABLED_PAT = re.compile(r"#define DEXNAV_ENABLED\s+(?P<cfg_val>[^ ]*)")
with open("./include/config/dexnav.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 GetTimeEnum():
DEFAULT_TIME_PAT = re.compile(r"enum\s+TimeOfDay\s*\{(?P<rtc_val>[\s*\w+,\=\d*]+)\s*\}\s*\;")
with open("./include/constants/rtc.h", "r") as rtc_include_file:
include_rtc = rtc_include_file.read()
include_enum = DEFAULT_TIME_PAT.search(include_rtc)
return include_enum.group("rtc_val")
def CheckEmpty(string):
return string == "" or string.isspace() or string == "\n"
def SetupUserTimeEnum(timeOfDay):
enum_string = GetTimeEnum()
enum_string = enum_string.split(",")
# check for extra element from trailing comma
if CheckEmpty(enum_string[-1]):
enum_string.pop(-1)
# we don't need the `TIMES_OF_DAY_COUNT` value, so - 1 from the value of len(enum_string)
strCount = 0
while strCount < len(enum_string) - 1:
tempStr = enum_string[strCount].strip("\n ")
"""
we need to ignore any value assignments, as the times will need to correspond
with the elements in the array.
"""
if "=" in tempStr:
tempStr = tempStr[0:tempStr.index("=")]
tempStr = tempStr.strip(" ")
#double check we didn't catch any empty values
if not CheckEmpty(enum_string[strCount]):
timeOfDay.add(tempStr)
strCount += 1
return timeOfDay
def TabStr(amount):
return tabStr * amount
def GetPascalCase(string):
stringArray = string.split("_")
pascalString = ""
for string in stringArray:
pascalString += string.capitalize()
return pascalString
def GetSnakeCase(string):
stringArray = string.split("_")
snakeString = ""
i = 0
for string in stringArray:
if i == 0:
snakeString += string
else:
snakeString += string.capitalize()
i += 1
return snakeString
def CheckFieldDataDupes(name):
for field in fieldData:
if field["name"] == name:
return True
return False
def AddFieldData(index, fieldType, fieldRates):
fieldData.append({})
fieldData[index]["name"] = fieldType
fieldData[index]["pascalName"] = GetPascalCase(fieldType)
fieldData[index]["snakeName"] = GetSnakeCase(fieldType)
fieldData[index]["encounter_rates"] = fieldRates
def main(): def main():
pass with open('src/data/wild_encounters.json', 'r') as json_file:
json_data = json.load(json_file)
ConvertToHeaderFile(json_data)
if __name__ == "__main__": if __name__ == '__main__':
ImportWildEncounterFile() main()
"""
!!!! 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)
- if DEXNAV_ENABLED is TRUE
- these macros are 1 and 0, respectively if hidden_mons isn't in the encounter
rate list at the top of wild_encounters.json
#define ENCOUNTER_CHANCE_HIDDEN_MONS_SLOT_0 1
#define ENCOUNTER_CHANCE_HIDDEN_MONS_SLOT_1 ENCOUNTER_CHANCE_HIDDEN_MONS_SLOT_0 + 0
#define ENCOUNTER_CHANCE_HIDDEN_MONS_TOTAL (ENCOUNTER_CHANCE_HIDDEN_MONS_SLOT_1)
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,
}
},
}
"""