CV64: Use AP Procedure Patch (#3159)
This commit is contained in:
parent
7fd7d5e492
commit
437843bf53
|
@ -4,7 +4,7 @@ import settings
|
|||
import base64
|
||||
import logging
|
||||
|
||||
from BaseClasses import Item, Region, MultiWorld, Tutorial, ItemClassification
|
||||
from BaseClasses import Item, Region, Tutorial, ItemClassification
|
||||
from .items import CV64Item, filler_item_names, get_item_info, get_item_names_to_ids, get_item_counts
|
||||
from .locations import CV64Location, get_location_info, verify_locations, get_location_names_to_ids, base_id
|
||||
from .entrances import verify_entrances, get_warp_entrances
|
||||
|
@ -18,7 +18,7 @@ from ..AutoWorld import WebWorld, World
|
|||
from .aesthetics import randomize_lighting, shuffle_sub_weapons, rom_empty_breakables_flags, rom_sub_weapon_flags, \
|
||||
randomize_music, get_start_inventory_data, get_location_data, randomize_shop_prices, get_loading_zone_bytes, \
|
||||
get_countdown_numbers
|
||||
from .rom import LocalRom, patch_rom, get_base_rom_path, CV64DeltaPatch
|
||||
from .rom import RomData, write_patch, get_base_rom_path, CV64ProcedurePatch, CV64_US_10_HASH
|
||||
from .client import Castlevania64Client
|
||||
|
||||
|
||||
|
@ -27,7 +27,7 @@ class CV64Settings(settings.Group):
|
|||
"""File name of the CV64 US 1.0 rom"""
|
||||
copy_to = "Castlevania (USA).z64"
|
||||
description = "CV64 (US 1.0) ROM File"
|
||||
md5s = [CV64DeltaPatch.hash]
|
||||
md5s = [CV64_US_10_HASH]
|
||||
|
||||
rom_file: RomFile = RomFile(RomFile.copy_to)
|
||||
|
||||
|
@ -86,12 +86,6 @@ class CV64World(World):
|
|||
|
||||
web = CV64Web()
|
||||
|
||||
@classmethod
|
||||
def stage_assert_generate(cls, multiworld: MultiWorld) -> None:
|
||||
rom_file = get_base_rom_path()
|
||||
if not os.path.exists(rom_file):
|
||||
raise FileNotFoundError(rom_file)
|
||||
|
||||
def generate_early(self) -> None:
|
||||
# Generate the player's unique authentication
|
||||
self.auth = bytearray(self.multiworld.random.getrandbits(8) for _ in range(16))
|
||||
|
@ -276,18 +270,13 @@ class CV64World(World):
|
|||
offset_data.update(get_start_inventory_data(self.player, self.options,
|
||||
self.multiworld.precollected_items[self.player]))
|
||||
|
||||
cv64_rom = LocalRom(get_base_rom_path())
|
||||
patch = CV64ProcedurePatch()
|
||||
write_patch(self, patch, offset_data, shop_name_list, shop_desc_list, shop_colors_list, active_locations)
|
||||
|
||||
rompath = os.path.join(output_directory, f"{self.multiworld.get_out_file_name_base(self.player)}.z64")
|
||||
rom_path = os.path.join(output_directory, f"{self.multiworld.get_out_file_name_base(self.player)}"
|
||||
f"{patch.patch_file_ending}")
|
||||
|
||||
patch_rom(self, cv64_rom, offset_data, shop_name_list, shop_desc_list, shop_colors_list, active_locations)
|
||||
|
||||
cv64_rom.write_to_file(rompath)
|
||||
|
||||
patch = CV64DeltaPatch(os.path.splitext(rompath)[0] + CV64DeltaPatch.patch_file_ending, player=self.player,
|
||||
player_name=self.multiworld.player_name[self.player], patched_path=rompath)
|
||||
patch.write()
|
||||
os.unlink(rompath)
|
||||
patch.write(rom_path)
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
return self.random.choice(filler_item_names)
|
||||
|
|
|
@ -14,111 +14,111 @@ if TYPE_CHECKING:
|
|||
from . import CV64World
|
||||
|
||||
rom_sub_weapon_offsets = {
|
||||
0x10C6EB: (0x10, rname.forest_of_silence), # Forest
|
||||
0x10C6F3: (0x0F, rname.forest_of_silence),
|
||||
0x10C6FB: (0x0E, rname.forest_of_silence),
|
||||
0x10C703: (0x0D, rname.forest_of_silence),
|
||||
0x10C6EB: (b"\x10", rname.forest_of_silence), # Forest
|
||||
0x10C6F3: (b"\x0F", rname.forest_of_silence),
|
||||
0x10C6FB: (b"\x0E", rname.forest_of_silence),
|
||||
0x10C703: (b"\x0D", rname.forest_of_silence),
|
||||
|
||||
0x10C81F: (0x0F, rname.castle_wall), # Castle Wall
|
||||
0x10C827: (0x10, rname.castle_wall),
|
||||
0x10C82F: (0x0E, rname.castle_wall),
|
||||
0x7F9A0F: (0x0D, rname.castle_wall),
|
||||
0x10C81F: (b"\x0F", rname.castle_wall), # Castle Wall
|
||||
0x10C827: (b"\x10", rname.castle_wall),
|
||||
0x10C82F: (b"\x0E", rname.castle_wall),
|
||||
0x7F9A0F: (b"\x0D", rname.castle_wall),
|
||||
|
||||
0x83A5D9: (0x0E, rname.villa), # Villa
|
||||
0x83A5E5: (0x0D, rname.villa),
|
||||
0x83A5F1: (0x0F, rname.villa),
|
||||
0xBFC903: (0x10, rname.villa),
|
||||
0x10C987: (0x10, rname.villa),
|
||||
0x10C98F: (0x0D, rname.villa),
|
||||
0x10C997: (0x0F, rname.villa),
|
||||
0x10CF73: (0x10, rname.villa),
|
||||
0x83A5D9: (b"\x0E", rname.villa), # Villa
|
||||
0x83A5E5: (b"\x0D", rname.villa),
|
||||
0x83A5F1: (b"\x0F", rname.villa),
|
||||
0xBFC903: (b"\x10", rname.villa),
|
||||
0x10C987: (b"\x10", rname.villa),
|
||||
0x10C98F: (b"\x0D", rname.villa),
|
||||
0x10C997: (b"\x0F", rname.villa),
|
||||
0x10CF73: (b"\x10", rname.villa),
|
||||
|
||||
0x10CA57: (0x0D, rname.tunnel), # Tunnel
|
||||
0x10CA5F: (0x0E, rname.tunnel),
|
||||
0x10CA67: (0x10, rname.tunnel),
|
||||
0x10CA6F: (0x0D, rname.tunnel),
|
||||
0x10CA77: (0x0F, rname.tunnel),
|
||||
0x10CA7F: (0x0E, rname.tunnel),
|
||||
0x10CA57: (b"\x0D", rname.tunnel), # Tunnel
|
||||
0x10CA5F: (b"\x0E", rname.tunnel),
|
||||
0x10CA67: (b"\x10", rname.tunnel),
|
||||
0x10CA6F: (b"\x0D", rname.tunnel),
|
||||
0x10CA77: (b"\x0F", rname.tunnel),
|
||||
0x10CA7F: (b"\x0E", rname.tunnel),
|
||||
|
||||
0x10CBC7: (0x0E, rname.castle_center), # Castle Center
|
||||
0x10CC0F: (0x0D, rname.castle_center),
|
||||
0x10CC5B: (0x0F, rname.castle_center),
|
||||
0x10CBC7: (b"\x0E", rname.castle_center), # Castle Center
|
||||
0x10CC0F: (b"\x0D", rname.castle_center),
|
||||
0x10CC5B: (b"\x0F", rname.castle_center),
|
||||
|
||||
0x10CD3F: (0x0E, rname.tower_of_execution), # Character towers
|
||||
0x10CD65: (0x0D, rname.tower_of_execution),
|
||||
0x10CE2B: (0x0E, rname.tower_of_science),
|
||||
0x10CE83: (0x10, rname.duel_tower),
|
||||
0x10CD3F: (b"\x0E", rname.tower_of_execution), # Character towers
|
||||
0x10CD65: (b"\x0D", rname.tower_of_execution),
|
||||
0x10CE2B: (b"\x0E", rname.tower_of_science),
|
||||
0x10CE83: (b"\x10", rname.duel_tower),
|
||||
|
||||
0x10CF8B: (0x0F, rname.room_of_clocks), # Room of Clocks
|
||||
0x10CF93: (0x0D, rname.room_of_clocks),
|
||||
0x10CF8B: (b"\x0F", rname.room_of_clocks), # Room of Clocks
|
||||
0x10CF93: (b"\x0D", rname.room_of_clocks),
|
||||
|
||||
0x99BC5A: (0x0D, rname.clock_tower), # Clock Tower
|
||||
0x10CECB: (0x10, rname.clock_tower),
|
||||
0x10CED3: (0x0F, rname.clock_tower),
|
||||
0x10CEDB: (0x0E, rname.clock_tower),
|
||||
0x10CEE3: (0x0D, rname.clock_tower),
|
||||
0x99BC5A: (b"\x0D", rname.clock_tower), # Clock Tower
|
||||
0x10CECB: (b"\x10", rname.clock_tower),
|
||||
0x10CED3: (b"\x0F", rname.clock_tower),
|
||||
0x10CEDB: (b"\x0E", rname.clock_tower),
|
||||
0x10CEE3: (b"\x0D", rname.clock_tower),
|
||||
}
|
||||
|
||||
rom_sub_weapon_flags = {
|
||||
0x10C6EC: 0x0200FF04, # Forest of Silence
|
||||
0x10C6FC: 0x0400FF04,
|
||||
0x10C6F4: 0x0800FF04,
|
||||
0x10C704: 0x4000FF04,
|
||||
0x10C6EC: b"\x02\x00\xFF\x04", # Forest of Silence
|
||||
0x10C6FC: b"\x04\x00\xFF\x04",
|
||||
0x10C6F4: b"\x08\x00\xFF\x04",
|
||||
0x10C704: b"\x40\x00\xFF\x04",
|
||||
|
||||
0x10C831: 0x08, # Castle Wall
|
||||
0x10C829: 0x10,
|
||||
0x10C821: 0x20,
|
||||
0xBFCA97: 0x04,
|
||||
0x10C831: b"\x08", # Castle Wall
|
||||
0x10C829: b"\x10",
|
||||
0x10C821: b"\x20",
|
||||
0xBFCA97: b"\x04",
|
||||
|
||||
# Villa
|
||||
0xBFC926: 0xFF04,
|
||||
0xBFC93A: 0x80,
|
||||
0xBFC93F: 0x01,
|
||||
0xBFC943: 0x40,
|
||||
0xBFC947: 0x80,
|
||||
0x10C989: 0x10,
|
||||
0x10C991: 0x20,
|
||||
0x10C999: 0x40,
|
||||
0x10CF77: 0x80,
|
||||
0xBFC926: b"\xFF\x04",
|
||||
0xBFC93A: b"\x80",
|
||||
0xBFC93F: b"\x01",
|
||||
0xBFC943: b"\x40",
|
||||
0xBFC947: b"\x80",
|
||||
0x10C989: b"\x10",
|
||||
0x10C991: b"\x20",
|
||||
0x10C999: b"\x40",
|
||||
0x10CF77: b"\x80",
|
||||
|
||||
0x10CA58: 0x4000FF0E, # Tunnel
|
||||
0x10CA6B: 0x80,
|
||||
0x10CA60: 0x1000FF05,
|
||||
0x10CA70: 0x2000FF05,
|
||||
0x10CA78: 0x4000FF05,
|
||||
0x10CA80: 0x8000FF05,
|
||||
0x10CA58: b"\x40\x00\xFF\x0E", # Tunnel
|
||||
0x10CA6B: b"\x80",
|
||||
0x10CA60: b"\x10\x00\xFF\x05",
|
||||
0x10CA70: b"\x20\x00\xFF\x05",
|
||||
0x10CA78: b"\x40\x00\xFF\x05",
|
||||
0x10CA80: b"\x80\x00\xFF\x05",
|
||||
|
||||
0x10CBCA: 0x02, # Castle Center
|
||||
0x10CC10: 0x80,
|
||||
0x10CC5C: 0x40,
|
||||
0x10CBCA: b"\x02", # Castle Center
|
||||
0x10CC10: b"\x80",
|
||||
0x10CC5C: b"\x40",
|
||||
|
||||
0x10CE86: 0x01, # Duel Tower
|
||||
0x10CD43: 0x02, # Tower of Execution
|
||||
0x10CE2E: 0x20, # Tower of Science
|
||||
0x10CE86: b"\x01", # Duel Tower
|
||||
0x10CD43: b"\x02", # Tower of Execution
|
||||
0x10CE2E: b"\x20", # Tower of Science
|
||||
|
||||
0x10CF8E: 0x04, # Room of Clocks
|
||||
0x10CF96: 0x08,
|
||||
0x10CF8E: b"\x04", # Room of Clocks
|
||||
0x10CF96: b"\x08",
|
||||
|
||||
0x10CECE: 0x08, # Clock Tower
|
||||
0x10CED6: 0x10,
|
||||
0x10CEE6: 0x20,
|
||||
0x10CEDE: 0x80,
|
||||
0x10CECE: b"\x08", # Clock Tower
|
||||
0x10CED6: b"\x10",
|
||||
0x10CEE6: b"\x20",
|
||||
0x10CEDE: b"\x80",
|
||||
}
|
||||
|
||||
rom_empty_breakables_flags = {
|
||||
0x10C74D: 0x40FF05, # Forest of Silence
|
||||
0x10C765: 0x20FF0E,
|
||||
0x10C774: 0x0800FF0E,
|
||||
0x10C755: 0x80FF05,
|
||||
0x10C784: 0x0100FF0E,
|
||||
0x10C73C: 0x0200FF0E,
|
||||
0x10C74D: b"\x40\xFF\x05", # Forest of Silence
|
||||
0x10C765: b"\x20\xFF\x0E",
|
||||
0x10C774: b"\x08\x00\xFF\x0E",
|
||||
0x10C755: b"\x80\xFF\x05",
|
||||
0x10C784: b"\x01\x00\xFF\x0E",
|
||||
0x10C73C: b"\x02\x00\xFF\x0E",
|
||||
|
||||
0x10C8D0: 0x0400FF0E, # Villa foyer
|
||||
0x10C8D0: b"\x04\x00\xFF\x0E", # Villa foyer
|
||||
|
||||
0x10CF9F: 0x08, # Room of Clocks flags
|
||||
0x10CFA7: 0x01,
|
||||
0xBFCB6F: 0x04, # Room of Clocks candle property IDs
|
||||
0xBFCB73: 0x05,
|
||||
0x10CF9F: b"\x08", # Room of Clocks flags
|
||||
0x10CFA7: b"\x01",
|
||||
0xBFCB6F: b"\x04", # Room of Clocks candle property IDs
|
||||
0xBFCB73: b"\x05",
|
||||
}
|
||||
|
||||
rom_axe_cross_lower_values = {
|
||||
|
@ -269,19 +269,18 @@ renon_item_dialogue = {
|
|||
}
|
||||
|
||||
|
||||
def randomize_lighting(world: "CV64World") -> Dict[int, int]:
|
||||
def randomize_lighting(world: "CV64World") -> Dict[int, bytes]:
|
||||
"""Generates randomized data for the map lighting table."""
|
||||
randomized_lighting = {}
|
||||
for entry in range(67):
|
||||
for sub_entry in range(19):
|
||||
if sub_entry not in [3, 7, 11, 15] and entry != 4:
|
||||
# The fourth entry in the lighting table affects the lighting on some item pickups; skip it
|
||||
randomized_lighting[0x1091A0 + (entry * 28) + sub_entry] = \
|
||||
world.random.randint(0, 255)
|
||||
randomized_lighting[0x1091A0 + (entry * 28) + sub_entry] = bytes([world.random.randint(0, 255)])
|
||||
return randomized_lighting
|
||||
|
||||
|
||||
def shuffle_sub_weapons(world: "CV64World") -> Dict[int, int]:
|
||||
def shuffle_sub_weapons(world: "CV64World") -> Dict[int, bytes]:
|
||||
"""Shuffles the sub-weapons amongst themselves."""
|
||||
sub_weapon_dict = {offset: rom_sub_weapon_offsets[offset][0] for offset in rom_sub_weapon_offsets if
|
||||
rom_sub_weapon_offsets[offset][1] in world.active_stage_exits}
|
||||
|
@ -295,7 +294,7 @@ def shuffle_sub_weapons(world: "CV64World") -> Dict[int, int]:
|
|||
return dict(zip(sub_weapon_dict, sub_bytes))
|
||||
|
||||
|
||||
def randomize_music(world: "CV64World") -> Dict[int, int]:
|
||||
def randomize_music(world: "CV64World") -> Dict[int, bytes]:
|
||||
"""Generates randomized or disabled data for all the music in the game."""
|
||||
music_array = bytearray(0x7A)
|
||||
for number in music_sfx_ids:
|
||||
|
@ -340,15 +339,10 @@ def randomize_music(world: "CV64World") -> Dict[int, int]:
|
|||
music_array[i] = fade_in_songs[i]
|
||||
del (music_array[0x00: 0x10])
|
||||
|
||||
# Convert the music array into a data dict
|
||||
music_offsets = {}
|
||||
for i in range(len(music_array)):
|
||||
music_offsets[0xBFCD30 + i] = music_array[i]
|
||||
|
||||
return music_offsets
|
||||
return {0xBFCD30: bytes(music_array)}
|
||||
|
||||
|
||||
def randomize_shop_prices(world: "CV64World") -> Dict[int, int]:
|
||||
def randomize_shop_prices(world: "CV64World") -> Dict[int, bytes]:
|
||||
"""Randomize the shop prices based on the minimum and maximum values chosen.
|
||||
The minimum price will adjust if it's higher than the max."""
|
||||
min_price = world.options.minimum_gold_price.value
|
||||
|
@ -363,21 +357,15 @@ def randomize_shop_prices(world: "CV64World") -> Dict[int, int]:
|
|||
|
||||
shop_price_list = [world.random.randint(min_price * 100, max_price * 100) for _ in range(7)]
|
||||
|
||||
# Convert the price list into a data dict. Which offset it starts from depends on how many bytes it takes up.
|
||||
# Convert the price list into a data dict.
|
||||
price_dict = {}
|
||||
for i in range(len(shop_price_list)):
|
||||
if shop_price_list[i] <= 0xFF:
|
||||
price_dict[0x103D6E + (i*12)] = 0
|
||||
price_dict[0x103D6F + (i*12)] = shop_price_list[i]
|
||||
elif shop_price_list[i] <= 0xFFFF:
|
||||
price_dict[0x103D6E + (i*12)] = shop_price_list[i]
|
||||
else:
|
||||
price_dict[0x103D6D + (i*12)] = shop_price_list[i]
|
||||
price_dict[0x103D6C + (i * 12)] = int.to_bytes(shop_price_list[i], 4, "big")
|
||||
|
||||
return price_dict
|
||||
|
||||
|
||||
def get_countdown_numbers(options: CV64Options, active_locations: Iterable[Location]) -> Dict[int, int]:
|
||||
def get_countdown_numbers(options: CV64Options, active_locations: Iterable[Location]) -> Dict[int, bytes]:
|
||||
"""Figures out which Countdown numbers to increase for each Location after verifying the Item on the Location should
|
||||
increase a number.
|
||||
|
||||
|
@ -400,16 +388,11 @@ def get_countdown_numbers(options: CV64Options, active_locations: Iterable[Locat
|
|||
if countdown_number is not None:
|
||||
countdown_list[countdown_number] += 1
|
||||
|
||||
# Convert the Countdown list into a data dict
|
||||
countdown_dict = {}
|
||||
for i in range(len(countdown_list)):
|
||||
countdown_dict[0xBFD818 + i] = countdown_list[i]
|
||||
|
||||
return countdown_dict
|
||||
return {0xBFD818: bytes(countdown_list)}
|
||||
|
||||
|
||||
def get_location_data(world: "CV64World", active_locations: Iterable[Location]) \
|
||||
-> Tuple[Dict[int, int], List[str], List[bytearray], List[List[Union[int, str, None]]]]:
|
||||
-> Tuple[Dict[int, bytes], List[str], List[bytearray], List[List[Union[int, str, None]]]]:
|
||||
"""Gets ALL the item data to go into the ROM. Item data consists of two bytes: the first dictates the appearance of
|
||||
the item, the second determines what the item actually is when picked up. All items from other worlds will be AP
|
||||
items that do nothing when picked up other than set their flag, and their appearance will depend on whether it's
|
||||
|
@ -449,12 +432,11 @@ def get_location_data(world: "CV64World", active_locations: Iterable[Location])
|
|||
|
||||
# Figure out the item ID bytes to put in each Location here. Write the item itself if either it's the player's
|
||||
# very own, or it belongs to an Item Link that the player is a part of.
|
||||
if loc.item.player == world.player or (loc.item.player in world.multiworld.groups and
|
||||
world.player in world.multiworld.groups[loc.item.player]['players']):
|
||||
if loc.item.player == world.player:
|
||||
if loc_type not in ["npc", "shop"] and get_item_info(loc.item.name, "pickup actor id") is not None:
|
||||
location_bytes[get_location_info(loc.name, "offset")] = get_item_info(loc.item.name, "pickup actor id")
|
||||
else:
|
||||
location_bytes[get_location_info(loc.name, "offset")] = get_item_info(loc.item.name, "code")
|
||||
location_bytes[get_location_info(loc.name, "offset")] = get_item_info(loc.item.name, "code") & 0xFF
|
||||
else:
|
||||
# Make the item the unused Wooden Stake - our multiworld item.
|
||||
location_bytes[get_location_info(loc.name, "offset")] = 0x11
|
||||
|
@ -534,11 +516,12 @@ def get_location_data(world: "CV64World", active_locations: Iterable[Location])
|
|||
|
||||
shop_colors_list.append(get_item_text_color(loc))
|
||||
|
||||
return location_bytes, shop_name_list, shop_colors_list, shop_desc_list
|
||||
return {offset: int.to_bytes(byte, 1, "big") for offset, byte in location_bytes.items()}, shop_name_list,\
|
||||
shop_colors_list, shop_desc_list
|
||||
|
||||
|
||||
def get_loading_zone_bytes(options: CV64Options, starting_stage: str,
|
||||
active_stage_exits: Dict[str, Dict[str, Union[str, int, None]]]) -> Dict[int, int]:
|
||||
active_stage_exits: Dict[str, Dict[str, Union[str, int, None]]]) -> Dict[int, bytes]:
|
||||
"""Figure out all the bytes for loading zones and map transitions based on which stages are where in the exit data.
|
||||
The same data was used earlier in figuring out the logic. Map transitions consist of two major components: which map
|
||||
to send the player to, and which spot within the map to spawn the player at."""
|
||||
|
@ -551,8 +534,8 @@ def get_loading_zone_bytes(options: CV64Options, starting_stage: str,
|
|||
# Start loading zones
|
||||
# If the start zone is the start of the line, have it simply refresh the map.
|
||||
if active_stage_exits[stage]["prev"] == "Menu":
|
||||
loading_zone_bytes[get_stage_info(stage, "startzone map offset")] = 0xFF
|
||||
loading_zone_bytes[get_stage_info(stage, "startzone spawn offset")] = 0x00
|
||||
loading_zone_bytes[get_stage_info(stage, "startzone map offset")] = b"\xFF"
|
||||
loading_zone_bytes[get_stage_info(stage, "startzone spawn offset")] = b"\x00"
|
||||
elif active_stage_exits[stage]["prev"]:
|
||||
loading_zone_bytes[get_stage_info(stage, "startzone map offset")] = \
|
||||
get_stage_info(active_stage_exits[stage]["prev"], "end map id")
|
||||
|
@ -563,7 +546,7 @@ def get_loading_zone_bytes(options: CV64Options, starting_stage: str,
|
|||
if active_stage_exits[stage]["prev"] == rname.castle_center:
|
||||
if options.character_stages == CharacterStages.option_carrie_only or \
|
||||
active_stage_exits[rname.castle_center]["alt"] == stage:
|
||||
loading_zone_bytes[get_stage_info(stage, "startzone spawn offset")] += 1
|
||||
loading_zone_bytes[get_stage_info(stage, "startzone spawn offset")] = b"\x03"
|
||||
|
||||
# End loading zones
|
||||
if active_stage_exits[stage]["next"]:
|
||||
|
@ -582,16 +565,16 @@ def get_loading_zone_bytes(options: CV64Options, starting_stage: str,
|
|||
return loading_zone_bytes
|
||||
|
||||
|
||||
def get_start_inventory_data(player: int, options: CV64Options, precollected_items: List[Item]) -> Dict[int, int]:
|
||||
def get_start_inventory_data(player: int, options: CV64Options, precollected_items: List[Item]) -> Dict[int, bytes]:
|
||||
"""Calculate and return the starting inventory values. Not every Item goes into the menu inventory, so everything
|
||||
has to be handled appropriately."""
|
||||
start_inventory_data = {0xBFD867: 0, # Jewels
|
||||
0xBFD87B: 0, # PowerUps
|
||||
0xBFD883: 0, # Sub-weapon
|
||||
0xBFD88B: 0} # Ice Traps
|
||||
start_inventory_data = {}
|
||||
|
||||
inventory_items_array = [0 for _ in range(35)]
|
||||
total_money = 0
|
||||
total_jewels = 0
|
||||
total_powerups = 0
|
||||
total_ice_traps = 0
|
||||
|
||||
items_max = 10
|
||||
|
||||
|
@ -615,42 +598,46 @@ def get_start_inventory_data(player: int, options: CV64Options, precollected_ite
|
|||
inventory_items_array[inventory_offset] = 2
|
||||
# Starting sub-weapon
|
||||
elif sub_equip_id is not None:
|
||||
start_inventory_data[0xBFD883] = sub_equip_id
|
||||
start_inventory_data[0xBFD883] = bytes(sub_equip_id)
|
||||
# Starting PowerUps
|
||||
elif item.name == iname.powerup:
|
||||
start_inventory_data[0xBFD87B] += 1
|
||||
if start_inventory_data[0xBFD87B] > 2:
|
||||
start_inventory_data[0xBFD87B] = 2
|
||||
total_powerups += 1
|
||||
# Can't have more than 2 PowerUps.
|
||||
if total_powerups > 2:
|
||||
total_powerups = 2
|
||||
# Starting Gold
|
||||
elif "GOLD" in item.name:
|
||||
total_money += int(item.name[0:4])
|
||||
# Money cannot be higher than 99999.
|
||||
if total_money > 99999:
|
||||
total_money = 99999
|
||||
# Starting Jewels
|
||||
elif "jewel" in item.name:
|
||||
if "L" in item.name:
|
||||
start_inventory_data[0xBFD867] += 10
|
||||
total_jewels += 10
|
||||
else:
|
||||
start_inventory_data[0xBFD867] += 5
|
||||
if start_inventory_data[0xBFD867] > 99:
|
||||
start_inventory_data[0xBFD867] = 99
|
||||
total_jewels += 5
|
||||
# Jewels cannot be higher than 99.
|
||||
if total_jewels > 99:
|
||||
total_jewels = 99
|
||||
# Starting Ice Traps
|
||||
else:
|
||||
start_inventory_data[0xBFD88B] += 1
|
||||
if start_inventory_data[0xBFD88B] > 0xFF:
|
||||
start_inventory_data[0xBFD88B] = 0xFF
|
||||
total_ice_traps += 1
|
||||
# Ice Traps cannot be higher than 255.
|
||||
if total_ice_traps > 0xFF:
|
||||
total_ice_traps = 0xFF
|
||||
|
||||
# Convert the jewels into data.
|
||||
start_inventory_data[0xBFD867] = bytes([total_jewels])
|
||||
|
||||
# Convert the Ice Traps into data.
|
||||
start_inventory_data[0xBFD88B] = bytes([total_ice_traps])
|
||||
|
||||
# Convert the inventory items into data.
|
||||
for i in range(len(inventory_items_array)):
|
||||
start_inventory_data[0xBFE518 + i] = inventory_items_array[i]
|
||||
start_inventory_data[0xBFE518] = bytes(inventory_items_array)
|
||||
|
||||
# Convert the starting money into data. Which offset it starts from depends on how many bytes it takes up.
|
||||
if total_money <= 0xFF:
|
||||
start_inventory_data[0xBFE517] = total_money
|
||||
elif total_money <= 0xFFFF:
|
||||
start_inventory_data[0xBFE516] = total_money
|
||||
else:
|
||||
start_inventory_data[0xBFE515] = total_money
|
||||
# Convert the starting money into data.
|
||||
start_inventory_data[0xBFE514] = int.to_bytes(total_money, 4, "big")
|
||||
|
||||
return start_inventory_data
|
||||
|
||||
|
|
|
@ -197,12 +197,15 @@ deathlink_nitro_edition = [
|
|||
0xA168FFFD, # SB T0, 0xFFFD (T3)
|
||||
]
|
||||
|
||||
nitro_fall_killer = [
|
||||
# Custom code to force the instant fall death if at a high enough falling speed after getting killed by the Nitro
|
||||
# explosion, since the game doesn't run the checks for the fall death after getting hit by said explosion and could
|
||||
# result in a softlock when getting blown into an abyss.
|
||||
launch_fall_killer = [
|
||||
# Custom code to force the instant fall death if at a high enough falling speed after getting killed by something
|
||||
# that launches you (whether it be the Nitro explosion or a Big Toss hit). The game doesn't normally run the check
|
||||
# that would trigger the fall death after you get killed by some other means, which could result in a softlock
|
||||
# when a killing blow launches you into an abyss.
|
||||
0x3C0C8035, # LUI T4, 0x8035
|
||||
0x918807E2, # LBU T0, 0x07E2 (T4)
|
||||
0x24090008, # ADDIU T1, R0, 0x0008
|
||||
0x11090002, # BEQ T0, T1, [forward 0x02]
|
||||
0x2409000C, # ADDIU T1, R0, 0x000C
|
||||
0x15090006, # BNE T0, T1, [forward 0x06]
|
||||
0x3C098035, # LUI T1, 0x8035
|
||||
|
@ -2863,3 +2866,13 @@ big_tosser = [
|
|||
0xAD000814, # SW R0, 0x0814 (T0)
|
||||
0x03200008 # JR T9
|
||||
]
|
||||
|
||||
dog_bite_ice_trap_fix = [
|
||||
# Sets the freeze timer to 0 when a maze garden dog bites the player to ensure the ice chunk model will break if the
|
||||
# player gets bitten while frozen via Ice Trap.
|
||||
0x3C088039, # LUI T0, 0x8039
|
||||
0xA5009E76, # SH R0, 0x9E76 (T0)
|
||||
0x3C090F00, # LUI T1, 0x0F00
|
||||
0x25291CB8, # ADDIU T1, T1, 0x1CB8
|
||||
0x01200008 # JR T1
|
||||
]
|
||||
|
|
|
@ -27,7 +27,7 @@ in vanilla, contains 5 checks in rando.
|
|||
#### Bat archway rock
|
||||
After the broken bridge containing the invisible pathway to the Special1 in vanilla, this rock is off to the side in front
|
||||
of the gate frame with a swarm of bats that come at you, before the Werewolf's territory. Contains 4 checks. If you are new
|
||||
to speedrunning the vanilla game and haven't yet learned the RNG manip strats, this is a guranteed spot to find a PowerUp at.
|
||||
to speedrunning the vanilla game and haven't yet learned the RNG manip strats, this is a guaranteed spot to find a PowerUp at.
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -74,7 +74,8 @@ class HardItemPool(Toggle):
|
|||
|
||||
|
||||
class Special1sPerWarp(Range):
|
||||
"""Sets how many Special1 jewels are needed per warp menu option unlock."""
|
||||
"""Sets how many Special1 jewels are needed per warp menu option unlock.
|
||||
This will decrease until the number x 7 is less than or equal to the Total Specail1s if it isn't already."""
|
||||
range_start = 1
|
||||
range_end = 10
|
||||
default = 1
|
||||
|
@ -82,8 +83,7 @@ class Special1sPerWarp(Range):
|
|||
|
||||
|
||||
class TotalSpecial1s(Range):
|
||||
"""Sets how many Speical1 jewels are in the pool in total.
|
||||
If this is set to be less than Special1s Per Warp x 7, it will decrease by 1 until it isn't."""
|
||||
"""Sets how many Speical1 jewels are in the pool in total."""
|
||||
range_start = 7
|
||||
range_end = 70
|
||||
default = 7
|
||||
|
|
1743
worlds/cv64/rom.py
1743
worlds/cv64/rom.py
File diff suppressed because it is too large
Load Diff
|
@ -47,9 +47,9 @@ if TYPE_CHECKING:
|
|||
# corresponding Locations and Entrances will all be created.
|
||||
stage_info = {
|
||||
"Forest of Silence": {
|
||||
"start region": rname.forest_start, "start map id": 0x00, "start spawn id": 0x00,
|
||||
"mid region": rname.forest_mid, "mid map id": 0x00, "mid spawn id": 0x04,
|
||||
"end region": rname.forest_end, "end map id": 0x00, "end spawn id": 0x01,
|
||||
"start region": rname.forest_start, "start map id": b"\x00", "start spawn id": b"\x00",
|
||||
"mid region": rname.forest_mid, "mid map id": b"\x00", "mid spawn id": b"\x04",
|
||||
"end region": rname.forest_end, "end map id": b"\x00", "end spawn id": b"\x01",
|
||||
"endzone map offset": 0xB6302F, "endzone spawn offset": 0xB6302B,
|
||||
"save number offsets": [0x1049C5, 0x1049CD, 0x1049D5],
|
||||
"regions": [rname.forest_start,
|
||||
|
@ -58,9 +58,9 @@ stage_info = {
|
|||
},
|
||||
|
||||
"Castle Wall": {
|
||||
"start region": rname.cw_start, "start map id": 0x02, "start spawn id": 0x00,
|
||||
"mid region": rname.cw_start, "mid map id": 0x02, "mid spawn id": 0x07,
|
||||
"end region": rname.cw_exit, "end map id": 0x02, "end spawn id": 0x10,
|
||||
"start region": rname.cw_start, "start map id": b"\x02", "start spawn id": b"\x00",
|
||||
"mid region": rname.cw_start, "mid map id": b"\x02", "mid spawn id": b"\x07",
|
||||
"end region": rname.cw_exit, "end map id": b"\x02", "end spawn id": b"\x10",
|
||||
"endzone map offset": 0x109A5F, "endzone spawn offset": 0x109A61,
|
||||
"save number offsets": [0x1049DD, 0x1049E5, 0x1049ED],
|
||||
"regions": [rname.cw_start,
|
||||
|
@ -69,9 +69,9 @@ stage_info = {
|
|||
},
|
||||
|
||||
"Villa": {
|
||||
"start region": rname.villa_start, "start map id": 0x03, "start spawn id": 0x00,
|
||||
"mid region": rname.villa_storeroom, "mid map id": 0x05, "mid spawn id": 0x04,
|
||||
"end region": rname.villa_crypt, "end map id": 0x1A, "end spawn id": 0x03,
|
||||
"start region": rname.villa_start, "start map id": b"\x03", "start spawn id": b"\x00",
|
||||
"mid region": rname.villa_storeroom, "mid map id": b"\x05", "mid spawn id": b"\x04",
|
||||
"end region": rname.villa_crypt, "end map id": b"\x1A", "end spawn id": b"\x03",
|
||||
"endzone map offset": 0xD9DA3, "endzone spawn offset": 0x109E81,
|
||||
"altzone map offset": 0xD9DAB, "altzone spawn offset": 0x109E81,
|
||||
"save number offsets": [0x1049F5, 0x1049FD, 0x104A05, 0x104A0D],
|
||||
|
@ -85,9 +85,9 @@ stage_info = {
|
|||
},
|
||||
|
||||
"Tunnel": {
|
||||
"start region": rname.tunnel_start, "start map id": 0x07, "start spawn id": 0x00,
|
||||
"mid region": rname.tunnel_end, "mid map id": 0x07, "mid spawn id": 0x03,
|
||||
"end region": rname.tunnel_end, "end map id": 0x07, "end spawn id": 0x11,
|
||||
"start region": rname.tunnel_start, "start map id": b"\x07", "start spawn id": b"\x00",
|
||||
"mid region": rname.tunnel_end, "mid map id": b"\x07", "mid spawn id": b"\x03",
|
||||
"end region": rname.tunnel_end, "end map id": b"\x07", "end spawn id": b"\x11",
|
||||
"endzone map offset": 0x109B4F, "endzone spawn offset": 0x109B51, "character": "Reinhardt",
|
||||
"save number offsets": [0x104A15, 0x104A1D, 0x104A25, 0x104A2D],
|
||||
"regions": [rname.tunnel_start,
|
||||
|
@ -95,9 +95,9 @@ stage_info = {
|
|||
},
|
||||
|
||||
"Underground Waterway": {
|
||||
"start region": rname.uw_main, "start map id": 0x08, "start spawn id": 0x00,
|
||||
"mid region": rname.uw_main, "mid map id": 0x08, "mid spawn id": 0x03,
|
||||
"end region": rname.uw_end, "end map id": 0x08, "end spawn id": 0x01,
|
||||
"start region": rname.uw_main, "start map id": b"\x08", "start spawn id": b"\x00",
|
||||
"mid region": rname.uw_main, "mid map id": b"\x08", "mid spawn id": b"\x03",
|
||||
"end region": rname.uw_end, "end map id": b"\x08", "end spawn id": b"\x01",
|
||||
"endzone map offset": 0x109B67, "endzone spawn offset": 0x109B69, "character": "Carrie",
|
||||
"save number offsets": [0x104A35, 0x104A3D],
|
||||
"regions": [rname.uw_main,
|
||||
|
@ -105,9 +105,9 @@ stage_info = {
|
|||
},
|
||||
|
||||
"Castle Center": {
|
||||
"start region": rname.cc_main, "start map id": 0x19, "start spawn id": 0x00,
|
||||
"mid region": rname.cc_main, "mid map id": 0x0E, "mid spawn id": 0x03,
|
||||
"end region": rname.cc_elev_top, "end map id": 0x0F, "end spawn id": 0x02,
|
||||
"start region": rname.cc_main, "start map id": b"\x19", "start spawn id": b"\x00",
|
||||
"mid region": rname.cc_main, "mid map id": b"\x0E", "mid spawn id": b"\x03",
|
||||
"end region": rname.cc_elev_top, "end map id": b"\x0F", "end spawn id": b"\x02",
|
||||
"endzone map offset": 0x109CB7, "endzone spawn offset": 0x109CB9,
|
||||
"altzone map offset": 0x109CCF, "altzone spawn offset": 0x109CD1,
|
||||
"save number offsets": [0x104A45, 0x104A4D, 0x104A55, 0x104A5D, 0x104A65, 0x104A6D, 0x104A75],
|
||||
|
@ -119,20 +119,20 @@ stage_info = {
|
|||
},
|
||||
|
||||
"Duel Tower": {
|
||||
"start region": rname.dt_main, "start map id": 0x13, "start spawn id": 0x00,
|
||||
"start region": rname.dt_main, "start map id": b"\x13", "start spawn id": b"\x00",
|
||||
"startzone map offset": 0x109DA7, "startzone spawn offset": 0x109DA9,
|
||||
"mid region": rname.dt_main, "mid map id": 0x13, "mid spawn id": 0x15,
|
||||
"end region": rname.dt_main, "end map id": 0x13, "end spawn id": 0x01,
|
||||
"mid region": rname.dt_main, "mid map id": b"\x13", "mid spawn id": b"\x15",
|
||||
"end region": rname.dt_main, "end map id": b"\x13", "end spawn id": b"\x01",
|
||||
"endzone map offset": 0x109D8F, "endzone spawn offset": 0x109D91, "character": "Reinhardt",
|
||||
"save number offsets": [0x104ACD],
|
||||
"regions": [rname.dt_main]
|
||||
},
|
||||
|
||||
"Tower of Execution": {
|
||||
"start region": rname.toe_main, "start map id": 0x10, "start spawn id": 0x00,
|
||||
"start region": rname.toe_main, "start map id": b"\x10", "start spawn id": b"\x00",
|
||||
"startzone map offset": 0x109D17, "startzone spawn offset": 0x109D19,
|
||||
"mid region": rname.toe_main, "mid map id": 0x10, "mid spawn id": 0x02,
|
||||
"end region": rname.toe_main, "end map id": 0x10, "end spawn id": 0x12,
|
||||
"mid region": rname.toe_main, "mid map id": b"\x10", "mid spawn id": b"\x02",
|
||||
"end region": rname.toe_main, "end map id": b"\x10", "end spawn id": b"\x12",
|
||||
"endzone map offset": 0x109CFF, "endzone spawn offset": 0x109D01, "character": "Reinhardt",
|
||||
"save number offsets": [0x104A7D, 0x104A85],
|
||||
"regions": [rname.toe_main,
|
||||
|
@ -140,10 +140,10 @@ stage_info = {
|
|||
},
|
||||
|
||||
"Tower of Science": {
|
||||
"start region": rname.tosci_start, "start map id": 0x12, "start spawn id": 0x00,
|
||||
"start region": rname.tosci_start, "start map id": b"\x12", "start spawn id": b"\x00",
|
||||
"startzone map offset": 0x109D77, "startzone spawn offset": 0x109D79,
|
||||
"mid region": rname.tosci_conveyors, "mid map id": 0x12, "mid spawn id": 0x03,
|
||||
"end region": rname.tosci_conveyors, "end map id": 0x12, "end spawn id": 0x04,
|
||||
"mid region": rname.tosci_conveyors, "mid map id": b"\x12", "mid spawn id": b"\x03",
|
||||
"end region": rname.tosci_conveyors, "end map id": b"\x12", "end spawn id": b"\x04",
|
||||
"endzone map offset": 0x109D5F, "endzone spawn offset": 0x109D61, "character": "Carrie",
|
||||
"save number offsets": [0x104A95, 0x104A9D, 0x104AA5],
|
||||
"regions": [rname.tosci_start,
|
||||
|
@ -153,28 +153,28 @@ stage_info = {
|
|||
},
|
||||
|
||||
"Tower of Sorcery": {
|
||||
"start region": rname.tosor_main, "start map id": 0x11, "start spawn id": 0x00,
|
||||
"start region": rname.tosor_main, "start map id": b"\x11", "start spawn id": b"\x00",
|
||||
"startzone map offset": 0x109D47, "startzone spawn offset": 0x109D49,
|
||||
"mid region": rname.tosor_main, "mid map id": 0x11, "mid spawn id": 0x01,
|
||||
"end region": rname.tosor_main, "end map id": 0x11, "end spawn id": 0x13,
|
||||
"mid region": rname.tosor_main, "mid map id": b"\x11", "mid spawn id": b"\x01",
|
||||
"end region": rname.tosor_main, "end map id": b"\x11", "end spawn id": b"\x13",
|
||||
"endzone map offset": 0x109D2F, "endzone spawn offset": 0x109D31, "character": "Carrie",
|
||||
"save number offsets": [0x104A8D],
|
||||
"regions": [rname.tosor_main]
|
||||
},
|
||||
|
||||
"Room of Clocks": {
|
||||
"start region": rname.roc_main, "start map id": 0x1B, "start spawn id": 0x00,
|
||||
"mid region": rname.roc_main, "mid map id": 0x1B, "mid spawn id": 0x02,
|
||||
"end region": rname.roc_main, "end map id": 0x1B, "end spawn id": 0x14,
|
||||
"start region": rname.roc_main, "start map id": b"\x1B", "start spawn id": b"\x00",
|
||||
"mid region": rname.roc_main, "mid map id": b"\x1B", "mid spawn id": b"\x02",
|
||||
"end region": rname.roc_main, "end map id": b"\x1B", "end spawn id": b"\x14",
|
||||
"endzone map offset": 0x109EAF, "endzone spawn offset": 0x109EB1,
|
||||
"save number offsets": [0x104AC5],
|
||||
"regions": [rname.roc_main]
|
||||
},
|
||||
|
||||
"Clock Tower": {
|
||||
"start region": rname.ct_start, "start map id": 0x17, "start spawn id": 0x00,
|
||||
"mid region": rname.ct_middle, "mid map id": 0x17, "mid spawn id": 0x02,
|
||||
"end region": rname.ct_end, "end map id": 0x17, "end spawn id": 0x03,
|
||||
"start region": rname.ct_start, "start map id": b"\x17", "start spawn id": b"\x00",
|
||||
"mid region": rname.ct_middle, "mid map id": b"\x17", "mid spawn id": b"\x02",
|
||||
"end region": rname.ct_end, "end map id": b"\x17", "end spawn id": b"\x03",
|
||||
"endzone map offset": 0x109E37, "endzone spawn offset": 0x109E39,
|
||||
"save number offsets": [0x104AB5, 0x104ABD],
|
||||
"regions": [rname.ct_start,
|
||||
|
@ -183,8 +183,8 @@ stage_info = {
|
|||
},
|
||||
|
||||
"Castle Keep": {
|
||||
"start region": rname.ck_main, "start map id": 0x14, "start spawn id": 0x02,
|
||||
"mid region": rname.ck_main, "mid map id": 0x14, "mid spawn id": 0x03,
|
||||
"start region": rname.ck_main, "start map id": b"\x14", "start spawn id": b"\x02",
|
||||
"mid region": rname.ck_main, "mid map id": b"\x14", "mid spawn id": b"\x03",
|
||||
"end region": rname.ck_drac_chamber,
|
||||
"save number offsets": [0x104AAD],
|
||||
"regions": [rname.ck_main]
|
||||
|
|
Loading…
Reference in New Issue