Skip to content

Commit

Permalink
Update code for new MEDS version
Browse files Browse the repository at this point in the history
  • Loading branch information
MayberryZoom committed Oct 17, 2024
1 parent 60b03c2 commit 3404309
Show file tree
Hide file tree
Showing 12 changed files with 963 additions and 968 deletions.
10 changes: 5 additions & 5 deletions src/open_dread_rando/door_locks/door_patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from open_dread_rando.patcher_editor import PatcherEditor

# copied from existing entity, so we don't have to make a whole shield
_EXAMPLE_SHIELD = {"scenario": "s010_cave", "layer": "default", "actor": "Door003_missileShield"}
_EXAMPLE_SHIELD = {"scenario": "s010_cave", "sublayer": "default", "actor": "Door003_missileShield"}


class MinimapIconData(Enum):
Expand Down Expand Up @@ -370,7 +370,7 @@ def rename_all_shields(self):
# we have to cache doors that have shields here and rename them outside the loop,
# as otherwise it will rename actors in the actor list and confuse the program.
shielded_doors = []
for layer_name, actor_name, actor in list(brfld.all_actors()):
for sublayer_name, actor_name, actor in list(brfld.all_actors_in_actor_layer()):

# this is the door added to the Artaria CU.
# For some reason is_door crashes on this so we add a check here.
Expand Down Expand Up @@ -414,9 +414,9 @@ def rename_shields(self, door: Container, scenario: str):

# make new actor, copy its groups, delete it
brfld = self.editor.get_scenario(scenario)
brfld.actors_for_layer('default')[new_id] = shieldActor
self.editor.copy_actor_groups(scenario, old_sName, new_id)
brfld.actors_for_layer('default').pop(old_sName)
brfld.actors_for_sublayer('default')[new_id] = shieldActor
self.editor.copy_actor_groups({ "actor": old_sName }, { "actor": new_id }, scenario)
brfld.actors_for_sublayer('default').pop(old_sName)

