Skip to content

Commit

Permalink
Merge branch 'main' into offworld
Browse files Browse the repository at this point in the history
  • Loading branch information
dyceron committed Jun 23, 2024
2 parents 8ff1d73 + 5c5f5f0 commit 34c1aef
Show file tree
Hide file tree
Showing 15 changed files with 393 additions and 71 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.4.7
rev: v0.4.9
hooks:
- id: ruff
args: [ --fix, --exit-non-zero-on-fix ]
2 changes: 1 addition & 1 deletion src/open_samus_returns_rando/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from .patch_util import patch_with_status_update


def patch(input_path: Path, input_exheader: Path, output_path: Path, configuration: dict) -> None:
def patch(input_path: Path, input_exheader: Path | None, output_path: Path, configuration: dict) -> None:
from .samus_returns_patcher import patch_extracted
return patch_extracted(input_path, input_exheader, output_path, configuration)

Expand Down
27 changes: 27 additions & 0 deletions src/open_samus_returns_rando/files/custom/sprites_texturehud.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,32 @@
Game.ImportLibrary("gui/scripts/sprites_texturehud_original.lua")

GUI.AddSpriteSheetItem("item_powerup", "gui/textures/texturehud.bctex", {
TextureWidth = "512",
TextureHeight = "256",
UPixelOffset = "144",
VPixelOffset = "24",
UPixelScale = "8",
VPixelScale = "8",
AngleOffset = "0"
})
GUI.AddSpriteSheetItem("item_offworld", "gui/textures/texturehud.bctex", {
TextureWidth = "512",
TextureHeight = "256",
UPixelOffset = "104",
VPixelOffset = "40",
UPixelScale = "8",
VPixelScale = "8",
AngleOffset = "0"
})
GUI.AddSpriteSheetItem("item_adn", "gui/textures/texturehud.bctex", {
TextureWidth = "512",
TextureHeight = "256",
UPixelOffset = "112",
VPixelOffset = "40",
UPixelScale = "8",
VPixelScale = "8",
AngleOffset = "0"
})
GUI.AddSpriteSheetItem("dooriceleft", "gui/textures/texturehud.bctex", {
TextureWidth = "512",
TextureHeight = "256",
Expand Down
6 changes: 5 additions & 1 deletion src/open_samus_returns_rando/files/levels/s100_area10.lua
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,11 @@ function s100_area10.OnLarva_010_Generated(_ARG_0_, _ARG_1_)
end
end
function s100_area10.OnMetroidDead()
-- Disable the intro and the camera change triggers if a Metroid has been defeated, which means that Reverse Area 8 should be enabled
Game.DisableTrigger("TG_Intro_Larva")
if Game.GetEntity("TG_ChangeCamera_IntroLarva") ~= nil then
Game.GetEntity("TG_ChangeCamera_IntroLarva").TRIGGER:DisableTrigger()
end
if Blackboard.GetProp("DEFEATED_ENEMIES", "Metroid") ~= nil and s100_area10.iNumMetroids == Blackboard.GetProp("DEFEATED_ENEMIES", "Metroid") then
if Game.GetEntity("LE_ValveQueen") ~= nil then
Game.GetEntity("LE_ValveQueen").MODELUPDATER:SetMeshVisible("Valve", false)
Expand All @@ -217,7 +222,6 @@ function s100_area10.OnMetroidDead()
Scenario.WriteToBlackboard("QueenDiscovered", "b", true)
Game.AddSF(3.5, "s100_area10.ScheduledQueenRoar", "")
for _FORV_6_, _FORV_7_ in pairs({
"collision_camera_008",
"collision_camera_013",
"collision_camera_014",
"collision_camera_015",
Expand Down
Binary file not shown.
31 changes: 31 additions & 0 deletions src/open_samus_returns_rando/files/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,24 @@
},
"additionalProperties": false
},
"constant_environment_damage": {
"description": "Each property, when not null, will change the damage from that type to be that much per second",
"default": {},
"type": "object",
"properties": {
"heat": {
"title": "Heated Rooms",
"$ref": "#/$defs/constant_damage_value",
"default": null
},
"lava": {
"title": "Lava",
"$ref": "#/$defs/constant_damage_value",
"default": null
}
},
"additionalProperties": false
},
"debug_spawn_points": {
"type": "array",
"description": "Creates new spawn points for debugging",
Expand Down Expand Up @@ -699,6 +717,19 @@
"scenario",
"spawngroup"
]
},
"constant_damage_value": {
"default": null,
"anyOf": [
{
"type": "number",
"minimum": 0,
"maximum": 1000
},
{
"type": "null"
}
]
}
}
}
16 changes: 9 additions & 7 deletions src/open_samus_returns_rando/lua_editor.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,13 +215,15 @@ def _create_custom_init(self, editor: PatcherEditor, configuration: dict) -> str
max_life += etanks * energy_per_tank

