Town map: unpack descriptions data & generate text bank (#798)

This commit is contained in:
Gudf 2025-10-31 04:39:52 +01:00 committed by GitHub
parent 4abbaaf2b6
commit 0ee44adb2c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
12 changed files with 3401 additions and 1013 deletions

View File

@ -92,8 +92,8 @@ res/prebuilt/data/t3_fl_p.nsbtx,/data/t3_fl_p.nsbtx
res/prebuilt/data/t3_fl_r.nsbtx,/data/t3_fl_r.nsbtx
res/prebuilt/data/t3_fl_y.nsbtx,/data/t3_fl_y.nsbtx
res/prebuilt/data/test.atr,/data/test.atr
res/prebuilt/data/tmap_block.dat,/data/tmap_block.dat
res/prebuilt/data/tmap_flags.dat,/data/tmap_flags.dat
res/town_map/town_map_blocks.dat,/data/tmap_block.dat
res/town_map/town_map_flags.dat,/data/tmap_flags.dat
res/prebuilt/data/tmapn_canm.resdat,/data/tmapn_canm.resdat
res/prebuilt/data/tmapn_celact.cldat,/data/tmapn_celact.cldat
res/prebuilt/data/tmapn_celact.txt,/data/tmapn_celact.txt

1 Source File Target File
92 res/prebuilt/data/t3_fl_r.nsbtx /data/t3_fl_r.nsbtx
93 res/prebuilt/data/t3_fl_y.nsbtx /data/t3_fl_y.nsbtx
94 res/prebuilt/data/test.atr /data/test.atr
95 res/prebuilt/data/tmap_block.dat res/town_map/town_map_blocks.dat /data/tmap_block.dat
96 res/prebuilt/data/tmap_flags.dat res/town_map/town_map_flags.dat /data/tmap_flags.dat
97 res/prebuilt/data/tmapn_canm.resdat /data/tmapn_canm.resdat
98 res/prebuilt/data/tmapn_celact.cldat /data/tmapn_celact.cldat
99 res/prebuilt/data/tmapn_celact.txt /data/tmapn_celact.txt

View File

@ -91,8 +91,8 @@ aa3240383bc0d0ed15b4848616698a25623a6d22 *res/prebuilt/data/t3_fl_p.nsbtx
2aead045ba9169c7eaf35d4e82ac8f37f5e615bf *res/prebuilt/data/t3_fl_r.nsbtx
efcb17fc440548aefb1ec4b830955fa9e7954025 *res/prebuilt/data/t3_fl_y.nsbtx
b5b6089df980ad54540fad84c76f76bcde0bb464 *res/prebuilt/data/test.atr
337f8bd7788eaea30946c8632bdbc9ef9b12b51f *res/prebuilt/data/tmap_block.dat
973dc6f187a2b7469c6a1a11d5c4c0c41b2f221c *res/prebuilt/data/tmap_flags.dat
337f8bd7788eaea30946c8632bdbc9ef9b12b51f *res/town_map/town_map_blocks.dat
973dc6f187a2b7469c6a1a11d5c4c0c41b2f221c *res/town_map/town_map_flags.dat
aba8f43ada2b1919151c50366ec00df4f875350d *res/prebuilt/data/tmapn_canm.resdat
b350396f553cf8c70888aca59e327310812b1ee9 *res/prebuilt/data/tmapn_celact.cldat
b2d44b4083a618fa1da3e463e757b7af5e839bb3 *res/prebuilt/data/tmapn_celact.txt

View File

@ -76,6 +76,8 @@ copy_gen = generator(find_program('cp'),
# is a dependency of some later build-rule.
subdir('pokemon')
subdir('trainers')
subdir('graphics')
subdir('town_map')
subdir('text')
# Common generator for "scripting" files, i.e. field and battle scripts

View File

@ -93,8 +93,6 @@ prebuilt_files = [
't3_fl_r.nsbtx',
't3_fl_y.nsbtx',
'test.atr',
'tmap_block.dat',
'tmap_flags.dat',
'tmapn_canm.resdat',
'tmapn_celact.cldat',
'tmapn_celact.txt',

Binary file not shown.

Binary file not shown.

View File

@ -48,11 +48,16 @@ frontier_text_bank_names = [
'frontier_trainer_names.json'
]
town_map_text_bank_name = [
'town_map.json'
]
# This list will be what's used hereafter when referring to any-and-all generated banks.
generated_text_banks = [
species_text_bank_names,
trainer_text_bank_names,
frontier_text_bank_names,
town_map_text_bank_name,
]
# 2. All other files must be static. Pull these from `generated/text_banks.txt`. This allows us
@ -139,6 +144,18 @@ frontier_text_banks = custom_target('frontier_text_banks',
text_bank_files += frontier_text_banks
town_map_text_bank = custom_target('town_map_text_bank',
output: town_map_text_bank_name,
command: [
'cp',
'@OUTDIR@/../town_map/town_map.json',
'@OUTDIR@'
],
depends: [ town_map_data ]
)
text_bank_files += town_map_text_bank
text_banks = custom_target('pl_msg.narc',
output: 'pl_msg.narc',
input: json_to_bin_gen.process(text_bank_files),

File diff suppressed because it is too large Load Diff

22
res/town_map/meson.build Normal file
View File

@ -0,0 +1,22 @@
town_map_env = environment()
town_map_env.append('PYTHONPATH', meson.project_build_root())
town_map_data = custom_target('town_map_data',
output: [
'town_map_blocks.dat',
'town_map_flags.dat',
'town_map.json'
],
command: [
make_town_map_data_py,
'--input', files('town_map_data.json'),
'--signpost-naix', field_board_narc[1],
'--blocks', '@OUTDIR@/town_map_blocks.dat',
'--flags', '@OUTDIR@/town_map_flags.dat',
'--text-bank', '@OUTDIR@/town_map.json'
],
env: town_map_env,
depends: [ py_consts_generators ],
)
nitrofs_files += town_map_data

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,199 @@
#!/usr/bin/env python3
import argparse
from dataclasses import dataclass
import json
import pathlib
import struct
import sys
from generated.first_arrival_to_zones import FirstArrivalToZone
from generated.hidden_locations import HiddenLocation
from generated.signpost_types import SignpostType
from generated.town_map_description_flag_types import TownMapDescriptionFlagType
from generated.vars_flags import VarFlag
ANSI_BOLD_WHITE = "\033[1;37m"
ANSI_BOLD_RED = "\033[1;31m"
ANSI_RED = "\033[31m"
ANSI_CLEAR = "\033[0m"
TOWN_MAP_BLOCK_FMT = '<HHHHHHHHHHHH'
TOWN_MAP_FLAGS_FMT = '<BBBB'
AREA_DESCRIPTION_X = 0
AREA_DESCRIPTION_Y = 0
LANDMARK_DESCRIPTION_X = 0
LANDMARK_DESCRIPTION_Y = 64
class TownMapBlock:
# block
x = 0
z = 0
signpost_type = 0
signpost_narc_member_idx = 0
area_desc_string_idx = 0xFFFF
landmark_desc_string_idx = 0xFFFF
area_desc_x = AREA_DESCRIPTION_X
area_desc_y = AREA_DESCRIPTION_Y
landmark_desc_x = LANDMARK_DESCRIPTION_X
landmark_desc_y = LANDMARK_DESCRIPTION_Y
hidden_location = 0
index = 0
#flag
area_desc_flag_type = 0
area_desc_flag = 0
landmark_desc_flag_type = 0
landmark_desc_flag = 0
def to_block_bytes(self):
return struct.pack(
TOWN_MAP_BLOCK_FMT,
self.x,
self.z,
self.signpost_type,
self.signpost_narc_member_idx,
self.area_desc_string_idx,
self.landmark_desc_string_idx,
self.area_desc_x ,
self.area_desc_y ,
self.landmark_desc_x ,
self.landmark_desc_y ,
self.hidden_location,
self.index
)
def to_flags_bytes(self):
return struct.pack(
TOWN_MAP_FLAGS_FMT,
self.area_desc_flag_type,
self.area_desc_flag,
self.landmark_desc_flag_type,
self.landmark_desc_flag
)
def snake_to_camel(string):
return ''.join(map(str.capitalize, string.split('_')))
argparser = argparse.ArgumentParser(
prog='make_town_map_blocks.py',
description='Packs the files containing the town map data (blocks and strings)'
)
argparser.add_argument('-i', '--input',
required=True,
help='Path to the input town map JSON.')
argparser.add_argument('-n', '--signpost-naix',
required=True,
help='Path to the NAIX file for the signpost graphics NARC.')
argparser.add_argument('-b', '--blocks',
required=True,
help='Path to the output town map blocks binary file.')
argparser.add_argument('-f', '--flags',
required=True,
help='Path to the output town map description flags binary file.')
argparser.add_argument('-t', '--text-bank',
required=True,
help='Path to the output town map text bank JSON.')
args = argparser.parse_args()
input_path = pathlib.Path(args.input)
signpost_naix_path = pathlib.Path(args.signpost_naix)
block_path = pathlib.Path(args.blocks)
flags_path = pathlib.Path(args.flags)
text_bank_path = pathlib.Path(args.text_bank)
try:
with open(input_path, 'r') as f:
town_map_data = json.load(f)
except json.decoder.JSONDecodeError as e:
doc_lines = e.doc.splitlines()
start_line = max(e.lineno - 2, 0)
end_line = min(e.lineno + 1, len(doc_lines))
error_lines = [f"{line_num:>4} | {line}" for line_num, line in zip(list(range(start_line + 1, end_line + 1)), doc_lines[start_line : end_line])][ : end_line - start_line]
error_line_index = e.lineno - start_line - 1
error_lines[error_line_index] = error_lines[error_line_index][ : 5] + f"{ANSI_RED}{error_lines[error_line_index][5 : ]}{ANSI_CLEAR}"
error_out = "\n".join(error_lines)
print(f"{ANSI_BOLD_WHITE}{args.pokedex}:{e.lineno}:{e.colno}: {ANSI_BOLD_RED}error: {ANSI_BOLD_WHITE}{e.msg}{ANSI_CLEAR}\n{error_out}", file=sys.stderr)
sys.exit(1)
signpost_naix = {}
with open(signpost_naix_path, 'r') as f:
for line in f:
line = line.strip().split()
if line and line[0] == '#define' and len(line) == 3:
signpost_naix[line[1]] = int(line[2])
text_bank = town_map_data["text_bank"]
with open(block_path, 'wb') as out_blocks, open(flags_path, 'wb') as out_flags:
area_indices = {}
landmark_indices = {}
num_blocks = len(town_map_data["blocks"])
out_blocks.write(struct.pack("<I", num_blocks))
out_flags.write(struct.pack("<I", num_blocks))
for i, block in enumerate(town_map_data["blocks"]):
b = TownMapBlock()
b.x, b.z = block["x"], block["z"]
b.signpost_type = SignpostType[block["signpost_type"]].value
b.signpost_narc_member_idx = signpost_naix[block["signpost_narc_member_idx"]]
if block["hidden_location"] is not None:
b.hidden_location = 1 << HiddenLocation[block["hidden_location"]].value
if (area_key := block["area"]) is not None:
if (copied := town_map_data["areas"][area_key].get("copy_description")):
area_key = copied
if area_key not in area_indices:
entry_id = "TownMap_Text_AreaDescription_" + snake_to_camel(area_key)
area_indices[area_key] = len(text_bank["messages"])
text_bank["messages"].append({ "id": entry_id, **town_map_data["areas"][area_key]["description"]})
area_indices[block["area"]] = area_indices[area_key]
b.area_desc_string_idx = area_indices[block["area"]]
if (area_condition := town_map_data["areas"][block["area"]]["condition"]) is not None:
if (area_condition["type"] == TownMapDescriptionFlagType.TOWN_MAP_DESC_FLAG_FIRST_ARRIVAL.name):
b.area_desc_flag_type = TownMapDescriptionFlagType.TOWN_MAP_DESC_FLAG_FIRST_ARRIVAL.value
b.area_desc_flag = FirstArrivalToZone[area_condition["flag"]].value
elif (area_condition["type"] == TownMapDescriptionFlagType.TOWN_MAP_DESC_FLAG_GENERAL.name):
b.area_desc_flag_type = TownMapDescriptionFlagType.TOWN_MAP_DESC_FLAG_GENERAL.value
b.area_desc_flag = VarFlag[area_condition["flag"]].value
if (landmark := block["landmark"]) is not None:
if landmark not in landmark_indices:
entry_id = "TownMap_Text_LandmarkDescription_" + snake_to_camel(landmark)
landmark_indices[landmark] = len(text_bank["messages"])
text_bank["messages"].append({ "id": entry_id, **town_map_data["landmarks"][landmark]["description"]})
b.landmark_desc_string_idx = landmark_indices[landmark]
if (landmark_condition := town_map_data["landmarks"][landmark]["condition"]) is not None:
if (landmark_condition["type"] == TownMapDescriptionFlagType.TOWN_MAP_DESC_FLAG_FIRST_ARRIVAL.name):
b.landmark_desc_flag_type = TownMapDescriptionFlagType.TOWN_MAP_DESC_FLAG_FIRST_ARRIVAL.value
b.landmark_desc_flag = FirstArrivalToZone[landmark_condition["flag"]].value
elif (landmark_condition["type"] == TownMapDescriptionFlagType.TOWN_MAP_DESC_FLAG_GENERAL.name):
b.landmark_desc_flag_type = TownMapDescriptionFlagType.TOWN_MAP_DESC_FLAG_GENERAL.value
b.landmark_desc_flag = VarFlag[landmark_condition["flag"]].value
elif town_map_data["areas"][block["area"]]["condition"] is not None:
b.landmark_desc_flag_type = b.area_desc_flag_type
b.landmark_desc_flag = b.area_desc_flag
# Blocks with an area and no landmark usually have their landmark description condition match their area's, but the Battle Frontier doesn't
elif town_map_data["areas"][block["area"]]["condition"] is not None and block["area"] != "battle_frontier":
b.landmark_desc_flag_type = b.area_desc_flag_type
b.landmark_desc_flag = b.area_desc_flag
out_blocks.write(b.to_block_bytes())
out_flags.write(b.to_flags_bytes())
with open(text_bank_path, 'w') as text_bank_file:
json.dump(text_bank, text_bank_file)

View File

@ -8,3 +8,4 @@ make_pl_growtbl_py = find_program('make_pl_growtbl.py', native: true)
make_pokedex_data_py = find_program('make_pokedex_data.py', native: true)
make_species_text_banks_py = find_program('make_species_text_banks.py', native: true)
make_pokedex_enc_platinum_py = find_program('make_pokedex_enc_platinum.py', native: true)
make_town_map_data_py = find_program('make_town_map_data.py', native: true)