Skip to content

Commit

Permalink
Merge pull request #369 from MayberryZoom/bump-meds
Browse files Browse the repository at this point in the history
 Update MEDS
  • Loading branch information
ThanatosGit authored Oct 22, 2024
2 parents 6ddb846 + 5076bd3 commit 8713592
Show file tree
Hide file tree
Showing 16 changed files with 3,857 additions and 1,027 deletions.
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ classifiers = [
]
requires-python = ">=3.10"
dependencies = [
"mercury-engine-data-structures>=0.24",
"mercury-engine-data-structures>=0.33",
"jsonschema>=4.0.0",
"json-delta>=2.0.2"
]
Expand Down
16 changes: 10 additions & 6 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 @@ -337,7 +337,11 @@ def create_shield(self, scenario: str, door: Container, shield_data: ActorData,
# make a new RandoShield by popping the lowest actor
shield = self.editor.copy_actor(scenario, door.vPos, self.SHIELD, self.get_shield_id(scenario))

self.editor.copy_actor_groups(scenario, door.sName, shield.sName)
self.editor.copy_actor_groups(
{ "actor": door.sName },
{ "actor": shield.sName},
scenario
)
shield.oActorDefLink = f"actordef:{shield_data.actordefs[0]}"
shield.vAng[1] = shield.vAng[1] if dir == "L" else -shield.vAng[1]
if (shield_data is ActorData.SHIELD_WIDE_BEAM):
Expand Down Expand Up @@ -370,7 +374,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 +418,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
6 changes: 3 additions & 3 deletions src/open_dread_rando/dread_patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -162,19 +162,19 @@ 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"]
new_actor_name = new_spawn["new_actor"]["actor"]
collision_camera_name = new_spawn["collision_camera_name"]
collision_camera_name = "eg_" + new_spawn["collision_camera_name"]
new_spawn_pos = ListContainer(
(new_spawn["location"]["x"], new_spawn["location"]["y"], new_spawn["location"]["z"]))

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
21 changes: 17 additions & 4 deletions src/open_dread_rando/files/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
"layout_uuid": {
"description": "An UUID exposed via a global lua variable",
"type": "string",
"pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
"pattern": "^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$",
"default": "00000000-0000-1111-0000-000000000000"
},
"starting_location": {
"$ref": "#/$defs/actor_reference"
Expand Down Expand Up @@ -666,8 +667,7 @@
"type": "array",
"items": {
"type": "string"
},
"default": []
}
},
"modifications": {
"description": "A list of patches to be applied",
Expand Down Expand Up @@ -835,9 +835,22 @@
"scenario": {
"$ref": "#/$defs/scenario_name"
},
"actor_layer": {
"type": "string",
"enum": [
"rEntitiesLayer",
"rSoundsLayer",
"rLightsLayer"
],
"default": "rEntitiesLayer"
},
"sublayer": {
"type": "string"
},
"layer": {
"type": "string",
"default": "default"
"default": "default",
"deprecated": true
},
"actor": {
"type": "string"
Expand Down
35 changes: 20 additions & 15 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.get("sublayer", new_reference["layer"])
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 @@ -25,19 +29,20 @@ def _modify_actor(editor: PatcherEditor, original_reference: dict[str, str], new
for modification in modifications
])

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"])
if actor_groups is not None:
for group in scenario.actor_groups_for_actor_layer(new_actor_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 All @@ -53,7 +58,7 @@ def apply_actor_patches(editor: PatcherEditor, actors_config: dict):
actor["actor"],
actor.get("new_reference", actor["actor"]),
actor["modifications"],
actor["actor_groups"],
actor.get("actor_groups"),
actor["make_copy"]
)

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"].get("layer", "default"))
actor = scenario.actors_for_sublayer(sublayer)[elevator["teleporter"]["actor"]]

try:
usable = actor.pComponents.USABLE
except AttributeError:
Expand Down
91 changes: 59 additions & 32 deletions src/open_dread_rando/patcher_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@
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

from open_dread_rando.pickups.map_icons import MapIconEditor

Expand All @@ -26,7 +29,7 @@ class PatcherEditor(FileTreeEditor):
map_icon_editor: MapIconEditor

def __init__(self, root: Path):
super().__init__(root, target_game=Game.DREAD)
super().__init__(ExtractedRomFs(root), target_game=Game.DREAD)
self.memory_files = {}
self.map_icon_editor = MapIconEditor(self)

Expand Down Expand Up @@ -57,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 @@ -81,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 @@ -120,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, str, Container]]:
"""
Returns a list of actors with given actordef in the scenario
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 @@ -155,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) -> str:
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 8713592

Please sign in to comment.