forked from mirror/Archipelago
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
703 lines
27 KiB
Python
703 lines
27 KiB
Python
"""Place Shuffled Shops."""
|
|
|
|
from randomizer.Enums.Regions import Regions
|
|
from randomizer.Enums.Maps import Maps
|
|
from randomizer.Enums.VendorType import VendorType
|
|
from randomizer.Patching.Library.DataTypes import float_to_hex, intf_to_float
|
|
from randomizer.Patching.Library.Assets import getPointerLocation, TableNames
|
|
from randomizer.Patching.Patcher import LocalROM
|
|
from randomizer.ShuffleShopLocations import available_shops
|
|
|
|
|
|
class FunctionData:
|
|
"""Function Data Class."""
|
|
|
|
def __init__(
|
|
self,
|
|
function: int,
|
|
parameters: list,
|
|
inverted: bool = False,
|
|
exist_vendors: list = [VendorType.Candy, VendorType.Cranky, VendorType.Funky, VendorType.Snide],
|
|
):
|
|
"""Initialize with given parameters."""
|
|
self.function = function
|
|
self.parameters = parameters.copy()
|
|
self.inverted = inverted
|
|
self.exist_vendors = exist_vendors.copy()
|
|
|
|
|
|
class ScriptBlock:
|
|
"""Script Block Class."""
|
|
|
|
def __init__(
|
|
self,
|
|
conditions: list,
|
|
executions: list,
|
|
exist_vendors: list = [VendorType.Candy, VendorType.Cranky, VendorType.Funky, VendorType.Snide],
|
|
):
|
|
"""Initialize with given parameters."""
|
|
self.conditions = conditions.copy()
|
|
self.executions = executions.copy()
|
|
self.exist_vendors = exist_vendors.copy()
|
|
|
|
|
|
def getShopkeeperInstanceScript(vendor: VendorType, water_id: int = None, lz_id: int = None) -> list:
|
|
"""Get the instance script associated with a shopkeeper."""
|
|
script = []
|
|
# Generate Script
|
|
data_arg_0 = {
|
|
VendorType.Candy: 2,
|
|
VendorType.Cranky: 3,
|
|
VendorType.Funky: 2,
|
|
VendorType.Snide: 4,
|
|
}
|
|
data_arg_1 = {
|
|
VendorType.Candy: 0,
|
|
VendorType.Cranky: 1,
|
|
VendorType.Funky: 2,
|
|
VendorType.Snide: 3,
|
|
}
|
|
data_arg_2 = {
|
|
VendorType.Candy: 80,
|
|
VendorType.Cranky: 120,
|
|
VendorType.Funky: 80,
|
|
VendorType.Snide: 50,
|
|
}
|
|
data_arg_3 = {
|
|
VendorType.Candy: 101,
|
|
VendorType.Cranky: 100,
|
|
VendorType.Funky: 102,
|
|
}
|
|
data_arg_4 = {
|
|
VendorType.Cranky: 4,
|
|
VendorType.Funky: 3,
|
|
}
|
|
data_arg_5 = {
|
|
VendorType.Candy: 815,
|
|
VendorType.Funky: 814,
|
|
}
|
|
data_arg_6 = {
|
|
VendorType.Candy: 8,
|
|
VendorType.Funky: 12,
|
|
}
|
|
data_arg_7 = {
|
|
VendorType.Candy: 816,
|
|
VendorType.Funky: 814,
|
|
}
|
|
data_arg_8 = {
|
|
VendorType.Candy: 20,
|
|
VendorType.Funky: 20,
|
|
VendorType.Cranky: 20,
|
|
VendorType.Snide: 15,
|
|
}
|
|
data_arg_9 = {
|
|
VendorType.Cranky: 50,
|
|
VendorType.Snide: 28,
|
|
}
|
|
data_arg_10 = {
|
|
VendorType.Candy: 2,
|
|
VendorType.Funky: 2,
|
|
VendorType.Cranky: 4,
|
|
VendorType.Snide: 4,
|
|
}
|
|
bone_id = {
|
|
VendorType.Candy: 1,
|
|
VendorType.Funky: 3,
|
|
VendorType.Cranky: 10,
|
|
VendorType.Snide: 10,
|
|
}
|
|
|
|
range_val = 100
|
|
if water_id is not None:
|
|
script.append(
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(0, [0, 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(27, [water_id, 1, 0]),
|
|
FunctionData(123, [lz_id, bone_id.get(vendor, 0), 0]),
|
|
],
|
|
)
|
|
)
|
|
range_val = 120
|
|
else:
|
|
script.append(
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [0, 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(20, [data_arg_4.get(vendor, 0), data_arg_8.get(vendor, 0), 0]),
|
|
FunctionData(24, [data_arg_4.get(vendor, 0), 1, 0]),
|
|
],
|
|
[VendorType.Cranky, VendorType.Funky],
|
|
)
|
|
)
|
|
|
|
script.extend(
|
|
[
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [0, 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(90, [range_val, range_val, range_val]),
|
|
FunctionData(61, [4, 0, 0]),
|
|
FunctionData(20, [data_arg_0.get(vendor, 0), data_arg_8.get(vendor, 0), 0]),
|
|
FunctionData(24, [data_arg_0.get(vendor, 0), 1, 0]),
|
|
],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [0, 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(20, [2, 80, 0], False, [VendorType.Cranky]),
|
|
FunctionData(24, [1, 1, 0]),
|
|
FunctionData(22, [1, 1, 0]),
|
|
FunctionData(20, [1, 0, 0]),
|
|
],
|
|
),
|
|
]
|
|
)
|
|
if vendor in (VendorType.Candy, VendorType.Funky):
|
|
script.extend(
|
|
[
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [0, 0, 0]),
|
|
FunctionData(32, [400, 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(17, [3, 65535, 0]),
|
|
],
|
|
[VendorType.Funky],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [0, 0, 0]),
|
|
FunctionData(32, [400, 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(17, [2, 65535, 0]),
|
|
FunctionData(26, [1, 41, 100]),
|
|
FunctionData(17, [1, 1, 0]),
|
|
FunctionData(1, [1, 0, 0]),
|
|
],
|
|
),
|
|
]
|
|
)
|
|
elif vendor in (VendorType.Cranky, VendorType.Snide):
|
|
script.extend(
|
|
[
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [0, 0, 0]),
|
|
FunctionData(32, [400, 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(17, [4, 65535, 0]),
|
|
],
|
|
[VendorType.Cranky],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [0, 0, 0]),
|
|
FunctionData(32, [400, 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(17, [4, 65535, 0], False, [VendorType.Snide]),
|
|
FunctionData(38, [1, 0, 0], False, [VendorType.Cranky]),
|
|
FunctionData(26, [1, data_arg_9.get(vendor, 0), 100]),
|
|
FunctionData(17, [1, 1, 0]),
|
|
FunctionData(1, [1, 0, 0]),
|
|
],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [0, 0, 0]),
|
|
FunctionData(32, [400, 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(38, [1, 0, 0]),
|
|
FunctionData(124, [1, 0, 0]),
|
|
FunctionData(125, [295, 0, 6480]),
|
|
],
|
|
[VendorType.Snide],
|
|
),
|
|
]
|
|
)
|
|
script.extend(
|
|
[
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [1, 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(38, [1, 0, 0], False, [VendorType.Candy, VendorType.Funky]),
|
|
FunctionData(18, [1, 0, 0]),
|
|
FunctionData(1, [12, 0, 0]),
|
|
],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [0, 0, 0]),
|
|
FunctionData(32, [400, 0, 0], True),
|
|
],
|
|
[
|
|
FunctionData(69, [1, 0, 255]),
|
|
FunctionData(26, [1, 0, 0]),
|
|
FunctionData(17, [1, 1, 0]),
|
|
FunctionData(1, [5, 0, 0]),
|
|
],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [5, 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(18, [1, 0, 0]),
|
|
FunctionData(1, [10, 0, 0]),
|
|
],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [10, 0, 0]),
|
|
FunctionData(32, [1000, 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(7, [94, data_arg_1.get(vendor, 0), 0]),
|
|
],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [10, 0, 0]),
|
|
FunctionData(32, [400, 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(69, [0, 0, 255]),
|
|
FunctionData(20, [1, data_arg_2.get(vendor, 0), 0]),
|
|
FunctionData(17, [1, 1, 0]),
|
|
FunctionData(1, [11, 0, 0], False, [VendorType.Candy, VendorType.Cranky, VendorType.Snide]),
|
|
],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [10, 0, 0]),
|
|
FunctionData(32, [400, 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(17, [4, 65535, 0], False, [VendorType.Cranky]),
|
|
FunctionData(15, [74, 12160, 40], False, [VendorType.Snide]),
|
|
FunctionData(38, [1, 0, 0], False, [VendorType.Cranky, VendorType.Candy, VendorType.Funky]),
|
|
FunctionData(15, [data_arg_5.get(vendor, 0), 0, 40], False, [VendorType.Candy, VendorType.Funky]),
|
|
FunctionData(124, [1, 0, 0], False, [VendorType.Snide]),
|
|
FunctionData(125, [295, 0, 6480], False, [VendorType.Snide]),
|
|
FunctionData(17, [2, 65535, 0], False, [VendorType.Candy, VendorType.Funky]),
|
|
FunctionData(17, [3, 65535, 0], False, [VendorType.Funky]),
|
|
FunctionData(17, [4, 65535, 0], False, [VendorType.Snide]),
|
|
FunctionData(14, [256, 0, 40], False, [VendorType.Cranky]),
|
|
FunctionData(72, [0, 9000, 20], False, [VendorType.Cranky]),
|
|
],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [10, 0, 0]),
|
|
FunctionData(32, [400, 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(3, [0, 18, 0], False, [VendorType.Funky, VendorType.Candy, VendorType.Cranky]),
|
|
FunctionData(38, [1, 0, 0], False, [VendorType.Snide]),
|
|
FunctionData(1, [11, 0, 0], False, [VendorType.Funky]),
|
|
],
|
|
[VendorType.Funky, VendorType.Snide],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [11, 0, 0]),
|
|
FunctionData(4, [1, 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(15, [91, 0, 40]),
|
|
],
|
|
[VendorType.Funky],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [11, 0, 0]),
|
|
FunctionData(21, [1, 0, 0], True),
|
|
],
|
|
[
|
|
FunctionData(16, [0, 0, 0], False, [VendorType.Cranky]),
|
|
FunctionData(1, [12, 0, 0]),
|
|
],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [12, 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(7, [data_arg_3.get(vendor, 0), 1, 0]),
|
|
],
|
|
[VendorType.Cranky, VendorType.Funky, VendorType.Candy],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [12, 0, 0]),
|
|
FunctionData(4, [0, 0, 0]),
|
|
FunctionData(21, [2, 0, 0], True),
|
|
],
|
|
[
|
|
FunctionData(26, [2, 0, 0]),
|
|
FunctionData(17, [2, 1, 0]),
|
|
FunctionData(3, [0, 100, 0]),
|
|
],
|
|
[VendorType.Cranky],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [12, 0, 0]),
|
|
FunctionData(4, [0, 1, 0]),
|
|
FunctionData(21, [3, 0, 0], True),
|
|
],
|
|
[
|
|
FunctionData(26, [3, 0, 0]),
|
|
FunctionData(17, [3, 1, 0]),
|
|
FunctionData(3, [0, 150, 1]),
|
|
],
|
|
[VendorType.Cranky],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [12, 0, 0]),
|
|
FunctionData(32, [500, 0, 0], True),
|
|
],
|
|
[
|
|
FunctionData(18, [data_arg_10.get(vendor, 0), 0, 0]),
|
|
FunctionData(20, [1, data_arg_2.get(vendor, 0), 0]),
|
|
FunctionData(17, [1, 1, 0]),
|
|
FunctionData(1, [13, 0, 0]),
|
|
],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [12, 0, 0]),
|
|
FunctionData(32, [500, 0, 0], True),
|
|
],
|
|
[
|
|
FunctionData(14, [256, 0, 40], False, [VendorType.Cranky]),
|
|
FunctionData(72, [0, 7000, 20], False, [VendorType.Cranky]),
|
|
FunctionData(18, [3, 0, 0], False, [VendorType.Funky]),
|
|
FunctionData(16, [0, 0, 0], False, [VendorType.Snide]),
|
|
FunctionData(3, [0, 18, 0]),
|
|
],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [13, 0, 0]),
|
|
FunctionData(4, [data_arg_6.get(vendor, 0), 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(15, [data_arg_7.get(vendor, 0), 0, 40]),
|
|
],
|
|
[VendorType.Candy, VendorType.Funky],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [13, 0, 0]),
|
|
FunctionData(4, [0, 0, 0]),
|
|
],
|
|
[
|
|
FunctionData(15, [661, 0, 40]),
|
|
FunctionData(1, [14, 0, 0]),
|
|
],
|
|
),
|
|
ScriptBlock(
|
|
[
|
|
FunctionData(1, [14, 0, 0]),
|
|
FunctionData(21, [1, 0, 0], True),
|
|
],
|
|
[
|
|
FunctionData(16, [0, 0, 0], False, [VendorType.Cranky]),
|
|
FunctionData(38, [0, 0, 0], False, [VendorType.Snide]),
|
|
FunctionData(69, [1, 0, 255]),
|
|
FunctionData(38, [0, 0, 0], False, [VendorType.Cranky, VendorType.Candy, VendorType.Funky]),
|
|
FunctionData(1, [10, 0, 0]),
|
|
],
|
|
),
|
|
]
|
|
)
|
|
# Parse Script to remove anything unused
|
|
script = [x for x in script if vendor in x.exist_vendors]
|
|
for block in script:
|
|
block.conditions = [x for x in block.conditions if vendor in x.exist_vendors]
|
|
block.executions = [x for x in block.executions if vendor in x.exist_vendors]
|
|
return script
|
|
|
|
|
|
def pushNewShopLocationWrite(ROM_COPY: LocalROM, cont_map_id: Maps, obj_id: int, old_vendor: VendorType, new_vendor: VendorType):
|
|
"""Write new shop location script to ROM."""
|
|
script_table = getPointerLocation(TableNames.InstanceScripts, cont_map_id)
|
|
ROM_COPY.seek(script_table)
|
|
script_count = int.from_bytes(ROM_COPY.readBytes(2), "big")
|
|
good_scripts = []
|
|
# Construct good pre-existing scripts
|
|
file_offset = 2
|
|
for script_item in range(script_count):
|
|
ROM_COPY.seek(script_table + file_offset)
|
|
script_start = script_table + file_offset
|
|
script_id = int.from_bytes(ROM_COPY.readBytes(2), "big")
|
|
block_count = int.from_bytes(ROM_COPY.readBytes(2), "big")
|
|
file_offset += 6
|
|
for block_item in range(block_count):
|
|
ROM_COPY.seek(script_table + file_offset)
|
|
cond_count = int.from_bytes(ROM_COPY.readBytes(2), "big")
|
|
file_offset += 2 + (8 * cond_count)
|
|
ROM_COPY.seek(script_table + file_offset)
|
|
exec_count = int.from_bytes(ROM_COPY.readBytes(2), "big")
|
|
file_offset += 2 + (8 * exec_count)
|
|
script_end = script_table + file_offset
|
|
if script_id != obj_id:
|
|
script_data = []
|
|
ROM_COPY.seek(script_start)
|
|
for x in range(int((script_end - script_start) / 2)):
|
|
script_data.append(int.from_bytes(ROM_COPY.readBytes(2), "big"))
|
|
good_scripts.append(script_data)
|
|
# Get new script data
|
|
water_ids = [None, None]
|
|
if cont_map_id == Maps.GloomyGalleon:
|
|
if old_vendor == VendorType.Candy:
|
|
water_ids = [94, 17]
|
|
elif old_vendor == VendorType.Funky:
|
|
water_ids = [501, 24]
|
|
new_script_data = getShopkeeperInstanceScript(new_vendor, water_ids[0], water_ids[1])
|
|
script_arr = [
|
|
obj_id,
|
|
len(new_script_data),
|
|
0,
|
|
]
|
|
for block in new_script_data:
|
|
script_arr.append(len(block.conditions))
|
|
for cond in block.conditions:
|
|
func = cond.function
|
|
if cond.inverted:
|
|
func |= 0x8000
|
|
script_arr.append(func)
|
|
script_arr.extend(cond.parameters)
|
|
script_arr.append(len(block.executions))
|
|
for ex in block.executions:
|
|
script_arr.append(ex.function)
|
|
script_arr.extend(ex.parameters)
|
|
good_scripts.append(script_arr)
|
|
# Reconstruct File
|
|
ROM_COPY.seek(script_table)
|
|
ROM_COPY.writeMultipleBytes(len(good_scripts), 2)
|
|
for script in good_scripts:
|
|
for x in script:
|
|
ROM_COPY.writeMultipleBytes(x, 2)
|
|
|
|
|
|
def ApplyShopRandomizer(spoiler, ROM_COPY: LocalROM):
|
|
"""Write shop locations to ROM."""
|
|
if spoiler.settings.shuffle_shops:
|
|
shop_assortment = spoiler.shuffled_shop_locations
|
|
shop_placement_maps = []
|
|
for level in available_shops:
|
|
shop_array = available_shops[level]
|
|
for shop in shop_array:
|
|
if shop.map not in shop_placement_maps:
|
|
shop_placement_maps.append(shop.map)
|
|
for map in shop_placement_maps:
|
|
setup_address = getPointerLocation(TableNames.Setups, map)
|
|
lz_address = getPointerLocation(TableNames.Triggers, map)
|
|
shops_in_map = []
|
|
map_level = 0
|
|
for level in available_shops:
|
|
shop_array = available_shops[level]
|
|
for shop in shop_array:
|
|
if shop.map == map and not shop.locked:
|
|
shops_in_map.append(shop.shop)
|
|
map_level = level
|
|
placement_data = []
|
|
for shop in shops_in_map:
|
|
if map_level not in shop_assortment.keys():
|
|
continue
|
|
shop_data = {}
|
|
new_shop = shop_assortment[map_level][shop]
|
|
new_model = -1
|
|
new_lz = -1
|
|
new_rot = -1
|
|
new_scale = -1
|
|
search_model = -1
|
|
search_lz = -1
|
|
search_rot = -1
|
|
search_scale = -1
|
|
obj_id = None
|
|
search_vars = [shop, new_shop]
|
|
for x_i, x in enumerate(search_vars):
|
|
if x == Regions.CrankyGeneric:
|
|
if x_i == 0:
|
|
search_model = 0x73
|
|
search_lz = Maps.Cranky
|
|
search_rot = 180
|
|
search_scale = 0.95
|
|
else:
|
|
new_model = 0x73
|
|
new_lz = Maps.Cranky
|
|
new_rot = 180
|
|
new_scale = 0.95
|
|
elif x == Regions.CandyGeneric:
|
|
if x_i == 0:
|
|
search_model = 0x124
|
|
search_lz = Maps.Candy
|
|
search_rot = 0
|
|
search_scale = 0.95
|
|
else:
|
|
new_model = 0x124
|
|
new_lz = Maps.Candy
|
|
new_rot = 0
|
|
new_scale = 0.95
|
|
elif x == Regions.FunkyGeneric:
|
|
if x_i == 0:
|
|
search_model = 0x7A
|
|
search_lz = Maps.Funky
|
|
search_rot = 90
|
|
search_scale = 1.045
|
|
else:
|
|
new_model = 0x7A
|
|
new_lz = Maps.Funky
|
|
new_rot = 90
|
|
new_scale = 1.045
|
|
elif x == Regions.Snide:
|
|
if x_i == 0:
|
|
search_model = 0x79
|
|
search_lz = Maps.Snide
|
|
search_rot = 270
|
|
search_scale = 3
|
|
else:
|
|
new_model = 0x79
|
|
new_lz = Maps.Snide
|
|
new_rot = 270
|
|
new_scale = 3
|
|
if new_model > -1 and new_lz > -1 and search_model > -1 and search_lz > -1:
|
|
model_index = -1
|
|
zone_index = -1
|
|
ROM_COPY.seek(setup_address)
|
|
model2_count = int.from_bytes(ROM_COPY.readBytes(4), "big")
|
|
for model2_index in range(model2_count):
|
|
if model_index == -1:
|
|
obj_start = setup_address + 4 + (model2_index * 0x30)
|
|
ROM_COPY.seek(obj_start + 0x28)
|
|
obj_type = int.from_bytes(ROM_COPY.readBytes(2), "big")
|
|
if obj_type == search_model:
|
|
model_index = model2_index
|
|
obj_id = int.from_bytes(ROM_COPY.readBytes(2), "big")
|
|
ROM_COPY.seek(lz_address)
|
|
lz_count = int.from_bytes(ROM_COPY.readBytes(2), "big")
|
|
for lz_index in range(lz_count):
|
|
if zone_index == -1:
|
|
lz_start = lz_address + 2 + (lz_index * 0x38)
|
|
ROM_COPY.seek(lz_start + 0x10)
|
|
lz_type = int.from_bytes(ROM_COPY.readBytes(2), "big")
|
|
if lz_type == 16:
|
|
ROM_COPY.seek(lz_start + 0x12)
|
|
lz_map = int.from_bytes(ROM_COPY.readBytes(2), "big")
|
|
if lz_map == search_lz:
|
|
zone_index = lz_index
|
|
if model_index > -1 and zone_index > -1:
|
|
shop_data["model_index"] = model_index
|
|
shop_data["zone_index"] = zone_index
|
|
shop_data["replace_model"] = new_model
|
|
shop_data["original_model"] = search_model
|
|
shop_data["replace_zone"] = new_lz
|
|
shop_data["angle_change"] = search_rot - new_rot
|
|
shop_data["scale_factor"] = search_scale / new_scale
|
|
shop_data["object_id"] = obj_id
|
|
placement_data.append(shop_data)
|
|
else:
|
|
print(f"ERROR: Couldn't find LZ or Model attributed to shop ({model_index} | {zone_index})")
|
|
else:
|
|
print("ERROR: Couldn't find shop in assortment")
|
|
for placement in placement_data:
|
|
setup_item = setup_address + 4 + (placement["model_index"] * 0x30)
|
|
zone_item = lz_address + 2 + (placement["zone_index"] * 0x38)
|
|
# Type
|
|
ROM_COPY.seek(setup_item + 0x28)
|
|
ROM_COPY.writeMultipleBytes(placement["replace_model"], 2)
|
|
# Angle
|
|
if placement["angle_change"] != 0:
|
|
ROM_COPY.seek(setup_item + 0x1C)
|
|
original_angle = intf_to_float(int.from_bytes(ROM_COPY.readBytes(4), "big"))
|
|
new_angle = original_angle + placement["angle_change"]
|
|
if new_angle < 0:
|
|
new_angle += 360
|
|
elif new_angle >= 360:
|
|
new_angle -= 360
|
|
ROM_COPY.seek(setup_item + 0x1C)
|
|
ROM_COPY.writeMultipleBytes(int(float_to_hex(new_angle), 16), 4)
|
|
# Scale
|
|
ROM_COPY.seek(setup_item + 0xC)
|
|
original_scale = intf_to_float(int.from_bytes(ROM_COPY.readBytes(4), "big"))
|
|
new_scale = original_scale * placement["scale_factor"]
|
|
ROM_COPY.seek(setup_item + 0xC)
|
|
ROM_COPY.writeMultipleBytes(int(float_to_hex(new_scale), 16), 4)
|
|
# Get Model X and Z
|
|
ROM_COPY.seek(setup_item)
|
|
model_x = intf_to_float(int.from_bytes(ROM_COPY.readBytes(4), "big"))
|
|
ROM_COPY.seek(setup_item + 0x8)
|
|
model_z = intf_to_float(int.from_bytes(ROM_COPY.readBytes(4), "big"))
|
|
# Get Base Zone X and Z
|
|
if model_x < 0:
|
|
model_x = int(model_x) + 65536
|
|
else:
|
|
model_x = int(model_x)
|
|
if model_z < 0:
|
|
model_z = int(model_z) + 65536
|
|
else:
|
|
model_z = int(model_z)
|
|
ROM_COPY.seek(zone_item + 0xA)
|
|
lz_id = int.from_bytes(ROM_COPY.readBytes(2), "big")
|
|
if map != Maps.GloomyGalleon or lz_id not in (17, 24):
|
|
ROM_COPY.seek(zone_item)
|
|
ROM_COPY.writeMultipleBytes(model_x, 2)
|
|
ROM_COPY.seek(zone_item + 0x4)
|
|
ROM_COPY.writeMultipleBytes(model_z, 2)
|
|
# Overwrite new radius
|
|
model_to_vendor_table = {
|
|
0x73: VendorType.Cranky,
|
|
0x7A: VendorType.Funky,
|
|
0x124: VendorType.Candy,
|
|
0x79: VendorType.Snide,
|
|
}
|
|
|
|
base_model_scale = [88, 88]
|
|
if placement["replace_model"] == 0x73:
|
|
# Cranky
|
|
base_model_scale = [50, 30]
|
|
elif placement["replace_model"] == 0x7A:
|
|
# Funky
|
|
base_model_scale = [55, 25]
|
|
elif placement["replace_model"] == 0x124:
|
|
# Candy
|
|
base_model_scale = [40.1, 14]
|
|
elif placement["replace_model"] == 0x79:
|
|
# Snide
|
|
base_model_scale = [87.5, 59.5]
|
|
|
|
base_model_idx = 0
|
|
if map == Maps.GloomyGalleon and lz_id in (17, 24):
|
|
base_model_idx = 1
|
|
ROM_COPY.seek(zone_item + 0x6)
|
|
ROM_COPY.writeMultipleBytes(int(base_model_scale[base_model_idx] * new_scale), 2)
|
|
# Loading Zone
|
|
ROM_COPY.seek(zone_item + 0x12)
|
|
ROM_COPY.writeMultipleBytes(placement["replace_zone"], 2)
|
|
original_vendor = model_to_vendor_table.get(placement["original_model"], None)
|
|
new_vendor = model_to_vendor_table.get(placement["replace_model"], None)
|
|
if original_vendor is None:
|
|
raise Exception(f"Original vendor could not be found (Model: {placement['original_model']})")
|
|
if new_vendor is None:
|
|
raise Exception(f"New vendor could not be found (Model: {placement['replace_model']})")
|
|
pushNewShopLocationWrite(ROM_COPY, map, placement["object_id"], original_vendor, new_vendor)
|