# use _MAX if main is unlocked to unlock the ammo too
# FIXME: These checks are kinda wrong if you use something like `"ITEM_WEAPON_MISSILE_LAUNCHER": 0`
if "ITEM_WEAPON_MISSILE_LAUNCHER" in inventory and "ITEM_MISSILE_TANKS" in inventory:
inventory["ITEM_WEAPON_MISSILE_MAX"] = inventory.pop("ITEM_MISSILE_TANKS")
if "ITEM_WEAPON_SUPER_MISSILE" in inventory and "ITEM_SUPER_MISSILE_TANKS" in inventory:
inventory["ITEM_WEAPON_SUPER_MISSILE_MAX"] = inventory.pop("ITEM_SUPER_MISSILE_TANKS")
if "ITEM_WEAPON_POWER_BOMB" in inventory and "ITEM_POWER_BOMB_TANKS" in inventory:
inventory["ITEM_WEAPON_POWER_BOMB_MAX"] = inventory.pop("ITEM_POWER_BOMB_TANKS")
LAUNCHER_TANK_MAPPING = [
("ITEM_WEAPON_MISSILE_LAUNCHER", "ITEM_MISSILE_TANKS"),
("ITEM_WEAPON_SUPER_MISSILE", "ITEM_SUPER_MISSILE_TANKS"),
("ITEM_WEAPON_POWER_BOMB", "ITEM_POWER_BOMB_TANKS"),
]
for launcher, tanks in LAUNCHER_TANK_MAPPING:
if launcher in inventory and inventory[launcher] > 0 and tanks in inventory:
ammo_max = "ITEM_WEAPON_" + tanks[5:].replace("TANKS", "MAX")
inventory[ammo_max] = inventory.pop(tanks)

# These fields are required to start the game
final_inventory = {
Expand Down
6 changes: 4 additions & 2 deletions src/open_samus_returns_rando/multiworld_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ def get_lua_for_item(progression: list[list[dict[str, str | int]]]) -> str:
)


def create_exefs_patches(out_code: Path, out_exheader: Path, input_exheader: Path, enabled: bool, region: str) -> None:
if not enabled:
def create_exefs_patches(
out_code: Path, out_exheader: Path, input_exheader: Path | None, enabled: bool, region: str
) -> None:
if not enabled or input_exheader is None:
return

import shutil
Expand Down
2 changes: 1 addition & 1 deletion src/open_samus_returns_rando/patch_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from open_samus_returns_rando.logger import LOG


