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
TITLE := POKEMON EMER
GAME_CODE := BPEE
MAKER_CODE := 01
REVISION := 0
KEEP_TEMPS ?= 0
# `File name`.gba
FILE_NAME := pokeemerald
FILE_NAME := poke$(BUILD_NAME)
BUILD_DIR := build
# Compares the ROM to a checksum of the original - only makes sense using when non-modern
@ -75,10 +95,10 @@ ifeq ($(RELEASE),1)
endif
ROM_NAME := $(FILE_NAME).gba
OBJ_DIR_NAME := $(BUILD_DIR)/modern
OBJ_DIR_NAME_TEST := $(BUILD_DIR)/modern-test
OBJ_DIR_NAME_DEBUG := $(BUILD_DIR)/modern-debug
OBJ_DIR_NAME_RELEASE := $(BUILD_DIR)/modern-release
OBJ_DIR_NAME := $(BUILD_DIR)/$(BUILD_NAME)
OBJ_DIR_NAME_TEST := $(BUILD_DIR)/$(BUILD_NAME)-test
OBJ_DIR_NAME_DEBUG := $(BUILD_DIR)/$(BUILD_NAME)-debug
OBJ_DIR_NAME_RELEASE := $(BUILD_DIR)/$(BUILD_NAME)-release
ELF_NAME := $(ROM_NAME:.gba=.elf)
MAP_NAME := $(ROM_NAME:.gba=.map)
@ -124,7 +144,7 @@ TEST_BUILDDIR = $(OBJ_DIR)/$(TEST_SUBDIR)
SHELL := bash -o pipefail
# 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_CPP_ARGS := $(INCLUDE_DIRS:%=-iquote %)
@ -135,7 +155,7 @@ O_LEVEL ?= g
else
O_LEVEL ?= 2
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)
override CPPFLAGS += -DRELEASE
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
$(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
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)
endif
# 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)
$(error Errors occurred while generating map-related sources. See error messages above for more details)
endif
@ -480,7 +500,7 @@ ifneq ($(NODEP),1)
endif
$(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
$(SCANINC) -M $@ $(INCLUDE_SCANINC_ARGS) -I "" $<
@ -490,7 +510,7 @@ ifneq ($(NODEP),1)
endif
$(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
$(SCANINC) -M $@ $(INCLUDE_SCANINC_ARGS) -I "" $<
@ -549,6 +569,9 @@ $(ROM): $(ELF)
$(OBJCOPY) -O binary $< $@
$(FIX) $@ -p --silent
emerald: all
firered: all
leafgreen: all
# Symbol file (`make syms`)
$(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' > $@

View File

@ -86,13 +86,13 @@
#define GEN_8_PLA GEN_LATEST + 2
#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_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_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_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
#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
#endif
// TIMES_OF_DAY_COUNT must be last
// TIMES_OF_DAY_COUNT must be last or things will break
enum TimeOfDay
{
TIME_MORNING,
@ -91,6 +91,10 @@ enum TimeOfDay
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
#endif // GUARD_CONSTANTS_RTC_H

View File

@ -43,6 +43,7 @@ u16 GetFullYear(void);
enum Month GetMonth(void);
u8 GetDay(void);
enum Weekday GetDayOfWeek(void);
enum TimeOfDay GenConfigTimeOfDay(enum TimeOfDay timeOfDay);
enum TimeOfDay TryIncrementTimeOfDay(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.")
quit()
sys.path.append("./tools/wild_encounters/")
from wild_encounters_to_header import TimeOfDay, SetupUserTimeEnum
from wild_encounters_to_header import Config
except ImportError:
print("Could not import the file tools/wild_encounters/wild_encounters_to_header.py")
quit()
@ -24,11 +24,13 @@ def GetWildEncounterFile():
print("Please run this script from the project's root folder.")
quit()
timeOfDay = SetupUserTimeEnum(TimeOfDay())
wFile = open("src/data/wild_encounters.json")
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)
wBackupFile = open("src/data/wild_encounters.json.bak", mode="w", encoding="utf-8")
wBackupFile.write(wBackupData)
@ -49,7 +51,7 @@ def GetWildEncounterFile():
wEncounters_New = list()
for map in wEncounters:
for suffix in timeOfDay.fvals:
for suffix in timeOfDay.values():
tempSuffix = "_" + suffix
if tempSuffix in map["base_label"]:
editMap = False
@ -59,7 +61,7 @@ def GetWildEncounterFile():
if editMap:
k = 0
for suffix in timeOfDay.fvals:
for suffix in timeOfDay.values():
tempDict = dict()
if k == TIME_OF_DAY_DEFAULT or COPY_FULL_ENCOUNTER:
tempDict = map.copy()

View File

@ -248,7 +248,7 @@ static const struct WindowTemplate sTimeOfDayWindowLabelTemplates[] =
.baseBlock = 0x16C
},
[DEX_AREA_LABEL_AREA_UNKNOWN] =
[DEX_AREA_LABEL_AREA_UNKNOWN] =
{
.bg = LABEL_WINDOW_BG,
.tilemapLeft = 12,

View File

@ -334,7 +334,8 @@ enum TimeOfDay GetTimeOfDay(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)
@ -456,12 +457,24 @@ enum Weekday GetDayOfWeek(void)
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)
{
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)
{
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;
if (InBattlePike() || CurrentBattlePyramidLocation() != PYRAMID_LOCATION_NONE)
{
return OW_TIME_OF_DAY_FALLBACK;
}
else
switch (area)
{
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;
}
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;
return GenConfigTimeOfDay(timeOfDay);
}
u8 PickWildMonNature(void)

View File

@ -1,720 +1,277 @@
import json
import re
import os
IS_ENABLED = False
DEXNAV_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
fieldData = []
fieldInfoStrings = []
fieldStrings = []
# time of day encounter data
TIME_DEFAULT = ""
TIME_DEFAULT_LABEL = "TIME_OF_DAY_DEFAULT"
TIME_DEFAULT_INDEX = 0
TIMES_OF_DAY_COUNT = TIME_DEFAULT_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]
# debug output control
mainSwitch = True
printWarningAndInclude = mainSwitch
printEncounterHeaders = mainSwitch
printEncounterRateMacros = mainSwitch
printEncounterStructsInfoString = mainSwitch
printEncounterStructs = mainSwitch
class TimeOfDay():
def __init__(self):
self.vals = []
self.lvals = []
self.fvals = []
self.count = 0
def __len__(self):
return self.count
# for debugging purposes
def __str__(self):
return str([self.vals, self.lvals, self.fvals, self.count])
def add(self, val):
self.vals.append(val)
self.lvals.append(val.lower())
self.fvals.append(GetTimeLabelFromString(val).capitalize())
self.count += 1
def indexOf(self, val):
tempArr = [self.vals, self.lvals, self.fvals]
for tvals in tempArr:
i = 0
for time in tvals:
if val in time:
return i
i += 1
# return -1 here so it returns a consistent type and can be checked against < 0
return -1
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 TIME_OF_DAY
TIME_OF_DAY = SetupUserTimeEnum(TimeOfDay())
global IS_ENABLED
global TIMES_OF_DAY_COUNT
if IsConfigEnabled():
IS_ENABLED = True
TIMES_OF_DAY_COUNT = len(TIME_OF_DAY)
global DEXNAV_ENABLED
DEXNAV_ENABLED = IsDexnavEnabled()
global fieldInfoStrings
global fieldStrings
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 encounterTotalCount
global encounterCount
global headerIndex
global fieldData
global tabStr
tabStr = " "
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"]
if headerIndex == 0:
wFields = wData["wild_encounter_groups"][headerIndex]["fields"]
fieldCounter = 0
for field in wFields:
if not CheckFieldDataDupes(field["type"]):
AddFieldData(fieldCounter, field["type"], field["encounter_rates"])
if "groups" in field:
fieldData[fieldCounter]["groups"] = field["groups"]
"""
hidden mons need a special bit of logic since they're not in the vanilla
wild_encounters.json file, but the code expects them to be there
"""
hidden_mons = "hidden_mons"
if (fieldCounter == len(wFields) - 1) and not CheckFieldDataDupes(hidden_mons):
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:
class Config:
def __init__(self, config_file_name, rtc_constants_file_name, encounters_json_data):
self.times_of_day = None
self.mon_types = None
self.time_encounters = None
self.disable_time_fallback = None
self.time_fallback = None
self.ParseTimeEnum(rtc_constants_file_name)
if self.times_of_day == None:
raise Exception(f"Failed to parse 'enum TimeOfDay' in '{rtc_constants_file_name}'")
self.ParseMonTypes(encounters_json_data)
if self.mon_types == None:
raise Exception("No fields defined in 'wild_encounters.json'")
with open(config_file_name, 'r') as config_file:
lines = config_file.readlines()
for line in lines:
self.ParseTimeConfig(line)
if self.time_encounters == None:
raise Exception("OW_TIME_OF_DAY_ENCOUNTERS not defined.")
if self.disable_time_fallback == None:
raise Exception("OW_TIME_OF_DAY_DISABLE_FALLBACK not defined.")
if self.time_fallback == None:
raise Exception("OW_TIME_OF_DAY_FALLBACK not defined.")
def ParseTimeEnum(self, rtc_constants_file_name):
with open(rtc_constants_file_name, 'r') as rtc_constants_file:
DEFAULT_TIME_PAT = re.compile(r"enum\s+TimeOfDay\s*\{(?P<rtc_val>[\s*\w+,\=\d*]+)\s*\}\s*\;")
file = rtc_constants_file.read()
m = DEFAULT_TIME_PAT.search(file)
if m:
txt = m.group('rtc_val')
values = re.findall(r'TIME_\w+', txt)
self.times_of_day = {}
for value in values:
self.times_of_day[value] = value.title().replace("Time_", "").replace("_", "")
def ParseMonTypes(self, encounters_json_data):
for group in encounters_json_data["wild_encounter_groups"]:
if "fields" in group:
for field in group["fields"]:
field_type = field["type"]
if not self.mon_types:
self.mon_types = []
self.mon_types.append(field_type)
def ParseTimeConfig(self, line):
m = re.search(r'#define OW_TIME_OF_DAY_ENCOUNTERS\s+(\w+)', line)
if m:
self.time_encounters = m.group(1) == "TRUE"
m = re.search(r'#define OW_TIME_OF_DAY_DISABLE_FALLBACK\s+(\w+)', line)
if m:
self.disable_time_fallback = m.group(1) == "TRUE"
m = re.search(r'#define OW_TIME_OF_DAY_FALLBACK\s+(\w+)', line)
if m:
self.time_fallback = m.group(1)
class WildEncounterAssembler:
def __init__(self, output_file, json_data, config):
self.output_file = output_file
self.json_data = json_data
self.config = config
def WriteLine(self, line="", indents = 0):
self.output_file.write(4 * indents * " " + line + "\n")
def WriteHeader(self):
self.WriteLine("//")
self.WriteLine("// DO NOT MODIFY THIS FILE! It is auto-generated by tools/wild_encounters/wild_encounters_to_header.py")
self.WriteLine("//")
self.output_file.write("\n\n")
def WriteMacro(self, macro, value):
self.output_file.write("#define " + macro + " " + value + "\n")
def WriteMacros(self):
wild_encounter_groups = self.json_data["wild_encounter_groups"]
for wild_encounter_group in wild_encounter_groups:
if "fields" in wild_encounter_group:
fields = wild_encounter_group["fields"]
for field in fields:
field_type = field["type"]
macro_base = "ENCOUNTER_CHANCE_" + field_type.upper()
previous_group = None
previous_macro = None
encounter_rates = field["encounter_rates"]
group_name_mapping = len(encounter_rates) * [""]
if "groups" in field:
groups = field["groups"]
for group_name, indices in groups.items():
for index in indices:
group_name_mapping[index] = "_" + group_name.upper()
for idx, rate in enumerate(encounter_rates):
macro_name = macro_base + group_name_mapping[idx] + "_SLOT_" + str(idx)
macro_value = str(rate)
if previous_group == group_name_mapping[idx]:
macro_value = "(" + previous_macro + " + " + macro_value + ")"
elif idx > 0:
macro_total_name = macro_base + group_name_mapping[idx - 1] + "_TOTAL"
self.WriteMacro(macro_total_name, "(" + previous_macro + ")")
self.WriteMacro(macro_name, macro_value)
previous_group = group_name_mapping[idx]
previous_macro = macro_name
if idx == len(encounter_rates) - 1:
macro_total_name = macro_base + group_name_mapping[idx] + "_TOTAL"
self.WriteMacro(macro_total_name, "(" + previous_macro + ")")
macro_total_name = macro_base + group_name_mapping[-1] + "_TOTAL"
self.WriteLine()
def WriteMonInfos(self, name, mons, encounter_rate):
info_name = name + "Info"
self.WriteLine(f"const struct WildPokemon {name}[] =")
self.WriteLine("{")
for mon in mons:
species = mon["species"]
min_level = 2 if "min_level" not in mon else mon["min_level"]
max_level = 100 if "max_level" not in mon else mon["max_level"]
self.WriteLine(f"{{ {min_level}, {max_level}, {species} }},", 1)
self.WriteLine("};")
self.WriteLine()
self.WriteLine(f"const struct WildPokemonInfo {info_name} = {{ {encounter_rate}, {name} }};")
self.WriteLine()
def WriteTerminator(self):
self.WriteLine("{", 1)
self.WriteLine(".mapGroup = MAP_GROUP(MAP_UNDEFINED),", 2)
self.WriteLine(".mapNum = MAP_NUM(MAP_UNDEFINED),", 2)
self.WriteLine(".encounterTypes =", 2)
self.WriteLine("{", 2)
for time in self.config.times_of_day:
if not self.config.time_encounters and time != self.config.time_fallback:
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"
self.WriteLine(f".{member_name} = NULL,", 4)
self.WriteLine("},", 3)
self.WriteLine("},", 2)
self.WriteLine("},", 1)
def WritePokemonHeaders(self, headers):
label = headers["label"]
self.WriteLine(f"const struct WildPokemonHeader {label}[] =")
self.WriteLine("{")
for shared_label in headers["data"]:
self.WriteLine()
map_data = headers["data"][shared_label]
encounter_data = map_data
map_group = map_data["mapGroup"]
map_num = map_data["mapNum"]
version = "EMERALD"
if "FireRed" in shared_label:
version = "FIRERED"
elif "LeafGreen" in shared_label:
version = "LEAFGREEN"
self.WriteLine(f"#ifdef {version}")
self.WriteLine("{", 1)
self.WriteLine(f".mapGroup = {map_group},", 2)
self.WriteLine(f".mapNum = {map_num},", 2)
self.WriteLine(".encounterTypes =", 2)
self.WriteLine("{", 2)
for time in self.config.times_of_day:
if not self.config.time_encounters and time != self.config.time_fallback:
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):
if methodPercentIndex < 0 or methodPercentIndex >= len(rates):
print(f"#error Invalid fishing encounter rate index {methodPercentIndex} for {method.upper()}")
self.WriteLine("},", 3)
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
rate_value = rates[methodPercentIndex]
if i == 0:
print(f"{define} {ENCOUNTER_CHANCE}_{tempName}_{method.upper()}_{SLOT}_{methodPercentIndex} {rate_value}")
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}")
mons_entry = map_encounters[mon_type]
encounter_rate = mons_entry["encounter_rate"]
mons = mons_entry["mons"]
if i == len(method_indices) - 1:
print(f"{define} {ENCOUNTER_CHANCE}_{tempName}_{method.upper()}_{TOTAL} ({ENCOUNTER_CHANCE}_{tempName}_{method.upper()}_{SLOT}_{methodPercentIndex})")
mon_array_name = base_label + "_" + mon_type.title().replace("_", "")
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
print()
self.WritePokemonHeaders(headers)
def GetTimeLabelFromString(string):
time = "TIME"
time_ = "TIME_"
if string == "TIMES_OF_DAY_COUNT":
return string
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 ConvertToHeaderFile(json_data):
with open('src/data/wild_encounters.h', 'w') as output_file:
config = Config('include/config/overworld.h', 'include/constants/rtc.h', json_data)
assembler = WildEncounterAssembler(output_file, json_data, config)
assembler.WriteHeader()
assembler.WriteMacros()
assembler.WriteEncounters()
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__":
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)
- 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,
}
},
}
"""
if __name__ == '__main__':
main()