Skip to content

Commit

Permalink
Add API support for editing any tweak
Browse files Browse the repository at this point in the history
  • Loading branch information
henriquegemignani committed Oct 11, 2024
1 parent 1d09648 commit 329d45d
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 4 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ requires-python = ">=3.10"
dynamic = ["version"]

dependencies = [
"retro-data-structures>=0.23.0",
"retro-data-structures>=0.28.0",
"jsonschema>=4.0.0",
"ppc-asm",
"py_randomprime", # for Prime 1 symbols
Expand Down
37 changes: 37 additions & 0 deletions src/open_prime_rando/echoes/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,43 @@
"required": [
"suits"
]
},
"tweaks": {
"type": "object",
"description": "Allows arbitrary changes to the tweaks",
"propertyNames": {
"enum": [
"TweakGui",
"TweakTargeting",
"TweakPlayerRes",
"TweakPlayerControls2",
"TweakParticle",
"TweakGuiColors",
"TweakGame",
"TweakPlayer2",
"TweakSlideShow",
"TweakBall",
"TweakAutoMapper",
"TweakPlayerControls",
"TweakPlayerGunMuli",
"TweakPlayerGun",
"TweakCameraBob",
"TweakPlayer"
]
},
"additionalProperties": {
"type": "object",
"description": "Mapping of full property path to new value. For nested properties, include parent property names split with .",
"additionalProperties": true
},
"examples": [
{
"TweakPlayer": {
"collision.ball_radius": 0.5,
"dark_world.damage_per_second.di_damage": 1
}
}
]
}
},
"required": [
Expand Down
29 changes: 29 additions & 0 deletions src/open_prime_rando/echoes_patcher.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
import logging
import typing
from collections.abc import Callable
from pathlib import Path
from typing import TYPE_CHECKING
Expand Down Expand Up @@ -124,6 +125,30 @@ def apply_corrupted_memory_card_change(editor: PatcherEditor):
table.set_string(name_to_index["ChoiceDeleteCorruptedFile"], "Delete Incompatible File")


def apply_tweak_edits(editor: PatcherEditor, tweak_edits: dict[str, dict[str, typing.Any]]) -> None:
"""
Edits the tweaks based on the generic schema api
:param editor:
:param tweak_edits:
:return:
"""
for instance in editor.tweaks.instances:
properties = instance.get_properties().to_json()
if properties["instance_name"] in tweak_edits:
logging.debug("Editing %s", properties["instance_name"])

for name, value in tweak_edits[properties["instance_name"]].items():
parent = properties
spit_name = name.split(".")

for part in spit_name[:-1]:
parent = parent[part]

parent[spit_name[-1]] = value

instance.set_properties(instance.type.from_json(properties))


def patch_paks(
file_provider: FileProvider,
output_path: Path,
Expand Down Expand Up @@ -155,6 +180,10 @@ def patch_paks(
apply_small_randomizations(editor, configuration["small_randomizations"])
apply_corrupted_memory_card_change(editor)

if "tweaks" in configuration:
status_update("Modifying tweaks", 0)
apply_tweak_edits(editor, configuration["tweaks"])

status_update("Modifying areas", 0)
apply_area_modifications(editor, configuration["worlds"], status_update)

Expand Down
11 changes: 9 additions & 2 deletions src/open_prime_rando/patcher_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from retro_data_structures.crc import crc32
from retro_data_structures.formats.mlvl import Mlvl
from retro_data_structures.formats.mrea import Area
from retro_data_structures.formats.ntwk import Ntwk
from retro_data_structures.formats.strg import Strg
from retro_data_structures.game_check import Game

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

class PatcherEditor(AssetManager):
memory_files: dict[NameOrAssetId, BaseResource]
dol: MemoryDol | None = None
tweaks: Ntwk | None = None

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

if game in [Game.PRIME, Game.ECHOES]:
self.dol = MemoryDol(provider.get_dol())
else:
self.dol = None
if game == Game.ECHOES:
with provider.open_binary("Standard.ntwk") as f:
self.tweaks = Ntwk.parse(f.read(), game)

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

if self.tweaks is not None:
output_path.joinpath("files/Standard.ntwk").write_bytes(self.tweaks.build())

def add_or_replace_custom_asset(self, name: str, new_data: Resource) -> AssetId:
if self.does_asset_exists(name):
asset_id = self.replace_asset(name, new_data)
Expand Down
2 changes: 1 addition & 1 deletion tests/echoes/test_full_patch.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,4 @@ def test_pal_paks(pal_prime2_iso_provider, tmp_path, test_files_dir):
output_path=output_path,
configuration=configuration,
)
assert len(list(output_path.rglob("*.pak"))) == 11
assert len(list(output_path.rglob("*.pak"))) == 10
5 changes: 5 additions & 0 deletions tests/test_files/echoes/door_lock.json
Original file line number Diff line number Diff line change
Expand Up @@ -3326,5 +3326,10 @@
"dark": "player2",
"light": "player3"
}
},
"tweaks": {
"TweakPlayer": {
"dark_world.damage_per_second.di_damage": 1
}
}
}

0 comments on commit 329d45d

Please sign in to comment.