def patch_with_status_update(input_path: Path, input_exheader: Path, output_path: Path, configuration: dict,
def patch_with_status_update(input_path: Path, input_exheader: Path | None, output_path: Path, configuration: dict,
status_update: typing.Callable[[float, str], None]) -> None:
from open_samus_returns_rando.samus_returns_patcher import patch_extracted
# messages depends on the layout but it is a good approximation
Expand Down
47 changes: 46 additions & 1 deletion src/open_samus_returns_rando/pickups/pickup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
from enum import Enum

from construct import Container, ListContainer
from mercury_engine_data_structures.formats import Bmsad, Lua
from mercury_engine_data_structures.formats import Bmsad, Bmsmsd, Lua
from mercury_engine_data_structures.formats.bmsmsd import TileType
from open_samus_returns_rando.constants import get_package_name
from open_samus_returns_rando.files import templates_path
from open_samus_returns_rando.logger import LOG
Expand Down Expand Up @@ -288,13 +289,57 @@ def patch(self, editor: PatcherEditor) -> None:

script_class.ensure_files(editor)

self.patch_minimap(editor, scenario_name, actor_name)

# Dependencies
for level_pkg in pkgs_for_level:
for model_name in model_names:
model_data = get_data(model_name)
for dep in model_data.dependencies:
editor.ensure_present(get_package_name(level_pkg, dep), dep)


def patch_minimap(self, editor: PatcherEditor, scenario_name: str, actor_name: str) -> None:
if scenario_name != "s110_surfaceb":
scenario_minimap = editor.get_file(f"gui/minimaps/c10_samus/{scenario_name}.bmsmsd", Bmsmsd)
else:
scenario_minimap = editor.get_file("gui/minimaps/c10_samus/s000_surface.bmsmsd", Bmsmsd)
tiles = scenario_minimap.raw["tiles"]

# find item tile
tile_for_item = [
x for x in tiles if len(x.icons) and next((y for y in x.icons if actor_name in y.actor_name), 0)
]

if len(tile_for_item) == 1:
pickup_tile_icon = next((y for y in tile_for_item[0].icons if "item" in y.icon), None)

if not pickup_tile_icon:
raise ValueError("No icon found")

pickup_model = self.pickup["model"][0]

# Custom blocks are no longer attached to the pickup, so make all hidden pickups consistent and generic
if pickup_tile_icon.icon_priority == "HIDDEN_ITEM" or (
# Boss pickups are now always visible on the map without fighting them, so make icons generic
actor_name in {"LE_PowerUp_Springball", "LE_PowerUp_Powerbomb", "LE_Baby_Hatchling"}
):
if tile_for_item[0]["tile_type"] == TileType.HEAT:
pickup_tile_icon.icon = "itemenabledheat"
else:
pickup_tile_icon.icon = "itemenabled"
# Powerups and Nothing items use a "custom" itemsphere so they update on the map
elif "itemsphere" in pickup_model or "powerup" in pickup_model or "babyhatchling" in pickup_model:
pickup_tile_icon.icon = "item_powerup"
# DNA uses a custom icon
elif "adn" in pickup_model:
pickup_tile_icon.icon = "item_adn"
else:
# Tanks and offworld items just use the icon with the same name as the pickup_model
pickup_tile_icon.icon = pickup_model



def get_scenario(self) -> str:
return self.pickup["pickup_actor"]["scenario"]

Expand Down
14 changes: 7 additions & 7 deletions src/open_samus_returns_rando/samus_returns_patcher.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@
from open_samus_returns_rando.specific_patches import cosmetic_patches, game_patches, tunable_patches
from open_samus_returns_rando.specific_patches.chozo_seal_patches import patch_chozo_seals
from open_samus_returns_rando.specific_patches.door_patches import patch_doors
from open_samus_returns_rando.specific_patches.environmental_damage import apply_constant_damage
from open_samus_returns_rando.specific_patches.heat_room_patches import patch_heat_rooms
from open_samus_returns_rando.specific_patches.hint_patches import patch_hints
from open_samus_returns_rando.specific_patches.map_icons import patch_tiles
from open_samus_returns_rando.specific_patches.metroid_patches import patch_metroids
from open_samus_returns_rando.specific_patches.static_fixes import apply_static_fixes
from open_samus_returns_rando.validator_with_default import DefaultValidatingDraft7Validator
Expand Down Expand Up @@ -66,7 +66,7 @@ def validate(configuration: dict, input_exheader: Path | None) -> None:



def patch_extracted(input_path: Path, input_exheader: Path, output_path: Path, configuration: dict) -> None:
def patch_extracted(input_path: Path, input_exheader: Path | None, output_path: Path, configuration: dict) -> None:
LOG.info("Will patch files from %s", input_path)

validate(configuration, input_exheader)
Expand All @@ -80,9 +80,6 @@ def patch_extracted(input_path: Path, input_exheader: Path, output_path: Path, c
# Apply fixes
apply_static_fixes(editor)

# Update Map Icons
patch_tiles(editor)

# Custom pickups
patch_custom_pickups(editor)
debug_custom_pickups(editor, configuration["debug_custom_pickups"])
Expand All @@ -97,6 +94,9 @@ def patch_extracted(input_path: Path, input_exheader: Path, output_path: Path, c
# Fix unheated heat rooms
patch_heat_rooms(editor)

# Environmental Damage
apply_constant_damage(editor, configuration["constant_environment_damage"])

# Patch door types and make shields on both sides
patch_doors(editor, configuration["door_patches"], configuration["custom_doors"])

Expand Down Expand Up @@ -133,8 +133,8 @@ def patch_extracted(input_path: Path, input_exheader: Path, output_path: Path, c
out_code = output_path.joinpath("code.bps")
out_exheader = output_path.joinpath("exheader.bin")
shutil.rmtree(out_romfs, ignore_errors=True)
shutil.rmtree(out_code, ignore_errors=True)
shutil.rmtree(out_exheader, ignore_errors=True)
out_code.unlink(missing_ok=True)
out_exheader.unlink(missing_ok=True)
# this is just to clean up old version
shutil.rmtree(out_exefs, ignore_errors=True)

Expand Down
35 changes: 35 additions & 0 deletions src/open_samus_returns_rando/specific_patches/door_patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,47 @@ def _patch_one_way_doors(editor: PatcherEditor) -> None:
properties.components[0]["arguments"][2]["value"] = False


def _patch_tiles(editor: PatcherEditor) -> None:
SCENARIO_TO_DOORS = {
"s000_surface": ["Door004", "Door011"],
"s010_area1": ["Door002", "Door004", "Door012", "Door016"],
"s025_area2b": ["Door008"],
"s028_area2c": ["Door001", "Door007"],
"s030_area3": ["Door002", "Door005", "Door006", "Door008"],
"s040_area4": ["Door001", "Door006", "Door014"],
"s050_area5": ["Door005"],
"s060_area6": ["Door001"],
"s067_area6c": ["Door005", "Door006", "Door009"],
"s090_area9": ["Door012"],
}

for scenario, _ in SCENARIO_TO_DOORS.items():
scenario_file = editor.get_file(f"gui/minimaps/c10_samus/{scenario}.bmsmsd", Bmsmsd)
tiles = scenario_file.raw["tiles"]
doors = SCENARIO_TO_DOORS.get(scenario, [])

for tile_idx in range(len(tiles)):
icons = tiles[tile_idx]["icons"]
if len(icons) != 0:
door_tile = icons[0] if len(icons) == 1 else icons[1]
for door in doors:
if door in door_tile["actor_name"]:
door_tile["clear_condition"] = ""
if "closed" in door_tile["icon"] or "charge" in door_tile["icon"]:
if "left" in door_tile["icon"]:
door_tile["icon"] = "doorpowerleft"
else:
door_tile["icon"] = "doorpowerright"



def _static_door_patches(editor: PatcherEditor) -> None:
_patch_one_way_doors(editor)
_patch_missile_covers(editor)
_patch_beam_bmsads(editor)
_patch_beam_covers(editor)
_patch_charge_doors(editor)
_patch_tiles(editor)


class ActorData(Enum):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from open_samus_returns_rando.patcher_editor import PatcherEditor
from open_samus_returns_rando.specific_patches.environmental_damage_sources import ALL_DAMAGE_ROOM_ACTORS


def apply_constant_damage(editor: PatcherEditor, configuration: dict) -> None:
for reference in ALL_DAMAGE_ROOM_ACTORS:
actor = editor.resolve_actor_reference(reference)

if actor["components"][1]["arguments"][26]["value"] == "HEAT":
config_field = "heat"
elif actor["components"][1]["arguments"][26]["value"] == "LAVA":
config_field = "lava"
else:
raise ValueError(f"{reference} does not have a valid actorDef for environmental damage")

if configuration[config_field] is None:
continue

# Uses the default time per tick of 0.5
damage = configuration[config_field] / 2

# Damage per tick
actor["components"][1]["arguments"][19]["value"] = damage
# Damage Scale
actor["components"][1]["arguments"][28]["value"] = damage
# Damage of first tick
actor["components"][2]["arguments"][19]["value"] = damage
Loading

0 comments on commit 34c1aef

Please sign in to comment.