Skip to content

Commit d7b30a4

Browse files
authored
Merge pull request #105 from randovania/feature/tweaks
Add API support for editing any tweak
2 parents 1d09648 + 7abcca5 commit d7b30a4

File tree

6 files changed

+85
-4
lines changed

6 files changed

+85
-4
lines changed

.github/workflows/python.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ jobs:
105105

106106
- name: Run Tests
107107
run:
108-
venv/bin/python -m pytest --cov src --cov-report=xml --durations=100
108+
venv/bin/python -m pytest --cov src --cov-report=xml --durations=100 -n 4
109109

110110
- name: codecov
111111
uses: codecov/codecov-action@v4

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ requires-python = ">=3.10"
2222
dynamic = ["version"]
2323

2424
dependencies = [
25-
"retro-data-structures>=0.23.0",
25+
"retro-data-structures>=0.28.0",
2626
"jsonschema>=4.0.0",
2727
"ppc-asm",
2828
"py_randomprime", # for Prime 1 symbols
@@ -37,6 +37,7 @@ test = [
3737
"pytest",
3838
"pytest-cov",
3939
"pytest-mock",
40+
"pytest-xdist",
4041
"pre-commit",
4142
]
4243

src/open_prime_rando/echoes/schema.json

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,44 @@
9797
"required": [
9898
"suits"
9999
]
100+
},
101+
"tweaks": {
102+
"type": "object",
103+
"description": "Allows arbitrary changes to the tweaks",
104+
"propertyNames": {
105+
"enum": [
106+
"TweakGui",
107+
"TweakTargeting",
108+
"TweakPlayerRes",
109+
"TweakPlayerControls2",
110+
"TweakParticle",
111+
"TweakGuiColors",
112+
"TweakGame",
113+
"TweakPlayer2",
114+
"TweakSlideShow",
115+
"TweakBall",
116+
"TweakAutoMapper",
117+
"TweakPlayerControls",
118+
"TweakPlayerGunMuli",
119+
"TweakPlayerGun",
120+
"TweakCameraBob",
121+
"TweakPlayer"
122+
]
123+
},
124+
"additionalProperties": {
125+
"type": "object",
126+
"description": "Mapping of full property path to new value. For nested properties, include parent property names split with .",
127+
"additionalProperties": true
128+
},
129+
"examples": [
130+
{
131+
"TweakPlayer": {
132+
"collision.ball_radius": 0.5,
133+
"dark_world.damage_per_second.di_damage": 1,
134+
"dark_world.unknown_0x19275a97": 0.5
135+
}
136+
}
137+
]
100138
}
101139
},
102140
"required": [

src/open_prime_rando/echoes_patcher.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import json
22
import logging
3+
import typing
34
from collections.abc import Callable
45
from pathlib import Path
56
from typing import TYPE_CHECKING
@@ -124,6 +125,30 @@ def apply_corrupted_memory_card_change(editor: PatcherEditor):
124125
table.set_string(name_to_index["ChoiceDeleteCorruptedFile"], "Delete Incompatible File")
125126

126127

128+
def apply_tweak_edits(editor: PatcherEditor, tweak_edits: dict[str, dict[str, typing.Any]]) -> None:
129+
"""
130+
Edits the tweaks based on the generic schema api
131+
:param editor:
132+
:param tweak_edits:
133+
:return:
134+
"""
135+
for instance in editor.tweaks.instances:
136+
properties = instance.get_properties().to_json()
137+
if properties["instance_name"] in tweak_edits:
138+
logging.debug("Editing %s", properties["instance_name"])
139+
140+
for name, value in tweak_edits[properties["instance_name"]].items():
141+
parent = properties
142+
spit_name = name.split(".")
143+
144+
for part in spit_name[:-1]:
145+
parent = parent[part]
146+
147+
parent[spit_name[-1]] = value
148+
149+
instance.set_properties(instance.type.from_json(properties))
150+
151+
127152
def patch_paks(
128153
file_provider: FileProvider,
129154
output_path: Path,
@@ -155,6 +180,10 @@ def patch_paks(
155180
apply_small_randomizations(editor, configuration["small_randomizations"])
156181
apply_corrupted_memory_card_change(editor)
157182

183+
if "tweaks" in configuration:
184+
status_update("Modifying tweaks", 0)
185+
apply_tweak_edits(editor, configuration["tweaks"])
186+
158187
status_update("Modifying areas", 0)
159188
apply_area_modifications(editor, configuration["worlds"], status_update)
160189

src/open_prime_rando/patcher_editor.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from retro_data_structures.crc import crc32
1010
from retro_data_structures.formats.mlvl import Mlvl
1111
from retro_data_structures.formats.mrea import Area
12+
from retro_data_structures.formats.ntwk import Ntwk
1213
from retro_data_structures.formats.strg import Strg
1314
from retro_data_structures.game_check import Game
1415

@@ -31,15 +32,18 @@ def _seek_and_write(self, seek: int, data: bytes):
3132

3233
class PatcherEditor(AssetManager):
3334
memory_files: dict[NameOrAssetId, BaseResource]
35+
dol: MemoryDol | None = None
36+
tweaks: Ntwk | None = None
3437

3538
def __init__(self, provider: FileProvider, game: Game):
3639
super().__init__(provider, game)
3740
self.memory_files = {}
3841

3942
if game in [Game.PRIME, Game.ECHOES]:
4043
self.dol = MemoryDol(provider.get_dol())
41-
else:
42-
self.dol = None
44+
if game == Game.ECHOES:
45+
with provider.open_binary("Standard.ntwk") as f:
46+
self.tweaks = Ntwk.parse(f.read(), game)
4347

4448
def get_file(self, path: NameOrAssetId, type_hint: type[T] = BaseResource) -> T:
4549
if path not in self.memory_files:
@@ -79,6 +83,9 @@ def save_modifications(self, output_path: Path):
7983
target_dol.parent.mkdir(exist_ok=True, parents=True)
8084
target_dol.write_bytes(self.dol.dol_file.getvalue())
8185

86+
if self.tweaks is not None:
87+
output_path.joinpath("files/Standard.ntwk").write_bytes(self.tweaks.build())
88+
8289
def add_or_replace_custom_asset(self, name: str, new_data: Resource) -> AssetId:
8390
if self.does_asset_exists(name):
8491
asset_id = self.replace_asset(name, new_data)

tests/test_files/echoes/door_lock.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3326,5 +3326,11 @@
33263326
"dark": "player2",
33273327
"light": "player3"
33283328
}
3329+
},
3330+
"tweaks": {
3331+
"TweakPlayer": {
3332+
"dark_world.damage_per_second.di_damage": 1,
3333+
"dark_world.unknown_0x19275a97": 0.5
3334+
}
33293335
}
33303336
}

0 commit comments

Comments
 (0)