Files
dockipelago/worlds/dk64/randomizer/Patching/KasplatLocationRando.py
Jonathan Tinney 7971961166
Some checks failed
Analyze modified files / flake8 (push) Failing after 2m28s
Build / build-win (push) Has been cancelled
Build / build-ubuntu2204 (push) Has been cancelled
ctest / Test C++ ubuntu-latest (push) Has been cancelled
ctest / Test C++ windows-latest (push) Has been cancelled
Analyze modified files / mypy (push) Has been cancelled
Build and Publish Docker Images / Push Docker image to Docker Hub (push) Successful in 5m4s
Native Code Static Analysis / scan-build (push) Failing after 5m2s
type check / pyright (push) Successful in 1m7s
unittests / Test Python 3.11.2 ubuntu-latest (push) Failing after 16m23s
unittests / Test Python 3.12 ubuntu-latest (push) Failing after 28m19s
unittests / Test Python 3.13 ubuntu-latest (push) Failing after 14m49s
unittests / Test hosting with 3.13 on ubuntu-latest (push) Successful in 5m0s
unittests / Test Python 3.13 macos-latest (push) Has been cancelled
unittests / Test Python 3.11 windows-latest (push) Has been cancelled
unittests / Test Python 3.13 windows-latest (push) Has been cancelled
add schedule I, sonic 1/frontiers/heroes, spirit island
2026-04-02 23:46:36 -07:00

193 lines
9.5 KiB
Python

