diff --git a/.github/workflows/python.yml b/.github/workflows/python.yml index a9f7ecf..1d17f3a 100644 --- a/.github/workflows/python.yml +++ b/.github/workflows/python.yml @@ -23,14 +23,14 @@ jobs: - name: Set up Python uses: actions/setup-python@v2 with: - python-version: "3.9" - + python-version: "3.10" + - name: Install Python packages run: python -m pip install --upgrade pip setuptools build - name: build run: python -m build - + - name: Store the packages uses: actions/upload-artifact@v2 with: @@ -40,14 +40,13 @@ jobs: test: needs: - build - + runs-on: ubuntu-latest name: Test Python ${{ matrix.python.version }} strategy: fail-fast: false matrix: python: - - {version: '3.9', wheel: 'cp39-cp39'} - {version: '3.10', wheel: 'cp310-cp310'} - {version: '3.11', wheel: 'cp311-cp311'} @@ -65,7 +64,7 @@ jobs: with: name: python-package-distributions path: dist/ - + - name: Install Python packages run: python -m pip install --upgrade pip pytest @@ -94,7 +93,7 @@ jobs: with: password: ${{ secrets.testpypi_password }} repository_url: https://test.pypi.org/legacy/ - + - name: Publish 📦 to PyPI if: ${{ startsWith(github.ref, 'refs/tags/') }} uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..c45574e --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1 @@ +recursive-exclude open_prime_rando/echoes/custom_assets *.pdn \ No newline at end of file diff --git a/open_prime_rando/dynamic_schema.py b/open_prime_rando/dynamic_schema.py index e4e0ad1..266e65a 100644 --- a/open_prime_rando/dynamic_schema.py +++ b/open_prime_rando/dynamic_schema.py @@ -9,13 +9,14 @@ def expand_schema(base_schema: dict, editor: PatcherEditor) -> dict: schema = copy.deepcopy(base_schema) world_props = schema["properties"]["worlds"]["properties"] = {} - for world, mlvl_id in open_prime_rando.echoes.asset_ids.world.NAME_TO_ID.items(): + for world, mlvl_id in open_prime_rando.echoes.asset_ids.world.NAME_TO_ID_MLVL.items(): world_def = copy.deepcopy(schema["$defs"]["world"]) world_props[world] = world_def mlvl = editor.get_mlvl(mlvl_id) area_props = {} - world_def["properties"]["areas"] = {"type": "object", "additionalProperties": False, "properties": area_props} + # FIXME: setting it to True for now to fix elevator rooms breaking when renamed + world_def["properties"]["areas"] = {"type": "object", "additionalProperties": True, "properties": area_props} world_details = open_prime_rando.echoes.asset_ids.world.load_dedicated_file(world) diff --git a/open_prime_rando/echoes/asset_ids/__init__.py b/open_prime_rando/echoes/asset_ids/__init__.py index b17ac76..631108a 100644 --- a/open_prime_rando/echoes/asset_ids/__init__.py +++ b/open_prime_rando/echoes/asset_ids/__init__.py @@ -1,5 +1,5 @@ from . import agon_wastes, great_temple, sanctuary_fortress, temple_grounds, torvus_bog, world __all__ = [ - agon_wastes, great_temple, sanctuary_fortress, temple_grounds, torvus_bog, world, + "agon_wastes", "great_temple", "sanctuary_fortress", "temple_grounds", "torvus_bog", "world", ] diff --git a/open_prime_rando/echoes/asset_ids/agon_wastes.py b/open_prime_rando/echoes/asset_ids/agon_wastes.py index a423c6e..6c919f4 100644 --- a/open_prime_rando/echoes/asset_ids/agon_wastes.py +++ b/open_prime_rando/echoes/asset_ids/agon_wastes.py @@ -73,7 +73,7 @@ WARRIORS_WALK_MREA = 0x33FACC15 WATERING_HOLE_MREA = 0x9B2B4246 -NAME_TO_ID = { +NAME_TO_ID_MREA = { "Agon Energy Controller": 0x02FC3717, "Agon Map Station": 0x845C85A7, "Agon Temple": 0x75FC9AAE, @@ -147,6 +147,155 @@ "Warrior's Walk": 0x33FACC15, "Watering Hole": 0x9B2B4246, } +# Generated by asset_id_files.py + +AGON_ENERGY_CONTROLLER_MAPA = 0x2B29CC2A +AGON_MAP_STATION_MAPA = 0xAD897E9A +AGON_TEMPLE_MAPA = 0x5C296193 +BATTLEGROUND_MAPA = 0xC3ACA911 +BIOENERGY_PRODUCTION_MAPA = 0x3A92F695 +BIOSTORAGE_ACCESS_MAPA = 0xF73EED55 +BIOSTORAGE_STATION_MAPA = 0xA95C97D4 +BITTER_WELL_MAPA = 0x2D243AE5 +CENTRAL_MINING_STATION_MAPA = 0xDC73224F +CENTRAL_STATION_ACCESS_MAPA = 0xD7BC1BFE +COMMAND_CENTER_MAPA = 0x1817AE6A +COMMAND_CENTER_ACCESS_MAPA = 0x59567CC4 +CONTROLLER_ACCESS_MAPA = 0x968662FD +CROSSROADS_MAPA = 0xA7F62002 +DARK_AGON_ENERGY_CONTROLLER_MAPA = 0x9E08BB3B +DARK_AGON_TEMPLE_MAPA = 0x5CA0FFB7 +DARK_AGON_TEMPLE_ACCESS_MAPA = 0x08D63393 +DARK_CONTROLLER_ACCESS_MAPA = 0x8B26BCEC +DARK_OASIS_MAPA = 0xB02E6E42 +DARK_TRANSIT_STATION_MAPA = 0xFEEB3655 +DOOMED_ENTRY_MAPA = 0x8D4A7150 +DOUBLE_PATH_MAPA = 0x563AB806 +DUELLING_RANGE_MAPA = 0x16892404 +FEEDING_PIT_MAPA = 0x2E8F8DD6 +FEEDING_PIT_ACCESS_MAPA = 0xC9E03B98 +HALL_OF_STAIRS_MAPA = 0x44C3BCBD +ING_CACHE_1_MAPA = 0x7B43D48B +ING_CACHE_2_MAPA = 0xBACD0B4B +ING_CACHE_3_MAPA = 0x93F61EC8 +ING_CACHE_4_MAPA = 0xEC1232EE +JUDGMENT_PIT_MAPA = 0x42A20280 +JUNCTION_SITE_MAPA = 0x3FDD3E5E +MAIN_ENERGY_CONTROLLER_MAPA = 0xF0FD6BFE +MAIN_REACTOR_MAPA = 0xE50C08A3 +MINE_SHAFT_MAPA = 0xFE74AF0C +MINING_PLAZA_MAPA = 0x6BAA56A7 +MINING_STATION_A_MAPA = 0xCA6B0942 +MINING_STATION_ACCESS_MAPA = 0xA4AF84B1 +MINING_STATION_B_MAPA = 0xF2AED7D0 +OASIS_ACCESS_MAPA = 0xDB193F23 +PHAZON_SITE_MAPA = 0x09DB0EDA +PLAZA_ACCESS_MAPA = 0x842D721A +PORTAL_ACCESS_MAPA = 0xA007BDC0 +PORTAL_ACCESS_A_MAPA = 0x0AC71520 +PORTAL_SITE_MAPA = 0x96AAAC11 +PORTAL_TERMINAL_MAPA = 0x0218BF9A +SAND_CACHE_MAPA = 0xDD640C1A +SAND_PROCESSING_MAPA = 0x8D673043 +SANDCANYON_MAPA = 0xCC62DD15 +SAVE_STATION_1_MAPA = 0x73C0B513 +SAVE_STATION_2_MAPA = 0x85F5B4B6 +SAVE_STATION_3_MAPA = 0x2BAC0CD2 +SAVE_STATION_A_MAPA = 0x79D48A6F +SAVE_STATION_C_MAPA = 0x90ACAD11 +SECURITY_STATION_A_MAPA = 0xDEF659A7 +SECURITY_STATION_B_MAPA = 0x230FA1D2 +STORAGE_A_MAPA = 0x39E7C279 +STORAGE_B_MAPA = 0xBF73B0D7 +STORAGE_C_MAPA = 0x742F6372 +STORAGE_D_MAPA = 0x692A53CA +TEMPLE_ACCESS_MAPA = 0x038D5779 +TRANSIT_STATION_MAPA = 0x501C3E9D +TRANSPORT_CENTER_MAPA = 0x620A4C84 +TRANSPORT_TO_SANCTUARY_FORTRESS_MAPA = 0xEF5EA06C +TRANSPORT_TO_TEMPLE_GROUNDS_MAPA = 0x7E1BC16F +TRANSPORT_TO_TORVUS_BOG_MAPA = 0x8E9B3B3F +TRIAL_GROUNDS_MAPA = 0xAF14500A +TRIAL_TUNNEL_MAPA = 0x6131B5CB +VENTILATION_AREA_A_MAPA = 0xADE5C6E8 +VENTILATION_AREA_B_MAPA = 0x709EC836 +WARRIORS_WALK_MAPA = 0x1A2F3728 +WATERING_HOLE_MAPA = 0xB2FEB97B + +NAME_TO_ID_MAPA = { + "Agon Energy Controller": 0x2B29CC2A, + "Agon Map Station": 0xAD897E9A, + "Agon Temple": 0x5C296193, + "Battleground": 0xC3ACA911, + "Bioenergy Production": 0x3A92F695, + "Biostorage Access": 0xF73EED55, + "Biostorage Station": 0xA95C97D4, + "Bitter Well": 0x2D243AE5, + "Central Mining Station": 0xDC73224F, + "Central Station Access": 0xD7BC1BFE, + "Command Center": 0x1817AE6A, + "Command Center Access": 0x59567CC4, + "Controller Access": 0x968662FD, + "Crossroads": 0xA7F62002, + "Dark Agon Energy Controller": 0x9E08BB3B, + "Dark Agon Temple": 0x5CA0FFB7, + "Dark Agon Temple Access": 0x08D63393, + "Dark Controller Access": 0x8B26BCEC, + "Dark Oasis": 0xB02E6E42, + "Dark Transit Station": 0xFEEB3655, + "Doomed Entry": 0x8D4A7150, + "Double Path": 0x563AB806, + "Duelling Range": 0x16892404, + "Feeding Pit": 0x2E8F8DD6, + "Feeding Pit Access": 0xC9E03B98, + "Hall of Stairs": 0x44C3BCBD, + "Ing Cache 1": 0x7B43D48B, + "Ing Cache 2": 0xBACD0B4B, + "Ing Cache 3": 0x93F61EC8, + "Ing Cache 4": 0xEC1232EE, + "Judgment Pit": 0x42A20280, + "Junction Site": 0x3FDD3E5E, + "Main Energy Controller": 0xF0FD6BFE, + "Main Reactor": 0xE50C08A3, + "Mine Shaft": 0xFE74AF0C, + "Mining Plaza": 0x6BAA56A7, + "Mining Station A": 0xCA6B0942, + "Mining Station Access": 0xA4AF84B1, + "Mining Station B": 0xF2AED7D0, + "Oasis Access": 0xDB193F23, + "Phazon Site": 0x09DB0EDA, + "Plaza Access": 0x842D721A, + "Portal Access": 0xA007BDC0, + "Portal Access A": 0x0AC71520, + "Portal Site": 0x96AAAC11, + "Portal Terminal": 0x0218BF9A, + "Sand Cache": 0xDD640C1A, + "Sand Processing": 0x8D673043, + "Sandcanyon": 0xCC62DD15, + "Save Station 1": 0x73C0B513, + "Save Station 2": 0x85F5B4B6, + "Save Station 3": 0x2BAC0CD2, + "Save Station A": 0x79D48A6F, + "Save Station C": 0x90ACAD11, + "Security Station A": 0xDEF659A7, + "Security Station B": 0x230FA1D2, + "Storage A": 0x39E7C279, + "Storage B": 0xBF73B0D7, + "Storage C": 0x742F6372, + "Storage D": 0x692A53CA, + "Temple Access": 0x038D5779, + "Transit Station": 0x501C3E9D, + "Transport Center": 0x620A4C84, + "Transport to Sanctuary Fortress": 0xEF5EA06C, + "Transport to Temple Grounds": 0x7E1BC16F, + "Transport to Torvus Bog": 0x8E9B3B3F, + "Trial Grounds": 0xAF14500A, + "Trial Tunnel": 0x6131B5CB, + "Ventilation Area A": 0xADE5C6E8, + "Ventilation Area B": 0x709EC836, + "Warrior's Walk": 0x1A2F3728, + "Watering Hole": 0xB2FEB97B, +} DOCK_NAMES = { "Agon Energy Controller": { diff --git a/open_prime_rando/echoes/asset_ids/frontend.py b/open_prime_rando/echoes/asset_ids/frontend.py index 52c7c5f..3aec2e0 100644 --- a/open_prime_rando/echoes/asset_ids/frontend.py +++ b/open_prime_rando/echoes/asset_ids/frontend.py @@ -2,9 +2,16 @@ FRONTEND04_MREA = 0xCEE3AF0D -NAME_TO_ID = { +NAME_TO_ID_MREA = { "FrontEnd04": 0xCEE3AF0D, } +# Generated by asset_id_files.py + +FRONTEND04_MAPA = 0xE7365430 + +NAME_TO_ID_MAPA = { + "FrontEnd04": 0xE7365430, +} DOCK_NAMES = { "FrontEnd04": { diff --git a/open_prime_rando/echoes/asset_ids/great_temple.py b/open_prime_rando/echoes/asset_ids/great_temple.py index 10f0449..459817d 100644 --- a/open_prime_rando/echoes/asset_ids/great_temple.py +++ b/open_prime_rando/echoes/asset_ids/great_temple.py @@ -13,7 +13,7 @@ TRANSPORT_B_ACCESS_MREA = 0xFEB7BD27 TRANSPORT_C_ACCESS_MREA = 0x1A4DDCC0 -NAME_TO_ID = { +NAME_TO_ID_MREA = { "Controller Transport": 0x11E50BC6, "Main Energy Controller": 0x02A01334, "Sanctum": 0xD7C3B839, @@ -27,6 +27,35 @@ "Transport B Access": 0xFEB7BD27, "Transport C Access": 0x1A4DDCC0, } +# Generated by asset_id_files.py + +CONTROLLER_TRANSPORT_MAPA = 0x3830F0FB +MAIN_ENERGY_CONTROLLER_MAPA = 0x2B75E809 +SANCTUM_MAPA = 0xFE164304 +SANCTUM_ACCESS_MAPA = 0xE27873A6 +SKY_TEMPLE_ENERGY_CONTROLLER_MAPA = 0x529F0152 +TEMPLE_SANCTUARY_MAPA = 0x8B1E850F +TEMPLE_TRANSPORT_A_MAPA = 0x318EBBCD +TEMPLE_TRANSPORT_B_MAPA = 0xA6D44A39 +TEMPLE_TRANSPORT_C_MAPA = 0xB1B5308D +TRANSPORT_A_ACCESS_MAPA = 0xCE4F5493 +TRANSPORT_B_ACCESS_MAPA = 0xD762461A +TRANSPORT_C_ACCESS_MAPA = 0x339827FD + +NAME_TO_ID_MAPA = { + "Controller Transport": 0x3830F0FB, + "Main Energy Controller": 0x2B75E809, + "Sanctum": 0xFE164304, + "Sanctum Access": 0xE27873A6, + "Sky Temple Energy Controller": 0x529F0152, + "Temple Sanctuary": 0x8B1E850F, + "Temple Transport A": 0x318EBBCD, + "Temple Transport B": 0xA6D44A39, + "Temple Transport C": 0xB1B5308D, + "Transport A Access": 0xCE4F5493, + "Transport B Access": 0xD762461A, + "Transport C Access": 0x339827FD, +} DOCK_NAMES = { "Controller Transport": { diff --git a/open_prime_rando/echoes/asset_ids/m01_sidehopperstation.py b/open_prime_rando/echoes/asset_ids/m01_sidehopperstation.py index 9d38fae..fd1914f 100644 --- a/open_prime_rando/echoes/asset_ids/m01_sidehopperstation.py +++ b/open_prime_rando/echoes/asset_ids/m01_sidehopperstation.py @@ -2,9 +2,16 @@ SIDEHOPPERSTATION_MREA = 0x02690DE1 -NAME_TO_ID = { +NAME_TO_ID_MREA = { "01_SidehopperStation": 0x02690DE1, } +# Generated by asset_id_files.py + +SIDEHOPPERSTATION_MAPA = 0x2BBCF6DC + +NAME_TO_ID_MAPA = { + "01_SidehopperStation": 0x2BBCF6DC, +} DOCK_NAMES = { "01_SidehopperStation": { diff --git a/open_prime_rando/echoes/asset_ids/m02_spires.py b/open_prime_rando/echoes/asset_ids/m02_spires.py index a2cee1b..a3c1874 100644 --- a/open_prime_rando/echoes/asset_ids/m02_spires.py +++ b/open_prime_rando/echoes/asset_ids/m02_spires.py @@ -2,9 +2,16 @@ MULTI_SPIRES_MREA = 0x09749B2F -NAME_TO_ID = { +NAME_TO_ID_MREA = { "07_Multi_Spires": 0x09749B2F, } +# Generated by asset_id_files.py + +MULTI_SPIRES_MAPA = 0x20A16012 + +NAME_TO_ID_MAPA = { + "07_Multi_Spires": 0x20A16012, +} DOCK_NAMES = { "07_Multi_Spires": { diff --git a/open_prime_rando/echoes/asset_ids/m03_crossfirechaos.py b/open_prime_rando/echoes/asset_ids/m03_crossfirechaos.py index 2984a48..5769954 100644 --- a/open_prime_rando/echoes/asset_ids/m03_crossfirechaos.py +++ b/open_prime_rando/echoes/asset_ids/m03_crossfirechaos.py @@ -2,9 +2,16 @@ CROSSFIRECHAOS_MREA = 0x44D34A2E -NAME_TO_ID = { +NAME_TO_ID_MREA = { "03_CrossfireChaos": 0x44D34A2E, } +# Generated by asset_id_files.py + +CROSSFIRECHAOS_MAPA = 0x6D06B113 + +NAME_TO_ID_MAPA = { + "03_CrossfireChaos": 0x6D06B113, +} DOCK_NAMES = { "03_CrossfireChaos": { diff --git a/open_prime_rando/echoes/asset_ids/m04_pipeline.py b/open_prime_rando/echoes/asset_ids/m04_pipeline.py index 5442ef2..1c38657 100644 --- a/open_prime_rando/echoes/asset_ids/m04_pipeline.py +++ b/open_prime_rando/echoes/asset_ids/m04_pipeline.py @@ -2,9 +2,16 @@ MULTI_PIPELINE_MREA = 0x845DCF93 -NAME_TO_ID = { +NAME_TO_ID_MREA = { "04_Multi_Pipeline": 0x845DCF93, } +# Generated by asset_id_files.py + +MULTI_PIPELINE_MAPA = 0xAD8834AE + +NAME_TO_ID_MAPA = { + "04_Multi_Pipeline": 0xAD8834AE, +} DOCK_NAMES = { "04_Multi_Pipeline": { diff --git a/open_prime_rando/echoes/asset_ids/m05_spidercomplex.py b/open_prime_rando/echoes/asset_ids/m05_spidercomplex.py index d03d74b..49f6b7a 100644 --- a/open_prime_rando/echoes/asset_ids/m05_spidercomplex.py +++ b/open_prime_rando/echoes/asset_ids/m05_spidercomplex.py @@ -2,9 +2,16 @@ MULTI_SPIDERCOMPLEX_MREA = 0xE7095052 -NAME_TO_ID = { +NAME_TO_ID_MREA = { "05_Multi_spiderComplex": 0xE7095052, } +# Generated by asset_id_files.py + +MULTI_SPIDERCOMPLEX_MAPA = 0xCEDCAB6F + +NAME_TO_ID_MAPA = { + "05_Multi_spiderComplex": 0xCEDCAB6F, +} DOCK_NAMES = { "05_Multi_spiderComplex": { diff --git a/open_prime_rando/echoes/asset_ids/m06_shootinggallery.py b/open_prime_rando/echoes/asset_ids/m06_shootinggallery.py index 03b8b08..58d0ff3 100644 --- a/open_prime_rando/echoes/asset_ids/m06_shootinggallery.py +++ b/open_prime_rando/echoes/asset_ids/m06_shootinggallery.py @@ -2,9 +2,16 @@ SHOOTINGGALLERY_MREA = 0xD97A600B -NAME_TO_ID = { +NAME_TO_ID_MREA = { "06_ShootingGallery": 0xD97A600B, } +# Generated by asset_id_files.py + +SHOOTINGGALLERY_MAPA = 0xF0AF9B36 + +NAME_TO_ID_MAPA = { + "06_ShootingGallery": 0xF0AF9B36, +} DOCK_NAMES = { "06_ShootingGallery": { diff --git a/open_prime_rando/echoes/asset_ids/sanctuary_fortress.py b/open_prime_rando/echoes/asset_ids/sanctuary_fortress.py index 8c0ce43..b945318 100644 --- a/open_prime_rando/echoes/asset_ids/sanctuary_fortress.py +++ b/open_prime_rando/echoes/asset_ids/sanctuary_fortress.py @@ -68,7 +68,7 @@ WATCH_STATION_ACCESS_MREA = 0x5076F9CB WORKERS_PATH_MREA = 0x03ECA24F -NAME_TO_ID = { +NAME_TO_ID_MREA = { "Aerial Training Site": 0x40F7447E, "Aerie": 0x5D3A0001, "Aerie Access": 0xDF073157, @@ -137,6 +137,145 @@ "Watch Station Access": 0x5076F9CB, "Workers Path": 0x03ECA24F, } +# Generated by asset_id_files.py + +AERIAL_TRAINING_SITE_MAPA = 0x6922BF43 +AERIE_MAPA = 0x74EFFB3C +AERIE_ACCESS_MAPA = 0xF6D2CA6A +AERIE_TRANSPORT_STATION_MAPA = 0x932CB12E +AGON_TRANSPORT_ACCESS_MAPA = 0x5CDE44D2 +CENTRAL_AREA_TRANSPORT_EAST_MAPA = 0x3887FA60 +CENTRAL_AREA_TRANSPORT_WEST_MAPA = 0x59317EBD +CENTRAL_HIVE_EAST_TRANSPORT_MAPA = 0xEB026D56 +CENTRAL_HIVE_WEST_TRANSPORT_MAPA = 0x45692F96 +CHECKPOINT_STATION_MAPA = 0xAD9F9231 +CONTROLLER_ACCESS_MAPA = 0xC14E27AF +CULLING_CHAMBER_MAPA = 0x40DE8357 +DYNAMO_ACCESS_MAPA = 0x6B1DA1E4 +DYNAMO_STORAGE_MAPA = 0x36A2D3C5 +DYNAMO_WORKS_MAPA = 0xE9C4C7D5 +ENTRANCE_DEFENSE_HALL_MAPA = 0x8EDB5316 +GRAND_ABYSS_MAPA = 0x0AAB2539 +HALL_OF_COMBAT_MASTERY_MAPA = 0x7CA413A3 +HAZING_CLIFF_MAPA = 0x1E54B417 +HIVE_AMMO_STATION_MAPA = 0xD80A83E1 +HIVE_CACHE_1_MAPA = 0x76968D40 +HIVE_CACHE_3_MAPA = 0x2EFA3481 +HIVE_CONTROLLER_ACCESS_MAPA = 0x0F798423 +HIVE_DYNAMO_ACCESS_MAPA = 0xD27AC016 +HIVE_DYNAMO_WORKS_MAPA = 0xDB9B0D89 +HIVE_ENERGY_CONTROLLER_MAPA = 0x04393825 +HIVE_ENTRANCE_MAPA = 0xDA2FBA1C +HIVE_GYRO_ACCESS_MAPA = 0x0EE0D856 +HIVE_GYRO_CHAMBER_MAPA = 0x8AB099AF +HIVE_PORTAL_CHAMBER_MAPA = 0x52414B56 +HIVE_REACTOR_MAPA = 0xD10E3B00 +HIVE_REACTOR_ACCESS_MAPA = 0x99F337D6 +HIVE_SAVE_STATION_1_MAPA = 0x9282D912 +HIVE_SAVE_STATION_2_MAPA = 0x0B60BF13 +HIVE_SUMMIT_MAPA = 0xB455B268 +HIVE_TEMPLE_MAPA = 0x80FAFB8E +HIVE_TEMPLE_ACCESS_MAPA = 0xC552A4D6 +JUDGMENT_DROP_MAPA = 0x7C1182D6 +MAIN_ENERGY_CONTROLLER_MAPA = 0x7EC6B6FF +MAIN_GYRO_CHAMBER_MAPA = 0x5AE1D169 +MAIN_RESEARCH_MAPA = 0xB89AE8BC +MINIGYRO_CHAMBER_MAPA = 0xBF213123 +POWER_JUNCTION_MAPA = 0x1815862B +REACTOR_ACCESS_MAPA = 0x9FB34D68 +REACTOR_CORE_MAPA = 0x63B909A8 +SANCTUARY_ENERGY_CONTROLLER_MAPA = 0x24D6D157 +SANCTUARY_ENTRANCE_MAPA = 0x6EF3A736 +SANCTUARY_MAP_STATION_MAPA = 0x7B6A72CE +SANCTUARY_TEMPLE_MAPA = 0x45E3C7E9 +SAVE_STATION_A_MAPA = 0x4C6DFAF3 +SAVE_STATION_B_MAPA = 0xCAF9885D +SENTINELS_PATH_MAPA = 0x8D0DEE7A +STAGING_AREA_MAPA = 0x726AC872 +TEMPLE_ACCESS_MAPA = 0xECBB6AA7 +TEMPLE_SECURITY_ACCESS_MAPA = 0x6B39E616 +TEMPLE_TRANSPORT_ACCESS_MAPA = 0x4B8FDDAF +TORVUS_TRANSPORT_ACCESS_MAPA = 0xDB788F91 +TRANSIT_STATION_MAPA = 0xDE97B5FE +TRANSPORT_TO_AGON_WASTES_MAPA = 0x1C7CBD3E +TRANSPORT_TO_TEMPLE_GROUNDS_MAPA = 0xFB9E9C00 +TRANSPORT_TO_TORVUS_BOG_MAPA = 0x92A2ADA3 +UNSEEN_WAY_MAPA = 0xFC2A0996 +VAULT_MAPA = 0x7BF0B85C +VAULT_ATTACK_PORTAL_MAPA = 0x3424CA74 +WATCH_STATION_MAPA = 0x8B9598BA +WATCH_STATION_ACCESS_MAPA = 0x79A302F6 +WORKERS_PATH_MAPA = 0x2A395972 + +NAME_TO_ID_MAPA = { + "Aerial Training Site": 0x6922BF43, + "Aerie": 0x74EFFB3C, + "Aerie Access": 0xF6D2CA6A, + "Aerie Transport Station": 0x932CB12E, + "Agon Transport Access": 0x5CDE44D2, + "Central Area Transport East": 0x3887FA60, + "Central Area Transport West": 0x59317EBD, + "Central Hive East Transport": 0xEB026D56, + "Central Hive West Transport": 0x45692F96, + "Checkpoint Station": 0xAD9F9231, + "Controller Access": 0xC14E27AF, + "Culling Chamber": 0x40DE8357, + "Dynamo Access": 0x6B1DA1E4, + "Dynamo Storage": 0x36A2D3C5, + "Dynamo Works": 0xE9C4C7D5, + "Entrance Defense Hall": 0x8EDB5316, + "Grand Abyss": 0x0AAB2539, + "Hall of Combat Mastery": 0x7CA413A3, + "Hazing Cliff": 0x1E54B417, + "Hive Ammo Station": 0xD80A83E1, + "Hive Cache 1": 0x76968D40, + "Hive Cache 3": 0x2EFA3481, + "Hive Controller Access": 0x0F798423, + "Hive Dynamo Access": 0xD27AC016, + "Hive Dynamo Works": 0xDB9B0D89, + "Hive Energy Controller": 0x04393825, + "Hive Entrance": 0xDA2FBA1C, + "Hive Gyro Access": 0x0EE0D856, + "Hive Gyro Chamber": 0x8AB099AF, + "Hive Portal Chamber": 0x52414B56, + "Hive Reactor": 0xD10E3B00, + "Hive Reactor Access": 0x99F337D6, + "Hive Save Station 1": 0x9282D912, + "Hive Save Station 2": 0x0B60BF13, + "Hive Summit": 0xB455B268, + "Hive Temple": 0x80FAFB8E, + "Hive Temple Access": 0xC552A4D6, + "Judgment Drop": 0x7C1182D6, + "Main Energy Controller": 0x7EC6B6FF, + "Main Gyro Chamber": 0x5AE1D169, + "Main Research": 0xB89AE8BC, + "Minigyro Chamber": 0xBF213123, + "Power Junction": 0x1815862B, + "Reactor Access": 0x9FB34D68, + "Reactor Core": 0x63B909A8, + "Sanctuary Energy Controller": 0x24D6D157, + "Sanctuary Entrance": 0x6EF3A736, + "Sanctuary Map Station": 0x7B6A72CE, + "Sanctuary Temple": 0x45E3C7E9, + "Save Station A": 0x4C6DFAF3, + "Save Station B": 0xCAF9885D, + "Sentinel's Path": 0x8D0DEE7A, + "Staging Area": 0x726AC872, + "Temple Access": 0xECBB6AA7, + "Temple Security Access": 0x6B39E616, + "Temple Transport Access": 0x4B8FDDAF, + "Torvus Transport Access": 0xDB788F91, + "Transit Station": 0xDE97B5FE, + "Transport to Agon Wastes": 0x1C7CBD3E, + "Transport to Temple Grounds": 0xFB9E9C00, + "Transport to Torvus Bog": 0x92A2ADA3, + "Unseen Way": 0xFC2A0996, + "Vault": 0x7BF0B85C, + "Vault Attack Portal": 0x3424CA74, + "Watch Station": 0x8B9598BA, + "Watch Station Access": 0x79A302F6, + "Workers Path": 0x2A395972, +} DOCK_NAMES = { "Aerial Training Site": { diff --git a/open_prime_rando/echoes/asset_ids/temple_grounds.py b/open_prime_rando/echoes/asset_ids/temple_grounds.py index d5dfd7e..621bed3 100644 --- a/open_prime_rando/echoes/asset_ids/temple_grounds.py +++ b/open_prime_rando/echoes/asset_ids/temple_grounds.py @@ -64,7 +64,7 @@ GAME_END_PART4_MREA = 0x4E154902 GAME_END_PART5_MREA = 0x85499AA7 -NAME_TO_ID = { +NAME_TO_ID_MREA = { "00_scandummy": 0x131C3388, "Abandoned Base": 0xC6F4E0C2, "Accursed Lake": 0x9FD196DF, @@ -129,6 +129,137 @@ "game_end_part4": 0x4E154902, "game_end_part5": 0x85499AA7, } +# Generated by asset_id_files.py + +SCANDUMMY_MAPA = 0x3AC9C8B5 +ABANDONED_BASE_MAPA = 0xEF211BFF +ACCURSED_LAKE_MAPA = 0xB6046DE2 +AGON_TRANSPORT_ACCESS_MAPA = 0x8A443296 +BASE_ACCESS_MAPA = 0x41BAA126 +COLLAPSED_TUNNEL_MAPA = 0x72472A7B +COMMAND_CHAMBER_MAPA = 0xFFF2FB36 +COMMUNICATION_AREA_MAPA = 0x4A8B769C +DEFILED_SHRINE_MAPA = 0x3F34ED89 +DYNAMO_CHAMBER_MAPA = 0x9D159C9A +FORTRESS_TRANSPORT_ACCESS_MAPA = 0x59301DE0 +GFMC_COMPOUND_MAPA = 0x7EA081E8 +GATEWAY_ACCESS_MAPA = 0xADE7B7A5 +GRAND_WINDCHAMBER_MAPA = 0x84A56A40 +HALL_OF_EYES_MAPA = 0x6EBE5371 +HALL_OF_HONORED_DEAD_MAPA = 0x9166BD09 +HIVE_ACCESS_TUNNEL_MAPA = 0x98C1F719 +HIVE_CHAMBER_A_MAPA = 0x79668998 +HIVE_CHAMBER_B_MAPA = 0x34AE2893 +HIVE_CHAMBER_C_MAPA = 0xD251C319 +HIVE_SAVE_STATION_MAPA = 0x3DB42712 +HIVE_STORAGE_MAPA = 0x4AB22E4A +HIVE_TRANSPORT_AREA_MAPA = 0xBDCA7C07 +HIVE_TUNNEL_MAPA = 0x3C571777 +INDUSTRIAL_SITE_MAPA = 0x4B7971F9 +ING_RELIQUARY_MAPA = 0x5D4B79DE +ING_WINDCHAMBER_MAPA = 0xE43AD0CC +LAKE_ACCESS_MAPA = 0x415F4393 +LANDING_SITE_MAPA = 0x4B652D40 +MEETING_GROUNDS_MAPA = 0xB28B0148 +PATH_OF_EYES_MAPA = 0xC792C983 +PATH_OF_HONOR_MAPA = 0x5298CAE6 +PHAZON_GROUNDS_MAPA = 0x4D8B097E +PHAZON_PIT_MAPA = 0x37E6DB3D +PLAIN_OF_DARK_WORSHIP_MAPA = 0x2DD01099 +PORTAL_SITE_MAPA = 0xF3E204AA +PROFANE_PATH_MAPA = 0x4D33BBEB +RELIQUARY_ACCESS_MAPA = 0xAC9531E9 +RELIQUARY_GROUNDS_MAPA = 0xD55A3B99 +SACRED_BRIDGE_MAPA = 0x9369201F +SACRED_PATH_MAPA = 0xEBE90480 +SERVICE_ACCESS_MAPA = 0xB662AB01 +SHRINE_ACCESS_MAPA = 0x36C8FCE2 +SKY_TEMPLE_GATEWAY_MAPA = 0xAE06A5D9 +STORAGE_CAVERN_A_MAPA = 0x11D534BF +STORAGE_CAVERN_B_MAPA = 0x97414611 +TEMPLE_ASSEMBLY_SITE_MAPA = 0x629BA22C +TEMPLE_TRANSPORT_A_MAPA = 0x79EFFD7D +TEMPLE_TRANSPORT_B_MAPA = 0x65168477 +TEMPLE_TRANSPORT_C_MAPA = 0x84388E13 +TORVUS_TRANSPORT_ACCESS_MAPA = 0x81ECE590 +TRANSPORT_TO_AGON_WASTES_MAPA = 0x4B2A6FD3 +TRANSPORT_TO_SANCTUARY_FORTRESS_MAPA = 0xE4229356 +TRANSPORT_TO_TORVUS_BOG_MAPA = 0x85E70805 +TROOPER_SECURITY_STATION_MAPA = 0x7C3B96FE +WAR_RITUAL_GROUNDS_MAPA = 0x83A21190 +WINDCHAMBER_GATEWAY_MAPA = 0x5CE47663 +WINDCHAMBER_TUNNEL_MAPA = 0xB3B6C082 +GAME_END_PART1_MAPA = 0x370D238C +GAME_END_PART2_MAPA = 0xB1995122 +GAME_END_PART3_MAPA = 0x7AC58287 +GAME_END_PART4_MAPA = 0x67C0B23F +GAME_END_PART5_MAPA = 0xAC9C619A + +NAME_TO_ID_MAPA = { + "00_scandummy": 0x3AC9C8B5, + "Abandoned Base": 0xEF211BFF, + "Accursed Lake": 0xB6046DE2, + "Agon Transport Access": 0x8A443296, + "Base Access": 0x41BAA126, + "Collapsed Tunnel": 0x72472A7B, + "Command Chamber": 0xFFF2FB36, + "Communication Area": 0x4A8B769C, + "Defiled Shrine": 0x3F34ED89, + "Dynamo Chamber": 0x9D159C9A, + "Fortress Transport Access": 0x59301DE0, + "GFMC Compound": 0x7EA081E8, + "Gateway Access": 0xADE7B7A5, + "Grand Windchamber": 0x84A56A40, + "Hall of Eyes": 0x6EBE5371, + "Hall of Honored Dead": 0x9166BD09, + "Hive Access Tunnel": 0x98C1F719, + "Hive Chamber A": 0x79668998, + "Hive Chamber B": 0x34AE2893, + "Hive Chamber C": 0xD251C319, + "Hive Save Station": 0x3DB42712, + "Hive Storage": 0x4AB22E4A, + "Hive Transport Area": 0xBDCA7C07, + "Hive Tunnel": 0x3C571777, + "Industrial Site": 0x4B7971F9, + "Ing Reliquary": 0x5D4B79DE, + "Ing Windchamber": 0xE43AD0CC, + "Lake Access": 0x415F4393, + "Landing Site": 0x4B652D40, + "Meeting Grounds": 0xB28B0148, + "Path of Eyes": 0xC792C983, + "Path of Honor": 0x5298CAE6, + "Phazon Grounds": 0x4D8B097E, + "Phazon Pit": 0x37E6DB3D, + "Plain of Dark Worship": 0x2DD01099, + "Portal Site": 0xF3E204AA, + "Profane Path": 0x4D33BBEB, + "Reliquary Access": 0xAC9531E9, + "Reliquary Grounds": 0xD55A3B99, + "Sacred Bridge": 0x9369201F, + "Sacred Path": 0xEBE90480, + "Service Access": 0xB662AB01, + "Shrine Access": 0x36C8FCE2, + "Sky Temple Gateway": 0xAE06A5D9, + "Storage Cavern A": 0x11D534BF, + "Storage Cavern B": 0x97414611, + "Temple Assembly Site": 0x629BA22C, + "Temple Transport A": 0x79EFFD7D, + "Temple Transport B": 0x65168477, + "Temple Transport C": 0x84388E13, + "Torvus Transport Access": 0x81ECE590, + "Transport to Agon Wastes": 0x4B2A6FD3, + "Transport to Sanctuary Fortress": 0xE4229356, + "Transport to Torvus Bog": 0x85E70805, + "Trooper Security Station": 0x7C3B96FE, + "War Ritual Grounds": 0x83A21190, + "Windchamber Gateway": 0x5CE47663, + "Windchamber Tunnel": 0xB3B6C082, + "game_end_part1": 0x370D238C, + "game_end_part2": 0xB1995122, + "game_end_part3": 0x7AC58287, + "game_end_part4": 0x67C0B23F, + "game_end_part5": 0xAC9C619A, +} DOCK_NAMES = { "00_scandummy": { diff --git a/open_prime_rando/echoes/asset_ids/torvus_bog.py b/open_prime_rando/echoes/asset_ids/torvus_bog.py index 1d37911..82d0bdb 100644 --- a/open_prime_rando/echoes/asset_ids/torvus_bog.py +++ b/open_prime_rando/echoes/asset_ids/torvus_bog.py @@ -71,7 +71,7 @@ UNDERTRANSIT_TWO_MREA = 0x4BFCA639 VENOMOUS_POND_MREA = 0x858EECD3 -NAME_TO_ID = { +NAME_TO_ID_MREA = { "Abandoned Worksite": 0x54DAEF57, "Ammo Station": 0x8697F62A, "Brooding Ground": 0xDCEF49B9, @@ -143,6 +143,151 @@ "Undertransit Two": 0x4BFCA639, "Venomous Pond": 0x858EECD3, } +# Generated by asset_id_files.py + +ABANDONED_WORKSITE_MAPA = 0x7D0F146A +AMMO_STATION_MAPA = 0xAF420D17 +BROODING_GROUND_MAPA = 0xF53AB284 +CACHE_A_MAPA = 0x257F15FB +CACHE_B_MAPA = 0x29A0AAC4 +CATACOMBS_MAPA = 0xD2B776F6 +CATACOMBS_ACCESS_MAPA = 0x9B1F5BF4 +CONTROLLER_ACCESS_MAPA = 0x0323A074 +CRYPT_MAPA = 0xE17DB0A5 +CRYPT_TUNNEL_MAPA = 0x6411DA90 +DARK_ARENA_TUNNEL_MAPA = 0x10D80784 +DARK_CONTROLLER_ACCESS_MAPA = 0x3811A956 +DARK_FALLS_MAPA = 0x07F06344 +DARK_FORGOTTEN_BRIDGE_MAPA = 0x2B868310 +DARK_TORVUS_ARENA_MAPA = 0x8D4FAC12 +DARK_TORVUS_ENERGY_CONTROLLER_MAPA = 0x77023624 +DARK_TORVUS_TEMPLE_MAPA = 0x1FB64AA8 +DARK_TORVUS_TEMPLE_ACCESS_MAPA = 0xCC421FC4 +DUNGEON_MAPA = 0x92314893 +FORGOTTEN_BRIDGE_MAPA = 0x200B341C +FORTRESS_TRANSPORT_ACCESS_MAPA = 0x4ACCF15C +GATHERING_ACCESS_MAPA = 0x7AD804BC +GATHERING_HALL_MAPA = 0x1CD4BC07 +GLOOM_VISTA_MAPA = 0xBC9D73FA +GREAT_BRIDGE_MAPA = 0xCA005E63 +GROVE_ACCESS_MAPA = 0x6FB1B778 +HYDROCHAMBER_STORAGE_MAPA = 0xBF731A43 +HYDRODYNAMO_SHAFT_MAPA = 0x1956AAD8 +HYDRODYNAMO_STATION_MAPA = 0xFC604B5B +MAIN_ENERGY_CONTROLLER_MAPA = 0x8F55C838 +MAIN_HYDROCHAMBER_MAPA = 0xE76F3B80 +MEDITATION_VISTA_MAPA = 0x742FC9E6 +PATH_OF_ROOTS_MAPA = 0x5D9D6821 +PLAZA_ACCESS_MAPA = 0xE8177C3B +POISONED_BOG_MAPA = 0x3D37389B +POLLUTED_MIRE_MAPA = 0xAA483FCB +PORTAL_CHAMBER_DARK_MAPA = 0x87CBE804 +PORTAL_CHAMBER_LIGHT_MAPA = 0xDA3BA362 +PUTRID_ALCOVE_MAPA = 0xE212D644 +RUINED_ALCOVE_MAPA = 0xFAA9DF29 +SACRIFICIAL_CHAMBER_MAPA = 0x4B426BC9 +SACRIFICIAL_CHAMBER_TUNNEL_MAPA = 0x4C308AF0 +SAVE_STATION_1_MAPA = 0xB3A28F7F +SAVE_STATION_2_MAPA = 0x3AF0B5FB +SAVE_STATION_A_MAPA = 0xF2BBBB48 +SAVE_STATION_B_MAPA = 0xE2C3A0E5 +TEMPLE_ACCESS_MAPA = 0x2E954FEE +TEMPLE_TRANSPORT_ACCESS_MAPA = 0x0E0733A5 +TORVUS_ENERGY_CONTROLLER_MAPA = 0x3AEE0E85 +TORVUS_GROVE_MAPA = 0xC1ED0125 +TORVUS_LAGOON_MAPA = 0x57DE1DA4 +TORVUS_MAP_STATION_MAPA = 0x01FEA342 +TORVUS_PLAZA_MAPA = 0x5C24A3C2 +TORVUS_TEMPLE_MAPA = 0xCF9535AB +TRAINING_ACCESS_MAPA = 0x5E7A64EC +TRAINING_CHAMBER_MAPA = 0x6260555D +TRANSIT_TUNNEL_EAST_MAPA = 0x4F23CB33 +TRANSIT_TUNNEL_SOUTH_MAPA = 0x9EF0619B +TRANSIT_TUNNEL_WEST_MAPA = 0xC8850070 +TRANSPORT_TO_AGON_WASTES_MAPA = 0xE6B06473 +TRANSPORT_TO_SANCTUARY_FORTRESS_MAPA = 0x96DB1F15 +TRANSPORT_TO_TEMPLE_GROUNDS_MAPA = 0x46B0EECF +UNDERGROUND_TRANSPORT_MAPA = 0xBB8D27BF +UNDERGROUND_TUNNEL_MAPA = 0xA93384AD +UNDERTEMPLE_MAPA = 0xAC767B22 +UNDERTEMPLE_ACCESS_MAPA = 0x006C6BC5 +UNDERTEMPLE_SHAFT_MAPA = 0x9B061951 +UNDERTRANSIT_ONE_MAPA = 0x750139C4 +UNDERTRANSIT_TWO_MAPA = 0x62295D04 +VENOMOUS_POND_MAPA = 0xAC5B17EE + +NAME_TO_ID_MAPA = { + "Abandoned Worksite": 0x7D0F146A, + "Ammo Station": 0xAF420D17, + "Brooding Ground": 0xF53AB284, + "Cache A": 0x257F15FB, + "Cache B": 0x29A0AAC4, + "Catacombs": 0xD2B776F6, + "Catacombs Access": 0x9B1F5BF4, + "Controller Access": 0x0323A074, + "Crypt": 0xE17DB0A5, + "Crypt Tunnel": 0x6411DA90, + "Dark Arena Tunnel": 0x10D80784, + "Dark Controller Access": 0x3811A956, + "Dark Falls": 0x07F06344, + "Dark Forgotten Bridge": 0x2B868310, + "Dark Torvus Arena": 0x8D4FAC12, + "Dark Torvus Energy Controller": 0x77023624, + "Dark Torvus Temple": 0x1FB64AA8, + "Dark Torvus Temple Access": 0xCC421FC4, + "Dungeon": 0x92314893, + "Forgotten Bridge": 0x200B341C, + "Fortress Transport Access": 0x4ACCF15C, + "Gathering Access": 0x7AD804BC, + "Gathering Hall": 0x1CD4BC07, + "Gloom Vista": 0xBC9D73FA, + "Great Bridge": 0xCA005E63, + "Grove Access": 0x6FB1B778, + "Hydrochamber Storage": 0xBF731A43, + "Hydrodynamo Shaft": 0x1956AAD8, + "Hydrodynamo Station": 0xFC604B5B, + "Main Energy Controller": 0x8F55C838, + "Main Hydrochamber": 0xE76F3B80, + "Meditation Vista": 0x742FC9E6, + "Path of Roots": 0x5D9D6821, + "Plaza Access": 0xE8177C3B, + "Poisoned Bog": 0x3D37389B, + "Polluted Mire": 0xAA483FCB, + "Portal Chamber (Dark)": 0x87CBE804, + "Portal Chamber (Light)": 0xDA3BA362, + "Putrid Alcove": 0xE212D644, + "Ruined Alcove": 0xFAA9DF29, + "Sacrificial Chamber": 0x4B426BC9, + "Sacrificial Chamber Tunnel": 0x4C308AF0, + "Save Station 1": 0xB3A28F7F, + "Save Station 2": 0x3AF0B5FB, + "Save Station A": 0xF2BBBB48, + "Save Station B": 0xE2C3A0E5, + "Temple Access": 0x2E954FEE, + "Temple Transport Access": 0x0E0733A5, + "Torvus Energy Controller": 0x3AEE0E85, + "Torvus Grove": 0xC1ED0125, + "Torvus Lagoon": 0x57DE1DA4, + "Torvus Map Station": 0x01FEA342, + "Torvus Plaza": 0x5C24A3C2, + "Torvus Temple": 0xCF9535AB, + "Training Access": 0x5E7A64EC, + "Training Chamber": 0x6260555D, + "Transit Tunnel East": 0x4F23CB33, + "Transit Tunnel South": 0x9EF0619B, + "Transit Tunnel West": 0xC8850070, + "Transport to Agon Wastes": 0xE6B06473, + "Transport to Sanctuary Fortress": 0x96DB1F15, + "Transport to Temple Grounds": 0x46B0EECF, + "Underground Transport": 0xBB8D27BF, + "Underground Tunnel": 0xA93384AD, + "Undertemple": 0xAC767B22, + "Undertemple Access": 0x006C6BC5, + "Undertemple Shaft": 0x9B061951, + "Undertransit One": 0x750139C4, + "Undertransit Two": 0x62295D04, + "Venomous Pond": 0xAC5B17EE, +} DOCK_NAMES = { "Abandoned Worksite": { diff --git a/open_prime_rando/echoes/asset_ids/world.py b/open_prime_rando/echoes/asset_ids/world.py index 3b34ee1..dde53f0 100644 --- a/open_prime_rando/echoes/asset_ids/world.py +++ b/open_prime_rando/echoes/asset_ids/world.py @@ -13,7 +13,7 @@ TEMPLE_GROUNDS_MLVL = 0x3BFA3EFF TORVUS_BOG_MLVL = 0x3DFD2249 -NAME_TO_ID = { +NAME_TO_ID_MLVL = { "Agon Wastes": 0x42B935E4, "FrontEnd": 0x69802220, "Great Temple": 0x863FCD72, @@ -27,6 +27,35 @@ "Temple Grounds": 0x3BFA3EFF, "Torvus Bog": 0x3DFD2249, } +# Generated by asset_id_files.py + +AGON_WASTES_MAPW = 0x38DBF6A5 +FRONTEND_MAPW = 0x13E2E161 +GREAT_TEMPLE_MAPW = 0xFC5D0E33 +M01_SIDEHOPPERSTATION_MAPW = 0xDF68438D +M02_SPIRES_MAPW = 0xD475D543 +M03_CROSSFIRECHAOS_MAPW = 0x99D20442 +M04_PIPELINE_MAPW = 0x595C81FF +M05_SPIDERCOMPLEX_MAPW = 0x3A081E3E +M06_SHOOTINGGALLERY_MAPW = 0x047B2E67 +SANCTUARY_FORTRESS_MAPW = 0x61C85583 +TEMPLE_GROUNDS_MAPW = 0x4198FDBE +TORVUS_BOG_MAPW = 0x479FE108 + +NAME_TO_ID_MAPW = { + "Agon Wastes": 0x38DBF6A5, + "FrontEnd": 0x13E2E161, + "Great Temple": 0xFC5D0E33, + "M01_SidehopperStation": 0xDF68438D, + "M02_Spires": 0xD475D543, + "M03_CrossfireChaos": 0x99D20442, + "M04_Pipeline": 0x595C81FF, + "M05_SpiderComplex": 0x3A081E3E, + "M06_ShootingGallery": 0x047B2E67, + "Sanctuary Fortress": 0x61C85583, + "Temple Grounds": 0x4198FDBE, + "Torvus Bog": 0x479FE108, +} _DEDICATED_FILES = { "Agon Wastes": ".agon_wastes", diff --git a/open_prime_rando/echoes/custom_assets/doors/custom_door_lock_darkburst.TXTR b/open_prime_rando/echoes/custom_assets/doors/custom_door_lock_darkburst.TXTR new file mode 100644 index 0000000..3243e0e Binary files /dev/null and b/open_prime_rando/echoes/custom_assets/doors/custom_door_lock_darkburst.TXTR differ diff --git a/open_prime_rando/echoes/custom_assets/doors/custom_door_lock_greyscale_emissive.TXTR b/open_prime_rando/echoes/custom_assets/doors/custom_door_lock_greyscale_emissive.TXTR new file mode 100644 index 0000000..38da424 Binary files /dev/null and b/open_prime_rando/echoes/custom_assets/doors/custom_door_lock_greyscale_emissive.TXTR differ diff --git a/open_prime_rando/echoes/custom_assets/doors/custom_door_lock_sonicboom.TXTR b/open_prime_rando/echoes/custom_assets/doors/custom_door_lock_sonicboom.TXTR new file mode 100644 index 0000000..1549695 Binary files /dev/null and b/open_prime_rando/echoes/custom_assets/doors/custom_door_lock_sonicboom.TXTR differ diff --git a/open_prime_rando/echoes/custom_assets/doors/custom_door_lock_sunburst.TXTR b/open_prime_rando/echoes/custom_assets/doors/custom_door_lock_sunburst.TXTR new file mode 100644 index 0000000..3c2c3a4 Binary files /dev/null and b/open_prime_rando/echoes/custom_assets/doors/custom_door_lock_sunburst.TXTR differ diff --git a/open_prime_rando/echoes/custom_assets/doors/custom_door_lock_template.pdn b/open_prime_rando/echoes/custom_assets/doors/custom_door_lock_template.pdn new file mode 100644 index 0000000..1c1fb28 Binary files /dev/null and b/open_prime_rando/echoes/custom_assets/doors/custom_door_lock_template.pdn differ diff --git a/open_prime_rando/echoes/dock_lock_rando/__init__.py b/open_prime_rando/echoes/dock_lock_rando/__init__.py index 0636c07..c59b316 100644 --- a/open_prime_rando/echoes/dock_lock_rando/__init__.py +++ b/open_prime_rando/echoes/dock_lock_rando/__init__.py @@ -1,55 +1,41 @@ -from retro_data_structures.enums import echoes -from retro_data_structures.formats.script_object import ScriptInstanceHelper -from retro_data_structures.properties.echoes.archetypes.DamageVulnerability import DamageVulnerability -from retro_data_structures.properties.echoes.archetypes.WeaponVulnerability import WeaponVulnerability -from retro_data_structures.properties.echoes.objects.Door import Door - -from open_prime_rando.echoes.asset_ids.temple_grounds import HIVE_ACCESS_TUNNEL_MREA +from pathlib import Path +from construct import Construct, Container +from open_prime_rando.echoes.dock_lock_rando.dock_type import * +from open_prime_rando.echoes.dock_lock_rando.dock_type_database import DOCK_TYPES from open_prime_rando.patcher_editor import PatcherEditor -def change_door_to_light(door: ScriptInstanceHelper): - door_props: Door = door.get_properties() - door_props.shell_color.r = 1.0 - door_props.shell_color.g = 1.0 - door_props.shell_color.b = 1.0 - - v = door_props.vulnerability - types = ["power", "dark", "annihilator", "bomb", "power_bomb", "missile", "power_charge", "entangler", - "sonic_boom", "super_missle", "black_hole", "imploder"] - for t in types: - dmg_vuln: WeaponVulnerability = getattr(v, t) - dmg_vuln.damage_multiplier = 0.0 - dmg_vuln.effect = echoes.Effect.Reflect - - for t in ["light", "light_blast", "sunburst"]: - dmg_vuln: WeaponVulnerability = getattr(v, t) - dmg_vuln.damage_multiplier = 100.0 - dmg_vuln.effect = echoes.Effect.Normal - - door.set_properties(door_props) - - -def apply_door_rando(editor: PatcherEditor, door_rando_configuration: list[dict]): - access_tunnel = editor.get_mrea(HIVE_ACCESS_TUNNEL_MREA) - - docks = {} - doors = {} - - for script_layer in access_tunnel.script_layers: - for instance in script_layer.instances: - # try: - # print("{}: {}".format(instance, instance.name)) - # # print(instance.get_properties()) - # except Exception as e: - # print("{}: {} --- {}".format(instance, e, instance._raw.instance.base_property.hex())) - # continue - - if instance.type == "DOCK": - docks[instance.id] = instance - - if instance.type == "DOOR": - doors[instance.id] = instance - - for door in doors.values(): - change_door_to_light(door) \ No newline at end of file +from retro_data_structures.base_resource import RawResource +from retro_data_structures.formats.cmdl import Cmdl +from retro_data_structures.game_check import Game + + +def add_custom_models(editor: PatcherEditor): + assets = Path(__file__).parent.parent.joinpath("custom_assets", "doors") + def get_txtr(n: str) -> AssetId: + res = RawResource( + type="TXTR", + data=assets.joinpath(f"{n}.TXTR").read_bytes() + ) + return editor.add_file(f"{n}.TXTR", res, []) + + emissive = get_txtr("custom_door_lock_greyscale_emissive") + template = editor.get_parsed_asset(0xF115F575, type_hint=Cmdl) + + for name in ("darkburst", "sunburst", "sonicboom"): + txtr = get_txtr(f"custom_door_lock_{name}") + cmdl = Container(template.raw) + cmdl.material_sets[0].texture_file_ids[0] = txtr + cmdl.material_sets[0].texture_file_ids[1] = emissive + editor.add_file(f"custom_door_lock_{name}.CMDL", Cmdl(cmdl, Game.ECHOES, editor), []) + + +def apply_door_rando(editor: PatcherEditor, world_name: str, area_name: str, dock_name: str, new_door_type: str, old_door_type: str | None, low_memory: bool): + if old_door_type is not None: + old_door = DOCK_TYPES[old_door_type] + + if isinstance(old_door, VanillaBlastShieldDoorType): + old_door.remove_blast_shield(editor, world_name, area_name, dock_name) + + new_door = DOCK_TYPES[new_door_type] + new_door.patch_door(editor, world_name, area_name, dock_name, low_memory) diff --git a/open_prime_rando/echoes/dock_lock_rando/dock_type.py b/open_prime_rando/echoes/dock_lock_rando/dock_type.py index 9d4ddd5..da8bcf8 100644 --- a/open_prime_rando/echoes/dock_lock_rando/dock_type.py +++ b/open_prime_rando/echoes/dock_lock_rando/dock_type.py @@ -1,16 +1,341 @@ import dataclasses +import functools +import logging +from open_prime_rando.echoes.dock_lock_rando.map_icons import DoorMapIcon +from open_prime_rando.patcher_editor import PatcherEditor +from open_prime_rando.echoes.asset_ids import * from retro_data_structures.base_resource import AssetId -from retro_data_structures.enums import echoes +from retro_data_structures.asset_manager import NameOrAssetId from retro_data_structures.properties.echoes.archetypes.DamageVulnerability import DamageVulnerability from retro_data_structures.properties.echoes.archetypes.WeaponVulnerability import WeaponVulnerability from retro_data_structures.properties.echoes.core.Color import Color +from retro_data_structures.properties.echoes.core.Vector import Vector +from retro_data_structures.properties.echoes.archetypes.EditorProperties import EditorProperties +from retro_data_structures.properties.echoes.archetypes.ScannableParameters import ScannableParameters +from retro_data_structures.properties.echoes.archetypes.SurroundPan import SurroundPan +from retro_data_structures.properties.echoes.archetypes.ActorParameters import ActorParameters +from retro_data_structures.properties.echoes.objects.Dock import Dock +from retro_data_structures.properties.echoes.objects.Door import Door +from retro_data_structures.properties.echoes.objects.Actor import Actor +from retro_data_structures.properties.echoes.objects.MemoryRelay import MemoryRelay +from retro_data_structures.properties.echoes.objects.ScannableObjectInfo import ScannableObjectInfo +from retro_data_structures.properties.echoes.objects.Sound import Sound +from retro_data_structures.properties.echoes.objects.StreamedAudio import StreamedAudio +from retro_data_structures.properties.echoes.objects.MemoryRelay import MemoryRelay +from retro_data_structures.properties.echoes.objects.Effect import Effect +from retro_data_structures.properties.echoes.core.Spline import Spline +from retro_data_structures.formats.mapa import Mapa +from retro_data_structures.formats.mlvl import AreaWrapper +from retro_data_structures.formats.scan import Scan +from retro_data_structures.formats.strg import Strg +from retro_data_structures.formats.script_object import ScriptInstanceHelper, InstanceId +from retro_data_structures.enums.echoes import State, Message +@dataclasses.dataclass(kw_only=True) +class DoorType: + name: str - -@dataclasses.dataclass(frozen=True) -class DockType: vulnerability: DamageVulnerability - shell_model: AssetId + + shell_model: NameOrAssetId = 0x6B78FD92 # normal door model shell_color: Color + + scan_text: tuple[str, ...] | None = None + + map_icon: DoorMapIcon + + patched_scan: AssetId | None = None + + def get_paks(self, editor: PatcherEditor, world_name: str, area_name: str): + world_file = world.load_dedicated_file(world_name) + return editor.find_paks(world_file.NAME_TO_ID_MREA[area_name]) + + def get_files(self, editor: PatcherEditor, world_name: str, area_name: str) -> tuple[AreaWrapper, Mapa]: + world_file = world.load_dedicated_file(world_name) + mrea = editor.get_area_helper(world.NAME_TO_ID_MLVL[world_name], world_file.NAME_TO_ID_MREA[area_name]) + mapa = editor.get_file(world_file.NAME_TO_ID_MAPA[area_name], type_hint=Mapa) + return mrea, mapa + + def get_dock_index(self, world_name: str, area_name: str, dock_name: str) -> int: + world_file = world.load_dedicated_file(world_name) + return world_file.DOCK_NAMES[area_name][dock_name] + + def get_mrea(self, editor: PatcherEditor, world_name: str, area_name: str) -> AreaWrapper: + return self.get_files(editor, world_name, area_name)[0] + + def get_door_from_dock_index(self, mrea: AreaWrapper, dock_index: int) -> ScriptInstanceHelper: + dock = next( + inst for inst in mrea.mrea.all_instances + if ( + inst.type == Dock and + inst.get_properties_as(Dock).dock_number == dock_index + ) + ) + for instance in mrea.mrea.all_instances: + if instance.type != Door: + continue + for connection in instance.connections: + if dock.id_matches(connection.target): + return instance + raise KeyError(f"no door found with a connection to {dock} in {mrea.name}") + + def patch_map_icon(self, mapa: Mapa, door: ScriptInstanceHelper): + for obj in mapa.raw.mappable_objects: + if door.id_matches(obj.editor_id): + obj.type = self.map_icon.value + return + + @staticmethod + def get_scan_templates(editor: PatcherEditor) -> tuple[Scan, Strg]: + # Uncategorized/There is a Blast Shield on the door blocking acces_0.SCAN + scan = editor.get_parsed_asset(0x36DE1342, type_hint=Scan) + # Strings/Uncategorized/There is a Blast Shield on the door blocking acces_0_0.STRG + strg = editor.get_parsed_asset(0x49DF4448, type_hint=Strg) + return scan, strg + + def get_patched_scan(self, editor: PatcherEditor, world_name: str, area_name: str) -> AssetId: + paks = self.get_paks(editor, world_name, area_name) + if self.patched_scan is None or not editor.does_asset_exists(self.patched_scan): + scan, strg = DoorType.get_scan_templates(editor) + for i, text in enumerate(self.scan_text): + strg.set_string(i, text) + + strg_id = editor.add_or_replace_custom_asset(f"custom_door_{self.name}.STRG", strg, paks) + + scan_info = scan.scannable_object_info.get_properties_as(ScannableObjectInfo) + scan_info.string = strg_id + scan.scannable_object_info.set_properties(scan_info) + + scan_id = editor.add_or_replace_custom_asset(f"custom_door_{self.name}.SCAN", scan, paks) + self.patched_scan = scan_id + + for pak in paks: + editor.ensure_present(pak, self.patched_scan) + return self.patched_scan + + def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, dock_name: str, low_memory: bool): + mrea, mapa = self.get_files(editor, world_name, area_name) + door = self.get_door_from_dock_index(mrea, self.get_dock_index(world_name, area_name, dock_name)) + self.patch_map_icon(mapa, door) + + shell_model = editor._resolve_asset_id(self.shell_model) + + door_props = door.get_properties_as(Door) + door_props.shell_model = shell_model + door_props.shell_color = self.shell_color + door.set_properties(door_props) + + for pak in self.get_paks(editor, world_name, area_name): + editor.ensure_present(pak, shell_model) + + +@dataclasses.dataclass(kw_only=True) +class NormalDoorType(DoorType): + def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, dock_name: str, low_memory: bool): + super().patch_door(editor, world_name, area_name, dock_name, low_memory) + mrea = self.get_mrea(editor, world_name, area_name) + door = self.get_door_from_dock_index(mrea, self.get_dock_index(world_name, area_name, dock_name)) + + door_props = door.get_properties_as(Door) + door_props.vulnerability = self.vulnerability + + if self.scan_text is not None: + door_props.alt_scannable.scannable_info0 = self.get_patched_scan(editor, world_name, area_name) + + door.set_properties(door_props) + + +@dataclasses.dataclass(kw_only=True) +class BlastShieldDoorType(DoorType): + shield_model: NameOrAssetId + shield_collision_box: Vector = dataclasses.field(default_factory=lambda: Vector(0.35, 5.0, 4.0)) + shield_collision_offset: Vector = dataclasses.field(default_factory=lambda: Vector(-2/3, 0, 2.0)) + + def get_spline(self) -> Spline: + return Spline(data=b'\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02A \x00\x00?\x80\x00\x00\x02\x02\x01\x00\x00\x00\x00?\x80\x00\x00') + + def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, dock_name: str, low_memory: bool): + """ + blast shield connections: + DEAD -> lock cleared MemoryRelay, ACTV + DEAD -> lock breaking SoundEffect, PLAY + DEAD -> lock explosion gibs, ACTV + DEAD -> jingle streamedaudio, PLAY + + door connections: + OPEN -> lock cleared MemoryRelay, ACTV + + memory relay connections: + ACTV -> door, RSET + ACTV -> blast shield, DCTV + """ + super().patch_door(editor, world_name, area_name, dock_name, low_memory) + mrea = self.get_mrea(editor, world_name, area_name) + door = self.get_door_from_dock_index(mrea, self.get_dock_index(world_name, area_name, dock_name)) + _door = door.get_properties_as(Door) + + default = mrea.get_layer("Default") + + sound = default.add_instance_with(Sound( + editor_properties=EditorProperties( + name="Metal Door Lock Breaking", + transform=_door.editor_properties.transform + ), + sound=948, + max_audible_distance=150.0, + surround_pan=SurroundPan(surround_pan=1.0) + )) + + streamed = default.add_instance_with(StreamedAudio( + editor_properties=EditorProperties( + name="StreamedAudio - Event Jingle", + transform=_door.editor_properties.transform + ), + song_file="/audio/evt_x_event_00.dsp", + fade_in_time=0.01, + volume=65, + software_channel=1, + unknown=False + )) + + model = editor._resolve_asset_id(self.shield_model) + lock = default.add_instance_with(Actor( + editor_properties=EditorProperties( + name=f"{self.name} Blast Shield Lock", + transform=_door.editor_properties.transform + ), + collision_box=self.shield_collision_box, + collision_offset=self.shield_collision_offset, + vulnerability=self.vulnerability, + model=model, + actor_information=ActorParameters( + scannable=ScannableParameters( + scannable_info0=self.get_patched_scan(editor, world_name, area_name) + ) + ) + )) + + relay = default.add_memory_relay("Lock Cleared") + with relay.edit_properties(MemoryRelay) as _relay: + _relay.editor_properties.transform = _door.editor_properties.transform + _relay.editor_properties.active = False + + lock.add_connection(State.Dead, Message.Play, sound) + lock.add_connection(State.Dead, Message.Play, streamed) + lock.add_connection(State.Dead, Message.Activate, relay) + + relay.add_connection(State.Active, Message.Deactivate, lock) + relay.add_connection(State.Active, Message.Reset, door) + + door.add_connection(State.Open, Message.Activate, relay) + + dependencies = [ + model, + 0x8B4CD966, # MetalDoorLockBreak AGSC + ] + + if not low_memory: + particle = 0xCDCBDF04 + + gibs = default.add_instance_with(Effect( + editor_properties=EditorProperties( + transform=_door.editor_properties.transform, + active=False + ), + particle_effect=particle, + restart_on_activate=True, + motion_control_spline=self.get_spline(), + )) + + lock.add_connection(State.Dead, Message.Activate, gibs) + dependencies.append(particle) + + for pak in self.get_paks(editor, world_name, area_name): + for asset in dependencies: + editor.ensure_present(pak, asset) + + + +@dataclasses.dataclass(kw_only=True) +class VanillaBlastShieldDoorType(BlastShieldDoorType): + def remove_blast_shield(self, editor: PatcherEditor, world_name: str, area_name: str, dock_name: str): + mrea = self.get_mrea(editor, world_name, area_name) + door = self.get_door_from_dock_index(mrea, self.get_dock_index(world_name, area_name, dock_name)) + + relay = None + for connection in door.connections: + if connection.state == State.Open and connection.message == Message.Activate: + target = mrea.get_instance(connection.target) + if target.type == MemoryRelay: + relay = target + break + + if relay is None: + raise TypeError(f"No MemoryRelay connected to {door} in {world_name}/{area_name}") + + lock = None + for connection in relay.connections: + if connection.state == State.Active and connection.message == Message.Deactivate: + target = mrea.get_instance(connection.target) + if target.type == Actor: + lock = target + break + + if lock is None: + raise TypeError(f"No lock Actor connected to {relay} in {world_name}/{area_name}") + + for connection in lock.connections: + mrea.mrea.remove_instance(connection.target) + mrea.mrea.remove_instance(lock) + + +@dataclasses.dataclass(kw_only=True) +class SeekerBlastShieldDoorType(VanillaBlastShieldDoorType): + def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, dock_name: str, low_memory: bool): + raise NotImplementedError() + + def remove_blast_shield(self, editor: PatcherEditor, world_name: str, area_name: str, dock_name: str): + raise NotImplementedError() + + +@dataclasses.dataclass(kw_only=True) +class PlanetaryEnergyDoorType(DoorType): + planetary_energy_item_id: int + + def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, dock_name: str, low_memory: bool): + raise NotImplementedError() + + +@dataclasses.dataclass(kw_only=True) +class GrappleDoorType(BlastShieldDoorType): + def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, dock_name: str, low_memory: bool): + raise NotImplementedError() + + +@dataclasses.dataclass(kw_only=True) +class DarkVisorDoorType(BlastShieldDoorType): + def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, dock_name: str, low_memory: bool): + raise NotImplementedError() + + +@dataclasses.dataclass(kw_only=True) +class EchoVisorDoorType(BlastShieldDoorType): + def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, dock_name: str, low_memory: bool): + raise NotImplementedError() + + +@dataclasses.dataclass(kw_only=True) +class ScanVisorDoorType(BlastShieldDoorType): + def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, dock_name: str, low_memory: bool): + raise NotImplementedError() + + +@dataclasses.dataclass(kw_only=True) +class TranslatorDoorType(ScanVisorDoorType): + translator_item_id: int + + def patch_door(self, editor: PatcherEditor, world_name: str, area_name: str, dock_name: str, low_memory: bool): + raise NotImplementedError() + \ No newline at end of file diff --git a/open_prime_rando/echoes/dock_lock_rando/dock_type_database.py b/open_prime_rando/echoes/dock_lock_rando/dock_type_database.py index 65c3434..a63afce 100644 --- a/open_prime_rando/echoes/dock_lock_rando/dock_type_database.py +++ b/open_prime_rando/echoes/dock_lock_rando/dock_type_database.py @@ -4,8 +4,11 @@ from retro_data_structures.properties.echoes.archetypes.DamageVulnerability import DamageVulnerability from retro_data_structures.properties.echoes.archetypes.WeaponVulnerability import WeaponVulnerability from retro_data_structures.properties.echoes.core.Color import Color +from retro_data_structures.properties.echoes.core.Vector import Vector +from retro_data_structures.crc import crc32 -from open_prime_rando.echoes.dock_lock_rando.dock_type import DockType +from open_prime_rando.echoes.dock_lock_rando.dock_type import * +from open_prime_rando.echoes.dock_lock_rando.map_icons import DoorMapIcon reflect = WeaponVulnerability(damage_multiplier=0, effect=echoes.Effect.Reflect) vulnerable = WeaponVulnerability(damage_multiplier=100, effect=echoes.Effect.Normal) @@ -22,8 +25,16 @@ weapon_vulnerability=immune, normal_safe_zone=immune, ) -DOCK_TYPES = { - "Normal": DockType( +normal_door_model = 0x6B78FD92 +dark_door_model = 0xbbcf134d +annihilator_door_model = 0xa50c7238 + +blast_collision_box = Vector(0.35, 5.0, 4.0) +blast_collision_offset = Vector(-2/3, 0, 2.0) + +DOCK_TYPES: dict[str, DoorType] = { + "Normal": NormalDoorType( + name="Normal", vulnerability=dataclasses.replace( resist_all_vuln, power=vulnerable, dark=vulnerable, light=vulnerable, annihilator=vulnerable, @@ -33,9 +44,210 @@ missile=vulnerable, bomb=vulnerable, power_bomb=vulnerable, ), - shell_model=0x6B78FD92, shell_color=Color(r=0, g=1, b=1, a=1), - - # Burn Texture: 0x98CBBFB8 + map_icon=DoorMapIcon.Normal, + ), + "Dark": NormalDoorType( + name="Dark", + vulnerability=dataclasses.replace(resist_all_vuln, dark=vulnerable, entangler=vulnerable, black_hole=vulnerable), + shell_model=dark_door_model, + shell_color=Color(r=1, g=1, b=1, a=1), + scan_text=( + "There is a Blast Shield on the door blocking access. ", + "Analysis indicates that the Blast Shield is invulnerable to most weapons. Dark energy may damage it." + ), + map_icon=DoorMapIcon.Dark + ), + "Light": NormalDoorType( + name="Light", + vulnerability=dataclasses.replace(resist_all_vuln, light=vulnerable, light_blast=vulnerable, sunburst=vulnerable), + shell_color=Color(r=1, g=1, b=1, a=1), + scan_text=( + "There is a Blast Shield on the door blocking access. ", + "Analysis indicates that the Blast Shield is invulnerable to most weapons. Light energy may damage it." + ), + map_icon=DoorMapIcon.Light + ), + "Annihilator": NormalDoorType( + name="Annihilator", + vulnerability=dataclasses.replace(resist_all_vuln, annihilator=vulnerable, sonic_boom=vulnerable, imploder=vulnerable), + shell_model=annihilator_door_model, + shell_color=Color(r=1, g=1, b=1, a=1), + scan_text=( + "There is a Blast Shield on the door blocking access. ", + "Analysis indicates that the Blast Shield is invulnerable to most weapons. A mix of light and dark energy may damage it." + ), + map_icon=DoorMapIcon.Annihilator + ), + "Disabled": NormalDoorType( + name="Disabled", + vulnerability=resist_all_vuln, + shell_color=Color(r=0, g=0, b=0, a=0), + scan_text=( + "Door system access denied.", + "Unable to bypass security codes. Seek an alternate exit." + ), + map_icon=DoorMapIcon.Disabled + ), + "Missile": VanillaBlastShieldDoorType( + name="Missile", + vulnerability=dataclasses.replace(resist_all_vuln, missile=vulnerable), + shell_color=Color(r=1, g=0, b=0, a=1), + scan_text=( + "There is a Blast Shield on the door blocking access. ", + "Analysis indicates that the Blast Shield is invulnerable to most weapons. A Missile blast may damage it." + ), + map_icon=DoorMapIcon.Missile, + shield_model=0xBFB4A8EE, + ), + "SuperMissile": VanillaBlastShieldDoorType( + name="SuperMissile", + vulnerability=dataclasses.replace(resist_all_vuln, super_missle=vulnerable), + shell_color=Color(r=0, g=1, b=0, a=1), + scan_text=( + "There is a Blast Shield on the door blocking access. ", + "Analysis indicates that the Blast Shield is invulnerable to most weapons. A Super Missile blast may damage it." + ), + map_icon=DoorMapIcon.SuperMissile, + shield_model=0xF115F575, + ), + "PowerBomb": VanillaBlastShieldDoorType( + name="PowerBomb", + vulnerability=dataclasses.replace(resist_all_vuln, power_bomb=vulnerable), + shell_color=Color(r=1, g=0.94, b=0, a=1), + scan_text=( + "There is a Blast Shield on the door blocking access. ", + "Analysis indicates that the Blast Shield is invulnerable to most weapons. A Power Bomb may damage it." + ), + map_icon=DoorMapIcon.PowerBomb, + shield_model=0xC2E4F075, + ), + "SeekerMissile": SeekerBlastShieldDoorType( + name="SeekerMissile", + vulnerability=dataclasses.replace(resist_all_vuln, missile=vulnerable), + shell_color=Color(r=0.5, g=0, b=0.64, a=1), + scan_text=( + "There is a Blast Shield on the door blocking access. ", + "Analysis indicates that the Blast Shield is invulnerable to most weapons. Five simultaneous blasts from Seeker Missiles may damage it." + ), + map_icon=DoorMapIcon.SeekerMissile, + shield_model=0x56F4208B, + ), + "ScrewAttack": BlastShieldDoorType( + name="ScrewAttack", + vulnerability=dataclasses.replace(resist_all_vuln, screw_attack=vulnerable), + shell_model=annihilator_door_model, + shell_color=Color(r=0, g=0, b=1, a=1), + scan_text=( + "There is a Blast Shield on the door blocking access. ", + "Analysis indicates that the Blast Shield is invulnerable to most weapons. The Screw Attack may damage it." + ), + map_icon=DoorMapIcon.ScrewAttack, + shield_model=0x56F4208B, + ), + "Bomb": BlastShieldDoorType( + name="Bomb", + vulnerability=dataclasses.replace(resist_all_vuln, bomb=vulnerable), + shell_model=annihilator_door_model, + shell_color=Color(r=0, g=1, b=1, a=1), + scan_text=( + "There is a Blast Shield on the door blocking access. ", + "Analysis indicates that the Blast Shield is invulnerable to most weapons. A Morph Ball Bomb blast may damage it." + ), + map_icon=DoorMapIcon.Bomb, + shield_model=0x56F4208B, + ), + "Boost": BlastShieldDoorType( + name="Boost", + vulnerability=dataclasses.replace(resist_all_vuln, boost_ball=vulnerable, cannon_ball=vulnerable), + shell_model=annihilator_door_model, + shell_color=Color(r=1, g=1, b=0, a=1), + scan_text=( + "There is a Blast Shield on the door blocking access. ", + "Analysis indicates that the Blast Shield is invulnerable to most weapons. The Boost Ball may damage it." + ), + map_icon=DoorMapIcon.Boost, + shield_model=0x56F4208B, + ), + "Grapple": GrappleDoorType( + name="Grapple", + vulnerability=resist_all_vuln, + shell_model=annihilator_door_model, + shell_color=Color(r=1, g=0, b=0, a=1), + scan_text=( + "There is a Blast Shield on the door blocking access. ", + "Analysis indicates that the Blast Shield is invulnerable to weapon fire, but is weakly secured. The Grapple Beam may be able to remove it." + ), + map_icon=DoorMapIcon.Grapple, + shield_model=0x56F4208B, + ), + "Darkburst": BlastShieldDoorType( + name="Darkburst", + vulnerability=dataclasses.replace(resist_all_vuln, black_hole=vulnerable), + shell_model=dark_door_model, + shell_color=Color(r=1, g=1, b=1, a=1), + scan_text=( + "There is a Blast Shield on the door blocking access. ", + "Analysis indicates that the Blast Shield is invulnerable to most weapons. A massive burst of dark energy may damage it." + ), + map_icon=DoorMapIcon.Dark, + shield_model="custom_door_lock_darkburst.CMDL", + ), + "Sunburst": BlastShieldDoorType( + name="Sunburst", + vulnerability=dataclasses.replace(resist_all_vuln, sunburst=vulnerable), + shell_model=normal_door_model, + shell_color=Color(r=1, g=1, b=1, a=1), + scan_text=( + "There is a Blast Shield on the door blocking access. ", + "Analysis indicates that the Blast Shield is invulnerable to most weapons. A massive burst of light energy may damage it." + ), + map_icon=DoorMapIcon.Light, + shield_model="custom_door_lock_sunburst.CMDL", + ), + "SonicBoom": BlastShieldDoorType( + name="SonicBoom", + vulnerability=dataclasses.replace(resist_all_vuln, imploder=vulnerable), + shell_model=annihilator_door_model, + shell_color=Color(r=1, g=1, b=1, a=1), + scan_text=( + "There is a Blast Shield on the door blocking access. ", + "Analysis indicates that the Blast Shield is invulnerable to most weapons. A massive burst of light and dark energy may damage it." + ), + map_icon=DoorMapIcon.Annihilator, + shield_model="custom_door_lock_sonicboom.CMDL", + ), + "AgonEnergy": PlanetaryEnergyDoorType( + name="AgonEnergy", + vulnerability=resist_all_vuln, + shell_color=Color(r=0.64, g=0.34, b=0, a=1), + scan_text=( + "There is a Luminoth barrier on the door blocking access. ", + "Analysis indicates that the barrier is linked to the energy of Agon. Return the energy to the Agon Temple." + ), + map_icon=DoorMapIcon.AgonEnergy, + planetary_energy_item_id=-1 # TODO + ), + "TorvusEnergy": PlanetaryEnergyDoorType( + name="TorvusEnergy", + vulnerability=resist_all_vuln, + shell_color=Color(r=0.31, g=0.59, b=38, a=1), + scan_text=( + "There is a Luminoth barrier on the door blocking access. ", + "Analysis indicates that the barrier is linked to the energy of Torvus. Return the energy to the Torvus Temple." + ), + map_icon=DoorMapIcon.TorvusEnergy, + planetary_energy_item_id=-1 # TODO + ), + "SanctuaryEnergy": PlanetaryEnergyDoorType( + name="SanctuaryEnergy", + vulnerability=resist_all_vuln, + shell_color=Color(r=0.64, g=0.34, b=0, a=1), + scan_text=( + "There is a Luminoth barrier on the door blocking access. ", + "Analysis indicates that the barrier is linked to the energy of Sanctuary. Return the energy to the Sanctuary Temple." + ), + map_icon=DoorMapIcon.AgonEnergy, + planetary_energy_item_id=-1 # TODO ), } diff --git a/open_prime_rando/echoes/dock_lock_rando/map_icons.py b/open_prime_rando/echoes/dock_lock_rando/map_icons.py new file mode 100644 index 0000000..fb1dd85 --- /dev/null +++ b/open_prime_rando/echoes/dock_lock_rando/map_icons.py @@ -0,0 +1,92 @@ +from enum import IntEnum +import struct +from typing import NamedTuple + + +class DoorIconColors(NamedTuple): + surface_color: int + outline_color: int | None = None # unused + + +class DoorMapIcon(IntEnum): + # vanilla + Normal = 0 + Missile = 1 + Dark = 2 + Annihilator = 3 + Light = 4 + SuperMissile = 5 + SeekerMissile = 6 + PowerBomb = 7 + + # planetary energy + AgonEnergy = -1 + TorvusEnergy = -2 + SanctuaryEnergy = -3 + + # visors + ScanVisor = -4 + DarkVisor = -5 + EchoVisor = -6 + + # misc + Disabled = -7 + ScrewAttack = -8 + Bomb = -9 + Boost = -10 + Grapple = -11 + + @property + def colors(self) -> DoorIconColors: + if self == DoorMapIcon.Normal: + return DoorIconColors(0x3379bfff) + if self == DoorMapIcon.Missile: + return DoorIconColors(0xd63333ff) + if self == DoorMapIcon.Dark: + return DoorIconColors(0x000000ff, 0x898989ff) + if self == DoorMapIcon.Annihilator: + return DoorIconColors(0x4b4b4bff) + if self == DoorMapIcon.Light: + return DoorIconColors(0xffffffff) + if self == DoorMapIcon.SuperMissile: + return DoorIconColors(0x50a148ff) + if self == DoorMapIcon.SeekerMissile: + return DoorIconColors(0x794f77ff) + if self == DoorMapIcon.PowerBomb: + return DoorIconColors(0xeae50bff) + + if self == DoorMapIcon.AgonEnergy: + return DoorIconColors(0xa45600ff) + if self == DoorMapIcon.TorvusEnergy: + return DoorIconColors(0x4e9761ff) + if self == DoorMapIcon.SanctuaryEnergy: + return DoorIconColors(0x56789dff) + + if self == DoorMapIcon.ScanVisor: + return DoorIconColors(0x007f7fff) + if self == DoorMapIcon.DarkVisor: + return DoorIconColors(0x660000ff) + # if self == DoorMapIcon.EchoVisor: + # return + + if self == DoorMapIcon.Disabled: + return DoorIconColors(0x202020ff) + # if self == DoorMapIcon.ScrewAttack: + # return + # if self == DoorMapIcon.Bomb: + # return + # if self == DoorMapIcon.Boost: + # return + # if self == DoorMapIcon.Grapple: + # return + + return DoorIconColors(0xff00ffff) + + @staticmethod + def get_surface_colors_as_bytes() -> bytes: + colors = [icon.colors.surface_color for icon in sorted(DoorMapIcon)] + return struct.pack(">" + "L"*len(colors), *colors) + + @staticmethod + def get_door_index_bounds() -> tuple[int, int]: + return min(*DoorMapIcon), max(*DoorMapIcon) diff --git a/open_prime_rando/echoes/schema.json b/open_prime_rando/echoes/schema.json index d6cf60b..edb3e9f 100644 --- a/open_prime_rando/echoes/schema.json +++ b/open_prime_rando/echoes/schema.json @@ -112,6 +112,10 @@ }, "default": {}, "$comment": "The list of layers is replaced in runtime with the existing layers of each specific area." + }, + "low_memory_mode": { + "type": "boolean", + "default": false } }, "additionalProperties": false @@ -131,10 +135,40 @@ }, "required": ["area", "dock"], "additionalProperties": false + }, + "old_door_type": { + "$ref": "#/$defs/dock_type" + }, + "new_door_type": { + "$ref": "#/$defs/dock_type" } }, "default": {}, "additionalProperties": false + }, + "dock_type": { + "type": "string", + "enum": [ + "Normal", + "Dark", + "Light", + "Annihilator", + "Disabled", + "Missile", + "SuperMissile", + "PowerBomb", + "SeekerMissile", + "ScrewAttack", + "Bomb", + "Boost", + "Grapple", + "Darkburst", + "Sunburst", + "SonicBoom", + "AgonEnergy", + "TorvusEnergy", + "SanctuaryEnergy" + ] } } } diff --git a/open_prime_rando/echoes_patcher.py b/open_prime_rando/echoes_patcher.py index c7fc597..07cd967 100644 --- a/open_prime_rando/echoes_patcher.py +++ b/open_prime_rando/echoes_patcher.py @@ -3,7 +3,7 @@ from pathlib import Path from open_prime_rando import dynamic_schema -from open_prime_rando.echoes import auto_enabled_elevator_patches, specific_area_patches, asset_ids +from open_prime_rando.echoes import auto_enabled_elevator_patches, specific_area_patches, asset_ids, dock_lock_rando from open_prime_rando.echoes.inverted import apply_inverted from open_prime_rando.echoes.small_randomizations import apply_small_randomizations from open_prime_rando.patcher_editor import PatcherEditor @@ -24,21 +24,36 @@ def _read_schema(): def apply_area_modifications(editor: PatcherEditor, configuration: dict[str, dict]): for world_name, world_config in configuration.items(): world_meta = asset_ids.world.load_dedicated_file(world_name) - mlvl = editor.get_mlvl(asset_ids.world.NAME_TO_ID[world_name]) + mlvl = editor.get_mlvl(asset_ids.world.NAME_TO_ID_MLVL[world_name]) areas_by_name: dict[str, AreaWrapper] = { get_name_for_area(area): area for area in mlvl.areas } - for area_name, area in areas_by_name.items(): + for i, (area_name, area) in enumerate(areas_by_name.items()): if area_name not in world_config["areas"]: continue + + LOG.info(f"[{100*i/len(areas_by_name)}%] Processing {area_name}...") area_config = world_config["areas"][area_name] + low_memory = area_config["low_memory_mode"] for dock_name, dock_config in area_config["docks"].items(): dock_number = world_meta.DOCK_NAMES[area_name][dock_name] + + if "new_door_type" in dock_config: + dock_lock_rando.apply_door_rando( + editor, + world_name, + area_name, + dock_name, + dock_config["new_door_type"], + dock_config.get("old_door_type"), + low_memory + ) + if "connect_to" in dock_config: dock_target = dock_config["connect_to"] LOG.debug("Connecting dock %s of %s - %s to %s - %s", @@ -49,6 +64,8 @@ def apply_area_modifications(editor: PatcherEditor, configuration: dict[str, dic for layer_name, layer_state in area_config["layers"].items(): LOG.debug("Setting layer %s of %s - %s to %s", layer_name, world_name, area_name, str(layer_state)) area.get_layer(layer_name).active = layer_state + + area.build_mlvl_dependencies(only_modified=True) def patch_paks(file_provider: FileProvider, output_path: Path, configuration: dict): @@ -63,12 +80,12 @@ def patch_paks(file_provider: FileProvider, output_path: Path, configuration: di DefaultValidatingDraft7Validator(schema).validate(configuration) # custom_assets.create_custom_assets(editor) + dock_lock_rando.add_custom_models(editor) if configuration["auto_enabled_elevators"]: auto_enabled_elevator_patches.apply_auto_enabled_elevators_patch(editor) specific_area_patches.specific_patches(editor, configuration["area_patches"]) apply_area_modifications(editor, configuration["worlds"]) apply_small_randomizations(editor, configuration["small_randomizations"]) - # apply_door_rando(editor, []) if configuration["inverted"]: apply_inverted(editor) diff --git a/open_prime_rando/patcher_editor.py b/open_prime_rando/patcher_editor.py index 35d413c..ee263a7 100644 --- a/open_prime_rando/patcher_editor.py +++ b/open_prime_rando/patcher_editor.py @@ -5,7 +5,7 @@ from ppc_asm.dol_file import DolEditor, DolHeader from retro_data_structures.asset_manager import AssetManager, FileProvider -from retro_data_structures.base_resource import BaseResource, NameOrAssetId, RawResource, AssetId +from retro_data_structures.base_resource import Resource, BaseResource, NameOrAssetId, RawResource, AssetId from retro_data_structures.crc import crc32 from retro_data_structures.formats.mlvl import Mlvl, AreaWrapper from retro_data_structures.formats.mrea import Mrea @@ -77,3 +77,12 @@ def save_modifications(self, output_path: Path): target_dol = output_path.joinpath("sys/main.dol") target_dol.parent.mkdir(exist_ok=True, parents=True) target_dol.write_bytes(self.dol.dol_file.getvalue()) + + def add_or_replace_custom_asset(self, name: str, new_data: Resource, in_paks: typing.Iterable[str] = ()) -> AssetId: + if self.does_asset_exists(name): + asset_id = self.replace_asset(name, new_data) + for pak in in_paks: + self.ensure_present(pak, asset_id) + else: + asset_id = self.add_file(name, new_data, in_paks) + return asset_id diff --git a/setup.cfg b/setup.cfg index 95b22e9..9384600 100644 --- a/setup.cfg +++ b/setup.cfg @@ -10,18 +10,18 @@ classifiers = License :: OSI Approved :: GNU General Public License v3 (GPLv3) Development Status :: 3 - Alpha Intended Audience :: Developers - Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 [options] packages = find: install_requires = - retro-data-structures>=0.18.0 + retro-data-structures>=0.19.1 jsonschema>=4.0.0 ppc-asm include_package_data = True zip_safe = False -python_requires = >=3.9 +python_requires = >=3.10 [options.packages.find] exclude = diff --git a/tools/asset_id_files.py b/tools/asset_id_files.py index ec336f8..590a7d3 100644 --- a/tools/asset_id_files.py +++ b/tools/asset_id_files.py @@ -55,7 +55,7 @@ def generate_template(items: dict[str, int], suffix: str) -> str: ) template += "\n" - template += "\nNAME_TO_ID = {\n" + template += f"\nNAME_TO_ID{suffix}" + " = {\n" for name in sorted(items): template += f" \"{name}\": 0x{items[name]:08X},\n" template += "}\n" @@ -68,6 +68,7 @@ def create_asset_id_files(editor: PatcherEditor, output_path: Path): custom_world_names = _CUSTOM_WORLD_NAMES.get(editor.target_game, {}) world_names = {} + mapw_names = {} for value in editor.all_asset_ids(): if editor.get_asset_type(value).lower() != "mlvl": @@ -85,11 +86,13 @@ def create_asset_id_files(editor: PatcherEditor, output_path: Path): world_name = custom_world_names[value] world_names[world_name] = value + mapw_names[world_name] = mlvl.raw.world_map_id area_names = {} + mapa_names = {} dock_names = {} - for area in mlvl.raw.areas: + for area, mapa in zip(mlvl.raw.areas, mlvl.mapw.mapa_ids): try: strg = editor.get_parsed_asset(area.area_name_id, type_hint=Strg) area_name = strg.raw.string_tables[0].strings[0].string @@ -99,6 +102,7 @@ def create_asset_id_files(editor: PatcherEditor, output_path: Path): assert area_name not in area_names, area_name area_names[area_name] = area.area_mrea_id + mapa_names[area_name] = mapa mrea = editor.get_parsed_asset(area_names[area_name], type_hint=Mrea) docks = {} @@ -116,10 +120,12 @@ def create_asset_id_files(editor: PatcherEditor, output_path: Path): dock_names[area_name] = docks world_file_body = generate_template(area_names, "_MREA") + world_file_body += generate_template(mapa_names, "_MAPA") world_file_body += dock_name_templates(dock_names) output_path.joinpath(f"{filter_name(world_name).lower()}.py").write_text(world_file_body) global_file_body = generate_template(world_names, "_MLVL") + global_file_body += generate_template(mapw_names, "_MAPW") global_file_body += "\n_DEDICATED_FILES = {\n" for name in sorted(world_names.keys()):