Skip to content

Commit

Permalink
Merge pull request #105 from randovania/feature/tweaks
Browse files Browse the repository at this point in the history
Add API support for editing any tweak
  • Loading branch information
duncathan authored Oct 11, 2024
2 parents 1d09648 + 7abcca5 commit d7b30a4
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/python.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ jobs:

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

- name: codecov
uses: codecov/codecov-action@v4
Expand Down
3 changes: 2 additions & 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 All @@ -37,6 +37,7 @@ test = [
"pytest",
"pytest-cov",
"pytest-mock",
"pytest-xdist",
"pre-commit",
]

Expand Down
38 changes: 38 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,44 @@
"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,
"dark_world.unknown_0x19275a97": 0.5
}
}
]
}
},
"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
6 changes: 6 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,11 @@
"dark": "player2",
"light": "player3"
}
},
"tweaks": {
"TweakPlayer": {
"dark_world.damage_per_second.di_damage": 1,
"dark_world.unknown_0x19275a97": 0.5
}
}
}

0 comments on commit d7b30a4

Please sign in to comment.