"""Apply Kasplat Locations."""
from randomizer.Enums.Enemies import Enemies
from randomizer.Lists.KasplatLocations import KasplatLocationList
from randomizer.Enums.Maps import Maps
from randomizer.Patching.Patcher import LocalROM
from randomizer.Patching.Library.Assets import getPointerLocation, TableNames
def randomize_kasplat_locations(spoiler, ROM_COPY: LocalROM):
"""Write replaced enemies to ROM."""
kasplat_types = [
Enemies.KasplatDK,
Enemies.KasplatDiddy,
Enemies.KasplatLanky,
Enemies.KasplatTiny,
Enemies.KasplatChunky,
]
vanilla_kasplat_maps = [
Maps.JungleJapes,
Maps.JapesUnderGround,
Maps.AngryAztec,
Maps.AztecChunky5DTemple,
Maps.AztecLlamaTemple,
Maps.FranticFactory,
Maps.GloomyGalleon,
Maps.FungiForest,
Maps.ForestGiantMushroom,
Maps.CrystalCaves,
Maps.CreepyCastle,
Maps.CastleUpperCave,
Maps.CastleLowerCave,
Maps.CastleTree,
Maps.HideoutHelmLobby,
Maps.CreepyCastleLobby,
Maps.CrystalCavesLobby,
Maps.FranticFactoryLobby,
Maps.GloomyGalleonLobby,
]
if spoiler.settings.kasplat_rando:
selected_kasplat_names = [name for name in spoiler.shuffled_kasplat_map.keys()]
for cont_map_id in range(216):
cont_map_spawner_address = getPointerLocation(TableNames.Spawners, cont_map_id)
ROM_COPY.seek(cont_map_spawner_address)
fence_count = int.from_bytes(ROM_COPY.readBytes(2), "big")
offset = 2
fence_bytes = []
used_fence_ids = []
if fence_count > 0:
for x in range(fence_count):
fence = []
fence_start = cont_map_spawner_address + offset
ROM_COPY.seek(cont_map_spawner_address + offset)
point_count = int.from_bytes(ROM_COPY.readBytes(2), "big")
offset += (point_count * 6) + 2
ROM_COPY.seek(cont_map_spawner_address + offset)
point0_count = int.from_bytes(ROM_COPY.readBytes(2), "big")
offset += (point0_count * 10) + 6
fence_finish = cont_map_spawner_address + offset
fence_size = fence_finish - fence_start
ROM_COPY.seek(fence_finish - 4)
used_fence_ids.append(int.from_bytes(ROM_COPY.readBytes(2), "big"))
ROM_COPY.seek(fence_start)
for y in range(int(fence_size / 2)):
fence.append(int.from_bytes(ROM_COPY.readBytes(2), "big"))
fence_bytes.append(fence)
ROM_COPY.seek(fence_finish)
spawner_count_location = cont_map_spawner_address + offset
ROM_COPY.seek(spawner_count_location)
spawner_count = int.from_bytes(ROM_COPY.readBytes(2), "big")
offset += 2
spawner_bytes = []
used_enemy_indexes = []
for x in range(spawner_count):
ROM_COPY.seek(cont_map_spawner_address + offset)
enemy_id = int.from_bytes(ROM_COPY.readBytes(1), "big")
ROM_COPY.seek(cont_map_spawner_address + offset + 0x4)
enemy_coords = []
for y in range(3):
coord = int.from_bytes(ROM_COPY.readBytes(2), "big")
if coord > 32767:
coord -= 65536
enemy_coords.append(coord)
ROM_COPY.seek(cont_map_spawner_address + offset + 0x13)
enemy_index = int.from_bytes(ROM_COPY.readBytes(1), "big")
used_enemy_indexes.append(enemy_index)
init_offset = offset
ROM_COPY.seek(cont_map_spawner_address + offset + 0x11)
extra_count = int.from_bytes(ROM_COPY.readBytes(1), "big")
offset += 0x16 + (extra_count * 2)
end_offset = offset
is_vanilla = False
new_id = 0
for level in KasplatLocationList:
kasplats = KasplatLocationList[level]
for kasplat in kasplats:
if kasplat.vanilla and kasplat.name in selected_kasplat_names and kasplat.map == cont_map_id:
coord_match_count = 0
for y in range(3):
if enemy_coords[y] == kasplat.coords[y]:
coord_match_count += 1
if coord_match_count == 3:
is_vanilla = True
kong_idx = spoiler.shuffled_kasplat_map[kasplat.name]
new_id = kasplat_types[kong_idx]
if enemy_id not in kasplat_types or is_vanilla or cont_map_id not in vanilla_kasplat_maps:
data_bytes = []
spawner_size = end_offset - init_offset
ROM_COPY.seek(cont_map_spawner_address + init_offset)
for x in range(spawner_size):
if x == 0 and is_vanilla:
data_bytes.append(new_id)
ROM_COPY.seek(cont_map_spawner_address + init_offset + 1)
else:
data_bytes.append(int.from_bytes(ROM_COPY.readBytes(1), "big"))
spawner_bytes.append(data_bytes)
spawn_index = 1
fence_index = 1
for level in KasplatLocationList:
kasplats = KasplatLocationList[level]
for kasplat in kasplats:
if cont_map_id == kasplat.map and kasplat.name in selected_kasplat_names and not kasplat.vanilla:
if spawn_index in used_enemy_indexes:
while spawn_index in used_enemy_indexes:
spawn_index += 1
used_enemy_indexes.append(spawn_index)
if fence_index in used_fence_ids:
while fence_index in used_fence_ids:
fence_index += 1
used_fence_ids.append(fence_index)
scale = int(kasplat.scale * 0x32) & 0xFF
# Spawner
data_bytes = []
kong_idx = spoiler.shuffled_kasplat_map[kasplat.name]
data_bytes.append(kasplat_types[kong_idx])
data_bytes.append(0x7A)
for x in range(2):
data_bytes.append(0)
for x in kasplat.coords:
if x < 0:
x += 65536 # Convert to unsigned
data_bytes.append(int(x / 256))
data_bytes.append(int(x % 256))
for x in range(2):
data_bytes.append(0)
data_bytes.append(0x23) # Idle Speed
data_bytes.append(0x3C) # Aggro Speed
data_bytes.append(fence_index) # Fence ID
data_bytes.append(scale) # Scale
data_bytes.append(1) # Init Control State
data_bytes.append(0) # Extra Data Count
data_bytes.append(2) # Init Spawn State
data_bytes.append(spawn_index) # Spawn Index
data_bytes.append(0x1E) # Init Respawn Timer
data_bytes.append(0)
spawner_bytes.append(data_bytes)
# Fence
new_fence_bytes = []
a_0 = [kasplat.bounds[0], 0, kasplat.bounds[2]]
a_1 = [kasplat.bounds[1], 0, kasplat.bounds[2]]
a_2 = [kasplat.bounds[1], 0, kasplat.bounds[3]]
a_3 = [kasplat.bounds[0], 0, kasplat.bounds[3]]
a_01 = []
a_12 = []
a_23 = []
a_30 = []
for x in range(3):
a_01.append(int((a_0[x] + a_1[x]) / 2))
a_12.append(int((a_1[x] + a_2[x]) / 2))
a_23.append(int((a_2[x] + a_3[x]) / 2))
a_30.append(int((a_3[x] + a_0[x]) / 2))
fence_coords = [a_0, a_01, a_1, a_12, a_2, a_23, a_3, a_30]
new_fence_bytes.append(len(fence_coords)) # 0: Fence Block 0x6 Count, 1: Fence Block 0xA Count
for x in fence_coords:
for y in x:
if y < 0:
y += 65536 # Signed to unsigned conversion
new_fence_bytes.append(y)
new_fence_bytes.append(0)
new_fence_bytes.append(fence_index)
new_fence_bytes.append(1)
fence_bytes.append(new_fence_bytes)
ROM_COPY.seek(cont_map_spawner_address)
ROM_COPY.writeMultipleBytes(len(fence_bytes), 2)
for x in fence_bytes:
for y in x:
ROM_COPY.writeMultipleBytes(y, 2)
ROM_COPY.writeMultipleBytes(len(spawner_bytes), 2)
for x in spawner_bytes:
for y in x:
ROM_COPY.writeMultipleBytes(y, 1)