# update the minimap entry as well
mapBlockages = self.editor.get_scenario_map(scenario).raw.Root.mapBlockages
Expand Down
4 changes: 2 additions & 2 deletions src/open_dread_rando/dread_patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def patch_doors(editor: PatcherEditor, doors_config: list[dict], shield_model_co

def patch_spawn_points(editor: PatcherEditor, spawn_config: list[dict]):
# create custom spawn point
_EXAMPLE_SP = {"scenario": "s010_cave", "layer": "default", "actor": "StartPoint0"}
_EXAMPLE_SP = {"scenario": "s010_cave", "actor": "StartPoint0"}
base_actor = editor.resolve_actor_reference(_EXAMPLE_SP)
for new_spawn in spawn_config:
scenario_name = new_spawn["new_actor"]["scenario"]
Expand All @@ -174,7 +174,7 @@ def patch_spawn_points(editor: PatcherEditor, spawn_config: list[dict]):
scenario = editor.get_scenario(scenario_name)

editor.copy_actor(scenario_name, new_spawn_pos, base_actor, new_actor_name)
scenario.add_actor_to_entity_groups(collision_camera_name, new_actor_name)
scenario.add_actor_to_actor_groups(collision_camera_name, new_actor_name)


def add_custom_files(editor: PatcherEditor):
Expand Down
18 changes: 17 additions & 1 deletion src/open_dread_rando/files/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -835,9 +835,16 @@
"scenario": {
"$ref": "#/$defs/scenario_name"
},
"actor_layer": {
"$ref": "#/$defs/actor_layer"
},
"sublayer": {
"type": "string"
},
"layer": {
"type": "string",
"default": "default"
"default": "default",
"deprecated": true
},
"actor": {
"type": "string"
Expand Down Expand Up @@ -877,6 +884,15 @@
"s090_skybase"
]
},
"actor_layer": {
"type": "string",
"enum": [
"rEntitiesLayer",
"rSoundsLayer",
"rLightsLayer"
],
"default": "rEntitiesLayer"
},
"constant_damage_value": {
"default": null,
"anyOf": [
Expand Down
29 changes: 17 additions & 12 deletions src/open_dread_rando/misc_patches/actor_patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@ def _modify_actor(editor: PatcherEditor, original_reference: dict[str, str], new
modifications: list[dict], actor_groups: list[str], make_copy: bool = False):
is_in_place = original_reference == new_reference
scenario = editor.get_scenario(new_reference["scenario"])
new_actor_name = new_reference["actor"]
new_sublayer = new_reference["sublayer"]
new_actor_layer = new_reference["actor_layer"]

if is_in_place:
actor = editor.resolve_actor_reference(original_reference)
make_copy = False
else:
actor = copy.deepcopy(editor.resolve_actor_reference(original_reference))
actor.sName = new_reference["actor"]
scenario.actors_for_layer(new_reference["layer"])[new_reference["actor"]] = actor
actor.sName = new_actor_name
actors_in_sublayer = scenario.actors_for_sublayer(new_sublayer, new_actor_layer)
actors_in_sublayer[new_actor_name] = actor

if modifications:
json_delta.patch(actor, [
Expand All @@ -27,17 +31,18 @@ def _modify_actor(editor: PatcherEditor, original_reference: dict[str, str], new

if actor_groups:
for group in scenario.all_actor_groups():
if (group in actor_groups):
scenario.add_actor_to_group(group, new_reference["actor"], new_reference["layer"])
else:
scenario.remove_actor_from_group(group, new_reference["actor"], new_reference["layer"])

elif not is_in_place and original_reference["scenario"] == new_reference["scenario"]:
for group in scenario.all_actor_groups():
if (scenario.is_actor_in_group(group, original_reference["actor"], original_reference["layer"])):
scenario.add_actor_to_group(group, new_reference["actor"], new_reference["layer"])
else:
scenario.remove_actor_from_group(group, new_reference["actor"], new_reference["layer"])
is_actor_in_group = scenario.is_actor_in_group(group, new_actor_name, new_sublayer, new_actor_layer)

if group in actor_groups and not is_actor_in_group:
scenario.add_actor_to_group(group, new_actor_name, new_sublayer, new_actor_layer)
elif is_actor_in_group:
scenario.remove_actor_from_group(group, new_actor_name, new_sublayer, new_actor_layer)

elif (not is_in_place and
original_reference["scenario"] == new_reference["scenario"] and
original_reference["actor_layer"] == new_actor_layer):
editor.copy_actor_groups(original_reference, new_reference, scenario.name)

if not is_in_place and not make_copy:
editor.remove_entity(original_reference, None)
Expand Down
6 changes: 4 additions & 2 deletions src/open_dread_rando/misc_patches/elevator.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,10 @@ def get_icon(cls, usable: dict):
}

def _get_type_and_usable(editor: PatcherEditor, elevator: dict) -> tuple[TransporterType, dict]:
level = editor.get_scenario(elevator["teleporter"]["scenario"])
actor = level.actors_for_layer(elevator["teleporter"]["layer"])[elevator["teleporter"]["actor"]]
scenario = editor.get_scenario(elevator["teleporter"]["scenario"])
sublayer = elevator["teleporter"].get("sublayer", elevator["teleporter"]["layer"])
actor = scenario.actors_for_sublayer(sublayer)[elevator["teleporter"]["actor"]]

try:
usable = actor.pComponents.USABLE
except AttributeError:
Expand Down
88 changes: 57 additions & 31 deletions src/open_dread_rando/patcher_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
from pathlib import Path

from construct import Container
from mercury_engine_data_structures.base_resource import BaseResource
from mercury_engine_data_structures.file_tree_editor import FileTreeEditor
from mercury_engine_data_structures.formats import ALL_FORMATS, BaseResource, Bmmap, Brfld, Brsa
from mercury_engine_data_structures.formats import ALL_FORMATS, Bmmap, Brfld, Brsa
from mercury_engine_data_structures.formats.brfld import ActorLayer
from mercury_engine_data_structures.game_check import Game
from mercury_engine_data_structures.romfs import ExtractedRomFs

Expand Down Expand Up @@ -58,8 +60,9 @@ def ensure_present_in_scenario(self, scenario: str, asset):

def resolve_actor_reference(self, ref: dict) -> Container:
scenario = self.get_scenario(ref["scenario"])
layer = ref.get("layer", "default")
return scenario.actors_for_layer(layer)[ref["actor"]]
actor_layer = ActorLayer(ref.get("actor_layer", "rEntitiesLayer"))
sublayer = ref.get("sublayer", ref.get("layer", "default"))
return scenario.actors_for_sublayer(sublayer, actor_layer)[ref["actor"]]

def flush_modified_assets(self):
for name, resource in self.memory_files.items():
Expand All @@ -82,29 +85,41 @@ def save_modified_saves_to(self, debug_path: Path):

def remove_entity(self, reference: dict, map_category: typing.Optional[str]):
scenario = self.get_scenario(reference["scenario"])
layer = reference.get("layer", "default")
actor_layer = ActorLayer(reference.get("actor_layer", "rEntitiesLayer"))
sublayer = reference.get("sublayer", reference.get("layer", "default"))
actor_name = reference["actor"]

for group_name in scenario.all_actor_groups():
scenario.remove_actor_from_group(group_name, actor_name, layer)
for group_name in scenario.actor_groups_for_actor_layer(actor_layer):
if scenario.is_actor_in_group(group_name, actor_name, sublayer, actor_layer):
scenario.remove_actor_from_group(group_name, actor_name, sublayer, actor_layer)

scenario.actors_for_layer(layer).pop(actor_name)
scenario.actors_for_sublayer(sublayer, actor_layer).pop(actor_name)
if map_category is not None:
self.get_scenario_map(reference["scenario"]).raw.Root[map_category].pop(actor_name)

def copy_actor_groups(self, scenario_name: str, base_actor_name: str, new_actor_name: str):
def copy_actor_groups(self, base_reference: dict, new_reference: dict, scenario_name: str,
actor_layer: ActorLayer = ActorLayer.ENTITIES):
"""
Copies a base actor's groups to a new actor's groups. Both actors must be in the same scenario.
Copies a base actor's groups to a new actor's groups. Both actors must be in the same scenario and actor layer.
param baseRef: the actor that you are copying groups from
param newRef: the actor that will have the same actor groups as baseRef
param base_reference: a reference to the actor to copy the groups from
param new_reference: a reference to the actor to copy the groups to
"""
scenario = self.get_scenario(scenario_name)
for group_name in scenario.all_actor_groups():
if (scenario.is_actor_in_group(group_name, base_actor_name)):
scenario.add_actor_to_group(group_name, new_actor_name)
else:
scenario.remove_actor_from_group(group_name, new_actor_name)

base_sublayer = base_reference.get("sublayer", base_reference.get("layer", "default"))
base_actor_name = base_reference["actor"]
new_sublayer = new_reference.get("sublayer", new_reference.get("layer", base_sublayer))
new_actor_name = new_reference["actor"]

for group_name in scenario.actor_groups_for_actor_layer(actor_layer):
base_actor_in_group = scenario.is_actor_in_group(group_name, base_actor_name, base_sublayer, actor_layer)
new_actor_in_group = scenario.is_actor_in_group(group_name, new_actor_name, new_sublayer, actor_layer)

if base_actor_in_group and not new_actor_in_group:
scenario.add_actor_to_group(group_name, new_actor_name, new_sublayer, actor_layer)
elif not base_actor_in_group and new_actor_in_group:
scenario.remove_actor_from_group(group_name, new_actor_name, new_sublayer, actor_layer)

def copy_actor(self, scenario: str, coords, templateActor: Container, newName: str, offset: tuple = (0, 0, 0)):
"""
Expand All @@ -121,27 +136,36 @@ def copy_actor(self, scenario: str, coords, templateActor: Container, newName: s
newActor = copy.deepcopy(templateActor)
newActor.sName = newName
currentScenario = self.get_scenario(scenario)
currentScenario.actors_for_layer('default')[newActor.sName] = newActor
currentScenario.actors_for_sublayer("default")[newActor.sName] = newActor
newActor.vPos = [float(c) + offset for c, offset in zip(coords, offset)]

return newActor

def find_type_of_actor(self, scenarioStr: str, actordef: str, layer: str = "default"):
def find_type_of_actor(self, scenario_name: str, actordef: str,
actor_layer: ActorLayer = ActorLayer.ENTITIES) -> list[tuple[str, Container]]:
"""
Returns a list of actors with given actordef in the scenario
Iterably get every actor with given actordef in a scenario
param scenario: the scenario string
param layer: an optional layer to filter, standard layer is default
param scenario: the name of the scenario
param actordef: the actor definition (bmsad) to filter for
returns: a list of all actors that match the criteria
param sublayer: if provided, the sublayer to search in, otherwise default sublayer
param actor_layer: if provided, the actor layer to search in, otherwise entities layer
returns: for each actor that matches the criteria: actor name, actor
"""
scenario = self.get_scenario(scenarioStr)
actors_on_layer = scenario.actors_for_layer(layer)
scenario = self.get_scenario(scenario_name)
filtered = []
for actor in actors_on_layer:
a = self.resolve_actor_reference({"actor": actor, "layer": layer, "scenario": scenarioStr})

for sublayer, actor_name, actor in scenario.all_actors_in_actor_layer(actor_layer):
a = self.resolve_actor_reference({
"actor": actor_name,
"sublayer": sublayer,
"actor_layer": actor_layer,
"scenario": scenario_name
})

if a.oActorDefLink.split(':')[1] == actordef:
filtered.append(actor)
filtered.append((sublayer, actor_name, actor))

return filtered

def reference_for_link(self, link: str, scenario: str) -> dict:
Expand All @@ -156,17 +180,19 @@ def reference_for_link(self, link: str, scenario: str) -> dict:
if len(split_link) != 7:
raise ValueError(f"Expected 7 components in {link}, got {len(split_link)}")

layer = split_link[4]
actor_layer = split_link[2]
sublayer = split_link[4]
actor = split_link[6]

return {
"scenario": scenario,
"layer": layer,
"actor_layer": actor_layer,
"sublayer": sublayer,
"actor": actor,
}

def build_link(self, sname: str, layer: str = "default"):
return f"Root:pScenario:rEntitiesLayer:dctSublayers:{layer}:dctActors:{sname}"
def build_link(self, sname: str, sublayer: str = "default", actor_layer: ActorLayer = ActorLayer.ENTITIES):
return f"Root:pScenario:{actor_layer.value}:dctSublayers:{sublayer}:dctActors:{sname}"

def get_asset_names_in_folder(self, folder: str) -> typing.Iterator[str]:
yield from (name for name in self._name_for_asset_id.values() if name.startswith(folder))
Expand Down
Loading

0 comments on commit 3404309

Please sign in to comment.