Merge branch 'main' into player-tracker

This commit is contained in:
Chris Wilson
2022-07-26 17:19:00 -04:00
68 changed files with 5817 additions and 406 deletions

3
.gitignore vendored
View File

@@ -116,6 +116,9 @@ target/
profile_default/
ipython_config.py
# vim editor
*.swp
# SageMath parsed files
*.sage.py

View File

@@ -1,6 +1,8 @@
from __future__ import annotations
import os
import sys
import asyncio
import shutil
import ModuleUpdate
ModuleUpdate.update()
@@ -32,6 +34,24 @@ class ChecksFinderContext(CommonContext):
self.send_index: int = 0
self.syncing = False
self.awaiting_bridge = False
# self.game_communication_path: files go in this path to pass data between us and the actual game
if "localappdata" in os.environ:
self.game_communication_path = os.path.expandvars(r"%localappdata%/ChecksFinder")
else:
# not windows. game is an exe so let's see if wine might be around to run it
if "WINEPREFIX" in os.environ:
wineprefix = os.environ["WINEPREFIX"]
elif shutil.which("wine") or shutil.which("wine-stable"):
wineprefix = os.path.expanduser("~/.wine") # default root of wine system data, deep in which is app data
else:
msg = "ChecksFinderClient couldn't detect system type. Unable to infer required game_communication_path"
logger.error("Error: " + msg)
Utils.messagebox("Error", msg, error=True)
sys.exit(1)
self.game_communication_path = os.path.join(
wineprefix,
"drive_c",
os.path.expandvars("users/$USER/Local Settings/Application Data/ChecksFinder"))
async def server_auth(self, password_requested: bool = False):
if password_requested and not self.password:
@@ -41,8 +61,7 @@ class ChecksFinderContext(CommonContext):
async def connection_closed(self):
await super(ChecksFinderContext, self).connection_closed()
path = os.path.expandvars(r"%localappdata%/ChecksFinder")
for root, dirs, files in os.walk(path):
for root, dirs, files in os.walk(self.game_communication_path):
for file in files:
if file.find("obtain") <= -1:
os.remove(root + "/" + file)
@@ -56,26 +75,25 @@ class ChecksFinderContext(CommonContext):
async def shutdown(self):
await super(ChecksFinderContext, self).shutdown()
path = os.path.expandvars(r"%localappdata%/ChecksFinder")
for root, dirs, files in os.walk(path):
for root, dirs, files in os.walk(self.game_communication_path):
for file in files:
if file.find("obtain") <= -1:
os.remove(root+"/"+file)
def on_package(self, cmd: str, args: dict):
if cmd in {"Connected"}:
if not os.path.exists(os.path.expandvars(r"%localappdata%/ChecksFinder")):
os.mkdir(os.path.expandvars(r"%localappdata%/ChecksFinder"))
if not os.path.exists(self.game_communication_path):
os.makedirs(self.game_communication_path)
for ss in self.checked_locations:
filename = f"send{ss}"
with open(os.path.expandvars(r"%localappdata%/ChecksFinder/" + filename), 'w') as f:
with open(os.path.join(self.game_communication_path, filename), 'w') as f:
f.close()
if cmd in {"ReceivedItems"}:
start_index = args["index"]
if start_index != len(self.items_received):
for item in args['items']:
filename = f"AP_{str(NetworkItem(*item).location)}PLR{str(NetworkItem(*item).player)}.item"
with open(os.path.expandvars(r"%localappdata%/ChecksFinder/" + filename), 'w') as f:
with open(os.path.join(self.game_communication_path, filename), 'w') as f:
f.write(str(NetworkItem(*item).item))
f.close()
@@ -83,7 +101,7 @@ class ChecksFinderContext(CommonContext):
if "checked_locations" in args:
for ss in self.checked_locations:
filename = f"send{ss}"
with open(os.path.expandvars(r"%localappdata%/ChecksFinder/" + filename), 'w') as f:
with open(os.path.join(self.game_communication_path, filename), 'w') as f:
f.close()
def run_gui(self):
@@ -109,10 +127,9 @@ async def game_watcher(ctx: ChecksFinderContext):
sync_msg.append({"cmd": "LocationChecks", "locations": list(ctx.locations_checked)})
await ctx.send_msgs(sync_msg)
ctx.syncing = False
path = os.path.expandvars(r"%localappdata%/ChecksFinder")
sending = []
victory = False
for root, dirs, files in os.walk(path):
for root, dirs, files in os.walk(ctx.game_communication_path):
for file in files:
if file.find("send") > -1:
st = file.split("send", -1)[1]

View File

@@ -126,7 +126,7 @@ components: Iterable[Component] = (
Component('Text Client', 'CommonClient', 'ArchipelagoTextClient'),
# SNI
Component('SNI Client', 'SNIClient',
file_identifier=SuffixIdentifier('.apz3', '.apm3', '.apsoe', '.aplttp', '.apsm', '.apsmz3')),
file_identifier=SuffixIdentifier('.apz3', '.apm3', '.apsoe', '.aplttp', '.apsm', '.apsmz3', '.apdkc3')),
Component('LttP Adjuster', 'LttPAdjuster'),
# Factorio
Component('Factorio Client', 'FactorioClient'),

View File

@@ -48,7 +48,7 @@ deathlink_sent_this_death: we interacted with the multiworld on this death, wait
oot_loc_name_to_id = network_data_package["games"]["Ocarina of Time"]["location_name_to_id"]
script_version: int = 1
script_version: int = 2
def get_item_value(ap_id):
return ap_id - 66000
@@ -186,7 +186,7 @@ async def n64_sync_task(ctx: OoTContext):
data = await asyncio.wait_for(reader.readline(), timeout=10)
data_decoded = json.loads(data.decode())
reported_version = data_decoded.get('scriptVersion', 0)
if reported_version == script_version:
if reported_version >= script_version:
if ctx.game is not None and 'locations' in data_decoded:
# Not just a keep alive ping, parse
asyncio.create_task(parse_payload(data_decoded, ctx, False))

View File

@@ -166,13 +166,15 @@ GAME_ALTTP = "A Link to the Past"
GAME_SM = "Super Metroid"
GAME_SOE = "Secret of Evermore"
GAME_SMZ3 = "SMZ3"
supported_games = {"A Link to the Past", "Super Metroid", "Secret of Evermore", "SMZ3"}
GAME_DKC3 = "Donkey Kong Country 3"
supported_games = {"A Link to the Past", "Super Metroid", "Secret of Evermore", "SMZ3", "Donkey Kong Country 3"}
preferred_endings = {
GAME_ALTTP: "apbp",
GAME_SM: "apm3",
GAME_SOE: "apsoe",
GAME_SMZ3: "apsmz"
GAME_SMZ3: "apsmz",
GAME_DKC3: "apdkc3"
}
@@ -187,6 +189,8 @@ def generate_yaml(patch: bytes, metadata: Optional[dict] = None, game: str = GAM
from worlds.alttp.Rom import LTTPJPN10HASH as ALTTPHASH
from worlds.sm.Rom import SMJUHASH as SMHASH
HASH = ALTTPHASH + SMHASH
elif game == GAME_DKC3:
from worlds.dkc3.Rom import USHASH as HASH
else:
raise RuntimeError(f"Selected game {game} for base rom not found.")
@@ -216,7 +220,10 @@ def create_patch_file(rom_file_to_patch: str, server: str = "", destination: str
meta,
game)
target = destination if destination else os.path.splitext(rom_file_to_patch)[0] + (
".apbp" if game == GAME_ALTTP else ".apsmz" if game == GAME_SMZ3 else ".apm3")
".apbp" if game == GAME_ALTTP
else ".apsmz" if game == GAME_SMZ3
else ".apdkc3" if game == GAME_DKC3
else ".apm3")
write_lzma(bytes, target)
return target
@@ -245,6 +252,8 @@ def get_base_rom_data(game: str):
get_base_rom_bytes = lambda: bytes(read_rom(open(get_base_rom_path(), "rb")))
elif game == GAME_SMZ3:
from worlds.smz3.Rom import get_base_rom_bytes
elif game == GAME_DKC3:
from worlds.dkc3.Rom import get_base_rom_bytes
else:
raise RuntimeError("Selected game for base rom not found.")
return get_base_rom_bytes()
@@ -389,6 +398,13 @@ if __name__ == "__main__":
if 'server' in data:
Utils.persistent_store("servers", data['hash'], data['server'])
print(f"Host is {data['server']}")
elif rom.endswith(".apdkc3"):
print(f"Applying patch {rom}")
data, target = create_rom_file(rom)
print(f"Created rom {target}.")
if 'server' in data:
Utils.persistent_store("servers", data['hash'], data['server'])
print(f"Host is {data['server']}")
elif rom.endswith(".zip"):
print(f"Updating host in patch files contained in {rom}")
@@ -396,7 +412,9 @@ if __name__ == "__main__":
def _handle_zip_file_entry(zfinfo: zipfile.ZipInfo, server: str):
data = zfr.read(zfinfo)
if zfinfo.filename.endswith(".apbp") or zfinfo.filename.endswith(".apm3"):
if zfinfo.filename.endswith(".apbp") or \
zfinfo.filename.endswith(".apm3") or \
zfinfo.filename.endswith(".apdkc3"):
data = update_patch_data(data, server)
with ziplock:
zfw.writestr(zfinfo, data)

View File

@@ -26,6 +26,8 @@ Currently, the following games are supported:
* The Witness
* Sonic Adventure 2: Battle
* Starcraft 2: Wings of Liberty
* Donkey Kong Country 3
* Dark Souls 3
For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/).
Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled

View File

@@ -33,7 +33,7 @@ from worlds.sm.Rom import ROM_PLAYER_LIMIT as SM_ROM_PLAYER_LIMIT
from worlds.smz3.Rom import ROM_PLAYER_LIMIT as SMZ3_ROM_PLAYER_LIMIT
import Utils
from CommonClient import CommonContext, server_loop, ClientCommandProcessor, gui_enabled, get_base_parser
from Patch import GAME_ALTTP, GAME_SM, GAME_SMZ3
from Patch import GAME_ALTTP, GAME_SM, GAME_SMZ3, GAME_DKC3
snes_logger = logging.getLogger("SNES")
@@ -251,6 +251,9 @@ async def deathlink_kill_player(ctx: Context):
if not gamemode or gamemode[0] in SM_DEATH_MODES or (
ctx.death_link_allow_survive and health is not None and health > 0):
ctx.death_state = DeathState.dead
elif ctx.game == GAME_DKC3:
from worlds.dkc3.Client import deathlink_kill_player as dkc3_deathlink_kill_player
await dkc3_deathlink_kill_player(ctx)
ctx.last_death_link = time.time()
@@ -1034,44 +1037,48 @@ async def game_watcher(ctx: Context):
if not ctx.rom:
ctx.finished_game = False
ctx.death_link_allow_survive = False
game_name = await snes_read(ctx, SM_ROMNAME_START, 5)
if game_name is None:
continue
elif game_name[:2] == b"SM":
ctx.game = GAME_SM
# versions lower than 0.3.0 dont have item handling flag nor remote item support
romVersion = int(game_name[2:5].decode('UTF-8'))
if romVersion < 30:
ctx.items_handling = 0b001 # full local
else:
item_handling = await snes_read(ctx, SM_REMOTE_ITEM_FLAG_ADDR, 1)
ctx.items_handling = 0b001 if item_handling is None else item_handling[0]
else:
game_name = await snes_read(ctx, SMZ3_ROMNAME_START, 3)
if game_name == b"ZSM":
ctx.game = GAME_SMZ3
ctx.items_handling = 0b101 # local items and remote start inventory
else:
ctx.game = GAME_ALTTP
ctx.items_handling = 0b001 # full local
rom = await snes_read(ctx, SM_ROMNAME_START if ctx.game == GAME_SM else SMZ3_ROMNAME_START if ctx.game == GAME_SMZ3 else ROMNAME_START, ROMNAME_SIZE)
if rom is None or rom == bytes([0] * ROMNAME_SIZE):
continue
from worlds.dkc3.Client import dkc3_rom_init
init_handled = await dkc3_rom_init(ctx)
if not init_handled:
game_name = await snes_read(ctx, SM_ROMNAME_START, 5)
if game_name is None:
continue
elif game_name[:2] == b"SM":
ctx.game = GAME_SM
# versions lower than 0.3.0 dont have item handling flag nor remote item support
romVersion = int(game_name[2:5].decode('UTF-8'))
if romVersion < 30:
ctx.items_handling = 0b001 # full local
else:
item_handling = await snes_read(ctx, SM_REMOTE_ITEM_FLAG_ADDR, 1)
ctx.items_handling = 0b001 if item_handling is None else item_handling[0]
else:
game_name = await snes_read(ctx, SMZ3_ROMNAME_START, 3)
if game_name == b"ZSM":
ctx.game = GAME_SMZ3
ctx.items_handling = 0b101 # local items and remote start inventory
else:
ctx.game = GAME_ALTTP
ctx.items_handling = 0b001 # full local
ctx.rom = rom
if ctx.game != GAME_SMZ3:
death_link = await snes_read(ctx, DEATH_LINK_ACTIVE_ADDR if ctx.game == GAME_ALTTP else
SM_DEATH_LINK_ACTIVE_ADDR, 1)
if death_link:
ctx.allow_collect = bool(death_link[0] & 0b100)
ctx.death_link_allow_survive = bool(death_link[0] & 0b10)
await ctx.update_death_link(bool(death_link[0] & 0b1))
if not ctx.prev_rom or ctx.prev_rom != ctx.rom:
ctx.locations_checked = set()
ctx.locations_scouted = set()
ctx.locations_info = {}
ctx.prev_rom = ctx.rom
rom = await snes_read(ctx, SM_ROMNAME_START if ctx.game == GAME_SM else SMZ3_ROMNAME_START if ctx.game == GAME_SMZ3 else ROMNAME_START, ROMNAME_SIZE)
if rom is None or rom == bytes([0] * ROMNAME_SIZE):
continue
ctx.rom = rom
if ctx.game != GAME_SMZ3:
death_link = await snes_read(ctx, DEATH_LINK_ACTIVE_ADDR if ctx.game == GAME_ALTTP else
SM_DEATH_LINK_ACTIVE_ADDR, 1)
if death_link:
ctx.allow_collect = bool(death_link[0] & 0b100)
ctx.death_link_allow_survive = bool(death_link[0] & 0b10)
await ctx.update_death_link(bool(death_link[0] & 0b1))
if not ctx.prev_rom or ctx.prev_rom != ctx.rom:
ctx.locations_checked = set()
ctx.locations_scouted = set()
ctx.locations_info = {}
ctx.prev_rom = ctx.rom
if ctx.awaiting_rom:
await ctx.server_auth(False)
@@ -1279,6 +1286,9 @@ async def game_watcher(ctx: Context):
color(ctx.item_names[item.item], 'red', 'bold'), color(ctx.player_names[item.player], 'yellow'),
ctx.location_names[item.location], itemOutPtr, len(ctx.items_received)))
await snes_flush_writes(ctx)
elif ctx.game == GAME_DKC3:
from worlds.dkc3.Client import dkc3_game_watcher
await dkc3_game_watcher(ctx)
async def run_game(romfile):

View File

@@ -78,6 +78,8 @@ def download_slot_file(room_id, player_id: int):
fname = f"AP_{app.jinja_env.filters['suuid'](room_id)}_SP.apv6"
elif slot_data.game == "Super Mario 64":
fname = f"AP_{app.jinja_env.filters['suuid'](room_id)}_SP.apsm64ex"
elif slot_data.game == "Dark Souls III":
fname = f"AP_{app.jinja_env.filters['suuid'](room_id)}.json"
else:
return "Game download not supported."
return send_file(io.BytesIO(slot_data.data), as_attachment=True, attachment_filename=fname)

View File

@@ -1,7 +1,7 @@
flask>=2.1.2
pony>=0.7.16
waitress>=2.1.1
flask-caching>=2.0.0
Flask-Caching>=2.0.0
Flask-Compress>=1.12
Flask-Limiter>=2.5.0
bokeh>=2.4.3

View File

@@ -43,6 +43,9 @@
{% elif patch.game in ["A Link to the Past", "Secret of Evermore", "Super Metroid", "SMZ3"] %}
<a href="{{ url_for("download_patch", patch_id=patch.id, room_id=room.id) }}" download>
Download Patch File...</a>
{% elif patch.game == "Dark Souls III" %}
<a href="{{ url_for("download_slot_file", room_id=room.id, player_id=patch.player_id) }}" download>
Download JSON File...</a>
{% else %}
No file to download for this game.
{% endif %}

View File

@@ -80,6 +80,11 @@ def upload_zip_to_db(zfile: zipfile.ZipFile, owner=None, meta={"race": False}, s
slots.add(Slot(data=zfile.open(file, "r").read(), player_name=slot_name,
player_id=int(slot_id[1:]), game="Ocarina of Time"))
elif file.filename.endswith(".json"):
_, seed_name, slot_id, slot_name = file.filename.split('.')[0].split('-', 3)
slots.add(Slot(data=zfile.open(file, "r").read(), player_name=slot_name,
player_id=int(slot_id[1:]), game="Dark Souls III"))
elif file.filename.endswith(".txt"):
spoiler = zfile.open(file, "r").read().decode("utf-8-sig")

View File

@@ -2,8 +2,8 @@ local socket = require("socket")
local json = require('json')
local math = require('math')
local last_modified_date = '2022-05-25' -- Should be the last modified date
local script_version = 1
local last_modified_date = '2022-07-24' -- Should be the last modified date
local script_version = 2
--------------------------------------------------
-- Heavily modified form of RiptideSage's tracker
@@ -1723,6 +1723,11 @@ function get_death_state()
end
function kill_link()
-- market entrance: 27/28/29
-- outside ToT: 35/36/37.
-- if killed on these scenes the game crashes, so we wait until not on this screen.
local scene = global_context:rawget('cur_scene'):rawget()
if scene == 27 or scene == 28 or scene == 29 or scene == 35 or scene == 36 or scene == 37 then return end
mainmemory.write_u16_be(0x11A600, 0)
end
@@ -1824,13 +1829,15 @@ function main()
elseif (curstate == STATE_UNINITIALIZED) then
if (frame % 60 == 0) then
server:settimeout(2)
print("Attempting to connect")
local client, timeout = server:accept()
if timeout == nil then
print('Initial Connection Made')
curstate = STATE_INITIAL_CONNECTION_MADE
ootSocket = client
ootSocket:settimeout(0)
else
print('Connection failed, ensure OoTClient is running and rerun oot_connector.lua')
return
end
end
end

View File

@@ -127,3 +127,12 @@ smz3_options:
# True for operating system default program
# Alternatively, a path to a program to open the .sfc file with
rom_start: true
dkc3_options:
# File name of the DKC3 US rom
rom_file: "Donkey Kong Country 3 - Dixie Kong's Double Trouble! (USA) (En,Fr).sfc"
# Set this to your SNI folder location if you want the MultiClient to attempt an auto start, does nothing if not found
sni: "SNI"
# Set this to false to never autostart a rom (such as after patching)
# True for operating system default program
# Alternatively, a path to a program to open the .sfc file with
rom_start: true

View File

@@ -54,6 +54,7 @@ Name: "custom"; Description: "Custom installation"; Flags: iscustom
Name: "core"; Description: "Core Files"; Types: full hosting playing custom; Flags: fixed
Name: "generator"; Description: "Generator"; Types: full hosting
Name: "generator/sm"; Description: "Super Metroid ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 3145728; Flags: disablenouninstallwarning
Name: "generator/dkc3"; Description: "Donkey Kong Country 3 ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 3145728; Flags: disablenouninstallwarning
Name: "generator/soe"; Description: "Secret of Evermore ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 3145728; Flags: disablenouninstallwarning
Name: "generator/lttp"; Description: "A Link to the Past ROM Setup and Enemizer"; Types: full hosting; ExtraDiskSpaceRequired: 5191680
Name: "generator/oot"; Description: "Ocarina of Time ROM Setup"; Types: full hosting; ExtraDiskSpaceRequired: 100663296; Flags: disablenouninstallwarning
@@ -62,6 +63,7 @@ Name: "client"; Description: "Clients"; Types: full playing
Name: "client/sni"; Description: "SNI Client"; Types: full playing
Name: "client/sni/lttp"; Description: "SNI Client - A Link to the Past Patch Setup"; Types: full playing; Flags: disablenouninstallwarning
Name: "client/sni/sm"; Description: "SNI Client - Super Metroid Patch Setup"; Types: full playing; Flags: disablenouninstallwarning
Name: "client/sni/dkc3"; Description: "SNI Client - Donkey Kong Country 3 Patch Setup"; Types: full playing; Flags: disablenouninstallwarning
Name: "client/factorio"; Description: "Factorio"; Types: full playing
Name: "client/minecraft"; Description: "Minecraft"; Types: full playing; ExtraDiskSpaceRequired: 226894278
Name: "client/oot"; Description: "Ocarina of Time"; Types: full playing
@@ -76,6 +78,7 @@ NAME: "{app}"; Flags: setntfscompression; Permissions: everyone-modify users-mod
[Files]
Source: "{code:GetROMPath}"; DestDir: "{app}"; DestName: "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"; Flags: external; Components: client/sni/lttp or generator/lttp
Source: "{code:GetSMROMPath}"; DestDir: "{app}"; DestName: "Super Metroid (JU).sfc"; Flags: external; Components: client/sni/sm or generator/sm
Source: "{code:GetDKC3ROMPath}"; DestDir: "{app}"; DestName: "Donkey Kong Country 3 - Dixie Kong's Double Trouble! (USA) (En,Fr).sfc"; Flags: external; Components: client/sni/dkc3 or generator/dkc3
Source: "{code:GetSoEROMPath}"; DestDir: "{app}"; DestName: "Secret of Evermore (USA).sfc"; Flags: external; Components: generator/soe
Source: "{code:GetOoTROMPath}"; DestDir: "{app}"; DestName: "The Legend of Zelda - Ocarina of Time.z64"; Flags: external; Components: client/oot or generator/oot
Source: "{#source_path}\*"; Excludes: "*.sfc, *.log, data\sprites\alttpr, SNI, EnemizerCLI, Archipelago*.exe"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs
@@ -143,6 +146,11 @@ Root: HKCR; Subkey: "{#MyAppName}smpatch"; ValueData: "Archi
Root: HKCR; Subkey: "{#MyAppName}smpatch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; Components: client/sni
Root: HKCR; Subkey: "{#MyAppName}smpatch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/sni
Root: HKCR; Subkey: ".apdkc3"; ValueData: "{#MyAppName}dkc3patch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/sni
Root: HKCR; Subkey: "{#MyAppName}dkc3patch"; ValueData: "Archipelago Donkey Kong Country 3 Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/sni
Root: HKCR; Subkey: "{#MyAppName}dkc3patch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; Components: client/sni
Root: HKCR; Subkey: "{#MyAppName}dkc3patch\shell\open\command"; ValueData: """{app}\ArchipelagoSNIClient.exe"" ""%1"""; ValueType: string; ValueName: ""; Components: client/sni
Root: HKCR; Subkey: ".apsmz3"; ValueData: "{#MyAppName}smz3patch"; Flags: uninsdeletevalue; ValueType: string; ValueName: ""; Components: client/sni
Root: HKCR; Subkey: "{#MyAppName}smz3patch"; ValueData: "Archipelago SMZ3 Patch"; Flags: uninsdeletekey; ValueType: string; ValueName: ""; Components: client/sni
Root: HKCR; Subkey: "{#MyAppName}smz3patch\DefaultIcon"; ValueData: "{app}\ArchipelagoSNIClient.exe,0"; ValueType: string; ValueName: ""; Components: client/sni
@@ -206,6 +214,9 @@ var LttPROMFilePage: TInputFileWizardPage;
var smrom: string;
var SMRomFilePage: TInputFileWizardPage;
var dkc3rom: string;
var DKC3RomFilePage: TInputFileWizardPage;
var soerom: string;
var SoERomFilePage: TInputFileWizardPage;
@@ -295,6 +306,8 @@ begin
Result := not (LttPROMFilePage.Values[0] = '')
else if (assigned(SMROMFilePage)) and (CurPageID = SMROMFilePage.ID) then
Result := not (SMROMFilePage.Values[0] = '')
else if (assigned(DKC3ROMFilePage)) and (CurPageID = DKC3ROMFilePage.ID) then
Result := not (DKC3ROMFilePage.Values[0] = '')
else if (assigned(SoEROMFilePage)) and (CurPageID = SoEROMFilePage.ID) then
Result := not (SoEROMFilePage.Values[0] = '')
else if (assigned(OoTROMFilePage)) and (CurPageID = OoTROMFilePage.ID) then
@@ -335,6 +348,22 @@ begin
Result := '';
end;
function GetDKC3ROMPath(Param: string): string;
begin
if Length(dkc3rom) > 0 then
Result := dkc3rom
else if Assigned(DKC3RomFilePage) then
begin
R := CompareStr(GetSNESMD5OfFile(DKC3ROMFilePage.Values[0]), '120abf304f0c40fe059f6a192ed4f947')
if R <> 0 then
MsgBox('Donkey Kong Country 3 ROM validation failed. Very likely wrong file.', mbInformation, MB_OK);
Result := DKC3ROMFilePage.Values[0]
end
else
Result := '';
end;
function GetSoEROMPath(Param: string): string;
begin
if Length(soerom) > 0 then
@@ -379,6 +408,10 @@ begin
if Length(smrom) = 0 then
SMRomFilePage:= AddRomPage('Super Metroid (JU).sfc');
dkc3rom := CheckRom('Donkey Kong Country 3 - Dixie Kong''s Double Trouble! (USA) (En,Fr).sfc', '120abf304f0c40fe059f6a192ed4f947');
if Length(dkc3rom) = 0 then
DKC3RomFilePage:= AddRomPage('Donkey Kong Country 3 - Dixie Kong''s Double Trouble! (USA) (En,Fr).sfc');
soerom := CheckRom('Secret of Evermore (USA).sfc', '6e9c94511d04fac6e0a1e582c170be3a');
if Length(soerom) = 0 then
SoEROMFilePage:= AddRomPage('Secret of Evermore (USA).sfc');
@@ -392,6 +425,8 @@ begin
Result := not (WizardIsComponentSelected('client/sni/lttp') or WizardIsComponentSelected('generator/lttp'));
if (assigned(SMROMFilePage)) and (PageID = SMROMFilePage.ID) then
Result := not (WizardIsComponentSelected('client/sni/sm') or WizardIsComponentSelected('generator/sm'));
if (assigned(DKC3ROMFilePage)) and (PageID = DKC3ROMFilePage.ID) then
Result := not (WizardIsComponentSelected('client/sni/dkc3') or WizardIsComponentSelected('generator/dkc3'));
if (assigned(SoEROMFilePage)) and (PageID = SoEROMFilePage.ID) then
Result := not (WizardIsComponentSelected('generator/soe'));
if (assigned(OoTROMFilePage)) and (PageID = OoTROMFilePage.ID) then

View File

@@ -215,9 +215,11 @@ class Scams(Choice):
option_all = 3
alias_false = 0
@property
def gives_king_zora_hint(self):
return self.value in {0, 2}
@property
def gives_bottle_merchant_hint(self):
return self.value in {0, 1}

View File

@@ -299,4 +299,5 @@ item_table = (
'A Shrubbery',
'Roomba with a Knife',
'Wet Cat',
'The missing moderator, Frostwares',
)

View File

@@ -25,7 +25,7 @@ class ArchipIDLEWorld(World):
"""
game = "ArchipIDLE"
topology_present = False
data_version = 3
data_version = 4
hidden = (datetime.now().month != 4) # ArchipIDLE is only visible during April
web = ArchipIDLEWebWorld()

View File

@@ -0,0 +1,41 @@
import typing
from Options import Toggle, Option
class AutoEquipOption(Toggle):
"""Automatically equips any received armor or left/right weapons."""
display_name = "Auto-equip"
class LockEquipOption(Toggle):
"""Lock the equipment slots so you cannot change your armor or your left/right weapons. Works great with the
Auto-equip option."""
display_name = "Lock Equipement Slots"
class NoWeaponRequirementsOption(Toggle):
"""Disable the weapon requirements by removing any movement or damage penalties.
Permitting you to use any weapon early"""
display_name = "No Weapon Requirements"
class RandomizeWeaponsLevelOption(Toggle):
"""Enable this option to upgrade 33% ( based on the probability chance ) of the pool of weapons to a random value
between +1 and +5/+10"""
display_name = "Randomize weapons level"
class LateBasinOfVowsOption(Toggle):
"""Force the Basin of Vows to be located as a reward of defeating Pontiff Sulyvahn. It permits to ease the
progression by preventing having to kill the Dancer of the Boreal Valley as the first boss"""
display_name = "Late Basin of Vows"
dark_souls_options: typing.Dict[str, type(Option)] = {
"auto_equip": AutoEquipOption,
"lock_equip": LockEquipOption,
"no_weapon_requirements": NoWeaponRequirementsOption,
"randomize_weapons_level": RandomizeWeaponsLevelOption,
"late_basin_of_vows": LateBasinOfVowsOption,
}

View File

@@ -0,0 +1,287 @@
# world/dark_souls_3/__init__.py
import json
import os
from .Options import dark_souls_options
from .data.items_data import weapons_upgrade_5_table, weapons_upgrade_10_table, item_dictionary_table, key_items_list
from .data.locations_data import location_dictionary_table, cemetery_of_ash_table, fire_link_shrine_table, \
high_wall_of_lothric, \
undead_settlement_table, road_of_sacrifice_table, consumed_king_garden_table, cathedral_of_the_deep_table, \
farron_keep_table, catacombs_of_carthus_table, smouldering_lake_table, irithyll_of_the_boreal_valley_table, \
irithyll_dungeon_table, profaned_capital_table, anor_londo_table, lothric_castle_table, grand_archives_table, \
untended_graves_table, archdragon_peak_table, firelink_shrine_bell_tower_table
from ..AutoWorld import World, WebWorld
from BaseClasses import MultiWorld, Location, Region, Item, RegionType, Entrance, Tutorial, ItemClassification
from ..generic.Rules import set_rule
class DarkSouls3Web(WebWorld):
tutorials = [Tutorial(
"Multiworld Setup Tutorial",
"A guide to setting up the Archipelago Dark Souls III randomizer on your computer.",
"English",
"setup_en.md",
"setup/en",
["Marech"]
)]
class DarkSouls3World(World):
"""
Dark souls III is an Action role-playing game and is part of the Souls series developed by FromSoftware.
Played in a third-person perspective, players have access to various weapons, armour, magic, and consumables that
they can use to fight their enemies.
"""
game: str = "Dark Souls III"
options = dark_souls_options
topology_present: bool = True
remote_items: bool = False
remote_start_inventory: bool = False
web = DarkSouls3Web()
data_version = 2
base_id = 100000
item_name_to_id = {name: id for id, name in enumerate(item_dictionary_table, base_id)}
location_name_to_id = {name: id for id, name in enumerate(location_dictionary_table, base_id)}
def __init__(self, world: MultiWorld, player: int):
super().__init__(world, player)
self.locked_items = []
self.locked_locations = []
self.main_path_locations = []
def create_item(self, name: str) -> Item:
data = self.item_name_to_id[name]
if name in key_items_list:
item_classification = ItemClassification.progression
elif name in weapons_upgrade_5_table or name in weapons_upgrade_10_table:
item_classification = ItemClassification.useful
else:
item_classification = ItemClassification.filler
return DarkSouls3Item(name, item_classification, data, self.player)
def create_regions(self):
menu_region = Region("Menu", RegionType.Generic, "Menu", self.player)
self.world.regions.append(menu_region)
# Create all Vanilla regions of Dark Souls III
cemetery_of_ash_region = self.create_region("Cemetery Of Ash", cemetery_of_ash_table)
firelink_shrine_region = self.create_region("Firelink Shrine", fire_link_shrine_table)
firelink_shrine_bell_tower_region = self.create_region("Firelink Shrine Bell Tower",
firelink_shrine_bell_tower_table)
high_wall_of_lothric_region = self.create_region("High Wall of Lothric", high_wall_of_lothric)
undead_settlement_region = self.create_region("Undead Settlement", undead_settlement_table)
road_of_sacrifices_region = self.create_region("Road of Sacrifices", road_of_sacrifice_table)
consumed_king_garden_region = self.create_region("Consumed King's Garden", consumed_king_garden_table)
cathedral_of_the_deep_region = self.create_region("Cathedral of the Deep", cathedral_of_the_deep_table)
farron_keep_region = self.create_region("Farron Keep", farron_keep_table)
catacombs_of_carthus_region = self.create_region("Catacombs of Carthus", catacombs_of_carthus_table)
smouldering_lake_region = self.create_region("Smouldering Lake", smouldering_lake_table)
irithyll_of_the_boreal_valley_region = self.create_region("Irithyll of the Boreal Valley",
irithyll_of_the_boreal_valley_table)
irithyll_dungeon_region = self.create_region("Irithyll Dungeon", irithyll_dungeon_table)
profaned_capital_region = self.create_region("Profaned Capital", profaned_capital_table)
anor_londo_region = self.create_region("Anor Londo", anor_londo_table)
lothric_castle_region = self.create_region("Lothric Castle", lothric_castle_table)
grand_archives_region = self.create_region("Grand Archives", grand_archives_table)
untended_graves_region = self.create_region("Untended Graves", untended_graves_table)
archdragon_peak_region = self.create_region("Archdragon Peak", archdragon_peak_table)
kiln_of_the_first_flame_region = self.create_region("Kiln Of The First Flame", None)
# Create the entrance to connect those regions
menu_region.exits.append(Entrance(self.player, "New Game", menu_region))
self.world.get_entrance("New Game", self.player).connect(cemetery_of_ash_region)
cemetery_of_ash_region.exits.append(Entrance(self.player, "Goto Firelink Shrine", cemetery_of_ash_region))
self.world.get_entrance("Goto Firelink Shrine", self.player).connect(firelink_shrine_region)
firelink_shrine_region.exits.append(Entrance(self.player, "Goto High Wall of Lothric",
firelink_shrine_region))
firelink_shrine_region.exits.append(Entrance(self.player, "Goto Kiln Of The First Flame",
firelink_shrine_region))
firelink_shrine_region.exits.append(Entrance(self.player, "Goto Bell Tower",
firelink_shrine_region))
self.world.get_entrance("Goto High Wall of Lothric", self.player).connect(high_wall_of_lothric_region)
self.world.get_entrance("Goto Kiln Of The First Flame", self.player).connect(kiln_of_the_first_flame_region)
self.world.get_entrance("Goto Bell Tower", self.player).connect(firelink_shrine_bell_tower_region)
high_wall_of_lothric_region.exits.append(Entrance(self.player, "Goto Undead Settlement",
high_wall_of_lothric_region))
high_wall_of_lothric_region.exits.append(Entrance(self.player, "Goto Lothric Castle",
high_wall_of_lothric_region))
self.world.get_entrance("Goto Undead Settlement", self.player).connect(undead_settlement_region)
self.world.get_entrance("Goto Lothric Castle", self.player).connect(lothric_castle_region)
undead_settlement_region.exits.append(Entrance(self.player, "Goto Road Of Sacrifices",
undead_settlement_region))
self.world.get_entrance("Goto Road Of Sacrifices", self.player).connect(road_of_sacrifices_region)
road_of_sacrifices_region.exits.append(Entrance(self.player, "Goto Cathedral", road_of_sacrifices_region))
road_of_sacrifices_region.exits.append(Entrance(self.player, "Goto Farron keep", road_of_sacrifices_region))
self.world.get_entrance("Goto Cathedral", self.player).connect(cathedral_of_the_deep_region)
self.world.get_entrance("Goto Farron keep", self.player).connect(farron_keep_region)
farron_keep_region.exits.append(Entrance(self.player, "Goto Carthus catacombs", farron_keep_region))
self.world.get_entrance("Goto Carthus catacombs", self.player).connect(catacombs_of_carthus_region)
catacombs_of_carthus_region.exits.append(Entrance(self.player, "Goto Irithyll of the boreal",
catacombs_of_carthus_region))
catacombs_of_carthus_region.exits.append(Entrance(self.player, "Goto Smouldering Lake",
catacombs_of_carthus_region))
self.world.get_entrance("Goto Irithyll of the boreal", self.player).\
connect(irithyll_of_the_boreal_valley_region)
self.world.get_entrance("Goto Smouldering Lake", self.player).connect(smouldering_lake_region)
irithyll_of_the_boreal_valley_region.exits.append(Entrance(self.player, "Goto Irithyll dungeon",
irithyll_of_the_boreal_valley_region))
irithyll_of_the_boreal_valley_region.exits.append(Entrance(self.player, "Goto Anor Londo",
irithyll_of_the_boreal_valley_region))
self.world.get_entrance("Goto Irithyll dungeon", self.player).connect(irithyll_dungeon_region)
self.world.get_entrance("Goto Anor Londo", self.player).connect(anor_londo_region)
irithyll_dungeon_region.exits.append(Entrance(self.player, "Goto Archdragon peak", irithyll_dungeon_region))
irithyll_dungeon_region.exits.append(Entrance(self.player, "Goto Profaned capital", irithyll_dungeon_region))
self.world.get_entrance("Goto Archdragon peak", self.player).connect(archdragon_peak_region)
self.world.get_entrance("Goto Profaned capital", self.player).connect(profaned_capital_region)
lothric_castle_region.exits.append(Entrance(self.player, "Goto Consumed King Garden", lothric_castle_region))
lothric_castle_region.exits.append(Entrance(self.player, "Goto Grand Archives", lothric_castle_region))
self.world.get_entrance("Goto Consumed King Garden", self.player).connect(consumed_king_garden_region)
self.world.get_entrance("Goto Grand Archives", self.player).connect(grand_archives_region)
consumed_king_garden_region.exits.append(Entrance(self.player, "Goto Untended Graves",
consumed_king_garden_region))
self.world.get_entrance("Goto Untended Graves", self.player).connect(untended_graves_region)
# For each region, add the associated locations retrieved from the corresponding location_table
def create_region(self, region_name, location_table) -> Region:
new_region = Region(region_name, RegionType.Generic, region_name, self.player)
if location_table:
for name, address in location_table.items():
location = DarkSouls3Location(self.player, name, self.location_name_to_id[name], new_region)
new_region.locations.append(location)
self.world.regions.append(new_region)
return new_region
def create_items(self):
for name, address in self.item_name_to_id.items():
# Specific items will be included in the item pool under certain conditions. See generate_basic
if name != "Basin of Vows":
self.world.itempool += [self.create_item(name)]
def generate_early(self):
pass
def set_rules(self) -> None:
# Define the access rules to the entrances
set_rule(self.world.get_entrance("Goto Bell Tower", self.player),
lambda state: state.has("Tower Key", self.player))
set_rule(self.world.get_entrance("Goto Undead Settlement", self.player),
lambda state: state.has("Small Lothric Banner", self.player))
set_rule(self.world.get_entrance("Goto Lothric Castle", self.player),
lambda state: state.has("Basin of Vows", self.player))
set_rule(self.world.get_entrance("Goto Irithyll of the boreal", self.player),
lambda state: state.has("Small Doll", self.player))
set_rule(self.world.get_entrance("Goto Archdragon peak", self.player),
lambda state: state.can_reach("CKG: Soul of Consumed Oceiros", "Location", self.player))
set_rule(self.world.get_entrance("Goto Profaned capital", self.player),
lambda state: state.has("Storm Ruler", self.player))
set_rule(self.world.get_entrance("Goto Grand Archives", self.player),
lambda state: state.has("Grand Archives Key", self.player))
set_rule(self.world.get_entrance("Goto Kiln Of The First Flame", self.player),
lambda state: state.has("Cinders of a Lord - Abyss Watcher", self.player) and
state.has("Cinders of a Lord - Yhorm the Giant", self.player) and
state.has("Cinders of a Lord - Aldrich", self.player) and
state.has("Cinders of a Lord - Lothric Prince", self.player))
# Define the access rules to some specific locations
set_rule(self.world.get_location("HWL: Soul of the Dancer", self.player),
lambda state: state.has("Basin of Vows", self.player))
set_rule(self.world.get_location("HWL: Greirat's Ashes", self.player),
lambda state: state.has("Cell Key", self.player))
set_rule(self.world.get_location("ID: Bellowing Dragoncrest Ring", self.player),
lambda state: state.has("Jailbreaker's Key", self.player))
set_rule(self.world.get_location("ID: Prisoner Chief's Ashes", self.player),
lambda state: state.has("Jailer's Key Ring", self.player))
set_rule(self.world.get_location("ID: Covetous Gold Serpent Ring", self.player),
lambda state: state.has("Old Cell Key", self.player))
black_hand_gotthard_corpse_rule = lambda state: \
(state.can_reach("AL: Cinders of a Lord - Aldrich", "Location", self.player) and
state.can_reach("PC: Cinders of a Lord - Yhorm the Giant", "Location", self.player))
set_rule(self.world.get_location("LC: Grand Archives Key", self.player), black_hand_gotthard_corpse_rule)
set_rule(self.world.get_location("LC: Gotthard Twinswords", self.player), black_hand_gotthard_corpse_rule)
self.world.completion_condition[self.player] = lambda state: \
state.has("Cinders of a Lord - Abyss Watcher", self.player) and \
state.has("Cinders of a Lord - Yhorm the Giant", self.player) and \
state.has("Cinders of a Lord - Aldrich", self.player) and \
state.has("Cinders of a Lord - Lothric Prince", self.player)
def generate_basic(self):
# Depending on the specified option, add the Basin of Vows to a specific location or to the item pool
item = self.create_item("Basin of Vows")
if self.world.late_basin_of_vows[self.player]:
self.world.get_location("IBV: Soul of Pontiff Sulyvahn", self.player).place_locked_item(item)
else:
self.world.itempool += [item]
# Fill item pool with additional items
item_pool_len = self.item_name_to_id.__len__()
total_required_locations = self.location_name_to_id.__len__()
for i in range(item_pool_len, total_required_locations):
self.world.itempool += [self.create_item("Soul of an Intrepid Hero")]
def generate_output(self, output_directory: str):
# Depending on the specified option, modify items hexadecimal value to add an upgrade level
item_dictionary = item_dictionary_table.copy()
if self.world.randomize_weapons_level[self.player]:
# Randomize some weapons upgrades
for name in weapons_upgrade_5_table.keys():
if self.world.random.randint(0, 100) < 33:
value = self.world.random.randint(1, 5)
item_dictionary[name] += value
for name in weapons_upgrade_10_table.keys():
if self.world.random.randint(0, 100) < 33:
value = self.world.random.randint(1, 10)
item_dictionary[name] += value
# Create the mandatory lists to generate the player's output file
items_id = []
items_address = []
locations_id = []
locations_address = []
locations_target = []
for location in self.world.get_filled_locations():
if location.item.player == self.player:
items_id.append(location.item.code)
items_address.append(item_dictionary[location.item.name])
if location.player == self.player:
locations_address.append(location_dictionary_table[location.name])
locations_id.append(location.address)
if location.item.player == self.player:
locations_target.append(item_dictionary[location.item.name])
else:
locations_target.append(0)
data = {
"options": {
"auto_equip": self.world.auto_equip[self.player].value,
"lock_equip": self.world.lock_equip[self.player].value,
"no_weapon_requirements": self.world.no_weapon_requirements[self.player].value,
},
"seed": self.world.seed_name, # to verify the server's multiworld
"slot": self.world.player_name[self.player], # to connect to server
"base_id": self.base_id, # to merge location and items lists
"locationsId": locations_id,
"locationsAddress": locations_address,
"locationsTarget": locations_target,
"itemsId": items_id,
"itemsAddress": items_address
}
# generate the file
filename = f"AP-{self.world.seed_name}-P{self.player}-{self.world.player_name[self.player]}.json"
with open(os.path.join(output_directory, filename), 'w') as outfile:
json.dump(data, outfile)
class DarkSouls3Location(Location):
game: str = "Dark Souls III"
class DarkSouls3Item(Item):
game: str = "Dark Souls III"

View File

@@ -0,0 +1,383 @@
"""
Tools used to create this list :
List of all items https://docs.google.com/spreadsheets/d/1nK2g7g6XJ-qphFAk1tjP3jZtlXWDQY-ItKLa_sniawo/edit#gid=1551945791
Regular expression parser https://regex101.com/r/XdtiLR/2
List of locations https://darksouls3.wiki.fextralife.com/Locations
"""
weapons_upgrade_5_table = {
"Irithyll Straight Sword": 0x0020A760,
"Chaos Blade": 0x004C9960,
"Dragonrider Bow": 0x00D6B0F0,
"White Hair Talisman": 0x00CAF120,
"Izalith Staff": 0x00C96A80,
"Fume Ultra Greatsword": 0x0060E4B0,
"Black Knight Sword": 0x005F5E10,
"Yorshka's Spear": 0x008C3A70,
"Smough's Great Hammer": 0x007E30B0,
"Dragonslayer Greatbow": 0x00CF8500,
"Golden Ritual Spear": 0x00C83200,
"Eleonora": 0x006CCB90,
"Witch's Locks": 0x00B7B740,
"Crystal Chime": 0x00CA2DD0,
"Black Knight Glaive": 0x009AE070,
"Dragonslayer Spear": 0x008CAFA0,
"Caitha's Chime": 0x00CA06C0,
"Sunlight Straight Sword": 0x00203230,
"Firelink Greatsword": 0x0060BDA0,
"Hollowslayer Greatsword": 0x00604870,
"Arstor's Spear": 0x008BEC50,
"Vordt's Great Hammer": 0x007CD120,
"Crystal Sage's Rapier": 0x002E6300,
"Farron Greatsword": 0x005E9AC0,
"Wolf Knight's Greatsword": 0x00602160,
"Dancer's Enchanted Swords": 0x00F4C040,
"Wolnir's Holy Sword": 0x005FFA50,
"Demon's Greataxe": 0x006CA480,
"Demon's Fist": 0x00A84DF0,
"Old King's Great Hammer": 0x007CF830,
"Greatsword of Judgment": 0x005E2590,
"Profaned Greatsword": 0x005E4CA0,
"Yhorm's Great Machete": 0x005F0FF0,
"Cleric's Candlestick": 0x0020F580,
"Dragonslayer Greataxe": 0x006C7D70,
"Moonlight Greatsword": 0x00606F80,
"Gundyr's Halberd": 0x009A1D20,
"Lothric's Holy Sword": 0x005FD340,
"Lorian's Greatsword": 0x005F8520,
"Twin Princes' Greatsword": 0x005FAC30,
"Storm Curved Sword": 0x003E4180,
"Dragonslayer Swordspear": 0x008BC540,
"Sage's Crystal Staff": 0x00C8CE40,
}
weapons_upgrade_10_table = {
"Broken Straight Sword": 0x001EF9B0,
"Deep Battle Axe": 0x0006AFA54,
"Club": 0x007A1200,
"Claymore": 0x005BDBA0,
"Longbow": 0x00D689E0,
"Mail Breaker": 0x002DEDD0,
"Broadsword": 0x001ED2A0,
"Astora's Straight Sword": 0x002191C0,
"Rapier": 0x002E14E0,
"Lucerne": 0x0098BD90,
"Whip": 0x00B71B00,
"Reinforced Club": 0x007A8730,
"Caestus": 0x00A7FFD0,
"Partizan": 0x0089C970,
"Red Hilted Halberd": 0x009AB960,
"Saint's Talisman": 0x00CACA10,
"Large Club": 0x007AFC60,
"Brigand Twindaggers": 0x00F50E60,
"Butcher Knife": 0x006BE130,
"Brigand Axe": 0x006B1DE0,
"Heretic's Staff": 0x00C8F550,
"Great Club": 0x007B4A80,
"Exile Greatsword": 0x005DD770,
"Sellsword Twinblades": 0x00F42400,
"Notched Whip": 0x00B7DE50,
"Astora Greatsword": 0x005C9EF0,
"Executioner's Greatsword": 0x0021DFE0,
"Saint-tree Bellvine": 0x00C9DFB0,
"Saint Bident": 0x008C1360,
"Drang Hammers": 0x00F61FD0,
"Arbalest": 0x00D662D0,
"Sunlight Talisman": 0x00CA54E0,
"Greatsword": 0x005C50D0,
"Black Bow of Pharis": 0x00D7E970,
"Great Axe": 0x006B9310,
"Black Blade": 0x004CC070,
"Blacksmith Hammer": 0x007E57C0,
"Witchtree Branch": 0x00C94370,
"Painting Guardian's Curved Sword": 0x003E6890,
"Pickaxe": 0x007DE290,
"Court Sorcerer's Staff": 0x00C91C60,
"Avelyn": 0x00D6FF10,
"Onikiri and Ubadachi": 0x00F58390,
"Ricard's Rapier": 0x002E3BF0,
"Drakeblood Greatsword": 0x00609690,
"Greatlance": 0x008A8CC0,
"Sniper Crossbow": 0x00D83790,
"Claw": 0x00A7D8C0,
"Drang Twinspears": 0x00F5AAA0,
}
shields_table = {
"East-West Shield": 0x0142B930,
"Silver Eagle Kite Shield": 0x014418C0,
"Small Leather Shield": 0x01315410,
"Blue Wooden Shield": 0x0143F1B0,
"Plank Shield": 0x01346150,
"Caduceus Round Shield": 0x01341330,
"Wargod Wooden Shield": 0x0144DC10,
"Grass Crest Shield": 0x01437C80,
"Golden Falcon Shield": 0x01354BB0,
"Twin Dragon Greatshield": 0x01513820,
"Spider Shield": 0x01435570,
"Crest Shield": 0x01430750,
"Curse Ward Greatshield": 0x01518640,
"Stone Parma": 0x01443FD0,
"Dragon Crest Shield": 0x01432E60,
"Shield of Want": 0x0144B500,
"Black Iron Greatshield": 0x0150EA00,
"Greatshield of Glory": 0x01515F30,
"Sacred Bloom Shield": 0x013572C0,
"Golden Wing Crest Shield": 0x0143CAA0,
"Ancient Dragon Greatshield": 0x013599D0,
"Spirit Tree Crest Shield": 0x014466E0,
}
goods_table = {
"Soul of an Intrepid Hero": 0x4000019D,
"Soul of the Nameless King": 0x400002D2,
"Soul of Champion Gundyr": 0x400002C8,
"Soul of the Twin Princes": 0x400002DB,
"Soul of Consumed Oceiros": 0x400002CE,
"Soul of Aldrich": 0x400002D5,
"Soul of Yhorm the Giant": 0x400002DC,
"Soul of Pontiff Sulyvahn": 0x400002D4,
"Soul of the Old Demon King": 0x400002D0,
"Soul of High Lord Wolnir": 0x400002D6,
"Soul of the Blood of the Wolf": 0x400002CD,
"Soul of the Deacons of the Deep": 0x400002D9,
"Soul of a Crystal Sage": 0x400002CB,
"Soul of Boreal Valley Vordt": 0x400002CF,
"Soul of a Stray Demon": 0x400002E7,
"Soul of a Demon": 0x400002E3,
}
armor_table = {
"Fire Keeper Robe": 0x140D9CE8,
"Fire Keeper Gloves": 0x140DA0D0,
"Fire Keeper Skirt": 0x140DA4B8,
"Deserter Trousers": 0x126265B8,
"Cleric Hat": 0x11D905C0,
"Cleric Blue Robe": 0x11D909A8,
"Cleric Gloves": 0x11D90D90,
"Cleric Trousers": 0x11D91178,
"Northern Helm": 0x116E3600,
"Northern Armor": 0x116E39E8,
"Northern Gloves": 0x116E3DD0,
"Northern Trousers": 0x116E41B8,
"Loincloth": 0x148F57D8,
"Brigand Hood": 0x148009E0,
"Brigand Armor": 0x14800DC8,
"Brigand Gauntlets": 0x148011B0,
"Brigand Trousers": 0x14801598,
"Sorcerer Hood": 0x11C9C380,
"Sorcerer Robe": 0x11C9C768,
"Sorcerer Gloves": 0x11C9CB50,
"Sorcerer Trousers": 0x11C9CF38,
"Fallen Knight Helm": 0x1121EAC0,
"Fallen Knight Armor": 0x1121EEA8,
"Fallen Knight Gauntlets": 0x1121F290,
"Fallen Knight Trousers": 0x1121F678,
"Conjurator Hood": 0x149E8E60,
"Conjurator Robe": 0x149E9248,
"Conjurator Manchettes": 0x149E9630,
"Conjurator Boots": 0x149E9A18,
"Sellsword Helm": 0x11481060,
"Sellsword Armor": 0x11481448,
"Sellsword Gauntlet": 0x11481830,
"Sellsword Trousers": 0x11481C18,
"Herald Helm": 0x114FB180,
"Herald Armor": 0x114FB568,
"Herald Gloves": 0x114FB950,
"Herald Trousers": 0x114FBD38,
"Maiden Hood": 0x14BD12E0,
"Maiden Robe": 0x14BD16C8,
"Maiden Gloves": 0x14BD1AB0,
"Maiden Skirt": 0x14BD1E98,
"Drang Armor": 0x154E0C28,
"Drang Gauntlets": 0x154E1010,
"Drang Shoes": 0x154E13F8,
"Archdeacon White Crown": 0x13EF1480,
"Archdeacon Holy Garb": 0x13EF1868,
"Archdeacon Skirt": 0x13EF2038,
"Antiquated Dress": 0x15D76068,
"Antiquated Gloves": 0x15D76450,
"Antiquated Skirt": 0x15D76838,
"Ragged Mask": 0x148F4C20,
"Crown of Dusk": 0x15D75C80,
"Pharis's Hat": 0x1487AB00,
"Old Sage's Blindfold": 0x11945BA0,
"Painting Guardian Hood": 0x156C8CC0,
"Painting Guardian Gown": 0x156C90A8,
"Painting Guardian Gloves": 0x156C9490,
"Painting Guardian Waistcloth": 0x156C9878,
"Brass Helm": 0x1501BD00,
"Brass Armor": 0x1501C0E8,
"Brass Gauntlets": 0x1501C4D0,
"Brass Leggings": 0x1501C8B8,
"Old Sorcerer Hat": 0x1496ED40,
"Old Sorcerer Coat": 0x1496F128,
"Old Sorcerer Gauntlets": 0x1496F510,
"Old Sorcerer Boots": 0x1496F8F8,
"Court Sorcerer Hood": 0x11BA8140,
"Court Sorcerer Robe": 0x11BA8528,
"Court Sorcerer Gloves": 0x11BA8910,
"Court Sorcerer Trousers": 0x11BA8CF8,
"Dragonslayer Helm": 0x158B1140,
"Dragonslayer Armor": 0x158B1528,
"Dragonslayer Gauntlets": 0x158B1910,
"Dragonslayer Leggings": 0x158B1CF8,
"Hood of Prayer": 0x13AA6A60,
"Robe of Prayer": 0x13AA6E48,
"Skirt of Prayer": 0x13AA7618,
"Winged Knight Helm": 0x12EBAE40,
"Winged Knight Armor": 0x12EBB228,
"Winged Knight Gauntlets": 0x12EBB610,
"Winged Knight Leggings": 0x12EBB9F8,
"Shadow Mask": 0x14D3F640,
"Shadow Garb": 0x14D3FA28,
"Shadow Gauntlets": 0x14D3FE10,
"Shadow Leggings": 0x14D401F8,
}
rings_table = {
"Estus Ring": 0x200050DC,
"Covetous Silver Serpent Ring": 0x20004FB0,
"Fire Clutch Ring": 0x2000501E,
"Flame Stoneplate Ring": 0x20004E52,
"Flynn's Ring": 0x2000503C,
"Chloranthy Ring": 0x20004E2A,
"Morne's Ring": 0x20004F1A,
"Sage Ring": 0x20004F38,
"Aldrich's Sapphire": 0x20005096,
"Lloyd's Sword Ring": 0x200050B4,
"Poisonbite Ring": 0x20004E8E,
"Deep Ring": 0x20004F60,
"Lingering Dragoncrest Ring": 0x20004F2E,
"Carthus Milkring": 0x20004FE2,
"Witch's Ring": 0x20004F11,
"Carthus Bloodring": 0x200050FA,
"Speckled Stoneplate Ring": 0x20004E7A,
"Magic Clutch Ring": 0x2000500A,
"Ring of the Sun's First Born": 0x20004F1B,
"Pontiff's Right Eye": 0x2000510E, "Leo Ring": 0x20004EE8,
"Dark Stoneplate Ring": 0x20004E70,
"Reversal Ring": 0x20005104,
"Ring of Favor": 0x20004E3E,
"Bellowing Dragoncrest Ring": 0x20004F07,
"Covetous Gold Serpent Ring": 0x20004FA6,
"Dusk Crown Ring": 0x20004F4C,
"Dark Clutch Ring": 0x20005028,
"Cursebite Ring": 0x20004E98,
"Sun Princess Ring": 0x20004FBA,
"Aldrich's Ruby": 0x2000508C,
"Scholar Ring": 0x20004EB6,
"Fleshbite Ring": 0x20004EA2,
"Hunter's Ring": 0x20004FF6,
"Ashen Estus Ring": 0x200050E6,
"Hornet Ring": 0x20004F9C,
"Lightning Clutch Ring": 0x20005014,
"Ring of Steel Protection": 0x20004E48,
"Calamity Ring": 0x20005078,
"Thunder Stoneplate Ring": 0x20004E5C,
"Knight's Ring": 0x20004FEC,
"Red Tearstone Ring": 0x20004ECA,
"Dragonscale Ring": 0x2000515E,
"Knight Slayer's Ring": 0x20005000,
}
spells_table = {
"Seek Guidance": 0x40360420,
"Lightning Spear": 0x40362B30,
"Atonement": 0x4039ADA0,
"Great Magic Weapon": 0x40140118,
"Iron Flesh": 0x40251430,
"Lightning Stake": 0x40389C30,
"Toxic Mist": 0x4024F108,
"Sacred Flame": 0x40284880,
"Dorhys' Gnawing": 0x40363EB8,
"Great Heal": 0x40356FB0,
"Lightning Blade": 0x4036C770,
"Profaned Flame": 0x402575D8,
"Wrath of the Gods": 0x4035E0F8,
"Power Within": 0x40253B40,
"Soul Stream": 0x4018B820,
"Divine Pillars of Light": 0x4038C340,
"Great Magic Barrier": 0x40365628,
"Great Magic Shield": 0x40144F38,
}
misc_items_table = {
"Tower Key": 0x400007DF,
"Grave Key": 0x400007D9,
"Cell Key": 0x400007DA,
"Small Lothric Banner": 0x40000836,
"Mortician's Ashes": 0x4000083B,
"Braille Divine Tome of Carim": 0x40000847, # Shop
"Great Swamp Pyromancy Tome": 0x4000084F, # Shop
"Farron Coal ": 0x40000837, # Shop
"Paladin's Ashes": 0x4000083D, #Shop
"Deep Braille Divine Tome": 0x40000860, # Shop
"Small Doll": 0x400007D5,
"Golden Scroll": 0x4000085C,
"Sage's Coal": 0x40000838, # Shop #Unique
"Sage's Scroll": 0x40000854,
"Dreamchaser's Ashes": 0x4000083C, # Shop #Unique
"Cinders of a Lord - Abyss Watcher": 0x4000084B,
"Cinders of a Lord - Yhorm the Giant": 0x4000084D,
"Cinders of a Lord - Aldrich": 0x4000084C,
"Grand Archives Key": 0x400007DE,
"Basin of Vows": 0x40000845,
"Cinders of a Lord - Lothric Prince": 0x4000084E,
"Carthus Pyromancy Tome": 0x40000850,
"Grave Warden's Ashes": 0x4000083E,
"Grave Warden Pyromancy Tome": 0x40000853,
"Quelana Pyromancy Tome": 0x40000852,
"Izalith Pyromancy Tome": 0x40000851,
"Greirat's Ashes": 0x4000083F,
"Excrement-covered Ashes": 0x40000862,
"Easterner's Ashes": 0x40000868,
"Prisoner Chief's Ashes": 0x40000863,
"Jailbreaker's Key": 0x400007D7,
"Dragon Torso Stone": 0x4000017A,
"Profaned Coal": 0x4000083A,
"Xanthous Ashes": 0x40000864,
"Old Cell Key": 0x400007DC,
"Jailer's Key Ring": 0x400007D8,
"Logan's Scroll": 0x40000855,
"Storm Ruler": 0x006132D0,
"Giant's Coal": 0x40000839,
"Coiled Sword Fragment": 0x4000015F,
"Dragon Chaser's Ashes": 0x40000867,
"Twinkling Dragon Torso Stone": 0x40000184,
"Braille Divine Tome of Lothric": 0x40000848,
}
key_items_list = {
"Small Lothric Banner",
"Basin of Vows",
"Small Doll",
"Storm Ruler",
"Grand Archives Key",
"Cinders of a Lord - Abyss Watcher",
"Cinders of a Lord - Yhorm the Giant",
"Cinders of a Lord - Aldrich",
"Cinders of a Lord - Lothric Prince",
"Mortician's Ashes",
"Cell Key",
"Tower Key",
"Jailbreaker's Key",
"Prisoner Chief's Ashes",
"Old Cell Key",
"Jailer's Key Ring",
}
item_dictionary_table = {**weapons_upgrade_5_table, **weapons_upgrade_10_table, **shields_table, **armor_table, **rings_table, **spells_table, **misc_items_table, **goods_table}

View File

@@ -0,0 +1,434 @@
"""
Tools used to create this list :
List of all items https://docs.google.com/spreadsheets/d/1nK2g7g6XJ-qphFAk1tjP3jZtlXWDQY-ItKLa_sniawo/edit#gid=1551945791
Regular expression parser https://regex101.com/r/XdtiLR/2
List of locations https://darksouls3.wiki.fextralife.com/Locations
"""
cemetery_of_ash_table = {
}
fire_link_shrine_table = {
# "FS: Coiled Sword": 0x40000859, You can still light the Firelink Shrine fire whether you have it or not, useless
"FS: Broken Straight Sword": 0x001EF9B0,
"FS: East-West Shield": 0x0142B930,
"FS: Uchigatana": 0x004C4B40,
"FS: Master's Attire": 0x148F5008,
"FS: Master's Gloves": 0x148F53F0,
}
firelink_shrine_bell_tower_table = {
"FSBT: Covetous Silver Serpent Ring": 0x20004FB0,
"FSBT: Fire Keeper Robe": 0x140D9CE8,
"FSBT: Fire Keeper Gloves": 0x140DA0D0,
"FSBT: Fire Keeper Skirt": 0x140DA4B8,
"FSBT: Estus Ring": 0x200050DC,
"FSBT: Fire Keeper Soul": 0x40000186
}
high_wall_of_lothric = {
"HWL: Deep Battle Axe": 0x0006AFA54,
"HWL: Club": 0x007A1200,
"HWL: Claymore": 0x005BDBA0,
"HWL: Binoculars": 0x40000173,
"HWL: Longbow": 0x00D689E0,
"HWL: Mail Breaker": 0x002DEDD0,
"HWL: Broadsword": 0x001ED2A0,
"HWL: Silver Eagle Kite Shield": 0x014418C0,
"HWL: Astora's Straight Sword": 0x002191C0,
"HWL: Cell Key": 0x400007DA,
"HWL: Rapier": 0x002E14E0,
"HWL: Lucerne": 0x0098BD90,
"HWL: Small Lothric Banner": 0x40000836,
"HWL: Basin of Vows": 0x40000845,
"HWL: Soul of Boreal Valley Vordt": 0x400002CF,
"HWL: Soul of the Dancer": 0x400002CA,
"HWL: Way of Blue Covenant": 0x2000274C,
"HWL: Greirat's Ashes": 0x4000083F,
}
undead_settlement_table = {
"US: Small Leather Shield": 0x01315410,
"US: Whip": 0x00B71B00,
"US: Reinforced Club": 0x007A8730,
"US: Blue Wooden Shield": 0x0143F1B0,
"US: Cleric Hat": 0x11D905C0,
"US: Cleric Blue Robe": 0x11D909A8,
"US: Cleric Gloves": 0x11D90D90,
"US: Cleric Trousers": 0x11D91178,
"US: Mortician's Ashes": 0x4000083B,
"US: Caestus": 0x00A7FFD0,
"US: Plank Shield": 0x01346150,
"US: Flame Stoneplate Ring": 0x20004E52,
"US: Caduceus Round Shield": 0x01341330,
"US: Fire Clutch Ring": 0x2000501E,
"US: Partizan": 0x0089C970,
"US: Bloodbite Ring": 0x20004E84,
"US: Red Hilted Halberd": 0x009AB960,
"US: Saint's Talisman": 0x00CACA10,
"US: Irithyll Straight Sword": 0x0020A760,
"US: Large Club": 0x007AFC60,
"US: Northern Helm": 0x116E3600,
"US: Northern Armor": 0x116E39E8,
"US: Northern Gloves": 0x116E3DD0,
"US: Northern Trousers": 0x116E41B8,
"US: Flynn's Ring": 0x2000503C,
"US: Mirrah Vest": 0x15204568,
"US: Mirrah Gloves": 0x15204950,
"US: Mirrah Trousers": 0x15204D38,
"US: Chloranthy Ring": 0x20004E2A,
"US: Loincloth": 0x148F57D8,
"US: Wargod Wooden Shield": 0x0144DC10,
"US: Loretta's Bone": 0x40000846,
"US: Hand Axe": 0x006ACFC0,
"US: Great Scythe": 0x00989680,
"US: Soul of the Rotted Greatwood": 0x400002D7,
"US: Hawk Ring": 0x20004F92,
"US: Warrior of Sunlight Covenant": 0x20002738,
}
road_of_sacrifice_table = {
"RS: Brigand Twindaggers": 0x00F50E60,
"RS: Brigand Hood": 0x148009E0,
"RS: Brigand Armor": 0x14800DC8,
"RS: Brigand Gauntlets": 0x148011B0,
"RS: Brigand Trousers": 0x14801598,
"RS: Butcher Knife": 0x006BE130,
"RS: Brigand Axe": 0x006B1DE0,
"RS: Braille Divine Tome of Carim": 0x40000847,
"RS: Morne's Ring": 0x20004F1A,
"RS: Twin Dragon Greatshield": 0x01513820,
"RS: Heretic's Staff": 0x00C8F550,
"RS: Sorcerer Hood": 0x11C9C380,
"RS: Sorcerer Robe": 0x11C9C768,
"RS: Sorcerer Gloves": 0x11C9CB50,
"RS: Sorcerer Trousers": 0x11C9CF38,
"RS: Sage Ring": 0x20004F38,
"RS: Fallen Knight Helm": 0x1121EAC0,
"RS: Fallen Knight Armor": 0x1121EEA8,
"RS: Fallen Knight Gauntlets": 0x1121F290,
"RS: Fallen Knight Trousers": 0x1121F678,
"RS: Conjurator Hood": 0x149E8E60,
"RS: Conjurator Robe": 0x149E9248,
"RS: Conjurator Manchettes": 0x149E9630,
"RS: Conjurator Boots": 0x149E9A18,
"RS: Great Swamp Pyromancy Tome": 0x4000084F,
"RS: Great Club": 0x007B4A80,
"RS: Exile Greatsword": 0x005DD770,
"RS: Farron Coal ": 0x40000837,
"RS: Sellsword Twinblades": 0x00F42400,
"RS: Sellsword Helm": 0x11481060,
"RS: Sellsword Armor": 0x11481448,
"RS: Sellsword Gauntlet": 0x11481830,
"RS: Sellsword Trousers": 0x11481C18,
"RS: Golden Falcon Shield": 0x01354BB0,
"RS: Herald Helm": 0x114FB180,
"RS: Herald Armor": 0x114FB568,
"RS: Herald Gloves": 0x114FB950,
"RS: Herald Trousers": 0x114FBD38,
"RS: Grass Crest Shield": 0x01437C80,
"RS: Soul of a Crystal Sage": 0x400002CB,
"RS: Great Swamp Ring": 0x20004F10,
}
cathedral_of_the_deep_table = {
"CD: Paladin's Ashes": 0x4000083D,
"CD: Spider Shield": 0x01435570,
"CD: Crest Shield": 0x01430750,
"CD: Notched Whip": 0x00B7DE50,
"CD: Astora Greatsword": 0x005C9EF0,
"CD: Executioner's Greatsword": 0x0021DFE0,
"CD: Curse Ward Greatshield": 0x01518640,
"CD: Saint-tree Bellvine": 0x00C9DFB0,
"CD: Poisonbite Ring": 0x20004E8E,
"CD: Lloyd's Sword Ring": 0x200050B4,
"CD: Seek Guidance": 0x40360420,
"CD: Aldrich's Sapphire": 0x20005096,
"CD: Deep Braille Divine Tome": 0x40000860,
"CD: Saint Bident": 0x008C1360,
"CD: Maiden Hood": 0x14BD12E0,
"CD: Maiden Robe": 0x14BD16C8,
"CD: Maiden Gloves": 0x14BD1AB0,
"CD: Maiden Skirt": 0x14BD1E98,
"CD: Drang Armor": 0x154E0C28,
"CD: Drang Gauntlets": 0x154E1010,
"CD: Drang Shoes": 0x154E13F8,
"CD: Drang Hammers": 0x00F61FD0,
"CD: Deep Ring": 0x20004F60,
"CD: Archdeacon White Crown": 0x13EF1480,
"CD: Archdeacon Holy Garb": 0x13EF1868,
"CD: Archdeacon Skirt": 0x13EF2038,
"CD: Arbalest": 0x00D662D0,
"CD: Small Doll": 0x400007D5,
"CD: Soul of the Deacons of the Deep": 0x400002D9,
"CD: Rosaria's Fingers Covenant": 0x20002760,
}
farron_keep_table = {
"FK: Ragged Mask": 0x148F4C20,
"FK: Iron Flesh": 0x40251430,
"FK: Golden Scroll": 0x4000085C,
"FK: Antiquated Dress": 0x15D76068,
"FK: Antiquated Gloves": 0x15D76450,
"FK: Antiquated Skirt": 0x15D76838,
"FK: Nameless Knight Helm": 0x143B5FC0,
"FK: Nameless Knight Armor": 0x143B63A8,
"FK: Nameless Knight Gauntlets": 0x143B6790,
"FK: Nameless Knight Leggings": 0x143B6B78,
"FK: Sunlight Talisman": 0x00CA54E0,
"FK: Wolf's Blood Swordgrass": 0x4000016E,
"FK: Greatsword": 0x005C50D0,
"FK: Sage's Coal": 0x40000838,
"FK: Stone Parma": 0x01443FD0,
"FK: Sage's Scroll": 0x40000854,
"FK: Crown of Dusk": 0x15D75C80,
"FK: Lingering Dragoncrest Ring": 0x20004F2E,
"FK: Pharis's Hat": 0x1487AB00,
"FK: Black Bow of Pharis": 0x00D7E970,
"FK: Dreamchaser's Ashes": 0x4000083C,
"FK: Great Axe": 0x006B9310,
"FK: Dragon Crest Shield": 0x01432E60,
"FK: Lightning Spear": 0x40362B30,
"FK: Atonement": 0x4039ADA0,
"FK: Great Magic Weapon": 0x40140118,
"FK: Cinders of a Lord - Abyss Watcher": 0x4000084B,
"FK: Soul of the Blood of the Wolf": 0x400002CD,
"FK: Soul of a Stray Demon": 0x400002E7,
"FK: Watchdogs of Farron Covenant": 0x20002724,
}
catacombs_of_carthus_table = {
"CC: Carthus Pyromancy Tome": 0x40000850,
"CC: Carthus Milkring": 0x20004FE2,
"CC: Grave Warden's Ashes": 0x4000083E,
"CC: Carthus Bloodring": 0x200050FA,
"CC: Grave Warden Pyromancy Tome": 0x40000853,
"CC: Old Sage's Blindfold": 0x11945BA0,
"CC: Witch's Ring": 0x20004F11,
"CC: Black Blade": 0x004CC070,
"CC: Soul of High Lord Wolnir": 0x400002D6,
"CC: Soul of a Demon": 0x400002E3,
}
smouldering_lake_table = {
"SL: Shield of Want": 0x0144B500,
"SL: Speckled Stoneplate Ring": 0x20004E7A,
"SL: Dragonrider Bow": 0x00D6B0F0,
"SL: Lightning Stake": 0x40389C30,
"SL: Izalith Pyromancy Tome": 0x40000851,
"SL: Black Knight Sword": 0x005F5E10,
"SL: Quelana Pyromancy Tome": 0x40000852,
"SL: Toxic Mist": 0x4024F108,
"SL: White Hair Talisman": 0x00CAF120,
"SL: Izalith Staff": 0x00C96A80,
"SL: Sacred Flame": 0x40284880,
"SL: Fume Ultra Greatsword": 0x0060E4B0,
"SL: Black Iron Greatshield": 0x0150EA00,
"SL: Soul of the Old Demon King": 0x400002D0,
"SL: Knight Slayer's Ring": 0x20005000,
}
irithyll_of_the_boreal_valley_table = {
"IBV: Dorhys' Gnawing": 0x40363EB8,
"IBV: Witchtree Branch": 0x00C94370,
"IBV: Magic Clutch Ring": 0x2000500A,
"IBV: Ring of the Sun's First Born": 0x20004F1B,
"IBV: Roster of Knights": 0x4000006C,
"IBV: Pontiff's Right Eye": 0x2000510E,
"IBV: Yorshka's Spear": 0x008C3A70,
"IBV: Great Heal": 0x40356FB0,
"IBV: Smough's Great Hammer": 0x007E30B0,
"IBV: Leo Ring": 0x20004EE8,
"IBV: Excrement-covered Ashes": 0x40000862,
"IBV: Dark Stoneplate Ring": 0x20004E70,
"IBV: Easterner's Ashes": 0x40000868,
"IBV: Painting Guardian's Curved Sword": 0x003E6890,
"IBV: Painting Guardian Hood": 0x156C8CC0,
"IBV: Painting Guardian Gown": 0x156C90A8,
"IBV: Painting Guardian Gloves": 0x156C9490,
"IBV: Painting Guardian Waistcloth": 0x156C9878,
"IBV: Dragonslayer Greatbow": 0x00CF8500,
"IBV: Reversal Ring": 0x20005104,
"IBV: Brass Helm": 0x1501BD00,
"IBV: Brass Armor": 0x1501C0E8,
"IBV: Brass Gauntlets": 0x1501C4D0,
"IBV: Brass Leggings": 0x1501C8B8,
"IBV: Ring of Favor": 0x20004E3E,
"IBV: Golden Ritual Spear": 0x00C83200,
"IBV: Soul of Pontiff Sulyvahn": 0x400002D4,
"IBV: Aldrich Faithful Covenant": 0x2000272E,
"IBV: Drang Twinspears": 0x00F5AAA0,
}
irithyll_dungeon_table = {
"ID: Bellowing Dragoncrest Ring": 0x20004F07,
"ID: Jailbreaker's Key": 0x400007D7,
"ID: Prisoner Chief's Ashes": 0x40000863,
"ID: Old Sorcerer Hat": 0x1496ED40,
"ID: Old Sorcerer Coat": 0x1496F128,
"ID: Old Sorcerer Gauntlets": 0x1496F510,
"ID: Old Sorcerer Boots": 0x1496F8F8,
"ID: Great Magic Shield": 0x40144F38,
"ID: Dragon Torso Stone": 0x4000017A,
"ID: Lightning Blade": 0x4036C770,
"ID: Profaned Coal": 0x4000083A,
"ID: Xanthous Ashes": 0x40000864,
"ID: Old Cell Key": 0x400007DC,
"ID: Pickaxe": 0x007DE290,
"ID: Profaned Flame": 0x402575D8,
"ID: Covetous Gold Serpent Ring": 0x20004FA6,
"ID: Jailer's Key Ring": 0x400007D8,
"ID: Dusk Crown Ring": 0x20004F4C,
"ID: Dark Clutch Ring": 0x20005028,
}
profaned_capital_table = {
"PC: Cursebite Ring": 0x20004E98,
"PC: Court Sorcerer Hood": 0x11BA8140,
"PC: Court Sorcerer Robe": 0x11BA8528,
"PC: Court Sorcerer Gloves": 0x11BA8910,
"PC: Court Sorcerer Trousers": 0x11BA8CF8,
"PC: Wrath of the Gods": 0x4035E0F8,
"PC: Logan's Scroll": 0x40000855,
"PC: Eleonora": 0x006CCB90,
"PC: Court Sorcerer's Staff": 0x00C91C60,
"PC: Greatshield of Glory": 0x01515F30,
"PC: Storm Ruler": 0x006132D0,
"PC: Cinders of a Lord - Yhorm the Giant": 0x4000084D,
"PC: Soul of Yhorm the Giant": 0x400002DC,
}
anor_londo_table = {
"AL: Giant's Coal": 0x40000839,
"AL: Sun Princess Ring": 0x20004FBA,
"AL: Aldrich's Ruby": 0x2000508C,
"AL: Cinders of a Lord - Aldrich": 0x4000084C,
"AL: Soul of Aldrich": 0x400002D5,
}
lothric_castle_table = {
"LC: Hood of Prayer": 0x13AA6A60,
"LC: Robe of Prayer": 0x13AA6E48,
"LC: Skirt of Prayer": 0x13AA7618,
"LC: Sacred Bloom Shield": 0x013572C0,
"LC: Winged Knight Helm": 0x12EBAE40,
"LC: Winged Knight Armor": 0x12EBB228,
"LC: Winged Knight Gauntlets": 0x12EBB610,
"LC: Winged Knight Leggings": 0x12EBB9F8,
"LC: Greatlance": 0x008A8CC0,
"LC: Sniper Crossbow": 0x00D83790,
"LC: Spirit Tree Crest Shield": 0x014466E0,
"LC: Red Tearstone Ring": 0x20004ECA,
"LC: Caitha's Chime": 0x00CA06C0,
"LC: Braille Divine Tome of Lothric": 0x40000848,
"LC: Knight's Ring": 0x20004FEC,
"LC: Sunlight Straight Sword": 0x00203230,
"LC: Soul of Dragonslayer Armour": 0x400002D1,
# The Black Hand Gotthard corpse appears when you have defeated Yhorm and Aldrich and triggered the cutscene
"LC: Grand Archives Key": 0x400007DE, # On Black Hand Gotthard corpse
"LC: Gotthard Twinswords": 0x00F53570 # On Black Hand Gotthard corpse
}
consumed_king_garden_table = {
"CKG: Dragonscale Ring": 0x2000515E,
"CKG: Shadow Mask": 0x14D3F640,
"CKG: Shadow Garb": 0x14D3FA28,
"CKG: Shadow Gauntlets": 0x14D3FE10,
"CKG: Shadow Leggings": 0x14D401F8,
"CKG: Claw": 0x00A7D8C0,
"CKG: Soul of Consumed Oceiros": 0x400002CE,
# "CKG: Path of the Dragon Gesture": 0x40002346, I can't technically randomize it as it is a gesture and not an item
}
grand_archives_table = {
"GA: Avelyn": 0x00D6FF10,
"GA: Witch's Locks": 0x00B7B740,
"GA: Power Within": 0x40253B40,
"GA: Scholar Ring": 0x20004EB6,
"GA: Soul Stream": 0x4018B820,
"GA: Fleshbite Ring": 0x20004EA2,
"GA: Crystal Chime": 0x00CA2DD0,
"GA: Golden Wing Crest Shield": 0x0143CAA0,
"GA: Onikiri and Ubadachi": 0x00F58390,
"GA: Hunter's Ring": 0x20004FF6,
"GA: Divine Pillars of Light": 0x4038C340,
"GA: Cinders of a Lord - Lothric Prince": 0x4000084E,
"GA: Soul of the Twin Princes": 0x400002DB,
"GA: Sage's Crystal Staff": 0x00C8CE40,
}
untended_graves_table = {
"UG: Ashen Estus Ring": 0x200050E6,
"UG: Black Knight Glaive": 0x009AE070,
"UG: Hornet Ring": 0x20004F9C,
"UG: Chaos Blade": 0x004C9960,
"UG: Blacksmith Hammer": 0x007E57C0,
"UG: Eyes of a Fire Keeper": 0x4000085A,
"UG: Coiled Sword Fragment": 0x4000015F,
"UG: Soul of Champion Gundyr": 0x400002C8,
}
archdragon_peak_table = {
"AP: Lightning Clutch Ring": 0x20005014,
"AP: Ancient Dragon Greatshield": 0x013599D0,
"AP: Ring of Steel Protection": 0x20004E48,
"AP: Calamity Ring": 0x20005078,
"AP: Drakeblood Greatsword": 0x00609690,
"AP: Dragonslayer Spear": 0x008CAFA0,
"AP: Thunder Stoneplate Ring": 0x20004E5C,
"AP: Great Magic Barrier": 0x40365628,
"AP: Dragon Chaser's Ashes": 0x40000867,
"AP: Twinkling Dragon Torso Stone": 0x40000184,
"AP: Dragonslayer Helm": 0x158B1140,
"AP: Dragonslayer Armor": 0x158B1528,
"AP: Dragonslayer Gauntlets": 0x158B1910,
"AP: Dragonslayer Leggings": 0x158B1CF8,
"AP: Ricard's Rapier": 0x002E3BF0,
"AP: Soul of the Nameless King": 0x400002D2,
"AP: Dragon Tooth": 0x007E09A0,
"AP: Havel's Greatshield": 0x013376F0,
}
location_dictionary_table = {**cemetery_of_ash_table, **fire_link_shrine_table, **firelink_shrine_bell_tower_table, **high_wall_of_lothric, **undead_settlement_table, **road_of_sacrifice_table,
**cathedral_of_the_deep_table, **farron_keep_table, **catacombs_of_carthus_table, **smouldering_lake_table, **irithyll_of_the_boreal_valley_table,
**irithyll_dungeon_table, **profaned_capital_table, **anor_londo_table, **lothric_castle_table, **consumed_king_garden_table,
**grand_archives_table, **untended_graves_table, **archdragon_peak_table}

View File

@@ -0,0 +1,22 @@
# Dark Souls III
## Where is the settings page?
The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
In Dark Souls III, all unique items you can earn from a static corpse, a chest or the death of a Boss/NPC are randomized.
This exclude the upgrade materials such as the titanite shards, the estus shards and the consumables which remain at
the same location. I also added an option available from the settings page to randomize the level of the generated
weapons( from +0 to +10/+5 )
## What Dark Souls III items can appear in other players' worlds?
Every unique items from Dark Souls III can appear in other player's worlds, such as a piece of armor, an upgraded weapon
or a key item.
## What does another world's item look like in Dark Souls III?
In Dark Souls III, items which need to be sent to other worlds appear as a Prism Stone.

View File

@@ -0,0 +1,35 @@
# Dark Souls III Randomizer Setup Guide
## Required Software
- [Dark Souls III](https://store.steampowered.com/app/374320/DARK_SOULS_III/)
- [Dark Souls III AP Client](https://github.com/Marechal-L/Dark-Souls-III-Archipelago-client)
## General Concept
The Dark Souls III AP Client is a dinput8.dll triggered when launching Dark Souls III. This .dll file will launch a command
prompt where you can read information about your run and write any command to interact with the Archipelago server.
The randomization is performed by the AP.json file, an output file generated by the Archipelago server.
## Installation Procedures
**This client has only been tested with the Official Steam version of the game (v1.15/1.35) not matter which DLCs are installed**
Get the dinput8.dll from the [Dark Souls III AP Client](https://github.com/Marechal-L/Dark-Souls-III-Archipelago-client).
Then you need to add the two following files at the root folder of your game
( e.g. "SteamLibrary\steamapps\common\DARK SOULS III\Game" ):
- **dinput8.dll**
- **AP.json** (renamed from the generated file AP-{ROOM_ID}.json)
## Joining a MultiWorld Game
1. Run DarkSoulsIII.exe or run the game through Steam
2. Type in /connect {SERVER_IP}:{SERVER_PORT} in the "Windows Command Prompt" that opened
3. Once connected, create a new game, choose a class and wait for the others before starting
4. You can quit and launch at anytime during a game
## Where do I get a config file?
The [Player Settings](/games/Dark%20Souls%20III/player-settings) page on the website allows you to
configure your personal settings and export them into a config file

220
worlds/dkc3/Client.py Normal file
View File

@@ -0,0 +1,220 @@
import logging
import asyncio
from NetUtils import ClientStatus, color
from SNIClient import Context, snes_buffered_write, snes_flush_writes, snes_read
from Patch import GAME_DKC3
snes_logger = logging.getLogger("SNES")
# DKC3 - DKC3_TODO: Check these values
ROM_START = 0x000000
WRAM_START = 0xF50000
WRAM_SIZE = 0x20000
SRAM_START = 0xE00000
SAVEDATA_START = WRAM_START + 0xF000
SAVEDATA_SIZE = 0x500
DKC3_ROMNAME_START = 0x00FFC0
DKC3_ROMHASH_START = 0x7FC0
ROMNAME_SIZE = 0x15
ROMHASH_SIZE = 0x15
DKC3_RECV_PROGRESS_ADDR = WRAM_START + 0x632 # DKC3_TODO: Find a permanent home for this
DKC3_FILE_NAME_ADDR = WRAM_START + 0x5D9
DEATH_LINK_ACTIVE_ADDR = DKC3_ROMNAME_START + 0x15 # DKC3_TODO: Find a permanent home for this
async def deathlink_kill_player(ctx: Context):
pass
#if ctx.game == GAME_DKC3:
# DKC3_TODO: Handle Receiving Deathlink
async def dkc3_rom_init(ctx: Context):
if not ctx.rom:
ctx.finished_game = False
ctx.death_link_allow_survive = False
game_name = await snes_read(ctx, DKC3_ROMNAME_START, 0x15)
if game_name is None or game_name != b"DONKEY KONG COUNTRY 3":
return False
else:
ctx.game = GAME_DKC3
ctx.items_handling = 0b111 # remote items
rom = await snes_read(ctx, DKC3_ROMHASH_START, ROMHASH_SIZE)
if rom is None or rom == bytes([0] * ROMHASH_SIZE):
return False
ctx.rom = rom
#death_link = await snes_read(ctx, DEATH_LINK_ACTIVE_ADDR, 1)
## DKC3_TODO: Handle Deathlink
#if death_link:
# ctx.allow_collect = bool(death_link[0] & 0b100)
# await ctx.update_death_link(bool(death_link[0] & 0b1))
return True
async def dkc3_game_watcher(ctx: Context):
if ctx.game == GAME_DKC3:
# DKC3_TODO: Handle Deathlink
save_file_name = await snes_read(ctx, DKC3_FILE_NAME_ADDR, 0x5)
if save_file_name is None or save_file_name[0] == 0x00:
# We haven't loaded a save file
return
new_checks = []
from worlds.dkc3.Rom import location_rom_data, item_rom_data
for loc_id, loc_data in location_rom_data.items():
if loc_id not in ctx.locations_checked:
data = await snes_read(ctx, WRAM_START + loc_data[0], 1)
masked_data = data[0] & (1 << loc_data[1])
bit_set = (masked_data != 0)
invert_bit = ((len(loc_data) >= 3) and loc_data[2])
if bit_set != invert_bit:
# DKC3_TODO: Handle non-included checks
new_checks.append(loc_id)
save_file_name = await snes_read(ctx, DKC3_FILE_NAME_ADDR, 0x5)
if save_file_name is None or save_file_name[0] == 0x00:
# We have somehow exited the save file
return
rom = await snes_read(ctx, DKC3_ROMHASH_START, ROMHASH_SIZE)
if rom != ctx.rom:
ctx.rom = None
# We have somehow loaded a different ROM
return
for new_check_id in new_checks:
ctx.locations_checked.add(new_check_id)
location = ctx.location_names[new_check_id]
snes_logger.info(
f'New Check: {location} ({len(ctx.locations_checked)}/{len(ctx.missing_locations) + len(ctx.checked_locations)})')
await ctx.send_msgs([{"cmd": 'LocationChecks', "locations": [new_check_id]}])
# DKC3_TODO: Make this actually visually display new things received (ASM Hook required)
recv_count = await snes_read(ctx, DKC3_RECV_PROGRESS_ADDR, 1)
recv_index = recv_count[0]
if recv_index < len(ctx.items_received):
item = ctx.items_received[recv_index]
recv_index += 1
logging.info('Received %s from %s (%s) (%d/%d in list)' % (
color(ctx.item_names[item.item], 'red', 'bold'),
color(ctx.player_names[item.player], 'yellow'),
ctx.location_names[item.location], recv_index, len(ctx.items_received)))
snes_buffered_write(ctx, DKC3_RECV_PROGRESS_ADDR, bytes([recv_index]))
if item.item in item_rom_data:
item_count = await snes_read(ctx, WRAM_START + item_rom_data[item.item][0], 0x1)
new_item_count = item_count[0] + 1
for address in item_rom_data[item.item]:
snes_buffered_write(ctx, WRAM_START + address, bytes([new_item_count]))
# Handle Coin Displays
current_level = await snes_read(ctx, WRAM_START + 0x5E3, 0x5)
if item.item == 0xDC3002 and (current_level[0] == 0x0A and current_level[2] == 0x00 and current_level[4] == 0x03):
# Bazaar and Barter
item_count = await snes_read(ctx, WRAM_START + 0xB02, 0x1)
new_item_count = item_count[0] + 1
snes_buffered_write(ctx, WRAM_START + 0xB02, bytes([new_item_count]))
elif item.item == 0xDC3002 and current_level[0] == 0x04:
# Swanky
item_count = await snes_read(ctx, WRAM_START + 0xA26, 0x1)
new_item_count = item_count[0] + 1
snes_buffered_write(ctx, WRAM_START + 0xA26, bytes([new_item_count]))
elif item.item == 0xDC3003 and (current_level[0] == 0x0A and current_level[2] == 0x08 and current_level[4] == 0x01):
# Boomer
item_count = await snes_read(ctx, WRAM_START + 0xB02, 0x1)
new_item_count = item_count[0] + 1
snes_buffered_write(ctx, WRAM_START + 0xB02, bytes([new_item_count]))
else:
# Handle Patch and Skis
if item.item == 0xDC3007:
num_upgrades = 1
inventory = await snes_read(ctx, WRAM_START + 0x605, 0xF)
if (inventory[0] & 0x02):
num_upgrades = 3
elif (inventory[13] & 0x08) or (inventory[0] & 0x01):
num_upgrades = 2
if num_upgrades == 1:
snes_buffered_write(ctx, WRAM_START + 0x605, bytes([inventory[0] | 0x01]))
if inventory[4] == 0:
snes_buffered_write(ctx, WRAM_START + 0x609, bytes([0x01]))
elif inventory[6] == 0:
snes_buffered_write(ctx, WRAM_START + 0x60B, bytes([0x01]))
elif inventory[8] == 0:
snes_buffered_write(ctx, WRAM_START + 0x60D, bytes([0x01]))
elif inventory[10] == 0:
snes_buffered_write(ctx, WRAM_START + 0x60F, bytes([0x01]))
cove_mekanos_progress = await snes_read(ctx, WRAM_START + 0x691, 0x2)
snes_buffered_write(ctx, WRAM_START + 0x691, bytes([cove_mekanos_progress[0] | 0x01]))
snes_buffered_write(ctx, WRAM_START + 0x692, bytes([cove_mekanos_progress[1] | 0x01]))
elif num_upgrades == 2:
snes_buffered_write(ctx, WRAM_START + 0x605, bytes([inventory[0] | 0x02]))
if inventory[4] == 0:
snes_buffered_write(ctx, WRAM_START + 0x609, bytes([0x02]))
elif inventory[6] == 0:
snes_buffered_write(ctx, WRAM_START + 0x60B, bytes([0x02]))
elif inventory[8] == 0:
snes_buffered_write(ctx, WRAM_START + 0x60D, bytes([0x02]))
elif inventory[10] == 0:
snes_buffered_write(ctx, WRAM_START + 0x60F, bytes([0x02]))
elif num_upgrades == 3:
snes_buffered_write(ctx, WRAM_START + 0x606, bytes([inventory[1] | 0x20]))
k3_ridge_progress = await snes_read(ctx, WRAM_START + 0x693, 0x2)
snes_buffered_write(ctx, WRAM_START + 0x693, bytes([k3_ridge_progress[0] | 0x01]))
snes_buffered_write(ctx, WRAM_START + 0x694, bytes([k3_ridge_progress[1] | 0x01]))
elif item.item == 0xDC3000:
# Handle Victory
if not ctx.finished_game:
await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}])
ctx.finished_game = True
else:
print("Item Not Recognized: ", item.item)
pass
await snes_flush_writes(ctx)
# DKC3_TODO: This method of collect should work, however it does not unlock the next level correctly when previous is flagged
# Handle Collected Locations
#for loc_id in ctx.checked_locations:
# if loc_id not in ctx.locations_checked:
# loc_data = location_rom_data[loc_id]
# data = await snes_read(ctx, WRAM_START + loc_data[0], 1)
# invert_bit = ((len(loc_data) >= 3) and loc_data[2])
# if not invert_bit:
# masked_data = data[0] | (1 << loc_data[1])
# print("Collected Location: ", hex(loc_data[0]), " | ", loc_data[1])
# snes_buffered_write(ctx, WRAM_START + loc_data[0], bytes([masked_data]))
# await snes_flush_writes(ctx)
# else:
# masked_data = data[0] & ~(1 << loc_data[1])
# print("Collected Inverted Location: ", hex(loc_data[0]), " | ", loc_data[1])
# snes_buffered_write(ctx, WRAM_START + loc_data[0], bytes([masked_data]))
# await snes_flush_writes(ctx)
# ctx.locations_checked.add(loc_id)
# Calculate Boomer Cost Text
boomer_cost_text = await snes_read(ctx, WRAM_START + 0xAAFD, 2)
if boomer_cost_text[0] == 0x31 and boomer_cost_text[1] == 0x35:
boomer_cost = await snes_read(ctx, ROM_START + 0x349857, 1)
boomer_cost_tens = int(boomer_cost[0]) // 10
boomer_cost_ones = int(boomer_cost[0]) % 10
snes_buffered_write(ctx, WRAM_START + 0xAAFD, bytes([0x30 + boomer_cost_tens, 0x30 + boomer_cost_ones]))
await snes_flush_writes(ctx)
boomer_final_cost_text = await snes_read(ctx, WRAM_START + 0xAB9B, 2)
if boomer_final_cost_text[0] == 0x32 and boomer_final_cost_text[1] == 0x35:
boomer_cost = await snes_read(ctx, ROM_START + 0x349857, 1)
boomer_cost_tens = boomer_cost[0] // 10
boomer_cost_ones = boomer_cost[0] % 10
snes_buffered_write(ctx, WRAM_START + 0xAB9B, bytes([0x30 + boomer_cost_tens, 0x30 + boomer_cost_ones]))
await snes_flush_writes(ctx)

52
worlds/dkc3/Items.py Normal file
View File

@@ -0,0 +1,52 @@
import typing
from BaseClasses import Item, ItemClassification
from .Names import ItemName
class ItemData(typing.NamedTuple):
code: typing.Optional[int]
progression: bool
quantity: int = 1
event: bool = False
class DKC3Item(Item):
game: str = "Donkey Kong Country 3"
# Separate tables for each type of item.
junk_table = {
ItemName.one_up_balloon: ItemData(0xDC3001, False),
ItemName.bear_coin: ItemData(0xDC3002, False),
}
collectable_table = {
ItemName.bonus_coin: ItemData(0xDC3003, True),
ItemName.dk_coin: ItemData(0xDC3004, True),
ItemName.banana_bird: ItemData(0xDC3005, True),
ItemName.krematoa_cog: ItemData(0xDC3006, True),
ItemName.progressive_boat: ItemData(0xDC3007, True),
}
inventory_table = {
ItemName.present: ItemData(0xDC3008, True),
ItemName.bowling_ball: ItemData(0xDC3009, True),
ItemName.shell: ItemData(0xDC300A, True),
ItemName.mirror: ItemData(0xDC300B, True),
ItemName.flower: ItemData(0xDC300C, True),
ItemName.wrench: ItemData(0xDC300D, True),
}
event_table = {
ItemName.victory: ItemData(0xDC3000, True),
}
# Complete item table.
item_table = {
**junk_table,
**collectable_table,
**event_table,
}
lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in item_table.items() if data.code}

115
worlds/dkc3/Levels.py Normal file
View File

@@ -0,0 +1,115 @@
from .Names import LocationName
class DKC3Level():
nameIDAddress: int
levelIDAddress: int
nameID: int
levelID: int
def __init__(self, nameIDAddress: int, levelIDAddress: int, nameID: int, levelID: int):
self.nameIDAddress = nameIDAddress
self.levelIDAddress = levelIDAddress
self.nameID = nameID
self.levelID = levelID
level_dict = {
LocationName.lakeside_limbo_region: DKC3Level(0x34D19C, 0x34D19D, 0x01, 0x25),
LocationName.doorstop_dash_region: DKC3Level(0x34D1A7, 0x34D1A8, 0x02, 0x28),
LocationName.tidal_trouble_region: DKC3Level(0x34D1BD, 0x34D1BE, 0x04, 0x27),
LocationName.skiddas_row_region: DKC3Level(0x34D1C8, 0x34D1C9, 0x05, 0x2B),
LocationName.murky_mill_region: DKC3Level(0x34D1D3, 0x34D1D4, 0x0D, 0x2A),
LocationName.barrel_shield_bust_up_region: DKC3Level(0x34D217, 0x34D218, 0x0B, 0x30),
LocationName.riverside_race_region: DKC3Level(0x34D22D, 0x34D22E, 0x0C, 0x32),
LocationName.squeals_on_wheels_region: DKC3Level(0x34D238, 0x34D239, 0x06, 0x29),
LocationName.springin_spiders_region: DKC3Level(0x34D24E, 0x34D24F, 0x0E, 0x2F),
LocationName.bobbing_barrel_brawl_region: DKC3Level(0x34D264, 0x34D265, 0x37, 0x34),
LocationName.bazzas_blockade_region: DKC3Level(0x34D29D, 0x34D29E, 0x14, 0x35),
LocationName.rocket_barrel_ride_region: DKC3Level(0x34D2A8, 0x34D2A9, 0x15, 0x38),
LocationName.kreeping_klasps_region: DKC3Level(0x34D2BE, 0x34D2BF, 0x16, 0x26),
LocationName.tracker_barrel_trek_region: DKC3Level(0x34D2D4, 0x34D2D5, 0x17, 0x39),
LocationName.fish_food_frenzy_region: DKC3Level(0x34D2DF, 0x34D2E0, 0x18, 0x36),
LocationName.fire_ball_frenzy_region: DKC3Level(0x34D30D, 0x34D30E, 0x1B, 0x3B),
LocationName.demolition_drain_pipe_region: DKC3Level(0x34D323, 0x34D324, 0x1D, 0x40),
LocationName.ripsaw_rage_region: DKC3Level(0x34D339, 0x34D33A, 0x1E, 0x2E),
LocationName.blazing_bazookas_region: DKC3Level(0x34D34F, 0x34D350, 0x1F, 0x3C),
LocationName.low_g_labyrinth_region: DKC3Level(0x34D35A, 0x34D35B, 0x20, 0x3E),
LocationName.krevice_kreepers_region: DKC3Level(0x34D388, 0x34D389, 0x23, 0x41),
LocationName.tearaway_toboggan_region: DKC3Level(0x34D393, 0x34D394, 0x24, 0x2D),
LocationName.barrel_drop_bounce_region: DKC3Level(0x34D39E, 0x34D39F, 0x25, 0x3A),
LocationName.krack_shot_kroc_region: DKC3Level(0x34D3A9, 0x34D3AA, 0x26, 0x3D),
LocationName.lemguin_lunge_region: DKC3Level(0x34D3B4, 0x34D3B5, 0x27, 0x2C),
LocationName.buzzer_barrage_region: DKC3Level(0x34D40E, 0x34D40F, 0x2B, 0x44),
LocationName.kong_fused_cliffs_region: DKC3Level(0x34D424, 0x34D425, 0x2D, 0x42),
LocationName.floodlit_fish_region: DKC3Level(0x34D42F, 0x34D430, 0x2E, 0x37),
LocationName.pothole_panic_region: DKC3Level(0x34D43A, 0x34D43B, 0x2F, 0x45),
LocationName.ropey_rumpus_region: DKC3Level(0x34D450, 0x34D451, 0x30, 0x43),
LocationName.konveyor_rope_clash_region: DKC3Level(0x34D489, 0x34D48A, 0x38, 0x48),
LocationName.creepy_caverns_region: DKC3Level(0x34D49F, 0x34D4A0, 0x36, 0x46),
LocationName.lightning_lookout_region: DKC3Level(0x34D4AA, 0x34D4AB, 0x10, 0x33),
LocationName.koindozer_klamber_region: DKC3Level(0x34D4C0, 0x34D4C1, 0x34, 0x47),
LocationName.poisonous_pipeline_region: DKC3Level(0x34D4D6, 0x34D4D7, 0x39, 0x3F),
LocationName.stampede_sprint_region: DKC3Level(0x34D51A, 0x34D51B, 0x3D, 0x49),
LocationName.criss_cross_cliffs_region: DKC3Level(0x34D525, 0x34D526, 0x3E, 0x4A),
LocationName.tyrant_twin_tussle_region: DKC3Level(0x34D530, 0x34D531, 0x3F, 0x4B),
LocationName.swoopy_salvo_region: DKC3Level(0x34D53B, 0x34D53C, 0x40, 0x31),
#LocationName.rocket_rush_region: DKC3Level(0x34D546, 0x34D547, 0x05, 0x4C), # Rocket Rush is not getting shuffled
}
level_list = [
LocationName.lakeside_limbo_region,
LocationName.doorstop_dash_region,
LocationName.tidal_trouble_region,
LocationName.skiddas_row_region,
LocationName.murky_mill_region,
LocationName.barrel_shield_bust_up_region,
LocationName.riverside_race_region,
LocationName.squeals_on_wheels_region,
LocationName.springin_spiders_region,
LocationName.bobbing_barrel_brawl_region,
LocationName.bazzas_blockade_region,
LocationName.rocket_barrel_ride_region,
LocationName.kreeping_klasps_region,
LocationName.tracker_barrel_trek_region,
LocationName.fish_food_frenzy_region,
LocationName.fire_ball_frenzy_region,
LocationName.demolition_drain_pipe_region,
LocationName.ripsaw_rage_region,
LocationName.blazing_bazookas_region,
LocationName.low_g_labyrinth_region,
LocationName.krevice_kreepers_region,
LocationName.tearaway_toboggan_region,
LocationName.barrel_drop_bounce_region,
LocationName.krack_shot_kroc_region,
LocationName.lemguin_lunge_region,
LocationName.buzzer_barrage_region,
LocationName.kong_fused_cliffs_region,
LocationName.floodlit_fish_region,
LocationName.pothole_panic_region,
LocationName.ropey_rumpus_region,
LocationName.konveyor_rope_clash_region,
LocationName.creepy_caverns_region,
LocationName.lightning_lookout_region,
LocationName.koindozer_klamber_region,
LocationName.poisonous_pipeline_region,
LocationName.stampede_sprint_region,
LocationName.criss_cross_cliffs_region,
LocationName.tyrant_twin_tussle_region,
LocationName.swoopy_salvo_region,
#LocationName.rocket_rush_region,
]

283
worlds/dkc3/Locations.py Normal file
View File

@@ -0,0 +1,283 @@
import typing
from BaseClasses import Location
from .Names import LocationName
class DKC3Location(Location):
game: str = "Donkey Kong Country 3"
progress_byte: int = 0x000000
progress_bit: int = 0
inverted_bit: bool = False
def __init__(self, player: int, name: str = '', address: int = None, parent=None, prog_byte: int = None, prog_bit: int = None, invert: bool = False):
super().__init__(player, name, address, parent)
self.progress_byte = prog_byte
self.progress_bit = prog_bit
self.inverted_bit = invert
level_location_table = {
LocationName.lakeside_limbo_flag: 0xDC3000,
LocationName.lakeside_limbo_bonus_1: 0xDC3001,
LocationName.lakeside_limbo_bonus_2: 0xDC3002,
LocationName.lakeside_limbo_dk: 0xDC3003,
LocationName.doorstop_dash_flag: 0xDC3004,
LocationName.doorstop_dash_bonus_1: 0xDC3005,
LocationName.doorstop_dash_bonus_2: 0xDC3006,
LocationName.doorstop_dash_dk: 0xDC3007,
LocationName.tidal_trouble_flag: 0xDC3008,
LocationName.tidal_trouble_bonus_1: 0xDC3009,
LocationName.tidal_trouble_bonus_2: 0xDC300A,
LocationName.tidal_trouble_dk: 0xDC300B,
LocationName.skiddas_row_flag: 0xDC300C,
LocationName.skiddas_row_bonus_1: 0xDC300D,
LocationName.skiddas_row_bonus_2: 0xDC300E,
LocationName.skiddas_row_dk: 0xDC300F,
LocationName.murky_mill_flag: 0xDC3010,
LocationName.murky_mill_bonus_1: 0xDC3011,
LocationName.murky_mill_bonus_2: 0xDC3012,
LocationName.murky_mill_dk: 0xDC3013,
LocationName.barrel_shield_bust_up_flag: 0xDC3014,
LocationName.barrel_shield_bust_up_bonus_1: 0xDC3015,
LocationName.barrel_shield_bust_up_bonus_2: 0xDC3016,
LocationName.barrel_shield_bust_up_dk: 0xDC3017,
LocationName.riverside_race_flag: 0xDC3018,
LocationName.riverside_race_bonus_1: 0xDC3019,
LocationName.riverside_race_bonus_2: 0xDC301A,
LocationName.riverside_race_dk: 0xDC301B,
LocationName.squeals_on_wheels_flag: 0xDC301C,
LocationName.squeals_on_wheels_bonus_1: 0xDC301D,
LocationName.squeals_on_wheels_bonus_2: 0xDC301E,
LocationName.squeals_on_wheels_dk: 0xDC301F,
LocationName.springin_spiders_flag: 0xDC3020,
LocationName.springin_spiders_bonus_1: 0xDC3021,
LocationName.springin_spiders_bonus_2: 0xDC3022,
LocationName.springin_spiders_dk: 0xDC3023,
LocationName.bobbing_barrel_brawl_flag: 0xDC3024,
LocationName.bobbing_barrel_brawl_bonus_1: 0xDC3025,
LocationName.bobbing_barrel_brawl_bonus_2: 0xDC3026,
LocationName.bobbing_barrel_brawl_dk: 0xDC3027,
LocationName.bazzas_blockade_flag: 0xDC3028,
LocationName.bazzas_blockade_bonus_1: 0xDC3029,
LocationName.bazzas_blockade_bonus_2: 0xDC302A,
LocationName.bazzas_blockade_dk: 0xDC302B,
LocationName.rocket_barrel_ride_flag: 0xDC302C,
LocationName.rocket_barrel_ride_bonus_1: 0xDC302D,
LocationName.rocket_barrel_ride_bonus_2: 0xDC302E,
LocationName.rocket_barrel_ride_dk: 0xDC302F,
LocationName.kreeping_klasps_flag: 0xDC3030,
LocationName.kreeping_klasps_bonus_1: 0xDC3031,
LocationName.kreeping_klasps_bonus_2: 0xDC3032,
LocationName.kreeping_klasps_dk: 0xDC3033,
LocationName.tracker_barrel_trek_flag: 0xDC3034,
LocationName.tracker_barrel_trek_bonus_1: 0xDC3035,
LocationName.tracker_barrel_trek_bonus_2: 0xDC3036,
LocationName.tracker_barrel_trek_dk: 0xDC3037,
LocationName.fish_food_frenzy_flag: 0xDC3038,
LocationName.fish_food_frenzy_bonus_1: 0xDC3039,
LocationName.fish_food_frenzy_bonus_2: 0xDC303A,
LocationName.fish_food_frenzy_dk: 0xDC303B,
LocationName.fire_ball_frenzy_flag: 0xDC303C,
LocationName.fire_ball_frenzy_bonus_1: 0xDC303D,
LocationName.fire_ball_frenzy_bonus_2: 0xDC303E,
LocationName.fire_ball_frenzy_dk: 0xDC303F,
LocationName.demolition_drain_pipe_flag: 0xDC3040,
LocationName.demolition_drain_pipe_bonus_1: 0xDC3041,
LocationName.demolition_drain_pipe_bonus_2: 0xDC3042,
LocationName.demolition_drain_pipe_dk: 0xDC3043,
LocationName.ripsaw_rage_flag: 0xDC3044,
LocationName.ripsaw_rage_bonus_1: 0xDC3045,
LocationName.ripsaw_rage_bonus_2: 0xDC3046,
LocationName.ripsaw_rage_dk: 0xDC3047,
LocationName.blazing_bazookas_flag: 0xDC3048,
LocationName.blazing_bazookas_bonus_1: 0xDC3049,
LocationName.blazing_bazookas_bonus_2: 0xDC304A,
LocationName.blazing_bazookas_dk: 0xDC304B,
LocationName.low_g_labyrinth_flag: 0xDC304C,
LocationName.low_g_labyrinth_bonus_1: 0xDC304D,
LocationName.low_g_labyrinth_bonus_2: 0xDC304E,
LocationName.low_g_labyrinth_dk: 0xDC304F,
LocationName.krevice_kreepers_flag: 0xDC3050,
LocationName.krevice_kreepers_bonus_1: 0xDC3051,
LocationName.krevice_kreepers_bonus_2: 0xDC3052,
LocationName.krevice_kreepers_dk: 0xDC3053,
LocationName.tearaway_toboggan_flag: 0xDC3054,
LocationName.tearaway_toboggan_bonus_1: 0xDC3055,
LocationName.tearaway_toboggan_bonus_2: 0xDC3056,
LocationName.tearaway_toboggan_dk: 0xDC3057,
LocationName.barrel_drop_bounce_flag: 0xDC3058,
LocationName.barrel_drop_bounce_bonus_1: 0xDC3059,
LocationName.barrel_drop_bounce_bonus_2: 0xDC305A,
LocationName.barrel_drop_bounce_dk: 0xDC305B,
LocationName.krack_shot_kroc_flag: 0xDC305C,
LocationName.krack_shot_kroc_bonus_1: 0xDC305D,
LocationName.krack_shot_kroc_bonus_2: 0xDC305E,
LocationName.krack_shot_kroc_dk: 0xDC305F,
LocationName.lemguin_lunge_flag: 0xDC3060,
LocationName.lemguin_lunge_bonus_1: 0xDC3061,
LocationName.lemguin_lunge_bonus_2: 0xDC3062,
LocationName.lemguin_lunge_dk: 0xDC3063,
LocationName.buzzer_barrage_flag: 0xDC3064,
LocationName.buzzer_barrage_bonus_1: 0xDC3065,
LocationName.buzzer_barrage_bonus_2: 0xDC3066,
LocationName.buzzer_barrage_dk: 0xDC3067,
LocationName.kong_fused_cliffs_flag: 0xDC3068,
LocationName.kong_fused_cliffs_bonus_1: 0xDC3069,
LocationName.kong_fused_cliffs_bonus_2: 0xDC306A,
LocationName.kong_fused_cliffs_dk: 0xDC306B,
LocationName.floodlit_fish_flag: 0xDC306C,
LocationName.floodlit_fish_bonus_1: 0xDC306D,
LocationName.floodlit_fish_bonus_2: 0xDC306E,
LocationName.floodlit_fish_dk: 0xDC306F,
LocationName.pothole_panic_flag: 0xDC3070,
LocationName.pothole_panic_bonus_1: 0xDC3071,
LocationName.pothole_panic_bonus_2: 0xDC3072,
LocationName.pothole_panic_dk: 0xDC3073,
LocationName.ropey_rumpus_flag: 0xDC3074,
LocationName.ropey_rumpus_bonus_1: 0xDC3075,
LocationName.ropey_rumpus_bonus_2: 0xDC3076,
LocationName.ropey_rumpus_dk: 0xDC3077,
LocationName.konveyor_rope_clash_flag: 0xDC3078,
LocationName.konveyor_rope_clash_bonus_1: 0xDC3079,
LocationName.konveyor_rope_clash_bonus_2: 0xDC307A,
LocationName.konveyor_rope_clash_dk: 0xDC307B,
LocationName.creepy_caverns_flag: 0xDC307C,
LocationName.creepy_caverns_bonus_1: 0xDC307D,
LocationName.creepy_caverns_bonus_2: 0xDC307E,
LocationName.creepy_caverns_dk: 0xDC307F,
LocationName.lightning_lookout_flag: 0xDC3080,
LocationName.lightning_lookout_bonus_1: 0xDC3081,
LocationName.lightning_lookout_bonus_2: 0xDC3082,
LocationName.lightning_lookout_dk: 0xDC3083,
LocationName.koindozer_klamber_flag: 0xDC3084,
LocationName.koindozer_klamber_bonus_1: 0xDC3085,
LocationName.koindozer_klamber_bonus_2: 0xDC3086,
LocationName.koindozer_klamber_dk: 0xDC3087,
LocationName.poisonous_pipeline_flag: 0xDC3088,
LocationName.poisonous_pipeline_bonus_1: 0xDC3089,
LocationName.poisonous_pipeline_bonus_2: 0xDC308A,
LocationName.poisonous_pipeline_dk: 0xDC308B,
LocationName.stampede_sprint_flag: 0xDC308C,
LocationName.stampede_sprint_bonus_1: 0xDC308D,
LocationName.stampede_sprint_bonus_2: 0xDC308E,
LocationName.stampede_sprint_bonus_3: 0xDC308F,
LocationName.stampede_sprint_dk: 0xDC3090,
LocationName.criss_cross_cliffs_flag: 0xDC3091,
LocationName.criss_cross_cliffs_bonus_1: 0xDC3092,
LocationName.criss_cross_cliffs_bonus_2: 0xDC3093,
LocationName.criss_cross_cliffs_dk: 0xDC3094,
LocationName.tyrant_twin_tussle_flag: 0xDC3095,
LocationName.tyrant_twin_tussle_bonus_1: 0xDC3096,
LocationName.tyrant_twin_tussle_bonus_2: 0xDC3097,
LocationName.tyrant_twin_tussle_bonus_3: 0xDC3098,
LocationName.tyrant_twin_tussle_dk: 0xDC3099,
LocationName.swoopy_salvo_flag: 0xDC309A,
LocationName.swoopy_salvo_bonus_1: 0xDC309B,
LocationName.swoopy_salvo_bonus_2: 0xDC309C,
LocationName.swoopy_salvo_bonus_3: 0xDC309D,
LocationName.swoopy_salvo_dk: 0xDC309E,
LocationName.rocket_rush_flag: 0xDC309F,
LocationName.rocket_rush_dk: 0xDC30A0,
}
boss_location_table = {
LocationName.belchas_barn: 0xDC30A1,
LocationName.arichs_ambush: 0xDC30A2,
LocationName.squirts_showdown: 0xDC30A3,
LocationName.kaos_karnage: 0xDC30A4,
LocationName.bleaks_house: 0xDC30A5,
LocationName.barboss_barrier: 0xDC30A6,
LocationName.kastle_kaos: 0xDC30A7,
LocationName.knautilus: 0xDC30A8,
}
secret_cave_location_table = {
LocationName.belchas_burrow: 0xDC30A9,
LocationName.kong_cave: 0xDC30AA,
LocationName.undercover_cove: 0xDC30AB,
LocationName.ks_cache: 0xDC30AC,
LocationName.hill_top_hoard: 0xDC30AD,
LocationName.bounty_beach: 0xDC30AE,
LocationName.smugglers_cove: 0xDC30AF,
LocationName.arichs_hoard: 0xDC30B0,
LocationName.bounty_bay: 0xDC30B1,
LocationName.sky_high_secret: 0xDC30B2,
LocationName.glacial_grotto: 0xDC30B3,
LocationName.cifftop_cache: 0xDC30B4,
LocationName.sewer_stockpile: 0xDC30B5,
LocationName.banana_bird_mother: 0xDC30B6,
}
brothers_bear_location_table = {
LocationName.bazaars_general_store_1: 0xDC30B7,
LocationName.bazaars_general_store_2: 0xDC30B8,
LocationName.brambles_bungalow: 0xDC30B9,
LocationName.flower_spot: 0xDC30BA,
LocationName.barters_swap_shop: 0xDC30BB,
LocationName.barnacles_island: 0xDC30BC,
LocationName.blues_beach_hut: 0xDC30BD,
LocationName.blizzards_basecamp: 0xDC30BE,
}
all_locations = {
**level_location_table,
**boss_location_table,
**secret_cave_location_table,
**brothers_bear_location_table,
}
location_table = {}
def setup_locations(world, player: int):
location_table = {**level_location_table, **boss_location_table, **secret_cave_location_table}
if False:#world.include_trade_sequence[player].value:
location_table.update({**brothers_bear_location_table})
return location_table
lookup_id_to_name: typing.Dict[int, str] = {id: name for name, _ in all_locations.items()}

View File

@@ -0,0 +1,21 @@
# Junk Definitions
one_up_balloon = "1-Up Balloon"
bear_coin = "Bear Coin"
# Collectable Definitions
bonus_coin = "Bonus Coin"
dk_coin = "DK Coin"
banana_bird = "Banana Bird"
krematoa_cog = "Krematoa Cog"
# Inventory Definitions
progressive_boat = "Progressive Boat Upgrade"
present = "Present"
bowling_ball = "Bowling Ball"
shell = "Shell"
mirror = "Mirror"
flower = "Flupperius Petallus Pongus"
wrench = "No. 6 Wrench"
# Other Definitions
victory = "Donkey Kong"

View File

@@ -0,0 +1,336 @@
# Level Definitions
lakeside_limbo_flag = "Lakeside Limbo - Flag"
lakeside_limbo_bonus_1 = "Lakeside Limbo - Bonus 1"
lakeside_limbo_bonus_2 = "Lakeside Limbo - Bonus 2"
lakeside_limbo_dk = "Lakeside Limbo - DK Coin"
doorstop_dash_flag = "Doorstop Dash - Flag"
doorstop_dash_bonus_1 = "Doorstop Dash - Bonus 1"
doorstop_dash_bonus_2 = "Doorstop Dash - Bonus 2"
doorstop_dash_dk = "Doorstop Dash - DK Coin"
tidal_trouble_flag = "Tidal Trouble - Flag"
tidal_trouble_bonus_1 = "Tidal Trouble - Bonus 1"
tidal_trouble_bonus_2 = "Tidal Trouble - Bonus 2"
tidal_trouble_dk = "Tidal Trouble - DK Coin"
skiddas_row_flag = "Skidda's Row - Flag"
skiddas_row_bonus_1 = "Skidda's Row - Bonus 1"
skiddas_row_bonus_2 = "Skidda's Row - Bonus 2"
skiddas_row_dk = "Skidda's Row - DK Coin"
murky_mill_flag = "Murky Mill - Flag"
murky_mill_bonus_1 = "Murky Mill - Bonus 1"
murky_mill_bonus_2 = "Murky Mill - Bonus 2"
murky_mill_dk = "Murky Mill - DK Coin"
barrel_shield_bust_up_flag = "Barrel Shield Bust-Up - Flag"
barrel_shield_bust_up_bonus_1 = "Barrel Shield Bust-Up - Bonus 1"
barrel_shield_bust_up_bonus_2 = "Barrel Shield Bust-Up - Bonus 2"
barrel_shield_bust_up_dk = "Barrel Shield Bust-Up - DK Coin"
riverside_race_flag = "Riverside Race - Flag"
riverside_race_bonus_1 = "Riverside Race - Bonus 1"
riverside_race_bonus_2 = "Riverside Race - Bonus 2"
riverside_race_dk = "Riverside Race - DK Coin"
squeals_on_wheels_flag = "Squeals On Wheels - Flag"
squeals_on_wheels_bonus_1 = "Squeals On Wheels - Bonus 1"
squeals_on_wheels_bonus_2 = "Squeals On Wheels - Bonus 2"
squeals_on_wheels_dk = "Squeals On Wheels - DK Coin"
springin_spiders_flag = "Springin' Spiders - Flag"
springin_spiders_bonus_1 = "Springin' Spiders - Bonus 1"
springin_spiders_bonus_2 = "Springin' Spiders - Bonus 2"
springin_spiders_dk = "Springin' Spiders - DK Coin"
bobbing_barrel_brawl_flag = "Bobbing Barrel Brawl - Flag"
bobbing_barrel_brawl_bonus_1 = "Bobbing Barrel Brawl - Bonus 1"
bobbing_barrel_brawl_bonus_2 = "Bobbing Barrel Brawl - Bonus 2"
bobbing_barrel_brawl_dk = "Bobbing Barrel Brawl - DK Coin"
bazzas_blockade_flag = "Bazza's Blockade - Flag"
bazzas_blockade_bonus_1 = "Bazza's Blockade - Bonus 1"
bazzas_blockade_bonus_2 = "Bazza's Blockade - Bonus 2"
bazzas_blockade_dk = "Bazza's Blockade - DK Coin"
rocket_barrel_ride_flag = "Rocket Barrel Ride - Flag"
rocket_barrel_ride_bonus_1 = "Rocket Barrel Ride - Bonus 1"
rocket_barrel_ride_bonus_2 = "Rocket Barrel Ride - Bonus 2"
rocket_barrel_ride_dk = "Rocket Barrel Ride - DK Coin"
kreeping_klasps_flag = "Kreeping Klasps - Flag"
kreeping_klasps_bonus_1 = "Kreeping Klasps - Bonus 1"
kreeping_klasps_bonus_2 = "Kreeping Klasps - Bonus 2"
kreeping_klasps_dk = "Kreeping Klasps - DK Coin"
tracker_barrel_trek_flag = "Tracker Barrel Trek - Flag"
tracker_barrel_trek_bonus_1 = "Tracker Barrel Trek - Bonus 1"
tracker_barrel_trek_bonus_2 = "Tracker Barrel Trek - Bonus 2"
tracker_barrel_trek_dk = "Tracker Barrel Trek - DK Coin"
fish_food_frenzy_flag = "Fish Food Frenzy - Flag"
fish_food_frenzy_bonus_1 = "Fish Food Frenzy - Bonus 1"
fish_food_frenzy_bonus_2 = "Fish Food Frenzy - Bonus 2"
fish_food_frenzy_dk = "Fish Food Frenzy - DK Coin"
fire_ball_frenzy_flag = "Fire-Ball Frenzy - Flag"
fire_ball_frenzy_bonus_1 = "Fire-Ball Frenzy - Bonus 1"
fire_ball_frenzy_bonus_2 = "Fire-Ball Frenzy - Bonus 2"
fire_ball_frenzy_dk = "Fire-Ball Frenzy - DK Coin"
demolition_drain_pipe_flag = "Demolition Drain-Pipe - Flag"
demolition_drain_pipe_bonus_1 = "Demolition Drain-Pipe - Bonus 1"
demolition_drain_pipe_bonus_2 = "Demolition Drain-Pipe - Bonus 2"
demolition_drain_pipe_dk = "Demolition Drain-Pipe - DK Coin"
ripsaw_rage_flag = "Ripsaw Rage - Flag"
ripsaw_rage_bonus_1 = "Ripsaw Rage - Bonus 1"
ripsaw_rage_bonus_2 = "Ripsaw Rage - Bonus 2"
ripsaw_rage_dk = "Ripsaw Rage - DK Coin"
blazing_bazookas_flag = "Blazing Bazookas - Flag"
blazing_bazookas_bonus_1 = "Blazing Bazookas - Bonus 1"
blazing_bazookas_bonus_2 = "Blazing Bazookas - Bonus 2"
blazing_bazookas_dk = "Blazing Bazookas - DK Coin"
low_g_labyrinth_flag = "Low-G Labyrinth - Flag"
low_g_labyrinth_bonus_1 = "Low-G Labyrinth - Bonus 1"
low_g_labyrinth_bonus_2 = "Low-G Labyrinth - Bonus 2"
low_g_labyrinth_dk = "Low-G Labyrinth - DK Coin"
krevice_kreepers_flag = "Krevice Kreepers - Flag"
krevice_kreepers_bonus_1 = "Krevice Kreepers - Bonus 1"
krevice_kreepers_bonus_2 = "Krevice Kreepers - Bonus 2"
krevice_kreepers_dk = "Krevice Kreepers - DK Coin"
tearaway_toboggan_flag = "Tearaway Toboggan - Flag"
tearaway_toboggan_bonus_1 = "Tearaway Toboggan - Bonus 1"
tearaway_toboggan_bonus_2 = "Tearaway Toboggan - Bonus 2"
tearaway_toboggan_dk = "Tearaway Toboggan - DK Coin"
barrel_drop_bounce_flag = "Barrel Drop Bounce - Flag"
barrel_drop_bounce_bonus_1 = "Barrel Drop Bounce - Bonus 1"
barrel_drop_bounce_bonus_2 = "Barrel Drop Bounce - Bonus 2"
barrel_drop_bounce_dk = "Barrel Drop Bounce - DK Coin"
krack_shot_kroc_flag = "Krack-Shot Kroc - Flag"
krack_shot_kroc_bonus_1 = "Krack-Shot Kroc - Bonus 1"
krack_shot_kroc_bonus_2 = "Krack-Shot Kroc - Bonus 2"
krack_shot_kroc_dk = "Krack-Shot Kroc - DK Coin"
lemguin_lunge_flag = "Lemguin Lunge - Flag"
lemguin_lunge_bonus_1 = "Lemguin Lunge - Bonus 1"
lemguin_lunge_bonus_2 = "Lemguin Lunge - Bonus 2"
lemguin_lunge_dk = "Lemguin Lunge - DK Coin"
buzzer_barrage_flag = "Buzzer Barrage - Flag"
buzzer_barrage_bonus_1 = "Buzzer Barrage - Bonus 1"
buzzer_barrage_bonus_2 = "Buzzer Barrage - Bonus 2"
buzzer_barrage_dk = "Buzzer Barrage - DK Coin"
kong_fused_cliffs_flag = "Kong-Fused Cliffs - Flag"
kong_fused_cliffs_bonus_1 = "Kong-Fused Cliffs - Bonus 1"
kong_fused_cliffs_bonus_2 = "Kong-Fused Cliffs - Bonus 2"
kong_fused_cliffs_dk = "Kong-Fused Cliffs - DK Coin"
floodlit_fish_flag = "Floodlit Fish - Flag"
floodlit_fish_bonus_1 = "Floodlit Fish - Bonus 1"
floodlit_fish_bonus_2 = "Floodlit Fish - Bonus 2"
floodlit_fish_dk = "Floodlit Fish - DK Coin"
pothole_panic_flag = "Pothole Panic - Flag"
pothole_panic_bonus_1 = "Pothole Panic - Bonus 1"
pothole_panic_bonus_2 = "Pothole Panic - Bonus 2"
pothole_panic_dk = "Pothole Panic - DK Coin"
ropey_rumpus_flag = "Ropey Rumpus - Flag"
ropey_rumpus_bonus_1 = "Ropey Rumpus - Bonus 1"
ropey_rumpus_bonus_2 = "Ropey Rumpus - Bonus 2"
ropey_rumpus_dk = "Ropey Rumpus - DK Coin"
konveyor_rope_clash_flag = "Konveyor Rope Klash - Flag"
konveyor_rope_clash_bonus_1 = "Konveyor Rope Klash - Bonus 1"
konveyor_rope_clash_bonus_2 = "Konveyor Rope Klash - Bonus 2"
konveyor_rope_clash_dk = "Konveyor Rope Klash - DK Coin"
creepy_caverns_flag = "Creepy Caverns - Flag"
creepy_caverns_bonus_1 = "Creepy Caverns - Bonus 1"
creepy_caverns_bonus_2 = "Creepy Caverns - Bonus 2"
creepy_caverns_dk = "Creepy Caverns - DK Coin"
lightning_lookout_flag = "Lightning Lookout - Flag"
lightning_lookout_bonus_1 = "Lightning Lookout - Bonus 1"
lightning_lookout_bonus_2 = "Lightning Lookout - Bonus 2"
lightning_lookout_dk = "Lightning Lookout - DK Coin"
koindozer_klamber_flag = "Koindozer Klamber - Flag"
koindozer_klamber_bonus_1 = "Koindozer Klamber - Bonus 1"
koindozer_klamber_bonus_2 = "Koindozer Klamber - Bonus 2"
koindozer_klamber_dk = "Koindozer Klamber - DK Coin"
poisonous_pipeline_flag = "Poisonous Pipeline - Flag"
poisonous_pipeline_bonus_1 = "Poisonous Pipeline - Bonus 1"
poisonous_pipeline_bonus_2 = "Poisonous Pipeline - Bonus 2"
poisonous_pipeline_dk = "Poisonous Pipeline - DK Coin"
stampede_sprint_flag = "Stampede Sprint - Flag"
stampede_sprint_bonus_1 = "Stampede Sprint - Bonus 1"
stampede_sprint_bonus_2 = "Stampede Sprint - Bonus 2"
stampede_sprint_bonus_3 = "Stampede Sprint - Bonus 3"
stampede_sprint_dk = "Stampede Sprint - DK Coin"
criss_cross_cliffs_flag = "Criss Kross Cliffs - Flag"
criss_cross_cliffs_bonus_1 = "Criss Kross Cliffs - Bonus 1"
criss_cross_cliffs_bonus_2 = "Criss Kross Cliffs - Bonus 2"
criss_cross_cliffs_dk = "Criss Kross Cliffs - DK Coin"
tyrant_twin_tussle_flag = "Tyrant Twin Tussle - Flag"
tyrant_twin_tussle_bonus_1 = "Tyrant Twin Tussle - Bonus 1"
tyrant_twin_tussle_bonus_2 = "Tyrant Twin Tussle - Bonus 2"
tyrant_twin_tussle_bonus_3 = "Tyrant Twin Tussle - Bonus 3"
tyrant_twin_tussle_dk = "Tyrant Twin Tussle - DK Coin"
swoopy_salvo_flag = "Swoopy Salvo - Flag"
swoopy_salvo_bonus_1 = "Swoopy Salvo - Bonus 1"
swoopy_salvo_bonus_2 = "Swoopy Salvo - Bonus 2"
swoopy_salvo_bonus_3 = "Swoopy Salvo - Bonus 3"
swoopy_salvo_dk = "Swoopy Salvo - DK Coin"
rocket_rush_flag = "Rocket Rush - Flag"
rocket_rush_dk = "Rocket Rush - DK Coin"
# Boss Definitions
belchas_barn = "Belcha's Barn"
arichs_ambush = "Arich's Ambush"
squirts_showdown = "Squirt's Showdown"
kaos_karnage = "KAOS Karnage"
bleaks_house = "Bleak's House"
barboss_barrier = "Barbos's Barrier"
kastle_kaos = "Kastle KAOS"
knautilus = "Knautilus"
# Banana Bird Cave Definitions
belchas_burrow = "Belcha's Burrow"
kong_cave = "Kong Cave"
undercover_cove = "Undercover Cove"
ks_cache = "K's Cache"
hill_top_hoard = "Hill-Top Hoard"
bounty_beach = "Bounty Beach"
smugglers_cove = "Smuggler's Cove"
arichs_hoard = "Arich's Hoard"
bounty_bay = "Bounty Bay"
sky_high_secret = "Sky-High Secret"
glacial_grotto = "Glacial Grotto"
cifftop_cache = "Clifftop Cache"
sewer_stockpile = "Sewer Stockpile"
banana_bird_mother = "Banana Bird Mother"
# Brothers Bear Definitions
bazaars_general_store_1 = "Bazaar's General Store - 1"
bazaars_general_store_2 = "Bazaar's General Store - 2"
brambles_bungalow = "Bramble's Bungalow"
flower_spot = "Flower Spot"
barters_swap_shop = "Barter's Swap Shop"
barnacles_island = "Barnacle's Island"
blues_beach_hut = "Blue's Beach Hut"
blizzards_basecamp = "Bizzard's Basecamp"
# Region Definitions
menu_region = "Menu"
overworld_1_region = "Overworld 1"
overworld_2_region = "Overworld 2"
overworld_3_region = "Overworld 3"
overworld_4_region = "Overworld 4"
bazaar_region = "Bazaar's General Store Region"
bramble_region = "Bramble's Bungalow Region"
flower_spot_region = "Flower Spot Region"
barter_region = "Barter's Swap Shop Region"
barnacle_region = "Barnacle's Island Region"
blue_region = "Blue's Beach Hut Region"
blizzard_region = "Bizzard's Basecamp Region"
lake_orangatanga_region = "Lake_Orangatanga"
kremwood_forest_region = "Kremwood Forest"
cotton_top_cove_region = "Cotton-Top Cove"
mekanos_region = "Mekanos"
k3_region = "K3"
razor_ridge_region = "Razor Ridge"
kaos_kore_region = "KAOS Kore"
krematoa_region = "Krematoa"
belchas_barn_region = "Belcha's Barn Region"
arichs_ambush_region = "Arich's Ambush Region"
squirts_showdown_region = "Squirt's Showdown Region"
kaos_karnage_region = "KAOS Karnage Region"
bleaks_house_region = "Bleak's House Region"
barboss_barrier_region = "Barbos's Barrier Region"
kastle_kaos_region = "Kastle KAOS Region"
knautilus_region = "Knautilus Region"
belchas_burrow_region = "Belcha's Burrow Region"
kong_cave_region = "Kong Cave Region"
undercover_cove_region = "Undercover Cove Region"
ks_cache_region = "K's Cache Region"
hill_top_hoard_region = "Hill-Top Hoard Region"
bounty_beach_region = "Bounty Beach Region"
smugglers_cove_region = "Smuggler's Cove Region"
arichs_hoard_region = "Arich's Hoard Region"
bounty_bay_region = "Bounty Bay Region"
sky_high_secret_region = "Sky-High Secret Region"
glacial_grotto_region = "Glacial Grotto Region"
cifftop_cache_region = "Clifftop Cache Region"
sewer_stockpile_region = "Sewer Stockpile Region"
lakeside_limbo_region = "Lakeside Limbo"
doorstop_dash_region = "Doorstop Dash"
tidal_trouble_region = "Tidal Trouble"
skiddas_row_region = "Skidda's Row"
murky_mill_region = "Murky Mill"
barrel_shield_bust_up_region = "Barrel Shield Bust-Up"
riverside_race_region = "Riverside Race"
squeals_on_wheels_region = "Squeals On Wheels"
springin_spiders_region = "Springin' Spiders"
bobbing_barrel_brawl_region = "Bobbing Barrel Brawl"
bazzas_blockade_region = "Bazza's Blockade"
rocket_barrel_ride_region = "Rocket Barrel Ride"
kreeping_klasps_region = "Kreeping Klasps"
tracker_barrel_trek_region = "Tracker Barrel Trek"
fish_food_frenzy_region = "Fish Food Frenzy"
fire_ball_frenzy_region = "Fire-Ball Frenzy"
demolition_drain_pipe_region = "Demolition Drain-Pipe"
ripsaw_rage_region = "Ripsaw Rage"
blazing_bazookas_region = "Blazing Bazukas"
low_g_labyrinth_region = "Low-G Labyrinth"
krevice_kreepers_region = "Krevice Kreepers"
tearaway_toboggan_region = "Tearaway Toboggan"
barrel_drop_bounce_region = "Barrel Drop Bounce"
krack_shot_kroc_region = "Krack-Shot Kroc"
lemguin_lunge_region = "Lemguin Lunge"
buzzer_barrage_region = "Buzzer Barrage"
kong_fused_cliffs_region = "Kong-Fused Cliffs"
floodlit_fish_region = "Floodlit Fish"
pothole_panic_region = "Pothole Panic"
ropey_rumpus_region = "Ropey Rumpus"
konveyor_rope_clash_region = "Konveyor Rope Klash"
creepy_caverns_region = "Creepy Caverns"
lightning_lookout_region = "Lightning Lookout"
koindozer_klamber_region = "Koindozer Klamber"
poisonous_pipeline_region = "Poisonous Pipeline"
stampede_sprint_region = "Stampede Sprint"
criss_cross_cliffs_region = "Criss Kross Cliffs"
tyrant_twin_tussle_region = "Tyrant Twin Tussle"
swoopy_salvo_region = "Swoopy Salvo"
rocket_rush_region = "Rocket Rush"

132
worlds/dkc3/Options.py Normal file
View File

@@ -0,0 +1,132 @@
import typing
from Options import Choice, Range, Option, Toggle, DeathLink, DefaultOnToggle, OptionList
class Goal(Choice):
"""
Determines the goal of the seed
Knautilus: Reach the Knautilus and defeat Baron K. Roolenstein
Banana Bird Hunt: Find a certain number of Banana Birds and rescue their mother
"""
display_name = "Goal"
option_knautilus = 0
option_banana_bird_hunt = 1
default = 0
class IncludeTradeSequence(Toggle):
"""
Allows logic to place items at the various steps of the trade sequence
"""
display_name = "Include Trade Sequence"
class DKCoinsForGyrocopter(Range):
"""
How many DK Coins are needed to unlock the Gyrocopter
Note: Achieving this number before unlocking the Turbo Ski will cause the game to grant you a
one-time upgrade to the next non-unlocked boat, until you return to Funky. Logic does not assume
that you will use this.
"""
display_name = "DK Coins for Gyrocopter"
range_start = 10
range_end = 41
default = 30
class KrematoaBonusCoinCost(Range):
"""
How many Bonus Coins are needed to unlock each level in Krematoa
"""
display_name = "Krematoa Bonus Coins Cost"
range_start = 1
range_end = 17
default = 15
class PercentageOfExtraBonusCoins(Range):
"""
What Percentage of unneeded Bonus Coins are included in the item pool
"""
display_name = "Percentage of Extra Bonus Coins"
range_start = 0
range_end = 100
default = 100
class NumberOfBananaBirds(Range):
"""
How many Banana Birds are put into the item pool
"""
display_name = "Number of Banana Birds"
range_start = 5
range_end = 15
default = 15
class PercentageOfBananaBirds(Range):
"""
What Percentage of Banana Birds in the item pool are required for Banana Bird Hunt
"""
display_name = "Percentage of Banana Birds"
range_start = 20
range_end = 100
default = 100
class LevelShuffle(Toggle):
"""
Whether levels are shuffled
"""
display_name = "Level Shuffle"
class MusicShuffle(Toggle):
"""
Whether music is shuffled
"""
display_name = "Music Shuffle"
class KongPaletteSwap(Choice):
"""
Which Palette to use for the Kongs
"""
display_name = "Kong Palette Swap"
option_default = 0
option_purple = 1
option_spooky = 2
option_dark = 3
option_chocolate = 4
option_shadow = 5
option_red_gold = 6
option_gbc = 7
option_halloween = 8
default = 0
class StartingLifeCount(Range):
"""
How many extra lives to start the game with
"""
display_name = "Starting Life Count"
range_start = 1
range_end = 99
default = 5
dkc3_options: typing.Dict[str, type(Option)] = {
#"death_link": DeathLink, # Disabled
"goal": Goal,
#"include_trade_sequence": IncludeTradeSequence, # Disabled
"dk_coins_for_gyrocopter": DKCoinsForGyrocopter,
"krematoa_bonus_coin_cost": KrematoaBonusCoinCost,
"percentage_of_extra_bonus_coins": PercentageOfExtraBonusCoins,
"number_of_banana_birds": NumberOfBananaBirds,
"percentage_of_banana_birds": PercentageOfBananaBirds,
"level_shuffle": LevelShuffle,
"music_shuffle": MusicShuffle,
"kong_palette_swap": KongPaletteSwap,
"starting_life_count": StartingLifeCount,
}

883
worlds/dkc3/Regions.py Normal file
View File

@@ -0,0 +1,883 @@
import typing
from BaseClasses import MultiWorld, Region, Entrance
from .Items import DKC3Item
from .Locations import DKC3Location
from .Names import LocationName, ItemName
def create_regions(world, player: int, active_locations):
menu_region = create_region(world, player, active_locations, 'Menu', None, None)
overworld_1_region_locations = {}
if world.goal[player] != "knautilus":
overworld_1_region_locations.update({LocationName.banana_bird_mother: []})
overworld_1_region = create_region(world, player, active_locations, LocationName.overworld_1_region,
overworld_1_region_locations, None)
overworld_2_region_locations = {}
overworld_2_region = create_region(world, player, active_locations, LocationName.overworld_2_region,
overworld_2_region_locations, None)
overworld_3_region_locations = {}
overworld_3_region = create_region(world, player, active_locations, LocationName.overworld_3_region,
overworld_3_region_locations, None)
overworld_4_region_locations = {}
overworld_4_region = create_region(world, player, active_locations, LocationName.overworld_4_region,
overworld_4_region_locations, None)
lake_orangatanga_region = create_region(world, player, active_locations, LocationName.lake_orangatanga_region, None, None)
kremwood_forest_region = create_region(world, player, active_locations, LocationName.kremwood_forest_region, None, None)
cotton_top_cove_region = create_region(world, player, active_locations, LocationName.cotton_top_cove_region, None, None)
mekanos_region = create_region(world, player, active_locations, LocationName.mekanos_region, None, None)
k3_region = create_region(world, player, active_locations, LocationName.k3_region, None, None)
razor_ridge_region = create_region(world, player, active_locations, LocationName.razor_ridge_region, None, None)
kaos_kore_region = create_region(world, player, active_locations, LocationName.kaos_kore_region, None, None)
krematoa_region = create_region(world, player, active_locations, LocationName.krematoa_region, None, None)
lakeside_limbo_region_locations = {
LocationName.lakeside_limbo_flag : [0x657, 1],
LocationName.lakeside_limbo_bonus_1 : [0x657, 2],
LocationName.lakeside_limbo_bonus_2 : [0x657, 3],
LocationName.lakeside_limbo_dk : [0x657, 5],
}
lakeside_limbo_region = create_region(world, player, active_locations, LocationName.lakeside_limbo_region,
lakeside_limbo_region_locations, None)
doorstop_dash_region_locations = {
LocationName.doorstop_dash_flag : [0x65A, 1],
LocationName.doorstop_dash_bonus_1 : [0x65A, 2],
LocationName.doorstop_dash_bonus_2 : [0x65A, 3],
LocationName.doorstop_dash_dk : [0x65A, 5],
}
doorstop_dash_region = create_region(world, player, active_locations, LocationName.doorstop_dash_region,
doorstop_dash_region_locations, None)
tidal_trouble_region_locations = {
LocationName.tidal_trouble_flag : [0x659, 1],
LocationName.tidal_trouble_bonus_1 : [0x659, 2],
LocationName.tidal_trouble_bonus_2 : [0x659, 3],
LocationName.tidal_trouble_dk : [0x659, 5],
}
tidal_trouble_region = create_region(world, player, active_locations, LocationName.tidal_trouble_region,
tidal_trouble_region_locations, None)
skiddas_row_region_locations = {
LocationName.skiddas_row_flag : [0x65D, 1],
LocationName.skiddas_row_bonus_1 : [0x65D, 2],
LocationName.skiddas_row_bonus_2 : [0x65D, 3],
LocationName.skiddas_row_dk : [0x65D, 5],
}
skiddas_row_region = create_region(world, player, active_locations, LocationName.skiddas_row_region,
skiddas_row_region_locations, None)
murky_mill_region_locations = {
LocationName.murky_mill_flag : [0x65C, 1],
LocationName.murky_mill_bonus_1 : [0x65C, 2],
LocationName.murky_mill_bonus_2 : [0x65C, 3],
LocationName.murky_mill_dk : [0x65C, 5],
}
murky_mill_region = create_region(world, player, active_locations, LocationName.murky_mill_region,
murky_mill_region_locations, None)
barrel_shield_bust_up_region_locations = {
LocationName.barrel_shield_bust_up_flag : [0x662, 1],
LocationName.barrel_shield_bust_up_bonus_1 : [0x662, 2],
LocationName.barrel_shield_bust_up_bonus_2 : [0x662, 3],
LocationName.barrel_shield_bust_up_dk : [0x662, 5],
}
barrel_shield_bust_up_region = create_region(world, player, active_locations, LocationName.barrel_shield_bust_up_region,
barrel_shield_bust_up_region_locations, None)
riverside_race_region_locations = {
LocationName.riverside_race_flag : [0x664, 1],
LocationName.riverside_race_bonus_1 : [0x664, 2],
LocationName.riverside_race_bonus_2 : [0x664, 3],
LocationName.riverside_race_dk : [0x664, 5],
}
riverside_race_region = create_region(world, player, active_locations, LocationName.riverside_race_region,
riverside_race_region_locations, None)
squeals_on_wheels_region_locations = {
LocationName.squeals_on_wheels_flag : [0x65B, 1],
LocationName.squeals_on_wheels_bonus_1 : [0x65B, 2],
LocationName.squeals_on_wheels_bonus_2 : [0x65B, 3],
LocationName.squeals_on_wheels_dk : [0x65B, 5],
}
squeals_on_wheels_region = create_region(world, player, active_locations, LocationName.squeals_on_wheels_region,
squeals_on_wheels_region_locations, None)
springin_spiders_region_locations = {
LocationName.springin_spiders_flag : [0x661, 1],
LocationName.springin_spiders_bonus_1 : [0x661, 2],
LocationName.springin_spiders_bonus_2 : [0x661, 3],
LocationName.springin_spiders_dk : [0x661, 5],
}
springin_spiders_region = create_region(world, player, active_locations, LocationName.springin_spiders_region,
springin_spiders_region_locations, None)
bobbing_barrel_brawl_region_locations = {
LocationName.bobbing_barrel_brawl_flag : [0x666, 1],
LocationName.bobbing_barrel_brawl_bonus_1 : [0x666, 2],
LocationName.bobbing_barrel_brawl_bonus_2 : [0x666, 3],
LocationName.bobbing_barrel_brawl_dk : [0x666, 5],
}
bobbing_barrel_brawl_region = create_region(world, player, active_locations, LocationName.bobbing_barrel_brawl_region,
bobbing_barrel_brawl_region_locations, None)
bazzas_blockade_region_locations = {
LocationName.bazzas_blockade_flag : [0x667, 1],
LocationName.bazzas_blockade_bonus_1 : [0x667, 2],
LocationName.bazzas_blockade_bonus_2 : [0x667, 3],
LocationName.bazzas_blockade_dk : [0x667, 5],
}
bazzas_blockade_region = create_region(world, player, active_locations, LocationName.bazzas_blockade_region,
bazzas_blockade_region_locations, None)
rocket_barrel_ride_region_locations = {
LocationName.rocket_barrel_ride_flag : [0x66A, 1],
LocationName.rocket_barrel_ride_bonus_1 : [0x66A, 2],
LocationName.rocket_barrel_ride_bonus_2 : [0x66A, 3],
LocationName.rocket_barrel_ride_dk : [0x66A, 5],
}
rocket_barrel_ride_region = create_region(world, player, active_locations, LocationName.rocket_barrel_ride_region,
rocket_barrel_ride_region_locations, None)
kreeping_klasps_region_locations = {
LocationName.kreeping_klasps_flag : [0x658, 1],
LocationName.kreeping_klasps_bonus_1 : [0x658, 2],
LocationName.kreeping_klasps_bonus_2 : [0x658, 3],
LocationName.kreeping_klasps_dk : [0x658, 5],
}
kreeping_klasps_region = create_region(world, player, active_locations, LocationName.kreeping_klasps_region,
kreeping_klasps_region_locations, None)
tracker_barrel_trek_region_locations = {
LocationName.tracker_barrel_trek_flag : [0x66B, 1],
LocationName.tracker_barrel_trek_bonus_1 : [0x66B, 2],
LocationName.tracker_barrel_trek_bonus_2 : [0x66B, 3],
LocationName.tracker_barrel_trek_dk : [0x66B, 5],
}
tracker_barrel_trek_region = create_region(world, player, active_locations, LocationName.tracker_barrel_trek_region,
tracker_barrel_trek_region_locations, None)
fish_food_frenzy_region_locations = {
LocationName.fish_food_frenzy_flag : [0x668, 1],
LocationName.fish_food_frenzy_bonus_1 : [0x668, 2],
LocationName.fish_food_frenzy_bonus_2 : [0x668, 3],
LocationName.fish_food_frenzy_dk : [0x668, 5],
}
fish_food_frenzy_region = create_region(world, player, active_locations, LocationName.fish_food_frenzy_region,
fish_food_frenzy_region_locations, None)
fire_ball_frenzy_region_locations = {
LocationName.fire_ball_frenzy_flag : [0x66D, 1],
LocationName.fire_ball_frenzy_bonus_1 : [0x66D, 2],
LocationName.fire_ball_frenzy_bonus_2 : [0x66D, 3],
LocationName.fire_ball_frenzy_dk : [0x66D, 5],
}
fire_ball_frenzy_region = create_region(world, player, active_locations, LocationName.fire_ball_frenzy_region,
fire_ball_frenzy_region_locations, None)
demolition_drain_pipe_region_locations = {
LocationName.demolition_drain_pipe_flag : [0x672, 1],
LocationName.demolition_drain_pipe_bonus_1 : [0x672, 2],
LocationName.demolition_drain_pipe_bonus_2 : [0x672, 3],
LocationName.demolition_drain_pipe_dk : [0x672, 5],
}
demolition_drain_pipe_region = create_region(world, player, active_locations, LocationName.demolition_drain_pipe_region,
demolition_drain_pipe_region_locations, None)
ripsaw_rage_region_locations = {
LocationName.ripsaw_rage_flag : [0x660, 1],
LocationName.ripsaw_rage_bonus_1 : [0x660, 2],
LocationName.ripsaw_rage_bonus_2 : [0x660, 3],
LocationName.ripsaw_rage_dk : [0x660, 5],
}
ripsaw_rage_region = create_region(world, player, active_locations, LocationName.ripsaw_rage_region,
ripsaw_rage_region_locations, None)
blazing_bazookas_region_locations = {
LocationName.blazing_bazookas_flag : [0x66E, 1],
LocationName.blazing_bazookas_bonus_1 : [0x66E, 2],
LocationName.blazing_bazookas_bonus_2 : [0x66E, 3],
LocationName.blazing_bazookas_dk : [0x66E, 5],
}
blazing_bazookas_region = create_region(world, player, active_locations, LocationName.blazing_bazookas_region,
blazing_bazookas_region_locations, None)
low_g_labyrinth_region_locations = {
LocationName.low_g_labyrinth_flag : [0x670, 1],
LocationName.low_g_labyrinth_bonus_1 : [0x670, 2],
LocationName.low_g_labyrinth_bonus_2 : [0x670, 3],
LocationName.low_g_labyrinth_dk : [0x670, 5],
}
low_g_labyrinth_region = create_region(world, player, active_locations, LocationName.low_g_labyrinth_region,
low_g_labyrinth_region_locations, None)
krevice_kreepers_region_locations = {
LocationName.krevice_kreepers_flag : [0x673, 1],
LocationName.krevice_kreepers_bonus_1 : [0x673, 2],
LocationName.krevice_kreepers_bonus_2 : [0x673, 3],
LocationName.krevice_kreepers_dk : [0x673, 5],
}
krevice_kreepers_region = create_region(world, player, active_locations, LocationName.krevice_kreepers_region,
krevice_kreepers_region_locations, None)
tearaway_toboggan_region_locations = {
LocationName.tearaway_toboggan_flag : [0x65F, 1],
LocationName.tearaway_toboggan_bonus_1 : [0x65F, 2],
LocationName.tearaway_toboggan_bonus_2 : [0x65F, 3],
LocationName.tearaway_toboggan_dk : [0x65F, 5],
}
tearaway_toboggan_region = create_region(world, player, active_locations, LocationName.tearaway_toboggan_region,
tearaway_toboggan_region_locations, None)
barrel_drop_bounce_region_locations = {
LocationName.barrel_drop_bounce_flag : [0x66C, 1],
LocationName.barrel_drop_bounce_bonus_1 : [0x66C, 2],
LocationName.barrel_drop_bounce_bonus_2 : [0x66C, 3],
LocationName.barrel_drop_bounce_dk : [0x66C, 5],
}
barrel_drop_bounce_region = create_region(world, player, active_locations, LocationName.barrel_drop_bounce_region,
barrel_drop_bounce_region_locations, None)
krack_shot_kroc_region_locations = {
LocationName.krack_shot_kroc_flag : [0x66F, 1],
LocationName.krack_shot_kroc_bonus_1 : [0x66F, 2],
LocationName.krack_shot_kroc_bonus_2 : [0x66F, 3],
LocationName.krack_shot_kroc_dk : [0x66F, 5],
}
krack_shot_kroc_region = create_region(world, player, active_locations, LocationName.krack_shot_kroc_region,
krack_shot_kroc_region_locations, None)
lemguin_lunge_region_locations = {
LocationName.lemguin_lunge_flag : [0x65E, 1],
LocationName.lemguin_lunge_bonus_1 : [0x65E, 2],
LocationName.lemguin_lunge_bonus_2 : [0x65E, 3],
LocationName.lemguin_lunge_dk : [0x65E, 5],
}
lemguin_lunge_region = create_region(world, player, active_locations, LocationName.lemguin_lunge_region,
lemguin_lunge_region_locations, None)
buzzer_barrage_region_locations = {
LocationName.buzzer_barrage_flag : [0x676, 1],
LocationName.buzzer_barrage_bonus_1 : [0x676, 2],
LocationName.buzzer_barrage_bonus_2 : [0x676, 3],
LocationName.buzzer_barrage_dk : [0x676, 5],
}
buzzer_barrage_region = create_region(world, player, active_locations, LocationName.buzzer_barrage_region,
buzzer_barrage_region_locations, None)
kong_fused_cliffs_region_locations = {
LocationName.kong_fused_cliffs_flag : [0x674, 1],
LocationName.kong_fused_cliffs_bonus_1 : [0x674, 2],
LocationName.kong_fused_cliffs_bonus_2 : [0x674, 3],
LocationName.kong_fused_cliffs_dk : [0x674, 5],
}
kong_fused_cliffs_region = create_region(world, player, active_locations, LocationName.kong_fused_cliffs_region,
kong_fused_cliffs_region_locations, None)
floodlit_fish_region_locations = {
LocationName.floodlit_fish_flag : [0x669, 1],
LocationName.floodlit_fish_bonus_1 : [0x669, 2],
LocationName.floodlit_fish_bonus_2 : [0x669, 3],
LocationName.floodlit_fish_dk : [0x669, 5],
}
floodlit_fish_region = create_region(world, player, active_locations, LocationName.floodlit_fish_region,
floodlit_fish_region_locations, None)
pothole_panic_region_locations = {
LocationName.pothole_panic_flag : [0x677, 1],
LocationName.pothole_panic_bonus_1 : [0x677, 2],
LocationName.pothole_panic_bonus_2 : [0x677, 3],
LocationName.pothole_panic_dk : [0x677, 5],
}
pothole_panic_region = create_region(world, player, active_locations, LocationName.pothole_panic_region,
pothole_panic_region_locations, None)
ropey_rumpus_region_locations = {
LocationName.ropey_rumpus_flag : [0x675, 1],
LocationName.ropey_rumpus_bonus_1 : [0x675, 2],
LocationName.ropey_rumpus_bonus_2 : [0x675, 3],
LocationName.ropey_rumpus_dk : [0x675, 5],
}
ropey_rumpus_region = create_region(world, player, active_locations, LocationName.ropey_rumpus_region,
ropey_rumpus_region_locations, None)
konveyor_rope_clash_region_locations = {
LocationName.konveyor_rope_clash_flag : [0x657, 1],
LocationName.konveyor_rope_clash_bonus_1 : [0x657, 2],
LocationName.konveyor_rope_clash_bonus_2 : [0x657, 3],
LocationName.konveyor_rope_clash_dk : [0x657, 5],
}
konveyor_rope_clash_region = create_region(world, player, active_locations, LocationName.konveyor_rope_clash_region,
konveyor_rope_clash_region_locations, None)
creepy_caverns_region_locations = {
LocationName.creepy_caverns_flag : [0x678, 1],
LocationName.creepy_caverns_bonus_1 : [0x678, 2],
LocationName.creepy_caverns_bonus_2 : [0x678, 3],
LocationName.creepy_caverns_dk : [0x678, 5],
}
creepy_caverns_region = create_region(world, player, active_locations, LocationName.creepy_caverns_region,
creepy_caverns_region_locations, None)
lightning_lookout_region_locations = {
LocationName.lightning_lookout_flag : [0x665, 1],
LocationName.lightning_lookout_bonus_1 : [0x665, 2],
LocationName.lightning_lookout_bonus_2 : [0x665, 3],
LocationName.lightning_lookout_dk : [0x665, 5],
}
lightning_lookout_region = create_region(world, player, active_locations, LocationName.lightning_lookout_region,
lightning_lookout_region_locations, None)
koindozer_klamber_region_locations = {
LocationName.koindozer_klamber_flag : [0x679, 1],
LocationName.koindozer_klamber_bonus_1 : [0x679, 2],
LocationName.koindozer_klamber_bonus_2 : [0x679, 3],
LocationName.koindozer_klamber_dk : [0x679, 5],
}
koindozer_klamber_region = create_region(world, player, active_locations, LocationName.koindozer_klamber_region,
koindozer_klamber_region_locations, None)
poisonous_pipeline_region_locations = {
LocationName.poisonous_pipeline_flag : [0x671, 1],
LocationName.poisonous_pipeline_bonus_1 : [0x671, 2],
LocationName.poisonous_pipeline_bonus_2 : [0x671, 3],
LocationName.poisonous_pipeline_dk : [0x671, 5],
}
poisonous_pipeline_region = create_region(world, player, active_locations, LocationName.poisonous_pipeline_region,
poisonous_pipeline_region_locations, None)
stampede_sprint_region_locations = {
LocationName.stampede_sprint_flag : [0x67B, 1],
LocationName.stampede_sprint_bonus_1 : [0x67B, 2],
LocationName.stampede_sprint_bonus_2 : [0x67B, 3],
LocationName.stampede_sprint_bonus_3 : [0x67B, 4],
LocationName.stampede_sprint_dk : [0x67B, 5],
}
stampede_sprint_region = create_region(world, player, active_locations, LocationName.stampede_sprint_region,
stampede_sprint_region_locations, None)
criss_cross_cliffs_region_locations = {
LocationName.criss_cross_cliffs_flag : [0x67C, 1],
LocationName.criss_cross_cliffs_bonus_1 : [0x67C, 2],
LocationName.criss_cross_cliffs_bonus_2 : [0x67C, 3],
LocationName.criss_cross_cliffs_dk : [0x67C, 5],
}
criss_cross_cliffs_region = create_region(world, player, active_locations, LocationName.criss_cross_cliffs_region,
criss_cross_cliffs_region_locations, None)
tyrant_twin_tussle_region_locations = {
LocationName.tyrant_twin_tussle_flag : [0x67D, 1],
LocationName.tyrant_twin_tussle_bonus_1 : [0x67D, 2],
LocationName.tyrant_twin_tussle_bonus_2 : [0x67D, 3],
LocationName.tyrant_twin_tussle_bonus_3 : [0x67D, 4],
LocationName.tyrant_twin_tussle_dk : [0x67D, 5],
}
tyrant_twin_tussle_region = create_region(world, player, active_locations, LocationName.tyrant_twin_tussle_region,
tyrant_twin_tussle_region_locations, None)
swoopy_salvo_region_locations = {
LocationName.swoopy_salvo_flag : [0x663, 1],
LocationName.swoopy_salvo_bonus_1 : [0x663, 2],
LocationName.swoopy_salvo_bonus_2 : [0x663, 3],
LocationName.swoopy_salvo_bonus_3 : [0x663, 4],
LocationName.swoopy_salvo_dk : [0x663, 5],
}
swoopy_salvo_region = create_region(world, player, active_locations, LocationName.swoopy_salvo_region,
swoopy_salvo_region_locations, None)
rocket_rush_region_locations = {
LocationName.rocket_rush_flag : [0x67E, 1],
LocationName.rocket_rush_dk : [0x67E, 5],
}
rocket_rush_region = create_region(world, player, active_locations, LocationName.rocket_rush_region,
rocket_rush_region_locations, None)
belchas_barn_region_locations = {
LocationName.belchas_barn: [0x64F, 1],
}
belchas_barn_region = create_region(world, player, active_locations, LocationName.belchas_barn_region,
belchas_barn_region_locations, None)
arichs_ambush_region_locations = {
LocationName.arichs_ambush: [0x650, 1],
}
arichs_ambush_region = create_region(world, player, active_locations, LocationName.arichs_ambush_region,
arichs_ambush_region_locations, None)
squirts_showdown_region_locations = {
LocationName.squirts_showdown: [0x651, 1],
}
squirts_showdown_region = create_region(world, player, active_locations, LocationName.squirts_showdown_region,
squirts_showdown_region_locations, None)
kaos_karnage_region_locations = {
LocationName.kaos_karnage: [0x652, 1],
}
kaos_karnage_region = create_region(world, player, active_locations, LocationName.kaos_karnage_region,
kaos_karnage_region_locations, None)
bleaks_house_region_locations = {
LocationName.bleaks_house: [0x653, 1],
}
bleaks_house_region = create_region(world, player, active_locations, LocationName.bleaks_house_region,
bleaks_house_region_locations, None)
barboss_barrier_region_locations = {
LocationName.barboss_barrier: [0x654, 1],
}
barboss_barrier_region = create_region(world, player, active_locations, LocationName.barboss_barrier_region,
barboss_barrier_region_locations, None)
kastle_kaos_region_locations = {
LocationName.kastle_kaos: [0x655, 1],
}
kastle_kaos_region = create_region(world, player, active_locations, LocationName.kastle_kaos_region,
kastle_kaos_region_locations, None)
knautilus_region_locations = {
LocationName.knautilus: [0x656, 1],
}
knautilus_region = create_region(world, player, active_locations, LocationName.knautilus_region,
knautilus_region_locations, None)
belchas_burrow_region_locations = {
LocationName.belchas_burrow: [0x647, 1],
}
belchas_burrow_region = create_region(world, player, active_locations, LocationName.belchas_burrow_region,
belchas_burrow_region_locations, None)
kong_cave_region_locations = {
LocationName.kong_cave: [0x645, 1],
}
kong_cave_region = create_region(world, player, active_locations, LocationName.kong_cave_region,
kong_cave_region_locations, None)
undercover_cove_region_locations = {
LocationName.undercover_cove: [0x644, 1],
}
undercover_cove_region = create_region(world, player, active_locations, LocationName.undercover_cove_region,
undercover_cove_region_locations, None)
ks_cache_region_locations = {
LocationName.ks_cache: [0x642, 1],
}
ks_cache_region = create_region(world, player, active_locations, LocationName.ks_cache_region,
ks_cache_region_locations, None)
hill_top_hoard_region_locations = {
LocationName.hill_top_hoard: [0x643, 1],
}
hill_top_hoard_region = create_region(world, player, active_locations, LocationName.hill_top_hoard_region,
hill_top_hoard_region_locations, None)
bounty_beach_region_locations = {
LocationName.bounty_beach: [0x646, 1],
}
bounty_beach_region = create_region(world, player, active_locations, LocationName.bounty_beach_region,
bounty_beach_region_locations, None)
smugglers_cove_region_locations = {
LocationName.smugglers_cove: [0x648, 1],
}
smugglers_cove_region = create_region(world, player, active_locations, LocationName.smugglers_cove_region,
smugglers_cove_region_locations, None)
arichs_hoard_region_locations = {
LocationName.arichs_hoard: [0x649, 1],
}
arichs_hoard_region = create_region(world, player, active_locations, LocationName.arichs_hoard_region,
arichs_hoard_region_locations, None)
bounty_bay_region_locations = {
LocationName.bounty_bay: [0x64A, 1],
}
bounty_bay_region = create_region(world, player, active_locations, LocationName.bounty_bay_region,
bounty_bay_region_locations, None)
sky_high_secret_region_locations = {}
if False:#world.include_trade_sequence[player]:
sky_high_secret_region_locations.update({
LocationName.sky_high_secret: [0x64B, 1],
})
sky_high_secret_region = create_region(world, player, active_locations, LocationName.sky_high_secret_region,
sky_high_secret_region_locations, None)
glacial_grotto_region_locations = {
LocationName.glacial_grotto: [0x64C, 1],
}
glacial_grotto_region = create_region(world, player, active_locations, LocationName.glacial_grotto_region,
glacial_grotto_region_locations, None)
cifftop_cache_region_locations = {}
if False:#world.include_trade_sequence[player]:
cifftop_cache_region_locations.update({
LocationName.cifftop_cache: [0x64D, 1],
})
cifftop_cache_region = create_region(world, player, active_locations, LocationName.cifftop_cache_region,
cifftop_cache_region_locations, None)
sewer_stockpile_region_locations = {
LocationName.sewer_stockpile: [0x64E, 1],
}
sewer_stockpile_region = create_region(world, player, active_locations, LocationName.sewer_stockpile_region,
sewer_stockpile_region_locations, None)
# Set up the regions correctly.
world.regions += [
menu_region,
overworld_1_region,
overworld_2_region,
overworld_3_region,
overworld_4_region,
lake_orangatanga_region,
kremwood_forest_region,
cotton_top_cove_region,
mekanos_region,
k3_region,
razor_ridge_region,
kaos_kore_region,
krematoa_region,
lakeside_limbo_region,
doorstop_dash_region,
tidal_trouble_region,
skiddas_row_region,
murky_mill_region,
barrel_shield_bust_up_region,
riverside_race_region,
squeals_on_wheels_region,
springin_spiders_region,
bobbing_barrel_brawl_region,
bazzas_blockade_region,
rocket_barrel_ride_region,
kreeping_klasps_region,
tracker_barrel_trek_region,
fish_food_frenzy_region,
fire_ball_frenzy_region,
demolition_drain_pipe_region,
ripsaw_rage_region,
blazing_bazookas_region,
low_g_labyrinth_region,
krevice_kreepers_region,
tearaway_toboggan_region,
barrel_drop_bounce_region,
krack_shot_kroc_region,
lemguin_lunge_region,
buzzer_barrage_region,
kong_fused_cliffs_region,
floodlit_fish_region,
pothole_panic_region,
ropey_rumpus_region,
konveyor_rope_clash_region,
creepy_caverns_region,
lightning_lookout_region,
koindozer_klamber_region,
poisonous_pipeline_region,
stampede_sprint_region,
criss_cross_cliffs_region,
tyrant_twin_tussle_region,
swoopy_salvo_region,
rocket_rush_region,
belchas_barn_region,
arichs_ambush_region,
squirts_showdown_region,
kaos_karnage_region,
bleaks_house_region,
barboss_barrier_region,
kastle_kaos_region,
knautilus_region,
belchas_burrow_region,
kong_cave_region,
undercover_cove_region,
ks_cache_region,
hill_top_hoard_region,
bounty_beach_region,
smugglers_cove_region,
arichs_hoard_region,
bounty_bay_region,
sky_high_secret_region,
glacial_grotto_region,
cifftop_cache_region,
sewer_stockpile_region,
]
bazaar_region_locations = {}
bramble_region_locations = {}
flower_spot_region_locations = {}
barter_region_locations = {}
barnacle_region_locations = {}
blue_region_locations = {}
blizzard_region_locations = {}
if False:#world.include_trade_sequence[player]:
bazaar_region_locations.update({
LocationName.bazaars_general_store_1: [0x615, 2, True],
LocationName.bazaars_general_store_2: [0x615, 3, True],
})
bramble_region_locations.update({
LocationName.brambles_bungalow: [0x619, 2],
})
#flower_spot_region_locations.update({
# LocationName.flower_spot: [0x615, 3, True],
#})
barter_region_locations.update({
LocationName.barters_swap_shop: [0x61B, 3],
})
barnacle_region_locations.update({
LocationName.barnacles_island: [0x61D, 2],
})
blue_region_locations.update({
LocationName.blues_beach_hut: [0x621, 4],
})
blizzard_region_locations.update({
LocationName.blizzards_basecamp: [0x625, 4, True],
})
bazaar_region = create_region(world, player, active_locations, LocationName.bazaar_region,
bazaar_region_locations, None)
bramble_region = create_region(world, player, active_locations, LocationName.bramble_region,
bramble_region_locations, None)
flower_spot_region = create_region(world, player, active_locations, LocationName.flower_spot_region,
flower_spot_region_locations, None)
barter_region = create_region(world, player, active_locations, LocationName.barter_region,
barter_region_locations, None)
barnacle_region = create_region(world, player, active_locations, LocationName.barnacle_region,
barnacle_region_locations, None)
blue_region = create_region(world, player, active_locations, LocationName.blue_region,
blue_region_locations, None)
blizzard_region = create_region(world, player, active_locations, LocationName.blizzard_region,
blizzard_region_locations, None)
world.regions += [
bazaar_region,
bramble_region,
flower_spot_region,
barter_region,
barnacle_region,
blue_region,
blizzard_region,
]
def connect_regions(world, player, level_list):
names: typing.Dict[str, int] = {}
# Overworld
connect(world, player, names, 'Menu', LocationName.overworld_1_region)
connect(world, player, names, LocationName.overworld_1_region, LocationName.overworld_2_region,
lambda state: (state.has(ItemName.progressive_boat, player, 1)))
connect(world, player, names, LocationName.overworld_2_region, LocationName.overworld_3_region,
lambda state: (state.has(ItemName.progressive_boat, player, 3)))
connect(world, player, names, LocationName.overworld_1_region, LocationName.overworld_4_region,
lambda state: (state.has(ItemName.dk_coin, player, world.dk_coins_for_gyrocopter[player].value) and
state.has(ItemName.progressive_boat, player, 3)))
# World Connections
connect(world, player, names, LocationName.overworld_1_region, LocationName.lake_orangatanga_region)
connect(world, player, names, LocationName.overworld_1_region, LocationName.kremwood_forest_region)
connect(world, player, names, LocationName.overworld_1_region, LocationName.bounty_beach_region)
connect(world, player, names, LocationName.overworld_1_region, LocationName.bazaar_region)
connect(world, player, names, LocationName.overworld_2_region, LocationName.cotton_top_cove_region)
connect(world, player, names, LocationName.overworld_2_region, LocationName.mekanos_region)
connect(world, player, names, LocationName.overworld_2_region, LocationName.kong_cave_region)
connect(world, player, names, LocationName.overworld_2_region, LocationName.bramble_region)
connect(world, player, names, LocationName.overworld_3_region, LocationName.k3_region)
connect(world, player, names, LocationName.overworld_3_region, LocationName.razor_ridge_region)
connect(world, player, names, LocationName.overworld_3_region, LocationName.kaos_kore_region)
connect(world, player, names, LocationName.overworld_3_region, LocationName.krematoa_region)
connect(world, player, names, LocationName.overworld_3_region, LocationName.undercover_cove_region)
connect(world, player, names, LocationName.overworld_3_region, LocationName.flower_spot_region)
connect(world, player, names, LocationName.overworld_3_region, LocationName.barter_region)
connect(world, player, names, LocationName.overworld_4_region, LocationName.belchas_burrow_region)
connect(world, player, names, LocationName.overworld_4_region, LocationName.ks_cache_region)
connect(world, player, names, LocationName.overworld_4_region, LocationName.hill_top_hoard_region)
# Lake Orangatanga Connections
lake_orangatanga_levels = [
level_list[0],
level_list[1],
level_list[2],
level_list[3],
level_list[4],
LocationName.belchas_barn_region,
LocationName.barnacle_region,
LocationName.smugglers_cove_region,
]
for i in range(0, len(lake_orangatanga_levels)):
connect(world, player, names, LocationName.lake_orangatanga_region, lake_orangatanga_levels[i])
# Kremwood Forest Connections
kremwood_forest_levels = [
level_list[5],
level_list[6],
level_list[7],
level_list[8],
level_list[9],
LocationName.arichs_ambush_region,
LocationName.arichs_hoard_region,
]
for i in range(0, len(kremwood_forest_levels) - 1):
connect(world, player, names, LocationName.kremwood_forest_region, kremwood_forest_levels[i])
connect(world, player, names, LocationName.kremwood_forest_region, kremwood_forest_levels[-1],
lambda state: (state.can_reach(LocationName.riverside_race_flag, "Location", player)))
# Cotton-Top Cove Connections
cotton_top_cove_levels = [
LocationName.blue_region,
level_list[10],
level_list[11],
level_list[12],
level_list[13],
level_list[14],
LocationName.squirts_showdown_region,
LocationName.bounty_bay_region,
]
for i in range(0, len(cotton_top_cove_levels)):
connect(world, player, names, LocationName.cotton_top_cove_region, cotton_top_cove_levels[i])
# Mekanos Connections
mekanos_levels = [
level_list[15],
level_list[16],
level_list[17],
level_list[18],
level_list[19],
LocationName.kaos_karnage_region,
]
for i in range(0, len(mekanos_levels)):
connect(world, player, names, LocationName.mekanos_region, mekanos_levels[i])
if False:#world.include_trade_sequence[player]:
connect(world, player, names, LocationName.mekanos_region, LocationName.sky_high_secret_region,
lambda state: (state.has(ItemName.bowling_ball, player, 1)))
else:
connect(world, player, names, LocationName.mekanos_region, LocationName.sky_high_secret_region,
lambda state: (state.can_reach(LocationName.bleaks_house, "Location", player)))
# K3 Connections
k3_levels = [
level_list[20],
level_list[21],
level_list[22],
level_list[23],
level_list[24],
LocationName.bleaks_house_region,
LocationName.blizzard_region,
LocationName.glacial_grotto_region,
]
for i in range(0, len(k3_levels)):
connect(world, player, names, LocationName.k3_region, k3_levels[i])
# Razor Ridge Connections
razor_ridge_levels = [
level_list[25],
level_list[26],
level_list[27],
level_list[28],
level_list[29],
LocationName.barboss_barrier_region,
]
for i in range(0, len(razor_ridge_levels)):
connect(world, player, names, LocationName.razor_ridge_region, razor_ridge_levels[i])
if False:#world.include_trade_sequence[player]:
connect(world, player, names, LocationName.razor_ridge_region, LocationName.cifftop_cache_region,
lambda state: (state.has(ItemName.wrench, player, 1)))
else:
connect(world, player, names, LocationName.razor_ridge_region, LocationName.cifftop_cache_region)
# KAOS Kore Connections
kaos_kore_levels = [
level_list[30],
level_list[31],
level_list[32],
level_list[33],
level_list[34],
LocationName.kastle_kaos_region,
LocationName.sewer_stockpile_region,
]
for i in range(0, len(kaos_kore_levels)):
connect(world, player, names, LocationName.kaos_kore_region, kaos_kore_levels[i])
# Krematoa Connections
krematoa_levels = [
level_list[35],
level_list[36],
level_list[37],
level_list[38],
LocationName.rocket_rush_region,
]
for i in range(0, len(krematoa_levels)):
connect(world, player, names, LocationName.krematoa_region, krematoa_levels[i],
lambda state: (state.has(ItemName.bonus_coin, player, world.krematoa_bonus_coin_cost[player].value * (i+1))))
connect(world, player, names, LocationName.krematoa_region, LocationName.knautilus_region,
lambda state: (state.has(ItemName.krematoa_cog, player, 5)))
def create_region(world: MultiWorld, player: int, active_locations, name: str, locations=None, exits=None):
# Shamelessly stolen from the ROR2 definition
ret = Region(name, None, name, player)
ret.world = world
if locations:
for locationName, locationData in locations.items():
loc_id = active_locations.get(locationName, 0)
if loc_id:
loc_byte = locationData[0] if (len(locationData) > 0) else 0
loc_bit = locationData[1] if (len(locationData) > 1) else 0
loc_invert = locationData[2] if (len(locationData) > 2) else False
location = DKC3Location(player, locationName, loc_id, ret, loc_byte, loc_bit, loc_invert)
ret.locations.append(location)
if exits:
for exit in exits:
ret.exits.append(Entrance(player, exit, ret))
return ret
def connect(world: MultiWorld, player: int, used_names: typing.Dict[str, int], source: str, target: str,
rule: typing.Optional[typing.Callable] = None):
source_region = world.get_region(source, player)
target_region = world.get_region(target, player)
if target not in used_names:
used_names[target] = 1
name = target
else:
used_names[target] += 1
name = target + (' ' * used_names[target])
connection = Entrance(player, name, source_region)
if rule:
connection.access_rule = rule
source_region.exits.append(connection)
connection.connect(target_region)

553
worlds/dkc3/Rom.py Normal file
View File

@@ -0,0 +1,553 @@
import Utils
from Patch import read_rom
from .Locations import lookup_id_to_name, all_locations
from .Levels import level_list, level_dict
USHASH = '120abf304f0c40fe059f6a192ed4f947'
ROM_PLAYER_LIMIT = 65535
import hashlib
import os
import math
location_rom_data = {
0xDC3000: [0x657, 1], # Lakeside Limbo
0xDC3001: [0x657, 2],
0xDC3002: [0x657, 3],
0xDC3003: [0x657, 5],
0xDC3004: [0x65A, 1], # Doorstop Dash
0xDC3005: [0x65A, 2],
0xDC3006: [0x65A, 3],
0xDC3007: [0x65A, 5],
0xDC3008: [0x659, 1], # Tidal Trouble
0xDC3009: [0x659, 2],
0xDC300A: [0x659, 3],
0xDC300B: [0x659, 5],
0xDC300C: [0x65D, 1], # Skidda's Row
0xDC300D: [0x65D, 2],
0xDC300E: [0x65D, 3],
0xDC300F: [0x65D, 5],
0xDC3010: [0x65C, 1], # Murky Mill
0xDC3011: [0x65C, 2],
0xDC3012: [0x65C, 3],
0xDC3013: [0x65C, 5],
0xDC3014: [0x662, 1], # Barrel Shield Bust-Up
0xDC3015: [0x662, 2],
0xDC3016: [0x662, 3],
0xDC3017: [0x662, 5],
0xDC3018: [0x664, 1], # Riverside Race
0xDC3019: [0x664, 2],
0xDC301A: [0x664, 3],
0xDC301B: [0x664, 5],
0xDC301C: [0x65B, 1], # Squeals on Wheels
0xDC301D: [0x65B, 2],
0xDC301E: [0x65B, 3],
0xDC301F: [0x65B, 5],
0xDC3020: [0x661, 1], # Springin' Spiders
0xDC3021: [0x661, 2],
0xDC3022: [0x661, 3],
0xDC3023: [0x661, 5],
0xDC3024: [0x666, 1], # Bobbing Barrel Brawl
0xDC3025: [0x666, 2],
0xDC3026: [0x666, 3],
0xDC3027: [0x666, 5],
0xDC3028: [0x667, 1], # Bazza's Blockade
0xDC3029: [0x667, 2],
0xDC302A: [0x667, 3],
0xDC302B: [0x667, 5],
0xDC302C: [0x66A, 1], # Rocket Barrel Ride
0xDC302D: [0x66A, 2],
0xDC302E: [0x66A, 3],
0xDC302F: [0x66A, 5],
0xDC3030: [0x658, 1], # Kreeping Klasps
0xDC3031: [0x658, 2],
0xDC3032: [0x658, 3],
0xDC3033: [0x658, 5],
0xDC3034: [0x66B, 1], # Tracker Barrel Trek
0xDC3035: [0x66B, 2],
0xDC3036: [0x66B, 3],
0xDC3037: [0x66B, 5],
0xDC3038: [0x668, 1], # Fish Food Frenzy
0xDC3039: [0x668, 2],
0xDC303A: [0x668, 3],
0xDC303B: [0x668, 5],
0xDC303C: [0x66D, 1], # Fire-ball Frenzy
0xDC303D: [0x66D, 2],
0xDC303E: [0x66D, 3],
0xDC303F: [0x66D, 5],
0xDC3040: [0x672, 1], # Demolition Drainpipe
0xDC3041: [0x672, 2],
0xDC3042: [0x672, 3],
0xDC3043: [0x672, 5],
0xDC3044: [0x660, 1], # Ripsaw Rage
0xDC3045: [0x660, 2],
0xDC3046: [0x660, 3],
0xDC3047: [0x660, 5],
0xDC3048: [0x66E, 1], # Blazing Bazukas
0xDC3049: [0x66E, 2],
0xDC304A: [0x66E, 3],
0xDC304B: [0x66E, 5],
0xDC304C: [0x670, 1], # Low-G Labyrinth
0xDC304D: [0x670, 2],
0xDC304E: [0x670, 3],
0xDC304F: [0x670, 5],
0xDC3050: [0x673, 1], # Krevice Kreepers
0xDC3051: [0x673, 2],
0xDC3052: [0x673, 3],
0xDC3053: [0x673, 5],
0xDC3054: [0x65F, 1], # Tearaway Toboggan
0xDC3055: [0x65F, 2],
0xDC3056: [0x65F, 3],
0xDC3057: [0x65F, 5],
0xDC3058: [0x66C, 1], # Barrel Drop Bounce
0xDC3059: [0x66C, 2],
0xDC305A: [0x66C, 3],
0xDC305B: [0x66C, 5],
0xDC305C: [0x66F, 1], # Krack-Shot Kroc
0xDC305D: [0x66F, 2],
0xDC305E: [0x66F, 3],
0xDC305F: [0x66F, 5],
0xDC3060: [0x65E, 1], # Lemguin Lunge
0xDC3061: [0x65E, 2],
0xDC3062: [0x65E, 3],
0xDC3063: [0x65E, 5],
0xDC3064: [0x676, 1], # Buzzer Barrage
0xDC3065: [0x676, 2],
0xDC3066: [0x676, 3],
0xDC3067: [0x676, 5],
0xDC3068: [0x674, 1], # Kong-Fused Cliffs
0xDC3069: [0x674, 2],
0xDC306A: [0x674, 3],
0xDC306B: [0x674, 5],
0xDC306C: [0x669, 1], # Floodlit Fish
0xDC306D: [0x669, 2],
0xDC306E: [0x669, 3],
0xDC306F: [0x669, 5],
0xDC3070: [0x677, 1], # Pothole Panic
0xDC3071: [0x677, 2],
0xDC3072: [0x677, 3],
0xDC3073: [0x677, 5],
0xDC3074: [0x675, 1], # Ropey Rumpus
0xDC3075: [0x675, 2],
0xDC3076: [0x675, 3],
0xDC3077: [0x675, 5],
0xDC3078: [0x67A, 1], # Konveyor Rope Klash
0xDC3079: [0x67A, 2],
0xDC307A: [0x67A, 3],
0xDC307B: [0x67A, 5],
0xDC307C: [0x678, 1], # Creepy Caverns
0xDC307D: [0x678, 2],
0xDC307E: [0x678, 3],
0xDC307F: [0x678, 5],
0xDC3080: [0x665, 1], # Lightning Lookout
0xDC3081: [0x665, 2],
0xDC3082: [0x665, 3],
0xDC3083: [0x665, 5],
0xDC3084: [0x679, 1], # Koindozer Klamber
0xDC3085: [0x679, 2],
0xDC3086: [0x679, 3],
0xDC3087: [0x679, 5],
0xDC3088: [0x671, 1], # Poisonous Pipeline
0xDC3089: [0x671, 2],
0xDC308A: [0x671, 3],
0xDC308B: [0x671, 5],
0xDC308C: [0x67B, 1], # Stampede Sprint
0xDC308D: [0x67B, 2],
0xDC308E: [0x67B, 3],
0xDC308F: [0x67B, 4],
0xDC3090: [0x67B, 5],
0xDC3091: [0x67C, 1], # Criss Kross Cliffs
0xDC3092: [0x67C, 2],
0xDC3093: [0x67C, 3],
0xDC3094: [0x67C, 5],
0xDC3095: [0x67D, 1], # Tyrant Twin Tussle
0xDC3096: [0x67D, 2],
0xDC3097: [0x67D, 3],
0xDC3098: [0x67D, 4],
0xDC3099: [0x67D, 5],
0xDC309A: [0x663, 1], # Swoopy Salvo
0xDC309B: [0x663, 2],
0xDC309C: [0x663, 3],
0xDC309D: [0x663, 4],
0xDC309E: [0x663, 5],
0xDC309F: [0x67E, 1], # Rocket Rush
0xDC30A0: [0x67E, 5],
0xDC30A1: [0x64F, 1], # Bosses
0xDC30A2: [0x650, 1],
0xDC30A3: [0x651, 1],
0xDC30A4: [0x652, 1],
0xDC30A5: [0x653, 1],
0xDC30A6: [0x654, 1],
0xDC30A7: [0x655, 1],
0xDC30A8: [0x656, 1],
0xDC30A9: [0x647, 1], # Banana Bird Caves
0xDC30AA: [0x645, 1],
0xDC30AB: [0x644, 1],
0xDC30AC: [0x642, 1],
0xDC30AD: [0x643, 1],
0xDC30AE: [0x646, 1],
0xDC30AF: [0x648, 1],
0xDC30B0: [0x649, 1],
0xDC30B1: [0x64A, 1],
#0xDC30B2: [0x64B, 1], # Disabled until Trade Sequence
0xDC30B3: [0x64C, 1],
#0xDC30B4: [0x64D, 1], # Disabled until Trade Sequence
0xDC30B5: [0x64E, 1],
0xDC30B6: [0x5FD, 4], # Banana Bird Mother
# DKC3_TODO: Disabled until Trade Sequence
#0xDC30B7: [0x615, 2, True],
#0xDC30B8: [0x615, 3, True],
#0xDC30B9: [0x619, 2],
##0xDC30BA:
#0xDC30BB: [0x61B, 3],
#0xDC30BC: [0x61D, 2],
#0xDC30BD: [0x621, 4],
#0xDC30BE: [0x625, 4, True],
}
item_rom_data = {
0xDC3001: [0x5D5], # 1-Up Balloon
0xDC3002: [0x5C9], # Bear Coin
0xDC3003: [0x5CB], # Bonus Coin
0xDC3004: [0x5CF], # DK Coin
0xDC3005: [0x5CD], # Banana Bird
0xDC3006: [0x5D1, 0x603], # Cog
}
music_rom_data = [
0x3D06B1,
0x3D0753,
0x3D071D,
0x3D07FA,
0x3D07C4,
0x3D08FE,
0x3D096C,
0x3D078E,
0x3D08CD,
0x3D09DD,
0x3D0A0E,
0x3D0AB3,
0x3D06E7,
0x3D0AE4,
0x3D0A45,
0x3D0B46,
0x3D0C40,
0x3D0897,
0x3D0B77,
0x3D0BD9,
0x3D0C71,
0x3D0866,
0x3D0B15,
0x3D0BA8,
0x3D0830,
0x3D0D04,
0x3D0CA2,
0x3D0A7C,
0x3D0D35,
0x3D0CD3,
0x3D0DC8,
0x3D0D66,
0x3D09AC,
0x3D0D97,
0x3D0C0F,
0x3D0DF9,
0x3D0E31,
0x3D0E62,
0x3D0934,
0x3D0E9A,
]
level_music_ids = [
0x06,
0x07,
0x08,
0x0A,
0x0B,
0x0E,
0x0F,
0x10,
0x17,
0x19,
0x1C,
0x1D,
0x1E,
0x21,
]
class LocalRom(object):
def __init__(self, file, patch=True, vanillaRom=None, name=None, hash=None):
self.name = name
self.hash = hash
self.orig_buffer = None
with open(file, 'rb') as stream:
self.buffer = read_rom(stream)
#if patch:
# self.patch_rom()
# self.orig_buffer = self.buffer.copy()
#if vanillaRom:
# with open(vanillaRom, 'rb') as vanillaStream:
# self.orig_buffer = read_rom(vanillaStream)
def read_bit(self, address: int, bit_number: int) -> bool:
bitflag = (1 << bit_number)
return ((self.buffer[address] & bitflag) != 0)
def read_byte(self, address: int) -> int:
return self.buffer[address]
def read_bytes(self, startaddress: int, length: int) -> bytes:
return self.buffer[startaddress:startaddress + length]
def write_byte(self, address: int, value: int):
self.buffer[address] = value
def write_bytes(self, startaddress: int, values):
self.buffer[startaddress:startaddress + len(values)] = values
def write_to_file(self, file):
with open(file, 'wb') as outfile:
outfile.write(self.buffer)
def read_from_file(self, file):
with open(file, 'rb') as stream:
self.buffer = bytearray(stream.read())
def patch_rom(world, rom, player, active_level_list):
local_random = world.slot_seeds[player]
# Boomer Costs
bonus_coin_cost = world.krematoa_bonus_coin_cost[player]
inverted_bonus_coin_cost = 0x100 - bonus_coin_cost
rom.write_byte(0x3498B9, inverted_bonus_coin_cost)
rom.write_byte(0x3498BA, inverted_bonus_coin_cost)
rom.write_byte(0x3498BB, inverted_bonus_coin_cost)
rom.write_byte(0x3498BC, inverted_bonus_coin_cost)
rom.write_byte(0x3498BD, inverted_bonus_coin_cost)
rom.write_byte(0x349857, bonus_coin_cost)
rom.write_byte(0x349862, bonus_coin_cost)
# Gyrocopter Costs
dk_coin_cost = world.dk_coins_for_gyrocopter[player]
rom.write_byte(0x3484A6, dk_coin_cost)
rom.write_byte(0x3484D5, dk_coin_cost)
rom.write_byte(0x3484D7, 0x90)
rom.write_byte(0x3484DC, 0xEA)
rom.write_byte(0x3484DD, 0xEA)
rom.write_byte(0x3484DE, 0xEA)
rom.write_byte(0x348528, 0x80) # Prevent Single-Ski Lock
# Make Swanky free
rom.write_byte(0x348C48, 0x00)
# Banana Bird Costs
if world.goal[player] == "banana_bird_hunt":
banana_bird_cost = math.floor(world.number_of_banana_birds[player] * world.percentage_of_banana_birds[player] / 100.0)
rom.write_byte(0x34AB85, banana_bird_cost)
rom.write_byte(0x329FD8, banana_bird_cost)
rom.write_byte(0x32A025, banana_bird_cost)
rom.write_byte(0x329FDA, 0xB0)
else:
# rom.write_byte(0x34AB84, 0x20) # These cause hangs at Wrinkly's
# rom.write_byte(0x329FD8, 0x20)
# rom.write_byte(0x32A025, 0x20)
rom.write_byte(0x329FDA, 0xB0)
# Baffle Mirror Fix
rom.write_byte(0x9133, 0x08)
rom.write_byte(0x9135, 0x0C)
rom.write_byte(0x9136, 0x2B)
rom.write_byte(0x9137, 0x06)
# Palette Swap
rom.write_byte(0x3B96A5, 0xD0)
if world.kong_palette_swap[player] == "default":
rom.write_byte(0x3B96A9, 0x00)
rom.write_byte(0x3B96A8, 0x00)
elif world.kong_palette_swap[player] == "purple":
rom.write_byte(0x3B96A9, 0x00)
rom.write_byte(0x3B96A8, 0x3C)
elif world.kong_palette_swap[player] == "spooky":
rom.write_byte(0x3B96A9, 0x00)
rom.write_byte(0x3B96A8, 0xA0)
elif world.kong_palette_swap[player] == "dark":
rom.write_byte(0x3B96A9, 0x05)
rom.write_byte(0x3B96A8, 0xA0)
elif world.kong_palette_swap[player] == "chocolate":
rom.write_byte(0x3B96A9, 0x1D)
rom.write_byte(0x3B96A8, 0xA0)
elif world.kong_palette_swap[player] == "shadow":
rom.write_byte(0x3B96A9, 0x45)
rom.write_byte(0x3B96A8, 0xA0)
elif world.kong_palette_swap[player] == "red_gold":
rom.write_byte(0x3B96A9, 0x5D)
rom.write_byte(0x3B96A8, 0xA0)
elif world.kong_palette_swap[player] == "gbc":
rom.write_byte(0x3B96A9, 0x20)
rom.write_byte(0x3B96A8, 0x3C)
elif world.kong_palette_swap[player] == "halloween":
rom.write_byte(0x3B96A9, 0x70)
rom.write_byte(0x3B96A8, 0x3C)
if world.music_shuffle[player]:
for address in music_rom_data:
rand_song = local_random.choice(level_music_ids)
rom.write_byte(address, rand_song)
# Starting Lives
rom.write_byte(0x9130, world.starting_life_count[player].value)
rom.write_byte(0x913B, world.starting_life_count[player].value)
# Handle Level Shuffle Here
if world.level_shuffle[player]:
for i in range(len(active_level_list)):
rom.write_byte(level_dict[level_list[i]].nameIDAddress, level_dict[active_level_list[i]].nameID)
rom.write_byte(level_dict[level_list[i]].levelIDAddress, level_dict[active_level_list[i]].levelID)
# First levels of each world
rom.write_byte(0x34BC3E, (0x32 + level_dict[active_level_list[0]].levelID))
rom.write_byte(0x34BC47, (0x32 + level_dict[active_level_list[5]].levelID))
rom.write_byte(0x34BC4A, (0x32 + level_dict[active_level_list[10]].levelID))
rom.write_byte(0x34BC53, (0x32 + level_dict[active_level_list[15]].levelID))
rom.write_byte(0x34BC59, (0x32 + level_dict[active_level_list[20]].levelID))
rom.write_byte(0x34BC5C, (0x32 + level_dict[active_level_list[25]].levelID))
rom.write_byte(0x34BC65, (0x32 + level_dict[active_level_list[30]].levelID))
rom.write_byte(0x34BC6E, (0x32 + level_dict[active_level_list[35]].levelID))
# Cotton-Top Cove Boss Unlock
rom.write_byte(0x34C02A, (0x32 + level_dict[active_level_list[14]].levelID))
# Kong-Fused Cliffs Unlock
rom.write_byte(0x34C213, (0x32 + level_dict[active_level_list[25]].levelID))
rom.write_byte(0x34C21B, (0x32 + level_dict[active_level_list[26]].levelID))
if world.goal[player] == "knautilus":
# Swap Kastle KAOS and Knautilus
rom.write_byte(0x34D4E1, 0xC2)
rom.write_byte(0x34D4E2, 0x24)
rom.write_byte(0x34D551, 0xBA)
rom.write_byte(0x34D552, 0x23)
rom.write_byte(0x32F339, 0x55)
from Main import __version__
rom.name = bytearray(f'D3{__version__.replace(".", "")[0:3]}_{player}_{world.seed:11}\0', 'utf8')[:21]
rom.name.extend([0] * (21 - len(rom.name)))
rom.write_bytes(0x7FC0, rom.name)
# DKC3_TODO: This is a hack, reconsider
# Don't grant (DK, Bonus, Bear) Coins
rom.write_byte(0x3BD454, 0xEA)
rom.write_byte(0x3BD455, 0xEA)
# Don't grant Cogs
rom.write_byte(0x3BD574, 0xEA)
rom.write_byte(0x3BD575, 0xEA)
rom.write_byte(0x3BD576, 0xEA)
# Don't grant Banana Birds at their caves
rom.write_byte(0x32DD62, 0xEA)
rom.write_byte(0x32DD63, 0xEA)
rom.write_byte(0x32DD64, 0xEA)
# Don't grant Patch and Skis from their bosses
rom.write_byte(0x3F3762, 0x00)
rom.write_byte(0x3F377B, 0x00)
rom.write_byte(0x3F3797, 0x00)
# Always allow Start+Select
rom.write_byte(0x8BAB, 0x01)
# Handle Alt Palettes in Krematoa
rom.write_byte(0x3B97E9, 0x80)
rom.write_byte(0x3B97EA, 0xEA)
def get_base_rom_bytes(file_name: str = "") -> bytes:
base_rom_bytes = getattr(get_base_rom_bytes, "base_rom_bytes", None)
if not base_rom_bytes:
file_name = get_base_rom_path(file_name)
base_rom_bytes = bytes(read_rom(open(file_name, "rb")))
basemd5 = hashlib.md5()
basemd5.update(base_rom_bytes)
if USHASH != basemd5.hexdigest():
raise Exception('Supplied Base Rom does not match known MD5 for US(1.0) release. '
'Get the correct game and version, then dump it')
get_base_rom_bytes.base_rom_bytes = base_rom_bytes
return base_rom_bytes
def get_base_rom_path(file_name: str = "") -> str:
options = Utils.get_options()
if not file_name:
file_name = options["dkc3_options"]["rom_file"]
if not os.path.exists(file_name):
file_name = Utils.local_path(file_name)
return file_name

32
worlds/dkc3/Rules.py Normal file
View File

@@ -0,0 +1,32 @@
import math
from BaseClasses import MultiWorld
from .Names import LocationName, ItemName
from ..AutoWorld import LogicMixin
from ..generic.Rules import add_rule, set_rule
def set_rules(world: MultiWorld, player: int):
if False:#world.include_trade_sequence[player]:
add_rule(world.get_location(LocationName.barnacles_island, player),
lambda state: state.has(ItemName.shell, player))
add_rule(world.get_location(LocationName.blues_beach_hut, player),
lambda state: state.has(ItemName.present, player))
add_rule(world.get_location(LocationName.brambles_bungalow, player),
lambda state: state.has(ItemName.flower, player))
add_rule(world.get_location(LocationName.barters_swap_shop, player),
lambda state: state.has(ItemName.mirror, player))
if world.goal[player] != "knautilus":
required_banana_birds = math.floor(
world.number_of_banana_birds[player].value * (world.percentage_of_banana_birds[player].value / 100.0))
add_rule(world.get_location(LocationName.banana_bird_mother, player),
lambda state: state.has(ItemName.banana_bird, player, required_banana_birds))
world.completion_condition[player] = lambda state: state.has(ItemName.victory, player)

204
worlds/dkc3/__init__.py Normal file
View File

@@ -0,0 +1,204 @@
import os
import typing
import math
import threading
from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification
from .Items import DKC3Item, ItemData, item_table, inventory_table
from .Locations import DKC3Location, all_locations, setup_locations
from .Options import dkc3_options
from .Regions import create_regions, connect_regions
from .Levels import level_list
from .Rules import set_rules
from .Names import ItemName, LocationName
from ..AutoWorld import WebWorld, World
from .Rom import LocalRom, patch_rom, get_base_rom_path
import Patch
class DKC3Web(WebWorld):
theme = "jungle"
setup_en = Tutorial(
"Multiworld Setup Guide",
"A guide to setting up the Donkey Kong Country 3 randomizer connected to an Archipelago Multiworld.",
"English",
"setup_en.md",
"setup/en",
["PoryGone"]
)
tutorials = [setup_en]
class DKC3World(World):
"""
Donkey Kong Country 3 is an action platforming game.
Play as Dixie Kong and her baby cousin Kiddy as they try to solve the
mystery of why Donkey Kong and Diddy disappeared while on vacation.
"""
game: str = "Donkey Kong Country 3"
options = dkc3_options
topology_present = False
data_version = 1
#hint_blacklist = {LocationName.rocket_rush_flag}
item_name_to_id = {name: data.code for name, data in item_table.items()}
location_name_to_id = all_locations
active_level_list: typing.List[str]
web = DKC3Web()
def __init__(self, world: MultiWorld, player: int):
self.rom_name_available_event = threading.Event()
super().__init__(world, player)
@classmethod
def stage_assert_generate(cls, world):
rom_file = get_base_rom_path()
if not os.path.exists(rom_file):
raise FileNotFoundError(rom_file)
def _get_slot_data(self):
return {
#"death_link": self.world.death_link[self.player].value,
"active_levels": self.active_level_list,
}
def _create_items(self, name: str):
data = item_table[name]
return [self.create_item(name)] * data.quantity
def fill_slot_data(self) -> dict:
slot_data = self._get_slot_data()
for option_name in dkc3_options:
option = getattr(self.world, option_name)[self.player]
slot_data[option_name] = option.value
return slot_data
def generate_basic(self):
self.topology_present = self.world.level_shuffle[self.player].value
itempool: typing.List[DKC3Item] = []
# Levels
total_required_locations = 159
number_of_banana_birds = 0
# Rocket Rush Cog
total_required_locations -= 1
number_of_cogs = 4
self.world.get_location(LocationName.rocket_rush_flag, self.player).place_locked_item(self.create_item(ItemName.krematoa_cog))
number_of_bosses = 8
if self.world.goal[self.player] == "knautilus":
self.world.get_location(LocationName.kastle_kaos, self.player).place_locked_item(self.create_item(ItemName.victory))
number_of_bosses = 7
else:
self.world.get_location(LocationName.banana_bird_mother, self.player).place_locked_item(self.create_item(ItemName.victory))
number_of_banana_birds = self.world.number_of_banana_birds[self.player]
# Bosses
total_required_locations += number_of_bosses
# Secret Caves
total_required_locations += 13
## Brothers Bear
if False:#self.world.include_trade_sequence[self.player]:
total_required_locations += 10
number_of_bonus_coins = (self.world.krematoa_bonus_coin_cost[self.player] * 5)
number_of_bonus_coins += math.ceil((85 - number_of_bonus_coins) * self.world.percentage_of_extra_bonus_coins[self.player] / 100)
itempool += [self.create_item(ItemName.bonus_coin)] * number_of_bonus_coins
itempool += [self.create_item(ItemName.dk_coin)] * 41
itempool += [self.create_item(ItemName.banana_bird)] * number_of_banana_birds
itempool += [self.create_item(ItemName.krematoa_cog)] * number_of_cogs
itempool += [self.create_item(ItemName.progressive_boat)] * 3
total_junk_count = total_required_locations - len(itempool)
itempool += [self.create_item(ItemName.bear_coin)] * total_junk_count
self.active_level_list = level_list.copy()
if self.world.level_shuffle[self.player]:
self.world.random.shuffle(self.active_level_list)
connect_regions(self.world, self.player, self.active_level_list)
self.world.itempool += itempool
def generate_output(self, output_directory: str):
try:
world = self.world
player = self.player
rom = LocalRom(get_base_rom_path())
patch_rom(self.world, rom, self.player, self.active_level_list)
self.active_level_list.append(LocationName.rocket_rush_region)
outfilepname = f'_P{player}'
outfilepname += f"_{world.player_name[player].replace(' ', '_')}" \
if world.player_name[player] != 'Player%d' % player else ''
rompath = os.path.join(output_directory, f'AP_{world.seed_name}{outfilepname}.sfc')
rom.write_to_file(rompath)
Patch.create_patch_file(rompath, player=player, player_name=world.player_name[player], game=Patch.GAME_DKC3)
os.unlink(rompath)
self.rom_name = rom.name
except:
raise
finally:
self.rom_name_available_event.set() # make sure threading continues and errors are collected
def modify_multidata(self, multidata: dict):
import base64
# wait for self.rom_name to be available.
self.rom_name_available_event.wait()
rom_name = getattr(self, "rom_name", None)
# we skip in case of error, so that the original error in the output thread is the one that gets raised
if rom_name:
new_name = base64.b64encode(bytes(self.rom_name)).decode()
multidata["connect_names"][new_name] = multidata["connect_names"][self.world.player_name[self.player]]
if self.topology_present:
world_names = [
LocationName.lake_orangatanga_region,
LocationName.kremwood_forest_region,
LocationName.cotton_top_cove_region,
LocationName.mekanos_region,
LocationName.k3_region,
LocationName.razor_ridge_region,
LocationName.kaos_kore_region,
LocationName.krematoa_region,
]
er_hint_data = {}
for world_index in range(len(world_names)):
for level_index in range(5):
level_region = self.world.get_region(self.active_level_list[world_index * 5 + level_index], self.player)
for location in level_region.locations:
er_hint_data[location.address] = world_names[world_index]
multidata['er_hint_data'][self.player] = er_hint_data
def create_regions(self):
location_table = setup_locations(self.world, self.player)
create_regions(self.world, self.player, location_table)
def create_item(self, name: str, force_non_progression=False) -> Item:
data = item_table[name]
if force_non_progression:
classification = ItemClassification.filler
elif data.progression:
classification = ItemClassification.progression
else:
classification = ItemClassification.filler
created_item = DKC3Item(name, classification, data.code, self.player)
return created_item
def set_rules(self):
set_rules(self.world, self.player)

View File

@@ -0,0 +1,35 @@
# Donkey Kong Country 3
## Where is the settings page?
The [player settings page for this game](../player-settings) contains all the options you need to configure and export a config file.
## What does randomization do to this game?
Items which the player would normally acquire throughout the game have been moved around. Logic remains, so the game is
always able to be completed, but because of the item shuffle the player may need to access certain areas before they
would in the vanilla game.
## What is the goal of Donkey Kong Country 3 when randomized?
There are two goals which can be chosen:
- `Knautilus`: Collect Bonus Coins and Krematoa Cogs to reach K. Rool's submarine in Krematoa
- `Banana Bird Hunt`: Collect Banana Birds to free the Banana Bird Mother
## What items and locations get shuffled?
All Bonus Coins, DK Coins, and Banana Birds (if on a `Banana Bird Hunt` goal) are randomized. Additionally, level clears award a location check.
The Patch and two Skis for upgrading the boat are included. Bear Coins are provided if additional items are needed for the item pool.
Four of the Five Krematoa Cogs are randomized, but the final one is always in its vanilla location at the Flag of Rocket Rush in Krematoa
## Which items can be in another player's world?
Any shuffled item can be in other players' worlds.
## What does another world's item look like in Donkey Kong Country 3
Items pickups all retain their original appearance. You won't know if an item belongs to another player until you collect.
## When the player receives an item, what happens?
Currently, the items are silently added to the player's inventory, which can be seen when saving the game.

View File

@@ -0,0 +1,161 @@
# Donkey Kong Country 3 Randomizer Setup Guide
## Required Software
- [Archipelago](https://github.com/ArchipelagoMW/Archipelago/releases). Make sure to check the box for `SNI Client - Donkey Kong Country 3 Patch Setup`
- Hardware or software capable of loading and playing SNES ROM files
- An emulator capable of connecting to SNI such as:
- snes9x Multitroid
from: [snes9x Multitroid Download](https://drive.google.com/drive/folders/1_ej-pwWtCAHYXIrvs5Hro16A1s9Hi3Jz),
- BizHawk from: [BizHawk Website](http://tasvideos.org/BizHawk.html)
- RetroArch 1.10.3 or newer from: [RetroArch Website](https://retroarch.com?page=platforms). Or,
- An SD2SNES, FXPak Pro ([FXPak Pro Store Page](https://krikzz.com/store/home/54-fxpak-pro.html)), or other
compatible hardware
- Your legally obtained Donkey Kong Country 3 ROM file, probably named `Donkey Kong Country 3 - Dixie Kong's Double Trouble! (USA) (En,Fr).sfc`
## Installation Procedures
### Windows Setup
1. During the installation of Archipelago, you will have been asked to install the SNI Client. If you did not do this,
or you are on an older version, you may run the installer again to install the SNI Client.
2. During setup, you will be asked to locate your base ROM file. This is your Donkey Kong Country 3 ROM file.
3. If you are using an emulator, you should assign your Lua capable emulator as your default program for launching ROM
files.
1. Extract your emulator's folder to your Desktop, or somewhere you will remember.
2. Right-click on a ROM file and select **Open with...**
3. Check the box next to **Always use this app to open .sfc files**
4. Scroll to the bottom of the list and click the grey text **Look for another App on this PC**
5. Browse for your emulator's `.exe` file and click **Open**. This file should be located inside the folder you
extracted in step one.
## Create a Config (.yaml) File
### What is a config file and why do I need one?
See the guide on setting up a basic YAML at the Archipelago setup
guide: [Basic Multiworld Setup Guide](/tutorial/Archipelago/setup/en)
### Where do I get a config file?
The Player Settings page on the website allows you to configure your personal settings and export a config file from
them. Player settings page: [Donkey Kong Country 3 Player Settings Page](/games/Donkey%20Kong%20Country%203/player-settings)
### Verifying your config file
If you would like to validate your config file to make sure it works, you may do so on the YAML Validator page. YAML
validator page: [YAML Validation page](/mysterycheck)
## Generating a Single-Player Game
1. Navigate to the Player Settings page, configure your options, and click the "Generate Game" button.
- Player Settings page: [Donkey Kong Country 3 Player Settings Page](/games/Donkey%20Kong%20Country%203/player-settings)
2. You will be presented with a "Seed Info" page.
3. Click the "Create New Room" link.
4. You will be presented with a server page, from which you can download your patch file.
5. Double-click on your patch file, and the Donkey Kong Country 3 Client will launch automatically, create your ROM from the
patch file, and open your emulator for you.
6. Since this is a single-player game, you will no longer need the client, so feel free to close it.
## Joining a MultiWorld Game
### Obtain your patch file and create your ROM
When you join a multiworld game, you will be asked to provide your config file to whoever is hosting. Once that is done,
the host will provide you with either a link to download your patch file, or with a zip file containing everyone's patch
files. Your patch file should have a `.apsm` extension.
Put your patch file on your desktop or somewhere convenient, and double click it. This should automatically launch the
client, and will also create your ROM in the same place as your patch file.
### Connect to the client
#### With an emulator
When the client launched automatically, SNI should have also automatically launched in the background. If this is its
first time launching, you may be prompted to allow it to communicate through the Windows Firewall.
##### snes9x Multitroid
1. Load your ROM file if it hasn't already been loaded.
2. Click on the File menu and hover on **Lua Scripting**
3. Click on **New Lua Script Window...**
4. In the new window, click **Browse...**
5. Select the connector lua file included with your client
- Look in the Archipelago folder for `/SNI/lua/x64` or `/SNI/lua/x86` depending on if the
emulator is 64-bit or 32-bit.
6. If you see an error while loading the script that states `socket.dll missing` or similar, navigate to the folder of
the lua you are using in your file explorer and copy the `socket.dll` to the base folder of your snes9x install.
##### BizHawk
1. Ensure you have the BSNES core loaded. You may do this by clicking on the Tools menu in BizHawk and following these
menu options:
`Config --> Cores --> SNES --> BSNES`
Once you have changed the loaded core, you must restart BizHawk.
2. Load your ROM file if it hasn't already been loaded.
3. Click on the Tools menu and click on **Lua Console**
4. Click the button to open a new Lua script.
5. Select the `Connector.lua` file included with your client
- Look in the Archipelago folder for `/SNI/lua/x64` or `/SNI/lua/x86` depending on if the
emulator is 64-bit or 32-bit. Please note the most recent versions of BizHawk are 64-bit only.
##### RetroArch 1.10.3 or newer
You only have to do these steps once. Note, RetroArch 1.9.x will not work as it is older than 1.10.3.
1. Enter the RetroArch main menu screen.
2. Go to Settings --> User Interface. Set "Show Advanced Settings" to ON.
3. Go to Settings --> Network. Set "Network Commands" to ON. (It is found below Request Device 16.) Leave the default
Network Command Port at 55355.
![Screenshot of Network Commands setting](/static/generated/docs/A%20Link%20to%20the%20Past/retroarch-network-commands-en.png)
4. Go to Main Menu --> Online Updater --> Core Downloader. Scroll down and select "Nintendo - SNES / SFC (bsnes-mercury
Performance)".
When loading a ROM, be sure to select a **bsnes-mercury** core. These are the only cores that allow external tools to
read ROM data.
#### With hardware
This guide assumes you have downloaded the correct firmware for your device. If you have not done so already, please do
this now. SD2SNES and FXPak Pro users may download the appropriate firmware on the SD2SNES releases page. SD2SNES
releases page: [SD2SNES Releases Page](https://github.com/RedGuyyyy/sd2snes/releases)
Other hardware may find helpful information on the usb2snes platforms
page: [usb2snes Supported Platforms Page](http://usb2snes.com/#supported-platforms)
1. Close your emulator, which may have auto-launched.
2. Power on your device and load the ROM.
### Connect to the Archipelago Server
The patch file which launched your client should have automatically connected you to the AP Server. There are a few
reasons this may not happen however, including if the game is hosted on the website but was generated elsewhere. If the
client window shows "Server Status: Not Connected", simply ask the host for the address of the server, and copy/paste it
into the "Server" input field then press enter.
The client will attempt to reconnect to the new server address, and should momentarily show "Server Status: Connected".
### Play the game
When the client shows both SNES Device and Server as connected, you're ready to begin playing. Congratulations on
successfully joining a multiworld game!
## Hosting a MultiWorld game
The recommended way to host a game is to use our hosting service. The process is relatively simple:
1. Collect config files from your players.
2. Create a zip file containing your players' config files.
3. Upload that zip file to the Generate page above.
- Generate page: [WebHost Seed Generation Page](/generate)
4. Wait a moment while the seed is generated.
5. When the seed is generated, you will be redirected to a "Seed Info" page.
6. Click "Create New Room". This will take you to the server page. Provide the link to this page to your players, so
they may download their patch files from there.
7. Note that a link to a MultiWorld Tracker is at the top of the room page. The tracker shows the progress of all
players in the game. Any observers may also be given the link to this page.
8. Once all players have joined, you may begin playing.

View File

@@ -1 +1 @@
factorio-rcon-py==1.2.1
factorio-rcon-py>=2.0.1

View File

@@ -272,7 +272,7 @@ Super Mario 64:
StrictCapRequirements: true
StrictCannonRequirements: true
StarsToFinish: 70
ExtraStars: 30
AmountOfStars: 70
DeathLink: true
BuddyChecks: true
AreaRandomizer: true

View File

@@ -222,6 +222,7 @@ class MinimumCharmPrice(Range):
class MaximumCharmPrice(MinimumCharmPrice):
"""The maximum charm price in the range of prices that an item should cost for Salubra's shop item which also
carry a charm cost."""
display_name = "Maximum Charm Requirement"
default = 20
@@ -235,7 +236,7 @@ class MinimumGeoPrice(Range):
class MaximumGeoPrice(Range):
"""The maximum geo price for items in geo shops."""
display_name = "Minimum Geo Price"
display_name = "Maximum Geo Price"
range_start = 1
range_end = 2000
default = 400

View File

@@ -254,6 +254,9 @@ class HKWorld(World):
if location_name == "Start":
if item_name in randomized_starting_items:
if item_name == "Focus":
self.create_location("Focus")
unfilled_locations += 1
pool.append(item)
else:
self.world.push_precollected(item)
@@ -502,6 +505,7 @@ class HKWorld(World):
location.place_locked_item(item)
if costs:
location.costs = costs.pop()
return location
def collect(self, state, item: HKItem) -> bool:
change = super(HKWorld, self).collect(state, item)

View File

@@ -51,20 +51,20 @@ known_logic_tricks = {
Can be reached by side-hopping off
the watchtower.
'''},
'Dodongo\'s Cavern Staircase with Bow': {
"Dodongo's Cavern Staircase with Bow": {
'name' : 'logic_dc_staircase',
'tags' : ("Dodongo's Cavern",),
'tooltip' : '''\
The Bow can be used to knock down the stairs
with two well-timed shots.
'''},
'Dodongo\'s Cavern Spike Trap Room Jump without Hover Boots': {
"Dodongo's Cavern Spike Trap Room Jump without Hover Boots": {
'name' : 'logic_dc_jump',
'tags' : ("Dodongo's Cavern",),
'tooltip' : '''\
Jump is adult only.
'''},
'Dodongo\'s Cavern Vines GS from Below with Longshot': {
"Dodongo's Cavern Vines GS from Below with Longshot": {
'name' : 'logic_dc_vines_gs',
'tags' : ("Dodongo's Cavern", "Skulltulas",),
'tooltip' : '''\
@@ -73,7 +73,7 @@ known_logic_tricks = {
from below, by shooting it through the vines,
bypassing the need to lower the staircase.
'''},
'Thieves\' Hideout "Kitchen" with No Additional Items': {
'''Thieves' Hideout "Kitchen" with No Additional Items''': {
'name' : 'logic_gerudo_kitchen',
'tags' : ("Thieves' Hideout", "Gerudo's Fortress"),
'tooltip' : '''\
@@ -157,7 +157,7 @@ known_logic_tricks = {
Can jump up to the spinning platform from
below as adult.
'''},
'Crater\'s Bean PoH with Hover Boots': {
"Crater's Bean PoH with Hover Boots": {
'name' : 'logic_crater_bean_poh_with_hovers',
'tags' : ("Death Mountain Crater",),
'tooltip' : '''\
@@ -165,7 +165,7 @@ known_logic_tricks = {
near Goron City and walk up the
very steep slope.
'''},
'Zora\'s Domain Entry with Cucco': {
"Zora's Domain Entry with Cucco": {
'name' : 'logic_zora_with_cucco',
'tags' : ("Zora's River",),
'tooltip' : '''\
@@ -404,7 +404,7 @@ known_logic_tricks = {
Longshot can be shot through the ceiling to obtain
the token with two fewer small keys than normal.
'''},
'Zora\'s River Lower Freestanding PoH as Adult with Nothing': {
"Zora's River Lower Freestanding PoH as Adult with Nothing": {
'name' : 'logic_zora_river_lower',
'tags' : ("Zora's River",),
'tooltip' : '''\
@@ -502,7 +502,7 @@ known_logic_tricks = {
you can get enough of a break to take some time
to aim more carefully.
'''},
'Dodongo\'s Cavern Scarecrow GS with Armos Statue': {
"Dodongo's Cavern Scarecrow GS with Armos Statue": {
'name' : 'logic_dc_scarecrow_gs',
'tags' : ("Dodongo's Cavern", "Skulltulas",),
'tooltip' : '''\
@@ -541,7 +541,7 @@ known_logic_tricks = {
'name' : 'logic_spirit_mq_lower_adult',
'tags' : ("Spirit Temple",),
'tooltip' : '''\
It can be done with Din\'s Fire and Bow.
It can be done with Din's Fire and Bow.
Whenever an arrow passes through a lit torch, it
resets the timer. It's finicky but it's also
possible to stand on the pillar next to the center
@@ -704,13 +704,13 @@ known_logic_tricks = {
in the same jump in order to destroy it before you
fall into the lava.
'''},
'Zora\'s Domain Entry with Hover Boots': {
"Zora's Domain Entry with Hover Boots": {
'name' : 'logic_zora_with_hovers',
'tags' : ("Zora's River",),
'tooltip' : '''\
Can hover behind the waterfall as adult.
'''},
'Zora\'s Domain GS with No Additional Items': {
"Zora's Domain GS with No Additional Items": {
'name' : 'logic_domain_gs',
'tags' : ("Zora's Domain", "Skulltulas",),
'tooltip' : '''\
@@ -736,7 +736,7 @@ known_logic_tricks = {
needing a Bow.
Applies in both vanilla and MQ Shadow.
'''},
'Stop Link the Goron with Din\'s Fire': {
"Stop Link the Goron with Din's Fire": {
'name' : 'logic_link_goron_dins',
'tags' : ("Goron City",),
'tooltip' : '''\
@@ -825,7 +825,7 @@ known_logic_tricks = {
Link will not be expected to do anything at Gerudo's
Fortress.
'''},
'Zora\'s River Upper Freestanding PoH as Adult with Nothing': {
"Zora's River Upper Freestanding PoH as Adult with Nothing": {
'name' : 'logic_zora_river_upper',
'tags' : ("Zora's River",),
'tooltip' : '''\
@@ -971,7 +971,7 @@ known_logic_tricks = {
in the Water Temple are not going to be relevant unless this
trick is first enabled.
'''},
'Water Temple Central Pillar GS with Farore\'s Wind': {
"Water Temple Central Pillar GS with Farore's Wind": {
'name' : 'logic_water_central_gs_fw',
'tags' : ("Water Temple", "Skulltulas",),
'tooltip' : '''\
@@ -1104,7 +1104,7 @@ known_logic_tricks = {
this allows you to obtain the GS on the door frame
as adult without Hookshot or Song of Time.
'''},
'Dodongo\'s Cavern MQ Early Bomb Bag Area as Child': {
"Dodongo's Cavern MQ Early Bomb Bag Area as Child": {
'name' : 'logic_dc_mq_child_bombs',
'tags' : ("Dodongo's Cavern",),
'tooltip' : '''\
@@ -1113,7 +1113,7 @@ known_logic_tricks = {
without needing a Slingshot. You will
take fall damage.
'''},
'Dodongo\'s Cavern Two Scrub Room with Strength': {
"Dodongo's Cavern Two Scrub Room with Strength": {
'name' : 'logic_dc_scrub_room',
'tags' : ("Dodongo's Cavern",),
'tooltip' : '''\
@@ -1122,7 +1122,7 @@ known_logic_tricks = {
destroy the mud wall blocking the room with two
Deku Scrubs.
'''},
'Dodongo\'s Cavern Child Slingshot Skips': {
"Dodongo's Cavern Child Slingshot Skips": {
'name' : 'logic_dc_slingshot_skip',
'tags' : ("Dodongo's Cavern",),
'tooltip' : '''\
@@ -1132,7 +1132,7 @@ known_logic_tricks = {
you also enable the Adult variant: "Dodongo's
Cavern Spike Trap Room Jump without Hover Boots".
'''},
'Dodongo\'s Cavern MQ Light the Eyes with Strength': {
"Dodongo's Cavern MQ Light the Eyes with Strength": {
'name' : 'logic_dc_mq_eyes',
'tags' : ("Dodongo's Cavern",),
'tooltip' : '''\
@@ -1145,7 +1145,7 @@ known_logic_tricks = {
Also, the bombable floor before King Dodongo can be
destroyed with Hammer if hit in the very center.
'''},
'Dodongo\'s Cavern MQ Back Areas as Child without Explosives': {
"Dodongo's Cavern MQ Back Areas as Child without Explosives": {
'name' : 'logic_dc_mq_child_back',
'tags' : ("Dodongo's Cavern",),
'tooltip' : '''\
@@ -1232,7 +1232,7 @@ known_logic_tricks = {
It can also be done as child, using the
Slingshot instead of the Bow.
'''},
'Fire Temple East Tower without Scarecrow\'s Song': {
"Fire Temple East Tower without Scarecrow's Song": {
'name' : 'logic_fire_scarecrow',
'tags' : ("Fire Temple",),
'tooltip' : '''\
@@ -1277,14 +1277,14 @@ known_logic_tricks = {
Removes the requirements for the Lens of Truth
in Bottom of the Well.
'''},
'Ganon\'s Castle MQ without Lens of Truth': {
"Ganon's Castle MQ without Lens of Truth": {
'name' : 'logic_lens_castle_mq',
'tags' : ("Lens of Truth","Ganon's Castle",),
'tooltip' : '''\
Removes the requirements for the Lens of Truth
in Ganon's Castle MQ.
'''},
'Ganon\'s Castle without Lens of Truth': {
"Ganon's Castle without Lens of Truth": {
'name' : 'logic_lens_castle',
'tags' : ("Lens of Truth","Ganon's Castle",),
'tooltip' : '''\

View File

@@ -1,37 +1,34 @@
# Starcraft 2 Wings of Liberty
## Where is the settings page?
The [player settings page for this game](../player-settings) contains all the options you need to configure and export a
config file.
## What does randomization do to this game?
Items which the player would normally acquire throughout the game have been moved around. Logic remains, so the game is
always able to be completed. Options exist to also shuffle around the mission order of the campaign.
The following unlocks are randomized as items:
1. Your ability to build any non-worker unit (including Marines!).
2. Your ability to upgrade infantry weapons, infantry armor, vehicle weapons, etc.
3. All armory upgrades
4. All laboratory upgrades
5. All mercenaries
6. Small boosts to your starting mineral and vespene gas totals on each mission
## What is the goal of Starcraft 2 when randomized?
You find items by making progress in bonus objectives (like by rescuing allies in 'Zero Hour') and by completing
missions. When you receive items, they will immediately become available, even during a mission, and you will be
notified via a text box in the top-right corner of the game screen. (The text client for StarCraft 2 also records all
items in all worlds.)
The goal remains unchanged. Beat the final mission All In.
Missions are launched only through the text client. The Hyperion is never visited. Aditionally, credits are not used.
## What items and locations get shuffled?
## What is the goal of this game when randomized?
Unit unlocks, upgrade unlocks, armory upgrades, laboratory researches, and mercenary unlocks can be shuffled, and all
bonus objectives, side missions, mission completions are now locations that can contain these items.
The goal is to beat the final mission: 'All In'. The config file determines which variant you must complete.
## What has been changed from vanilla Starcraft 2?
## What non-randomized changes are there from vanilla Starcraft 2?
Some missions have been given more vespene gas available to mine to allow for a wider variety of unit compositions on
those missions. Starports no longer require Factories in order to be built. In 'A Sinister Turn' and 'Echoes
of the Future', you can research protoss air armor and weapon upgrades.
1. Some missions have more vespene geysers available to allow a wider variety of units.
2. Starports no longer require Factories in order to be built.
3. In 'A Sinister Turn' and 'Echoes of the Future', you can research Protoss air weapon/armor upgrades.
## Which items can be in another player's world?
Any of the items which can be shuffled may also be placed into another player's world. It is possible to choose to limit
certain items to your own world.
## When the player receives an item, what happens?
When the player receives an item, they will receive a message through their text client and in game if currently playing
a mission. They will immediately be able to use that unlock/upgrade.
## Which of my items can be in another player's world?
By default, any of StarCraft 2's items (specified above) can be in another player's world. See the
[Advanced YAML Guide](https://archipelago.gg/tutorial/Archipelago/advanced_settings/en)
for more information on how to change this.

View File

@@ -1,62 +1,69 @@
# Starcraft 2 Wings of Liberty Randomizer Setup Guide
# StarCraft 2 Wings of Liberty Randomizer Setup Guide
This guide contains instructions on how to install and troubleshoot the StarCraft 2 Archipelago client, as well as where
to obtain a config file for StarCraft 2.
## Required Software
- [Starcraft 2](https://starcraft2.com/en-us/)
- [Starcraft 2 AP Client](https://github.com/ArchipelagoMW/Archipelago)
- [Starcraft 2 AP Maps and Data](https://github.com/TheCondor07/Starcraft2ArchipelagoData)
- [StarCraft 2](https://starcraft2.com/en-us/)
- [The most recent Archipelago release](https://github.com/ArchipelagoMW/Archipelago/releases)
- [StarCraft 2 AP Maps and Data](https://github.com/TheCondor07/Starcraft2ArchipelagoData)
## General Concept
## How do I install this randomizer?
Starcraft 2 AP Client launches a custom version of Starcraft 2 running modified Wings of Liberty campaign maps
to allow for randomization of the items
1. Install StarCraft 2 and Archipelago using the first two links above. (The StarCraft 2 client for Archipelago is
included by default.)
2. Click the third link above and follow the instructions there.
3. Linux users should also follow the instructions found at the bottom of this page
(["Running in Linux"](#running-in-linux)).
## Installation Procedures
## Where do I get a config file (aka "YAML") for this game?
Follow the installation directions at the
[Starcraft 2 AP Maps and Data](https://github.com/TheCondor07/Starcraft2ArchipelagoData) page you can find the .zip
files on the releases page. After it is installed, just run ArchipelagoStarcraft2Client.exe to start the client and connect
to a Multiworld Game.
The [Player Settings](https://archipelago.gg/games/Starcraft%202%20Wings%20of%20Liberty/player-settings) page on this
website allows you to choose your personal settings for the randomizer and download them into a config file. Remember
the name you type in the `Player Name` box; that's the "slot name" the client will ask you for when you attempt to
connect!
## Joining a MultiWorld Game
### And why do I need a config file?
Config files tell Archipelago how you'd like your game to be randomized, even if you're only using default settings.
When you're setting up a multiworld, every world needs its own config file.
Check out [Creating a YAML](https://archipelago.gg/tutorial/Archipelago/setup/en#creating-a-yaml) for more information.
## How do I join a MultiWorld game?
1. Run ArchipelagoStarcraft2Client.exe.
2. Type in `/connect [server ip]`.
3. Insert slot name and password as prompted.
4. Once connected, use `/unfinished` to find what missions you can play and `/play [mission id]` to launch a mission.
For new games under default settings the first mission available will always be Liberation Day[1] playable using the
command `/play 1`.
2. Type `/connect [server ip]`.
3. Type your slot name and the server's password when prompted.
4. Once connected, switch to the 'StarCraft 2 Launcher' tab in the client. There, you can see every mission. By default,
only 'Liberation Day' will be available at the beginning. Just click on a mission to start it!
## Where do I get a config file?
## The game isn't launching when I try to start a mission.
The [Player Settings](/games/Starcraft%202%20Wings%20of%20Liberty/player-settings) page on the website allows you to
configure your personal settings and export them into a config file.
First, check the log file for issues (stored at `[Archipelago Directory]/logs/SC2Client.txt`). If the below fix doesn't
work for you, and you can't figure out the log file, visit our [Discord's](https://discord.com/invite/8Z65BR2)
tech-support channel for help. Please include a specific description of what's going wrong and attach your log file to
your message.
## Game isn't launching when I type /play
### Check your installation
First check the log file for issues (stored at [Archipelago Directory]/logs/SC2Client.txt. There is sometimes an issue
where the client can not find Starcraft 2. Usually 'Documents/StarCraft II/ExecuteInfo.txt' is checked to find where
Starcraft 2 is installed. On some computers particularly if you have OneDrive running this may fail. The following
directions may help you in this case if you are on Windows.
1. Navigate to '%userprofile%'. Easiest way to do this is to hit Windows key+R type in `%userprofile%` and hit run or
type in `%userprofile%` in the navigation bar of your file explorer.
2. If it does not exist create a folder in her named 'Documents'.
3. Locate your 'My Documents' folder on your PC. If you navigate to 'My PC' on the sidebar of file explorer should be a
link to this folder there labeled 'Documents'.
4. Find a folder labeled 'StarCraft II' and copy it.
5. Paste this 'StarCraft II' folder into the folder created or found in step 2.
These steps have been shown to work for some people for some people having issues with launching the game. If you are
still having issues check out our [Discord](https://discord.com/invite/8Z65BR2) for help.
Make sure you've followed the installation instructions completely. Specifically, make sure that you've placed the Maps
and Mods folders directly inside the StarCraft II installation folder. They should be in the same location as the
SC2Data, Support, Support64, and Versions folders.
## Running in Linux
To run StarCraft 2 through Archipelago in Linux, you will need to install the game using Wine then run the Linux build of the Archipelago client.
To run StarCraft 2 through Archipelago in Linux, you will need to install the game using Wine, then run the Linux build
of the Archipelago client.
Make sure you have StarCraft 2 installed using Wine and you have followed the [Installation Procedures](#installation-procedures) to add the Archipelago maps to the correct location. You will not need to copy the .dll files. If you're having trouble installing or running StarCraft 2 on Linux, I recommend using the Lutris installer.
Make sure you have StarCraft 2 installed using Wine, and that you have followed the
[installation procedures](#how-do-i-install-this-randomizer?) to add the Archipelago maps to the correct location. You will not
need to copy the .dll files. If you're having trouble installing or running StarCraft 2 on Linux, I recommend using the
Lutris installer.
Copy the following into a .sh file, replacing the values of **WINE** and **SC2PATH** variables to the relevant locations, as well as setting **PATH_TO_ARCHIPELAGO** to the directory containing the AppImage if it is not in the same folder as the script.
Copy the following into a .sh file, replacing the values of **WINE** and **SC2PATH** variables with the relevant
locations, as well as setting **PATH_TO_ARCHIPELAGO** to the directory containing the AppImage if it is not in the same
folder as the script.
```sh
# Let the client know we're running SC2 in Wine
@@ -81,8 +88,11 @@ ARCHIPELAGO="$(ls ${PATH_TO_ARCHIPELAGO:-$(dirname $0)}/Archipelago_*.AppImage |
$ARCHIPELAGO Starcraft2Client
```
For Lutris installs, you can run `lutris -l` to get the numerical ID of your StarCraft II install, then run the command below, replacing **${ID}** with the numerical ID.
For Lutris installs, you can run `lutris -l` to get the numerical ID of your StarCraft II install, then run the command
below, replacing **${ID}** with the numerical ID.
lutris lutris:rungameid/${ID} --output-script sc2.sh
This will get all of the relevant environment variables Lutris sets to run StarCraft 2 in a script, including the path to the Wine binary that Lutris uses. You can then remove the line that runs the Battle.Net launcher and copy the code above into the existing script.
This will get all of the relevant environment variables Lutris sets to run StarCraft 2 in a script, including the path
to the Wine binary that Lutris uses. You can then remove the line that runs the Battle.Net launcher and copy the code
above into the existing script.

View File

@@ -143,15 +143,15 @@ class BossRandomization(Toggle):
display_name = "Boss Randomization"
class FunCombat(Toggle):
"""Forces removal of Plasma Beam and Screw Attack if the preset and settings allow it. In addition, can randomly remove Spazer and Wave Beam from the Combat set. If used, might force 'items' accessibility."""
"""Forces removal of Plasma Beam and Screw Attack if the preset and settings allow it. In addition, can randomly remove Spazer and Wave Beam from the Combat set. If used, might force 'minimal' accessibility."""
display_name = "Fun Combat"
class FunMovement(Toggle):
"""Forces removal of Space Jump if the preset allows it. In addition, can randomly remove High Jump, Grappling Beam, Spring Ball, Speed Booster, and Bombs from the Movement set. If used, might force 'items' accessibility."""
"""Forces removal of Space Jump if the preset allows it. In addition, can randomly remove High Jump, Grappling Beam, Spring Ball, Speed Booster, and Bombs from the Movement set. If used, might force 'minimal' accessibility."""
display_name = "Fun Movement"
class FunSuits(Toggle):
"""If the preset and seed layout allow it, will force removal of at least one of Varia Suit and/or Gravity Suit. If used, might force 'items' accessibility."""
"""If the preset and seed layout allow it, will force removal of at least one of Varia Suit and/or Gravity Suit. If used, might force 'minimal' accessibility."""
display_name = "Fun Suits"
class LayoutPatches(DefaultOnToggle):
@@ -182,6 +182,10 @@ class SpinJumpRestart(Toggle):
"""Allows Samus to start spinning in mid air after jumping or falling."""
display_name = "Spin Jump Restart"
class SpeedKeep(Toggle):
"""Let Samus keeps her momentum when landing from a fall or from jumping."""
display_name = "Momentum conservation (a.k.a. Speedkeep)"
class InfiniteSpaceJump(Toggle):
"""Space jumps can be done quicker and at any time in air, water or lava, even after falling long distances."""
display_name = "Infinite Space Jump"
@@ -266,7 +270,7 @@ sm_options: typing.Dict[str, type(Option)] = {
#"item_sounds": "on",
"elevators_doors_speed": ElevatorsDoorsSpeed,
"spin_jump_restart": SpinJumpRestart,
#"rando_speed": "off",
"rando_speed": SpeedKeep,
"infinite_space_jump": InfiniteSpaceJump,
"refill_before_save": RefillBeforeSave,
"hud": Hud,

View File

@@ -1,11 +1,12 @@
import hashlib
import os
import json
import Utils
from Patch import read_rom, APDeltaPatch
SMJUHASH = '21f3e98df4780ee1c667b84e57d88675'
ROM_PLAYER_LIMIT = 65535
ROM_PLAYER_LIMIT = 65535 # max archipelago player ID. note, SM ROM itself will only store 201 names+ids max
class SMDeltaPatch(APDeltaPatch):
@@ -17,7 +18,6 @@ class SMDeltaPatch(APDeltaPatch):
def get_source_data(cls) -> bytes:
return get_base_rom_bytes()
def get_base_rom_bytes(file_name: str = "") -> bytes:
base_rom_bytes = getattr(get_base_rom_bytes, "base_rom_bytes", None)
if not base_rom_bytes:
@@ -40,3 +40,48 @@ def get_base_rom_path(file_name: str = "") -> str:
if not os.path.exists(file_name):
file_name = Utils.user_path(file_name)
return file_name
def get_sm_symbols(sym_json_path) -> dict:
with open(sym_json_path, "r") as stream:
symbols = json.load(stream)
symboltable = {}
for name, sixdigitaddr in symbols.items():
(bank, addr_within_bank) = sixdigitaddr.split(":")
bank = int(bank, 16)
addr_within_bank = int(addr_within_bank, 16)
# categorize addresses using snes lorom mapping:
# (reference: https://en.wikibooks.org/wiki/Super_NES_Programming/SNES_memory_map)
if (bank >= 0x70 and bank <= 0x7d):
offset_within_rom_file = None
# SRAM is not continuous, but callers may want it in continuous terms
# SRAM @ data bank $70-$7D, addr_within_bank $0000-$7FFF
#
# symbol aka snes offestwithincontinuousSRAM
# --------------- --------------------------
# $70:0000-7FFF -> 0x0000- 7FFF
# $71:0000-7FFF -> 0x8000- FFFF
# $72:0000-7FFF -> 0x10000-17FFF
# etc...
offset_within_continuous_sram = (bank - 0x70) * 0x8000 + addr_within_bank
offset_within_wram = None
elif bank == 0x7e or bank == 0x7f or (bank == 0x00 and addr_within_bank <= 0x1fff):
offset_within_rom_file = None
offset_within_continuous_sram = None
offset_within_wram = addr_within_bank
if bank == 0x7f:
offset_within_wram += 0x10000
elif bank >= 0x80:
offset_within_rom_file = ((bank - 0x80) * 0x8000) + (addr_within_bank % 0x8000)
offset_within_continuous_sram = None
offset_within_wram = None
else:
offset_within_rom_file = None
offset_within_continuous_sram = None
offset_within_wram = None
symboltable[name] = {"bank": bank,
"addr_within_bank": addr_within_bank,
"offset_within_rom_file": offset_within_rom_file,
"offset_within_continuous_sram": offset_within_continuous_sram,
"offset_within_wram": offset_within_wram
}
return symboltable

View File

@@ -16,7 +16,7 @@ from .Items import lookup_name_to_id as items_lookup_name_to_id
from .Regions import create_regions
from .Rules import set_rules, add_entrance_rule
from .Options import sm_options
from .Rom import get_base_rom_path, ROM_PLAYER_LIMIT, SMDeltaPatch
from .Rom import get_base_rom_path, ROM_PLAYER_LIMIT, SMDeltaPatch, get_sm_symbols
import Utils
from BaseClasses import Region, Entrance, Location, MultiWorld, Item, ItemClassification, RegionType, CollectionState, Tutorial
@@ -125,8 +125,8 @@ class SMWorld(World):
self.remote_items = self.world.remote_items[self.player]
if (len(self.variaRando.randoExec.setup.restrictedLocs) > 0):
self.world.accessibility[self.player] = self.world.accessibility[self.player].from_text("items")
logger.warning(f"accessibility forced to 'items' for player {self.world.get_player_name(self.player)} because of 'fun' settings")
self.world.accessibility[self.player] = self.world.accessibility[self.player].from_text("minimal")
logger.warning(f"accessibility forced to 'minimal' for player {self.world.get_player_name(self.player)} because of 'fun' settings")
def generate_basic(self):
itemPool = self.variaRando.container.itemPool
@@ -201,10 +201,7 @@ class SMWorld(World):
create_locations(self, self.player)
create_regions(self, self.world, self.player)
def getWord(self, w):
return (w & 0x00FF, (w & 0xFF00) >> 8)
def getWordArray(self, w):
def getWordArray(self, w): # little-endian convert a 16-bit number to an array of numbers <= 255 each
return [w & 0x00FF, (w & 0xFF00) >> 8]
# used for remote location Credits Spoiler of local items
@@ -269,109 +266,175 @@ class SMWorld(World):
itemName = "___" + itemName + "___"
for char in itemName:
(w0, w1) = self.getWord(charMap.get(char, 0x3C4E))
[w0, w1] = self.getWordArray(charMap.get(char, 0x3C4E))
data.append(w0)
data.append(w1)
return data
def APPatchRom(self, romPatcher):
multiWorldLocations = {}
multiWorldItems = {}
# first apply the sm multiworld code patch named 'basepatch' (also has empty tables that we'll overwrite),
# + apply some patches from varia that we want to be always-on.
# basepatch and variapatches are both generated from https://github.com/lordlou/SMBasepatch
romPatcher.applyIPSPatch(os.path.join(os.path.dirname(__file__),
"data", "SMBasepatch_prebuilt", "multiworld-basepatch.ips"))
romPatcher.applyIPSPatch(os.path.join(os.path.dirname(__file__),
"data", "SMBasepatch_prebuilt", "variapatches.ips"))
symbols = get_sm_symbols(os.path.join(os.path.dirname(__file__),
"data", "SMBasepatch_prebuilt", "sm-basepatch-symbols.json"))
multiWorldLocations = []
multiWorldItems = []
idx = 0
self.playerIDMap = {}
playerIDCount = 0 # 0 is for "Archipelago" server
playerIDCount = 0 # 0 is for "Archipelago" server; highest possible = 200 (201 entries)
vanillaItemTypesCount = 21
for itemLoc in self.world.get_locations():
romPlayerID = itemLoc.item.player if itemLoc.item.player <= ROM_PLAYER_LIMIT else 0
if itemLoc.player == self.player and locationsDict[itemLoc.name].Id != None:
# this SM world can find this item: write full item data to tables and assign player data for writing
romPlayerID = itemLoc.item.player if itemLoc.item.player <= ROM_PLAYER_LIMIT else 0
if itemLoc.item.type in ItemManager.Items:
itemId = ItemManager.Items[itemLoc.item.type].Id
else:
itemId = ItemManager.Items['ArchipelagoItem'].Id + idx
multiWorldItems[0x029EA3 + idx*64] = self.convertToROMItemName(itemLoc.item.name)
multiWorldItems.append({"sym": symbols["message_item_names"],
"offset": (vanillaItemTypesCount + idx)*64,
"values": self.convertToROMItemName(itemLoc.item.name)})
idx += 1
if (romPlayerID > 0 and romPlayerID not in self.playerIDMap.keys()):
playerIDCount += 1
self.playerIDMap[romPlayerID] = playerIDCount
(w0, w1) = self.getWord(0 if itemLoc.item.player == self.player else 1)
(w2, w3) = self.getWord(itemId)
(w4, w5) = self.getWord(romPlayerID)
(w6, w7) = self.getWord(0 if itemLoc.item.advancement else 1)
multiWorldLocations[0x1C6000 + locationsDict[itemLoc.name].Id*8] = [w0, w1, w2, w3, w4, w5, w6, w7]
[w0, w1] = self.getWordArray(0 if itemLoc.item.player == self.player else 1)
[w2, w3] = self.getWordArray(itemId)
[w4, w5] = self.getWordArray(romPlayerID)
[w6, w7] = self.getWordArray(0 if itemLoc.item.advancement else 1)
multiWorldLocations.append({"sym": symbols["rando_item_table"],
"offset": locationsDict[itemLoc.name].Id*8,
"values": [w0, w1, w2, w3, w4, w5, w6, w7]})
if itemLoc.item.player == self.player:
elif itemLoc.item.player == self.player:
# this SM world owns the item: so in case the sending player might not have anything placed in this
# world to receive from it, assign them space in playerIDMap so that the ROM can display their name
# (SM item name not needed, as SM item type id will be in the message they send to this world live)
romPlayerID = itemLoc.player if itemLoc.player <= ROM_PLAYER_LIMIT else 0
if (romPlayerID > 0 and romPlayerID not in self.playerIDMap.keys()):
playerIDCount += 1
self.playerIDMap[romPlayerID] = playerIDCount
itemSprites = ["off_world_prog_item.bin", "off_world_item.bin"]
itemSprites = [{"fileName": "off_world_prog_item.bin",
"paletteSymbolName": "prog_item_eight_palette_indices",
"dataSymbolName": "offworld_graphics_data_progression_item"},
{"fileName": "off_world_item.bin",
"paletteSymbolName": "nonprog_item_eight_palette_indices",
"dataSymbolName": "offworld_graphics_data_item"}]
idx = 0
offworldSprites = {}
for fileName in itemSprites:
with open(Utils.local_path("lib", "worlds", "sm", "data", "custom_sprite", fileName) if Utils.is_frozen() else Utils.local_path("worlds", "sm", "data", "custom_sprite", fileName), 'rb') as stream:
offworldSprites = []
for itemSprite in itemSprites:
with open(os.path.join(os.path.dirname(__file__), "data", "custom_sprite", itemSprite["fileName"]), 'rb') as stream:
buffer = bytearray(stream.read())
offworldSprites[0x027882 + 10*(21 + idx) + 2] = buffer[0:8]
offworldSprites[0x049100 + idx*256] = buffer[8:264]
offworldSprites.append({"sym": symbols[itemSprite["paletteSymbolName"]],
"offset": 0,
"values": buffer[0:8]})
offworldSprites.append({"sym": symbols[itemSprite["dataSymbolName"]],
"offset": 0,
"values": buffer[8:264]})
idx += 1
openTourianGreyDoors = {0x07C823 + 5: [0x0C], 0x07C831 + 5: [0x0C]}
deathLink = {0x277f04: [self.world.death_link[self.player].value]}
remoteItem = {0x277f06: self.getWordArray(0b001 + (0b010 if self.remote_items else 0b000))}
deathLink = [{"sym": symbols["config_deathlink"],
"offset": 0,
"values": [self.world.death_link[self.player].value]}]
remoteItem = [{"sym": symbols["config_remote_items"],
"offset": 0,
"values": self.getWordArray(0b001 + (0b010 if self.remote_items else 0b000))}]
ownPlayerId = [{"sym": symbols["config_player_id"],
"offset": 0,
"values": self.getWordArray(self.player)}]
playerNames = {}
playerNameIDMap = {}
playerNames[0x1C5000] = "Archipelago".upper().center(16).encode()
playerNameIDMap[0x1C5800] = self.getWordArray(0)
playerNames = []
playerNameIDMap = []
playerNames.append({"sym": symbols["rando_player_table"],
"offset": 0,
"values": "Archipelago".upper().center(16).encode()})
playerNameIDMap.append({"sym": symbols["rando_player_id_table"],
"offset": 0,
"values": self.getWordArray(0)})
for key,value in self.playerIDMap.items():
playerNames[0x1C5000 + value * 16] = self.world.player_name[key][:16].upper().center(16).encode()
playerNameIDMap[0x1C5800 + value * 2] = self.getWordArray(key)
playerNames.append({"sym": symbols["rando_player_table"],
"offset": value * 16,
"values": self.world.player_name[key][:16].upper().center(16).encode()})
playerNameIDMap.append({"sym": symbols["rando_player_id_table"],
"offset": value * 2,
"values": self.getWordArray(key)})
patchDict = { 'MultiWorldLocations': multiWorldLocations,
'MultiWorldItems': multiWorldItems,
'offworldSprites': offworldSprites,
'openTourianGreyDoors': openTourianGreyDoors,
'deathLink': deathLink,
'remoteItem': remoteItem,
'ownPlayerId': ownPlayerId,
'PlayerName': playerNames,
'PlayerNameIDMap': playerNameIDMap}
# convert an array of symbolic byte_edit dicts like {"sym": symobj, "offset": 0, "values": [1, 0]}
# to a single rom patch dict like {0x438c: [1, 0], 0xa4a5: [0, 0, 0]} which varia will understand and apply
def resolve_symbols_to_file_offset_based_dict(byte_edits_arr) -> dict:
this_patch_as_dict = {}
for byte_edit in byte_edits_arr:
offset_within_rom_file = byte_edit["sym"]["offset_within_rom_file"] + byte_edit["offset"]
this_patch_as_dict[offset_within_rom_file] = byte_edit["values"]
return this_patch_as_dict
for patchname, byte_edits_arr in patchDict.items():
patchDict[patchname] = resolve_symbols_to_file_offset_based_dict(byte_edits_arr)
romPatcher.applyIPSPatchDict(patchDict)
openTourianGreyDoors = {0x07C823 + 5: [0x0C], 0x07C831 + 5: [0x0C]}
romPatcher.applyIPSPatchDict({'openTourianGreyDoors': openTourianGreyDoors})
# set rom name
# 21 bytes
from Main import __version__
self.romName = bytearray(f'SM{__version__.replace(".", "")[0:3]}_{self.player}_{self.world.seed:11}\0', 'utf8')[:21]
self.romName = bytearray(f'SM{__version__.replace(".", "")[0:3]}_{self.player}_{self.world.seed:11}', 'utf8')[:21]
self.romName.extend([0] * (21 - len(self.romName)))
# clients should read from 0x7FC0, the location of the rom title in the SNES header.
# duplicative ROM name at 0x1C4F00 is still written here for now, since people with archipelago pre-0.3.0 client installed will still be depending on this location for connecting to SM
romPatcher.applyIPSPatch('ROMName', { 'ROMName': {0x1C4F00 : self.romName, 0x007FC0 : self.romName} })
startItemROMAddressBase = 0x2FD8B9
startItemROMAddressBase = symbols["start_item_data_major"]["offset_within_rom_file"]
# current, base value or bitmask, max, base value or bitmask
startItemROMDict = {'ETank': [0x8, 0x64, 0xA, 0x64],
'Missile': [0xC, 0x5, 0xE, 0x5],
'Super': [0x10, 0x5, 0x12, 0x5],
'PowerBomb': [0x14, 0x5, 0x16, 0x5],
'Reserve': [0x1A, 0x64, 0x18, 0x64],
'Morph': [0x2, 0x4, 0x0, 0x4],
'Bomb': [0x3, 0x10, 0x1, 0x10],
'SpringBall': [0x2, 0x2, 0x0, 0x2],
'HiJump': [0x3, 0x1, 0x1, 0x1],
'Varia': [0x2, 0x1, 0x0, 0x1],
'Gravity': [0x2, 0x20, 0x0, 0x20],
'SpeedBooster': [0x3, 0x20, 0x1, 0x20],
'SpaceJump': [0x3, 0x2, 0x1, 0x2],
'ScrewAttack': [0x2, 0x8, 0x0, 0x8],
'Charge': [0x7, 0x10, 0x5, 0x10],
'Ice': [0x6, 0x2, 0x4, 0x2],
'Wave': [0x6, 0x1, 0x4, 0x1],
'Spazer': [0x6, 0x4, 0x4, 0x4],
'Plasma': [0x6, 0x8, 0x4, 0x8],
'Grapple': [0x3, 0x40, 0x1, 0x40],
'XRayScope': [0x3, 0x80, 0x1, 0x80]
# array for each item:
# offset within ROM table "start_item_data_major" of this item"s info (starting status)
# item bitmask or amount per pickup (BVOB = base value or bitmask),
# offset within ROM table "start_item_data_major" of this item"s info (starting maximum/starting collected items)
# current BVOB max
# ------- ---- ---
startItemROMDict = {"ETank": [ 0x8, 0x64, 0xA],
"Missile": [ 0xC, 0x5, 0xE],
"Super": [0x10, 0x5, 0x12],
"PowerBomb": [0x14, 0x5, 0x16],
"Reserve": [0x1A, 0x64, 0x18],
"Morph": [ 0x2, 0x4, 0x0],
"Bomb": [ 0x3, 0x10, 0x1],
"SpringBall": [ 0x2, 0x2, 0x0],
"HiJump": [ 0x3, 0x1, 0x1],
"Varia": [ 0x2, 0x1, 0x0],
"Gravity": [ 0x2, 0x20, 0x0],
"SpeedBooster": [ 0x3, 0x20, 0x1],
"SpaceJump": [ 0x3, 0x2, 0x1],
"ScrewAttack": [ 0x2, 0x8, 0x0],
"Charge": [ 0x7, 0x10, 0x5],
"Ice": [ 0x6, 0x2, 0x4],
"Wave": [ 0x6, 0x1, 0x4],
"Spazer": [ 0x6, 0x4, 0x4],
"Plasma": [ 0x6, 0x8, 0x4],
"Grapple": [ 0x3, 0x40, 0x1],
"XRayScope": [ 0x3, 0x80, 0x1]
# BVOB = base value or bitmask
}
mergedData = {}
hasETank = False
@@ -379,48 +442,52 @@ class SMWorld(World):
hasPlasma = False
for startItem in self.startItems:
item = startItem.Type
if item == 'ETank': hasETank = True
if item == 'Spazer': hasSpazer = True
if item == 'Plasma': hasPlasma = True
if (item in ['ETank', 'Missile', 'Super', 'PowerBomb', 'Reserve']):
(currentValue, currentBase, maxValue, maxBase) = startItemROMDict[item]
if item == "ETank": hasETank = True
if item == "Spazer": hasSpazer = True
if item == "Plasma": hasPlasma = True
if (item in ["ETank", "Missile", "Super", "PowerBomb", "Reserve"]):
(currentValue, amountPerItem, maxValue) = startItemROMDict[item]
if (startItemROMAddressBase + currentValue) in mergedData:
mergedData[startItemROMAddressBase + currentValue] += currentBase
mergedData[startItemROMAddressBase + maxValue] += maxBase
mergedData[startItemROMAddressBase + currentValue] += amountPerItem
mergedData[startItemROMAddressBase + maxValue] += amountPerItem
else:
mergedData[startItemROMAddressBase + currentValue] = currentBase
mergedData[startItemROMAddressBase + maxValue] = maxBase
mergedData[startItemROMAddressBase + currentValue] = amountPerItem
mergedData[startItemROMAddressBase + maxValue] = amountPerItem
else:
(collected, currentBitmask, equipped, maxBitmask) = startItemROMDict[item]
(collected, bitmask, equipped) = startItemROMDict[item]
if (startItemROMAddressBase + collected) in mergedData:
mergedData[startItemROMAddressBase + collected] |= currentBitmask
mergedData[startItemROMAddressBase + equipped] |= maxBitmask
mergedData[startItemROMAddressBase + collected] |= bitmask
mergedData[startItemROMAddressBase + equipped] |= bitmask
else:
mergedData[startItemROMAddressBase + collected] = currentBitmask
mergedData[startItemROMAddressBase + equipped] = maxBitmask
mergedData[startItemROMAddressBase + collected] = bitmask
mergedData[startItemROMAddressBase + equipped] = bitmask
if hasETank:
# we are overwriting the starting energy, so add up the E from 99 (normal starting energy) rather than from 0
mergedData[startItemROMAddressBase + 0x8] += 99
mergedData[startItemROMAddressBase + 0xA] += 99
if hasSpazer and hasPlasma:
# de-equip spazer.
# otherwise, firing the unintended spazer+plasma combo would cause massive game glitches and crashes
mergedData[startItemROMAddressBase + 0x4] &= ~0x4
for key, value in mergedData.items():
if (key - startItemROMAddressBase > 7):
(w0, w1) = self.getWord(value)
[w0, w1] = self.getWordArray(value)
mergedData[key] = [w0, w1]
else:
mergedData[key] = [value]
startItemPatch = { 'startItemPatch': mergedData }
romPatcher.applyIPSPatch('startItemPatch', startItemPatch)
startItemPatch = { "startItemPatch": mergedData }
romPatcher.applyIPSPatch("startItemPatch", startItemPatch)
# commit all the changes we've made here to the ROM
romPatcher.commitIPS()
itemLocs = [ItemLocation(ItemManager.Items[itemLoc.item.type if itemLoc.item.type in ItemManager.Items else 'ArchipelagoItem'], locationsDict[itemLoc.name], True) for itemLoc in self.world.get_locations() if itemLoc.player == self.player]
romPatcher.writeItemsLocs(itemLocs)
romPatcher.writeItemsLocs(itemLocs)
itemLocs = [ItemLocation(ItemManager.Items[itemLoc.item.type], locationsDict[itemLoc.name] if itemLoc.name in locationsDict and itemLoc.player == self.player else self.DummyLocation(self.world.get_player_name(itemLoc.player) + " " + itemLoc.name), True) for itemLoc in self.world.get_locations() if itemLoc.item.player == self.player]
progItemLocs = [ItemLocation(ItemManager.Items[itemLoc.item.type], locationsDict[itemLoc.name] if itemLoc.name in locationsDict and itemLoc.player == self.player else self.DummyLocation(self.world.get_player_name(itemLoc.player) + " " + itemLoc.name), True) for itemLoc in self.world.get_locations() if itemLoc.item.player == self.player and itemLoc.item.advancement == True]

View File

@@ -0,0 +1,689 @@
; wla symbolic information file
; generated by asar
[labels]
B8:8026 :neg_1_1
85:B9B4 :neg_1_2
85:B9E6 :neg_1_3
B8:C81F :neg_1_4
B8:C831 :neg_1_5
B8:C843 :neg_1_6
B8:800C :pos_1_0
B8:81DE :pos_1_1
84:FA6B :pos_1_2
84:FA75 :pos_1_3
B8:C862 :pos_1_4
B8:C86F :pos_1_5
B8:C87C :pos_1_6
85:FF00 CLIPCHECK
85:9900 CLIPLEN
85:990F CLIPLEN_end
85:990C CLIPLEN_no_multi
85:FF1D CLIPSET
B8:80EF COLLECTTANK
85:FF45 MISCFX
84:8BF2 NORMAL
85:FF4E SETFX
85:FF30 SOUNDFX
84:F9E0 SOUNDFX_84
85:FF3C SPECIALFX
84:F896 ammo_loop_table
84:F874 archipelago_chozo_item_plm
84:F878 archipelago_hidden_item_plm
84:F870 archipelago_visible_item_plm
84:F892 c_item
CE:FF04 config_deathlink
CE:FF00 config_flags
CE:FF00 config_multiworld
CE:FF08 config_player_id
CE:FF06 config_remote_items
CE:FF02 config_sprite
84:F894 h_item
84:F8AD i_chozo_item
84:F8B4 i_hidden_item
84:FA5A i_hidden_item_setup
B8:885C i_item_setup_shared
B8:8878 i_item_setup_shared_all_items
B8:8883 i_item_setup_shared_alwaysloaded
84:FA79 i_live_pickup
B8:817F i_live_pickup_multiworld
B8:81C4 i_live_pickup_multiworld_end
B8:819B i_live_pickup_multiworld_local_item_or_offworld
B8:81B0 i_live_pickup_multiworld_own_item
B8:81BC i_live_pickup_multiworld_own_item1
84:FA1E i_load_custom_graphics
84:FA39 i_load_custom_graphics_all_items
84:FA49 i_load_custom_graphics_alwaysloaded
84:FA61 i_load_rando_item
84:FA78 i_load_rando_item_end
84:F9F1 i_start_draw_loop
84:FA0A i_start_draw_loop_all_items
84:F9EC i_start_draw_loop_hidden
84:FA1C i_start_draw_loop_non_ammo_item
84:F9E5 i_start_draw_loop_visible_or_chozo
84:F8A6 i_visible_item
84:FA53 i_visible_item_setup
85:BA8A message_PlaceholderBig
85:BA0A message_char_table
85:BABC message_hook_tilemap_calc
85:BADC message_hook_tilemap_calc_msgbox_mwrecv
85:BACE message_hook_tilemap_calc_msgbox_mwsend
85:824C message_hook_tilemap_calc_normal
85:BAC9 message_hook_tilemap_calc_vanilla
85:9963 message_item_names
85:B8A3 message_item_received
85:B9A3 message_item_received_end
85:B7A3 message_item_sent
85:B8A3 message_item_sent_end
85:BA95 message_multiworld_init_new_messagebox_if_needed
85:BAB1 message_multiworld_init_new_messagebox_if_needed_msgbox_mwrecv
85:BAB1 message_multiworld_init_new_messagebox_if_needed_msgbox_mwsend
85:BAA9 message_multiworld_init_new_messagebox_if_needed_vanilla
85:B9A3 message_write_placeholders
85:B9A5 message_write_placeholders_adjust
85:BA04 message_write_placeholders_end
85:B9CA message_write_placeholders_loop
85:B9DC message_write_placeholders_notfound
85:B9DF message_write_placeholders_value_ok
B8:8092 mw_display_item_sent
B8:80FF mw_handle_queue
B8:8178 mw_handle_queue_end
B8:8101 mw_handle_queue_loop
B8:8151 mw_handle_queue_new_remote_item
B8:816D mw_handle_queue_next
B8:8163 mw_handle_queue_perform_receive
B8:81C8 mw_hook_main_game
B8:8011 mw_init
B8:8044 mw_init_end
B8:8000 mw_init_memory
B8:8083 mw_load_sram
B8:80B0 mw_receive_item
B8:80E8 mw_receive_item_end
B8:8070 mw_save_sram
B8:8049 mw_write_message
84:F888 nonprog_item_eight_palette_indices
89:9200 offworld_graphics_data_item
89:9100 offworld_graphics_data_progression_item
84:F972 p_chozo_item
84:F9A0 p_chozo_item_end
84:F98D p_chozo_item_loop
84:F999 p_chozo_item_trigger
84:F8FB p_etank_hloop
84:F8BB p_etank_loop
84:F9A6 p_hidden_item
84:F9D8 p_hidden_item_end
84:F9BD p_hidden_item_loop
84:F9A8 p_hidden_item_loop2
84:F9D1 p_hidden_item_trigger
84:F90F p_missile_hloop
84:F8CB p_missile_loop
84:F937 p_pb_hloop
84:F8EB p_pb_loop
84:F923 p_super_hloop
84:F8DB p_super_loop
84:F94B p_visible_item
84:F96E p_visible_item_end
84:F95B p_visible_item_loop
84:F967 p_visible_item_trigger
B8:81DF patch_load_multiworld
84:FA7E perform_item_pickup
84:F886 plm_graphics_entry_offworld_item
84:F87C plm_graphics_entry_offworld_progression_item
84:FA90 plm_sequence_generic_item_0_bitmask
84:F87E prog_item_eight_palette_indices
B8:E000 rando_item_table
B8:DC90 rando_player_id_table
B8:DE22 rando_player_id_table_end
B8:D000 rando_player_table
B8:CF00 rando_seed_data
B8:8800 sm_item_graphics
B8:882E sm_item_plm_pickup_sequence_pointers
B8:C81C start_item
B8:C800 start_item_data_major
B8:C808 start_item_data_minor
B8:C818 start_item_data_reserve
B8:C856 update_graphic
84:F890 v_item
[source files]
0000 e25029c5 main.asm
0001 06780555 ../common/nofanfare.asm
0002 e76d1f83 ../common/multiworld.asm
0003 613d24e1 ../common/itemextras.asm
0004 d6616c0c ../common/items.asm
0005 440b54fe ../common/startitem.asm
[rom checksum]
09b134c5
[addr-to-line mapping]
ff:ffff 0000:00000001
85:ff00 0001:0000010b
85:ff03 0001:0000010c
85:ff06 0001:0000010d
85:ff08 0001:0000010e
85:ff0b 0001:0000010f
85:ff0f 0001:00000110
85:ff12 0001:00000111
85:ff16 0001:00000112
85:ff19 0001:00000113
85:ff1c 0001:00000114
85:ff1d 0001:00000117
85:ff20 0001:00000118
85:ff24 0001:00000119
85:ff28 0001:0000011a
85:ff2b 0001:0000011c
85:ff2f 0001:0000011d
85:ff30 0001:00000120
85:ff34 0001:00000121
85:ff37 0001:00000122
85:ff3b 0001:00000123
85:ff3c 0001:00000126
85:ff40 0001:00000127
85:ff44 0001:00000128
85:ff45 0001:0000012b
85:ff49 0001:0000012c
85:ff4d 0001:0000012d
85:ff4e 0001:00000131
85:ff51 0001:00000132
85:ff54 0001:00000134
85:ff57 0001:00000135
85:ff58 0001:00000136
85:8490 0001:0000013a
85:9900 0001:0000013e
85:9901 0001:0000013f
85:9905 0001:00000140
85:9907 0001:00000141
85:990a 0001:00000142
85:990c 0001:00000144
85:990f 0001:00000146
85:9910 0001:00000147
82:e126 0001:0000014a
82:e12a 0001:0000014b
85:8089 0001:0000014e
84:8bf2 0001:00000152
84:8bf6 0001:00000153
84:8bf7 0001:00000153
b8:8000 0002:00000019
b8:8002 0002:0000001a
b8:8006 0002:0000001b
b8:8008 0002:0000001c
b8:800c 0002:00000020
b8:800e 0002:00000021
b8:8010 0002:00000022
b8:8011 0002:00000025
b8:8012 0002:00000025
b8:8013 0002:00000025
b8:8014 0002:00000025
b8:8015 0000:00000013
b8:8017 0002:00000029
b8:801b 0002:0000002a
b8:801e 0002:0000002b
b8:8020 0002:0000002d
b8:8023 0002:0000002e
b8:8026 0002:00000031
b8:802a 0002:00000032
b8:802e 0002:00000033
b8:8032 0002:00000034
b8:8036 0002:00000035
b8:8037 0002:00000035
b8:8038 0002:00000036
b8:803b 0002:00000037
b8:803d 0002:00000039
b8:8040 0002:0000003a
b8:8044 0002:0000003d
b8:8045 0002:0000003d
b8:8046 0002:0000003d
b8:8047 0002:0000003d
b8:8048 0002:0000003e
b8:8049 0002:00000043
b8:804a 0002:00000043
b8:804b 0002:00000044
b8:804c 0002:00000044
b8:804d 0002:00000045
b8:8051 0002:00000046
b8:8054 0002:00000046
b8:8055 0002:00000047
b8:8056 0002:00000048
b8:805a 0002:00000049
b8:805b 0002:0000004a
b8:805f 0002:0000004b
b8:8060 0002:0000004c
b8:8064 0002:0000004e
b8:8068 0002:0000004f
b8:8069 0002:00000050
b8:806d 0002:00000051
b8:806e 0002:00000051
b8:806f 0002:00000052
b8:8070 0002:00000055
b8:8071 0002:00000055
b8:8072 0000:00000013
b8:8074 0002:00000057
b8:8078 0002:00000058
b8:807c 0002:00000059
b8:807d 0002:00000059
b8:807e 0002:0000005b
b8:807f 0002:0000005c
b8:8082 0002:0000005d
b8:8083 0002:00000060
b8:8084 0002:00000060
b8:8085 0000:00000013
b8:8087 0002:00000062
b8:808b 0002:00000063
b8:808f 0002:00000064
b8:8090 0002:00000064
b8:8091 0002:00000065
b8:8092 0002:0000006a
b8:8094 0002:0000006b
b8:8096 0002:0000006e
b8:8099 0002:0000006f
b8:809b 0002:00000070
b8:809e 0002:00000071
b8:80a0 0002:00000072
b8:80a3 0002:00000073
b8:80a7 0002:00000074
b8:80a9 0002:00000075
b8:80ab 0002:00000076
b8:80ad 0002:00000077
b8:80af 0002:00000078
b8:80b0 0002:0000007c
b8:80b1 0002:0000007c
b8:80b2 0002:0000007d
b8:80b5 0002:0000007e
b8:80b7 0002:0000007f
b8:80ba 0002:00000080
b8:80bc 0002:00000081
b8:80bd 0002:00000082
b8:80be 0002:00000084
b8:80c1 0002:00000085
b8:80c3 0002:00000086
b8:80c6 0002:00000087
b8:80c7 0002:00000088
b8:80ca 0002:00000089
b8:80cb 0002:00000089
b8:80cc 0002:0000008a
b8:80d0 0002:0000008b
b8:80d1 0002:0000008c
b8:80d4 0002:0000008d
b8:80d8 0002:0000008e
b8:80da 0002:00000090
b8:80dd 0002:00000091
b8:80df 0002:00000092
b8:80e2 0002:00000093
b8:80e4 0002:00000095
b8:80e8 0002:00000097
b8:80ea 0002:00000098
b8:80ec 0002:00000099
b8:80ed 0002:00000099
b8:80ee 0002:0000009a
b8:80ef 0002:000000a5
b8:80f0 0002:000000a6
b8:80f4 0002:000000a7
b8:80f5 0002:000000a8
b8:80f9 0002:000000a9
b8:80fa 0002:000000ab
b8:80fe 0002:000000ac
b8:80ff 0002:000000de
b8:8100 0002:000000de
b8:8101 0002:000000e1
b8:8105 0002:000000e2
b8:8109 0002:000000e3
b8:810b 0002:000000e5
b8:810d 0002:000000e5
b8:810e 0002:000000e8
b8:8112 0002:000000e9
b8:8114 0002:000000ea
b8:8118 0002:000000eb
b8:811a 0002:000000ec
b8:811e 0002:000000ed
b8:8121 0002:000000ee
b8:8123 0002:000000ef
b8:8125 0002:000000f0
b8:8129 0002:000000f1
b8:812b 0002:000000f2
b8:812d 0002:000000f3
b8:8130 0002:000000f4
b8:8133 0002:000000f5
b8:8135 0002:000000f6
b8:813d 0002:000000fa
b8:813e 0002:000000fb
b8:813f 0002:000000fc
b8:8143 0002:000000ff
b8:8147 0002:00000100
b8:814b 0002:00000101
b8:814d 0002:00000103
b8:814e 0002:00000104
b8:814f 0002:00000105
b8:8151 0002:0000010a
b8:8152 0002:0000010b
b8:8156 0002:0000010e
b8:815a 0002:0000010f
b8:815e 0002:00000110
b8:8162 0002:00000111
b8:8163 0002:00000115
b8:8165 0002:00000116
b8:8168 0002:00000117
b8:816a 0002:00000118
b8:816d 0002:0000011b
b8:8171 0002:0000011c
b8:8172 0002:0000011d
b8:8176 0002:0000011f
b8:8178 0002:00000122
b8:817a 0002:00000123
b8:817c 0002:00000124
b8:817d 0002:00000124
b8:817e 0002:00000125
b8:817f 0002:00000129
b8:8180 0002:00000129
b8:8181 0002:00000129
b8:8182 0002:0000012a
b8:8186 0002:0000012b
b8:8189 0002:0000012b
b8:818a 0002:0000012d
b8:818e 0002:0000012e
b8:818f 0002:0000012f
b8:8193 0002:00000130
b8:8196 0002:00000131
b8:8198 0002:00000133
b8:819b 0002:00000136
b8:819f 0002:00000137
b8:81a3 0002:00000138
b8:81a5 0002:0000013a
b8:81a9 0002:0000013b
b8:81aa 0002:0000013d
b8:81ae 0002:0000013e
b8:81b0 0002:00000141
b8:81b4 0002:00000142
b8:81b7 0002:00000143
b8:81b9 0002:00000144
b8:81bc 0002:00000147
b8:81bd 0002:00000148
b8:81be 0002:00000149
b8:81c2 0002:0000014a
b8:81c4 0002:0000014d
b8:81c5 0002:0000014d
b8:81c6 0002:0000014d
b8:81c7 0002:0000014e
b8:81c8 0002:00000152
b8:81cc 0002:00000153
b8:81d0 0002:00000154
b8:81d2 0002:00000155
b8:81d6 0002:00000156
b8:81d9 0002:00000157
b8:81db 0002:00000158
b8:81de 0002:0000015a
b8:81df 0002:0000015d
b8:81e3 0002:0000015e
b8:81e4 0002:0000015f
b8:81e7 0002:00000160
b8:81eb 0002:00000162
b8:81ec 0002:00000163
b8:81ed 0002:00000164
b8:81ee 0002:00000165
b8:81ef 0002:00000166
8b:914a 0002:0000016b
81:80f7 0002:0000016e
81:8027 0002:00000171
82:8bb3 0002:00000174
85:b9a3 0002:0000020e
85:b9a4 0002:0000020e
85:b9a5 0002:00000211
85:b9a7 0002:00000212
85:b9ad 0002:00000212
85:b9ae 0002:00000213
85:b9b1 0002:00000214
85:b9b2 0002:00000215
85:b9b3 0002:00000215
85:b9b4 0002:00000219
85:b9b7 0002:0000021a
85:b9bb 0002:0000021b
85:b9bd 0002:0000021b
85:b9bf 0002:0000021c
85:b9c2 0002:0000021d
85:b9c4 0002:0000021f
85:b9c5 0002:00000220
85:b9c7 0002:00000224
85:b9ca 0002:00000226
85:b9cd 0002:00000227
85:b9cf 0002:00000228
85:b9d1 0002:00000229
85:b9d5 0002:0000022a
85:b9d7 0002:0000022b
85:b9d9 0002:0000022c
85:b9da 0002:0000022d
85:b9dc 0002:0000022f
85:b9df 0002:00000231
85:b9e2 0002:00000231
85:b9e3 0002:00000232
85:b9e6 0002:00000234
85:b9ea 0002:00000235
85:b9ed 0002:00000236
85:b9ee 0002:00000237
85:b9ef 0002:00000237
85:b9f0 0002:00000238
85:b9f4 0002:00000239
85:b9f5 0002:0000023a
85:b9f9 0002:0000023b
85:b9fb 0002:0000023c
85:b9fc 0002:0000023d
85:b9fd 0002:0000023e
85:ba00 0002:0000023f
85:ba02 0002:00000240
85:ba04 0002:00000243
85:ba05 0002:00000243
85:ba06 0002:00000244
85:ba09 0002:00000245
85:ba8a 0002:00000253
85:ba8c 0002:00000254
85:ba8f 0002:00000255
85:ba92 0002:00000256
85:ba95 0002:0000025e
85:ba96 0002:0000025f
85:ba98 0002:00000260
85:ba9b 0002:00000261
85:ba9d 0002:00000262
85:ba9f 0002:00000263
85:baa2 0002:00000264
85:baa4 0002:00000265
85:baa7 0002:00000266
85:baa9 0002:00000269
85:baaa 0002:0000026a
85:baab 0002:0000026b
85:baac 0002:0000026c
85:baae 0002:0000026d
85:baaf 0002:0000026e
85:bab0 0002:0000026f
85:bab1 0002:00000274
85:bab4 0002:00000275
85:bab5 0002:00000276
85:bab8 0002:00000277
85:bab9 0002:00000278
85:baba 0002:00000279
85:babb 0002:0000027a
85:babc 0002:00000285
85:babd 0002:00000286
85:babf 0002:00000287
85:bac2 0002:00000288
85:bac4 0002:00000289
85:bac7 0002:0000028a
85:bac9 0002:0000028d
85:baca 0002:0000028e
85:bacb 0002:0000028f
85:bacd 0002:00000290
85:bace 0002:00000292
85:bacf 0002:00000293
85:bad1 0002:00000294
85:bad4 0002:00000295
85:bad6 0002:00000296
85:bad9 0002:00000297
85:badb 0002:00000298
85:badc 0002:0000029a
85:badd 0002:0000029b
85:badf 0002:0000029c
85:bae2 0002:0000029d
85:bae4 0002:0000029e
85:bae7 0002:0000029f
85:bae9 0002:000002a0
85:8246 0002:000002a5
85:8249 0002:000002a6
85:824b 0002:000002a7
85:82f9 0002:000002ab
b8:885c 0003:00000045
b8:885d 0003:00000045
b8:885e 0003:00000046
b8:885f 0003:00000047
b8:8863 0003:00000048
b8:8866 0003:00000049
b8:8867 0003:0000004a
b8:886b 0003:0000004b
b8:886e 0003:0000004c
b8:8870 0003:0000004e
b8:8873 0003:0000004f
b8:8874 0003:0000004f
b8:8878 0003:00000051
b8:8879 0003:00000052
b8:887a 0003:00000053
b8:887e 0003:00000054
b8:8880 0003:00000056
b8:8881 0003:00000056
b8:8882 0003:00000057
b8:8883 0003:0000005a
b8:8884 0003:0000005a
b8:8885 0003:0000005b
b8:8886 0003:0000005c
b8:888a 0003:0000005d
84:f8a6 0004:00000051
84:f8a9 0004:00000052
84:f8ac 0004:00000053
84:f8ad 0004:00000056
84:f8b0 0004:00000057
84:f8b3 0004:00000058
84:f8b4 0004:0000005b
84:f8b7 0004:0000005c
84:f8ba 0004:0000005d
84:f9e0 0004:000000d4
84:f9e4 0004:000000d5
84:f9e5 0004:000000d8
84:f9e8 0004:000000d9
84:f9ea 0004:000000da
84:f9ec 0004:000000dd
84:f9ef 0004:000000de
84:f9f1 0004:000000e5
84:f9f2 0004:000000e6
84:f9f5 0004:000000e7
84:f9f8 0004:000000e7
84:f9f9 0004:000000e8
84:f9fd 0004:000000e9
84:fa00 0004:000000ea
84:fa02 0004:000000ec
84:fa05 0004:000000ed
84:fa06 0004:000000ee
84:fa0a 0004:000000f1
84:fa0d 0004:000000f2
84:fa0f 0004:000000f4
84:fa11 0004:000000f5
84:fa12 0004:000000f6
84:fa14 0004:000000f7
84:fa15 0004:000000f8
84:fa19 0004:000000f9
84:fa1a 0004:000000fa
84:fa1b 0004:000000fb
84:fa1c 0004:000000fe
84:fa1d 0004:000000ff
84:fa1e 0004:00000103
84:fa1f 0004:00000103
84:fa20 0004:00000103
84:fa21 0004:00000104
84:fa24 0004:00000105
84:fa27 0004:00000106
84:fa28 0004:00000107
84:fa2c 0004:00000108
84:fa2f 0004:00000109
84:fa31 0004:0000010b
84:fa34 0004:0000010c
84:fa35 0004:0000010c
84:fa39 0004:0000010e
84:fa3a 0004:00000110
84:fa3b 0004:00000111
84:fa3c 0004:00000112
84:fa40 0004:00000113
84:fa42 0004:00000114
84:fa43 0004:00000115
84:fa44 0004:00000116
84:fa47 0004:00000117
84:fa48 0004:00000118
84:fa49 0004:0000011b
84:fa4a 0004:0000011c
84:fa4c 0004:0000011d
84:fa4d 0004:0000011e
84:fa51 0004:0000011f
84:fa52 0004:00000120
84:fa53 0004:00000123
84:fa57 0004:00000124
84:fa5a 0004:00000127
84:fa5e 0004:00000128
84:fa61 0004:0000012c
84:fa64 0004:0000012c
84:fa66 0004:0000012d
84:fa69 0004:0000012e
84:fa6b 0004:0000012f
84:fa6e 0004:0000012f
84:fa70 0004:00000130
84:fa73 0004:00000131
84:fa75 0004:00000132
84:fa78 0004:00000135
84:fa79 0004:00000139
84:fa7d 0004:0000013a
84:fa7e 0004:0000013f
84:fa7f 0004:00000140
84:fa80 0004:00000141
84:fa81 0004:00000141
84:fa82 0004:00000145
84:fa86 0004:00000146
84:fa87 0004:00000147
84:fa88 0004:00000148
84:fa89 0004:00000148
84:fa8a 0004:00000149
84:fa8d 0004:0000014a
84:fa8e 0004:0000014b
84:fa8f 0004:0000014c
81:b303 0005:00000003
81:b307 0005:00000004
81:b308 0005:00000005
b8:c81c 0005:00000016
b8:c81f 0005:00000018
b8:c823 0005:00000019
b8:c827 0005:0000001a
b8:c828 0005:0000001b
b8:c829 0005:0000001c
b8:c82c 0005:0000001d
b8:c82e 0005:0000001e
b8:c831 0005:00000020
b8:c835 0005:00000021
b8:c839 0005:00000022
b8:c83a 0005:00000023
b8:c83b 0005:00000024
b8:c83e 0005:00000025
b8:c840 0005:00000026
b8:c843 0005:00000028
b8:c847 0005:00000029
b8:c84b 0005:0000002a
b8:c84c 0005:0000002b
b8:c84d 0005:0000002c
b8:c850 0005:0000002d
b8:c852 0005:0000002e
b8:c855 0005:00000031
b8:c856 0005:00000034
b8:c859 0005:00000035
b8:c85b 0005:00000036
b8:c85e 0005:00000037
b8:c862 0005:00000039
b8:c866 0005:0000003a
b8:c869 0005:0000003b
b8:c86b 0005:0000003c
b8:c86f 0005:0000003e
b8:c873 0005:0000003f
b8:c876 0005:00000040
b8:c878 0005:00000041
b8:c87c 0005:00000043
b8:c880 0005:00000044

View File

@@ -0,0 +1,141 @@
{
"CLIPCHECK": "85:FF00",
"CLIPLEN": "85:9900",
"CLIPLEN_end": "85:990F",
"CLIPLEN_no_multi": "85:990C",
"CLIPSET": "85:FF1D",
"COLLECTTANK": "B8:80EF",
"MISCFX": "85:FF45",
"NORMAL": "84:8BF2",
"SETFX": "85:FF4E",
"SOUNDFX": "85:FF30",
"SOUNDFX_84": "84:F9E0",
"SPECIALFX": "85:FF3C",
"ammo_loop_table": "84:F896",
"archipelago_chozo_item_plm": "84:F874",
"archipelago_hidden_item_plm": "84:F878",
"archipelago_visible_item_plm": "84:F870",
"c_item": "84:F892",
"config_deathlink": "CE:FF04",
"config_flags": "CE:FF00",
"config_multiworld": "CE:FF00",
"config_player_id": "CE:FF08",
"config_remote_items": "CE:FF06",
"config_sprite": "CE:FF02",
"h_item": "84:F894",
"i_chozo_item": "84:F8AD",
"i_hidden_item": "84:F8B4",
"i_hidden_item_setup": "84:FA5A",
"i_item_setup_shared": "B8:885C",
"i_item_setup_shared_all_items": "B8:8878",
"i_item_setup_shared_alwaysloaded": "B8:8883",
"i_live_pickup": "84:FA79",
"i_live_pickup_multiworld": "B8:817F",
"i_live_pickup_multiworld_end": "B8:81C4",
"i_live_pickup_multiworld_local_item_or_offworld": "B8:819B",
"i_live_pickup_multiworld_own_item": "B8:81B0",
"i_live_pickup_multiworld_own_item1": "B8:81BC",
"i_load_custom_graphics": "84:FA1E",
"i_load_custom_graphics_all_items": "84:FA39",
"i_load_custom_graphics_alwaysloaded": "84:FA49",
"i_load_rando_item": "84:FA61",
"i_load_rando_item_end": "84:FA78",
"i_start_draw_loop": "84:F9F1",
"i_start_draw_loop_all_items": "84:FA0A",
"i_start_draw_loop_hidden": "84:F9EC",
"i_start_draw_loop_non_ammo_item": "84:FA1C",
"i_start_draw_loop_visible_or_chozo": "84:F9E5",
"i_visible_item": "84:F8A6",
"i_visible_item_setup": "84:FA53",
"message_PlaceholderBig": "85:BA8A",
"message_char_table": "85:BA0A",
"message_hook_tilemap_calc": "85:BABC",
"message_hook_tilemap_calc_msgbox_mwrecv": "85:BADC",
"message_hook_tilemap_calc_msgbox_mwsend": "85:BACE",
"message_hook_tilemap_calc_normal": "85:824C",
"message_hook_tilemap_calc_vanilla": "85:BAC9",
"message_item_names": "85:9963",
"message_item_received": "85:B8A3",
"message_item_received_end": "85:B9A3",
"message_item_sent": "85:B7A3",
"message_item_sent_end": "85:B8A3",
"message_multiworld_init_new_messagebox_if_needed": "85:BA95",
"message_multiworld_init_new_messagebox_if_needed_msgbox_mwrecv": "85:BAB1",
"message_multiworld_init_new_messagebox_if_needed_msgbox_mwsend": "85:BAB1",
"message_multiworld_init_new_messagebox_if_needed_vanilla": "85:BAA9",
"message_write_placeholders": "85:B9A3",
"message_write_placeholders_adjust": "85:B9A5",
"message_write_placeholders_end": "85:BA04",
"message_write_placeholders_loop": "85:B9CA",
"message_write_placeholders_notfound": "85:B9DC",
"message_write_placeholders_value_ok": "85:B9DF",
"mw_display_item_sent": "B8:8092",
"mw_handle_queue": "B8:80FF",
"mw_handle_queue_end": "B8:8178",
"mw_handle_queue_loop": "B8:8101",
"mw_handle_queue_new_remote_item": "B8:8151",
"mw_handle_queue_next": "B8:816D",
"mw_handle_queue_perform_receive": "B8:8163",
"mw_hook_main_game": "B8:81C8",
"mw_init": "B8:8011",
"mw_init_end": "B8:8044",
"mw_init_memory": "B8:8000",
"mw_load_sram": "B8:8083",
"mw_receive_item": "B8:80B0",
"mw_receive_item_end": "B8:80E8",
"mw_save_sram": "B8:8070",
"mw_write_message": "B8:8049",
"nonprog_item_eight_palette_indices": "84:F888",
"offworld_graphics_data_item": "89:9200",
"offworld_graphics_data_progression_item": "89:9100",
"p_chozo_item": "84:F972",
"p_chozo_item_end": "84:F9A0",
"p_chozo_item_loop": "84:F98D",
"p_chozo_item_trigger": "84:F999",
"p_etank_hloop": "84:F8FB",
"p_etank_loop": "84:F8BB",
"p_hidden_item": "84:F9A6",
"p_hidden_item_end": "84:F9D8",
"p_hidden_item_loop": "84:F9BD",
"p_hidden_item_loop2": "84:F9A8",
"p_hidden_item_trigger": "84:F9D1",
"p_missile_hloop": "84:F90F",
"p_missile_loop": "84:F8CB",
"p_pb_hloop": "84:F937",
"p_pb_loop": "84:F8EB",
"p_super_hloop": "84:F923",
"p_super_loop": "84:F8DB",
"p_visible_item": "84:F94B",
"p_visible_item_end": "84:F96E",
"p_visible_item_loop": "84:F95B",
"p_visible_item_trigger": "84:F967",
"patch_load_multiworld": "B8:81DF",
"perform_item_pickup": "84:FA7E",
"plm_graphics_entry_offworld_item": "84:F886",
"plm_graphics_entry_offworld_progression_item": "84:F87C",
"plm_sequence_generic_item_0_bitmask": "84:FA90",
"prog_item_eight_palette_indices": "84:F87E",
"rando_item_table": "B8:E000",
"rando_player_id_table": "B8:DC90",
"rando_player_id_table_end": "B8:DE22",
"rando_player_table": "B8:D000",
"rando_seed_data": "B8:CF00",
"sm_item_graphics": "B8:8800",
"sm_item_plm_pickup_sequence_pointers": "B8:882E",
"start_item": "B8:C81C",
"start_item_data_major": "B8:C800",
"start_item_data_minor": "B8:C808",
"start_item_data_reserve": "B8:C818",
"update_graphic": "B8:C856",
"v_item": "84:F890",
"ITEM_RAM": "7E:09A2",
"SRAM_MW_ITEMS_RECV": "70:2000",
"SRAM_MW_ITEMS_RECV_RPTR": "70:2600",
"SRAM_MW_ITEMS_RECV_WPTR": "70:2602",
"SRAM_MW_ITEMS_RECV_SPTR": "70:2604",
"SRAM_MW_ITEMS_SENT_RPTR": "70:2680",
"SRAM_MW_ITEMS_SENT_WPTR": "70:2682",
"SRAM_MW_ITEMS_SENT": "70:2700",
"SRAM_MW_INITIALIZED": "70:26fe",
"CollectedItems": "7E:D86E"
}

View File

@@ -0,0 +1,3 @@
SMBasepatch_prebuilt:
- comes exactly from build/vanilla/ directory of https://github.com/lordlou/SMBasepatch
- keep it in sync with the basepatch repo; do not modify the contents in this repo alone!

View File

@@ -24,8 +24,6 @@ class RomPatcher:
'Removes_Gravity_Suit_heat_protection',
# door ASM to skip G4 cutscene when all 4 bosses are dead
'g4_skip.ips',
# basepatch is generated from https://github.com/lordlou/SMBasepatch
'basepatch.ips'
],
# VARIA tweaks
'VariaTweaks' : ['WS_Etank', 'LN_Chozo_SpaceJump_Check_Disable', 'ln_chozo_platform.ips', 'bomb_torizo.ips'],

View File

@@ -182,26 +182,50 @@ loc100Coin_table = {
"RR: 100 Coins": 3626104
}
locPSS_table = {
"The Princess's Secret Slide Block": 3626126,
"The Princess's Secret Slide Fast": 3626127,
}
locSA_table = {
"The Secret Aquarium": 3626161
}
locBitDW_table = {
"Bowser in the Dark World Red Coins": 3626105,
"Bowser in the Dark World Key": 3626178
}
locTotWC_table = {
"Tower of the Wing Cap Switch": 3626181,
"Tower of the Wing Cap Red Coins": 3626140
}
locCotMC_table = {
"Cavern of the Metal Cap Switch": 3626182,
"Cavern of the Metal Cap Red Coins": 3626133
}
locVCutM_table = {
"Vanish Cap Under the Moat Switch": 3626183,
"Vanish Cap Under the Moat Red Coins": 3626147
}
locBitFS_table = {
"Bowser in the Fire Sea Red Coins": 3626112,
"Bowser in the Fire Sea Key": 3626179
}
#Secret Stars and Stages
locWMotR_table = {
"Wing Mario Over the Rainbow": 3626154
}
locBitS_table = {
"Bowser in the Sky Red Coins": 3626119
}
#Secret Stars found inside the Castle
locSS_table = {
"Bowser in the Sky Red Coins": 3626119,
"The Princess's Secret Slide Block": 3626126,
"The Princess's Secret Slide Fast": 3626127,
"Cavern of the Metal Cap Red Coins": 3626133,
"Tower of the Wing Cap Red Coins": 3626140,
"Vanish Cap Under the Moat Red Coins": 3626147,
"Wing Mario Over the Rainbow": 3626154,
"The Secret Aquarium": 3626161,
"Toad (Basement)": 3626168,
"Toad (Second Floor)": 3626169,
"Toad (Third Floor)": 3626170,
@@ -209,15 +233,10 @@ locSS_table = {
"MIPS 2": 3626172
}
#Caps
locCap_table = {
"Tower of the Wing Cap Switch": 3626181,
"Cavern of the Metal Cap Switch": 3626182,
"Vanish Cap Under the Moat Switch": 3626183
}
# Correspond to 3626000 + course index * 7 + star index, then secret stars, then keys, then 100 Coin Stars
location_table = {**locBoB_table,**locWhomp_table,**locJRB_table,**locCCM_table,**locBBH_table, \
**locHMC_table,**locLLL_table,**locSSL_table,**locDDD_table,**locSL_table, \
**locWDW_table,**locTTM_table,**locTHI_table,**locTTC_table,**locRR_table, \
**loc100Coin_table,**locBitDW_table,**locBitFS_table,**locSS_table,**locCap_table}
**loc100Coin_table,**locPSS_table,**locSA_table,**locBitDW_table,**locTotWC_table, \
**locCotMC_table, **locVCutM_table, **locBitFS_table, **locWMotR_table, **locBitS_table, \
**locSS_table}

View File

@@ -1,5 +1,5 @@
import typing
from Options import Option, DefaultOnToggle, Range, Toggle, DeathLink
from Options import Option, DefaultOnToggle, Range, Toggle, DeathLink, Choice
class EnableCoinStars(DefaultOnToggle):
"""Disable to Ignore 100 Coin Stars. You can still collect them, but they don't do anything"""
@@ -16,19 +16,31 @@ class StrictCannonRequirements(DefaultOnToggle):
class FirstBowserStarDoorCost(Range):
"""How many stars are required at the Star Door to Bowser in the Dark World"""
range_start = 0
range_end = 20
range_end = 50
default = 8
class BasementStarDoorCost(Range):
"""How many stars are required at the Star Door in the Basement"""
range_start = 0
range_end = 50
range_end = 70
default = 30
class SecondFloorStarDoorCost(Range):
"""How many stars are required to access the third floor"""
range_start = 0
range_end = 50
range_end = 90
default = 50
class MIPS1Cost(Range):
"""How many stars are required to spawn MIPS the first time"""
range_start = 0
range_end = 40
default = 15
class MIPS2Cost(Range):
"""How many stars are required to spawn MIPS the secound time. Must be bigger or equal MIPS1Cost"""
range_start = 0
range_end = 80
default = 50
class StarsToFinish(Range):
@@ -38,15 +50,19 @@ class StarsToFinish(Range):
range_end = 100
default = 70
class ExtraStars(Range):
"""How many stars exist beyond those set for StarsToFinish"""
range_start = 0
range_end = 50
default = 50
class AmountOfStars(Range):
"""How many stars exist. Disabling 100 Coin Stars removes 15 from the Pool. At least max of any Cost set"""
range_start = 35
range_end = 120
default = 120
class AreaRandomizer(Toggle):
"""Randomize Entrances to Courses"""
display_name = "Course Randomizer"
class AreaRandomizer(Choice):
"""Randomize Entrances"""
display_name = "Entrance Randomizer"
alias_false = 0
option_Off = 0
option_Courses_Only = 1
option_Courses_and_Secrets = 2
class BuddyChecks(Toggle):
"""Bob-omb Buddies are checks, Cannon Unlocks are items"""
@@ -60,13 +76,15 @@ sm64_options: typing.Dict[str,type(Option)] = {
"AreaRandomizer": AreaRandomizer,
"ProgressiveKeys": ProgressiveKeys,
"EnableCoinStars": EnableCoinStars,
"AmountOfStars": AmountOfStars,
"StrictCapRequirements": StrictCapRequirements,
"StrictCannonRequirements": StrictCannonRequirements,
"FirstBowserStarDoorCost": FirstBowserStarDoorCost,
"BasementStarDoorCost": BasementStarDoorCost,
"SecondFloorStarDoorCost": SecondFloorStarDoorCost,
"MIPS1Cost": MIPS1Cost,
"MIPS2Cost": MIPS2Cost,
"StarsToFinish": StarsToFinish,
"ExtraStars": ExtraStars,
"death_link": DeathLink,
"BuddyChecks": BuddyChecks,
}

View File

@@ -4,150 +4,168 @@ from .Locations import SM64Location, location_table, locBoB_table, locWhomp_tabl
locBBH_table, \
locHMC_table, locLLL_table, locSSL_table, locDDD_table, locSL_table, \
locWDW_table, locTTM_table, locTHI_table, locTTC_table, locRR_table, \
locBitDW_table, locBitFS_table, locSS_table, locCap_table
locPSS_table, locSA_table, locBitDW_table, locTotWC_table, locCotMC_table, \
locVCutM_table, locBitFS_table, locWMotR_table, locBitS_table, locSS_table
# List of all courses, including secrets, without BitS as that one is static
sm64courses = ["Bob-omb Battlefield", "Whomp's Fortress", "Jolly Roger Bay", "Cool, Cool Mountain", "Big Boo's Haunt",
"Hazy Maze Cave", "Lethal Lava Land", "Shifting Sand Land", "Dire, Dire Docks", "Snowman's Land",
"Wet-Dry World",
"Tall, Tall Mountain", "Tiny-Huge Island", "Tick Tock Clock", "Rainbow Ride"]
"Wet-Dry World", "Tall, Tall Mountain", "Tiny-Huge Island", "Tick Tock Clock", "Rainbow Ride",
"The Princess's Secret Slide", "The Secret Aquarium", "Bowser in the Dark World", "Tower of the Wing Cap",
"Cavern of the Metal Cap", "Vanish Cap under the Moat", "Bowser in the Fire Sea", "Wing Mario over the Rainbow"]
# sm64paintings is list of strings for quick reference for Painting IDs (NOT warp node IDs!)
sm64paintings = ["BOB", "WF", "JRB", "CCM", "BBH", "HMC", "LLL", "SSL", "DDD", "SL", "WDW", "TTM", "THI Tiny", "THI Huge", "TTC", "RR"]
# sm64paintings is list of entrances, format LEVEL | AREA. String Reference below
sm64paintings = [91,241,121,51,41,71,221,81,231,101,111,361,132,131,141,151]
sm64paintings_s = ["BOB", "WF", "JRB", "CCM", "BBH", "HMC", "LLL", "SSL", "DDD", "SL", "WDW", "TTM", "THI Tiny", "THI Huge", "TTC", "RR"]
# sm64secrets is list of secret areas
sm64secrets = [271, 201, 171, 291, 281, 181, 191, 311]
sm64secrets_s = ["PSS", "SA", "BitDW", "TOTWC", "COTMC", "VCUTM", "BitFS", "WMOTR"]
sm64entrances = sm64paintings + sm64secrets
sm64entrances_s = sm64paintings_s + sm64secrets_s
sm64_internalloc_to_string = dict(zip(sm64paintings+sm64secrets, sm64entrances_s))
sm64_internalloc_to_regionid = dict(zip(sm64paintings+sm64secrets, list(range(13)) + [12,13,14] + list(range(15,15+len(sm64secrets)))))
def create_regions(world: MultiWorld, player: int):
regSS = Region("Menu", RegionType.Generic, "Castle Area", player, world)
locSS_names = [name for name, id in locSS_table.items()]
locSS_names += [name for name, id in locCap_table.items()]
regSS.locations += [SM64Location(player, loc_name, location_table[loc_name], regSS) for loc_name in locSS_names]
create_default_locs(regSS, locSS_table, player)
world.regions.append(regSS)
regBoB = Region("Bob-omb Battlefield", RegionType.Generic, "Bob-omb Battlefield", player, world)
locBoB_names = [name for name, id in locBoB_table.items()]
regBoB.locations += [SM64Location(player, loc_name, location_table[loc_name], regBoB) for loc_name in locBoB_names]
regBoB = create_region("Bob-omb Battlefield", player, world)
create_default_locs(regBoB, locBoB_table, player)
if (world.EnableCoinStars[player].value):
regBoB.locations.append(SM64Location(player, "BoB: 100 Coins", location_table["BoB: 100 Coins"], regBoB))
world.regions.append(regBoB)
regWhomp = Region("Whomp's Fortress", RegionType.Generic, "Whomp's Fortress", player, world)
locWhomp_names = [name for name, id in locWhomp_table.items()]
regWhomp.locations += [SM64Location(player, loc_name, location_table[loc_name], regWhomp) for loc_name in
locWhomp_names]
regWhomp = create_region("Whomp's Fortress", player, world)
create_default_locs(regWhomp, locWhomp_table, player)
if (world.EnableCoinStars[player].value):
regWhomp.locations.append(SM64Location(player, "WF: 100 Coins", location_table["WF: 100 Coins"], regWhomp))
world.regions.append(regWhomp)
regJRB = Region("Jolly Roger Bay", RegionType.Generic, "Jolly Roger Bay", player, world)
locJRB_names = [name for name, id in locJRB_table.items()]
regJRB.locations += [SM64Location(player, loc_name, location_table[loc_name], regJRB) for loc_name in locJRB_names]
regJRB = create_region("Jolly Roger Bay", player, world)
create_default_locs(regJRB, locJRB_table, player)
if (world.EnableCoinStars[player].value):
regJRB.locations.append(SM64Location(player, "JRB: 100 Coins", location_table["JRB: 100 Coins"], regJRB))
world.regions.append(regJRB)
regCCM = Region("Cool, Cool Mountain", RegionType.Generic, "Cool, Cool Mountain", player, world)
locCCM_names = [name for name, id in locCCM_table.items()]
regCCM.locations += [SM64Location(player, loc_name, location_table[loc_name], regCCM) for loc_name in locCCM_names]
regCCM = create_region("Cool, Cool Mountain", player, world)
create_default_locs(regCCM, locCCM_table, player)
if (world.EnableCoinStars[player].value):
regCCM.locations.append(SM64Location(player, "CCM: 100 Coins", location_table["CCM: 100 Coins"], regCCM))
world.regions.append(regCCM)
regBBH = Region("Big Boo's Haunt", RegionType.Generic, "Big Boo's Haunt", player, world)
locBBH_names = [name for name, id in locBBH_table.items()]
regBBH.locations += [SM64Location(player, loc_name, location_table[loc_name], regBBH) for loc_name in locBBH_names]
regBBH = create_region("Big Boo's Haunt", player, world)
create_default_locs(regBBH, locBBH_table, player)
if (world.EnableCoinStars[player].value):
regBBH.locations.append(SM64Location(player, "BBH: 100 Coins", location_table["BBH: 100 Coins"], regBBH))
world.regions.append(regBBH)
regBitDW = Region("Bowser in the Dark World", RegionType.Generic, "Bowser in the Dark World", player, world)
locBitDW_names = [name for name, id in locBitDW_table.items()]
regBitDW.locations += [SM64Location(player, loc_name, location_table[loc_name], regBitDW) for loc_name in
locBitDW_names]
regPSS = create_region("The Princess's Secret Slide", player, world)
create_default_locs(regPSS, locPSS_table, player)
world.regions.append(regPSS)
regSA = create_region("The Secret Aquarium", player, world)
create_default_locs(regSA, locSA_table, player)
world.regions.append(regSA)
regTotWC = create_region("Tower of the Wing Cap", player, world)
create_default_locs(regTotWC, locTotWC_table, player)
world.regions.append(regTotWC)
regBitDW = create_region("Bowser in the Dark World", player, world)
create_default_locs(regBitDW, locBitDW_table, player)
world.regions.append(regBitDW)
regBasement = Region("Basement", RegionType.Generic, "Basement", player, world)
regBasement = create_region("Basement", player, world)
world.regions.append(regBasement)
regHMC = Region("Hazy Maze Cave", RegionType.Generic, "Hazy Maze Cave", player, world)
locHMC_names = [name for name, id in locHMC_table.items()]
regHMC.locations += [SM64Location(player, loc_name, location_table[loc_name], regHMC) for loc_name in locHMC_names]
regHMC = create_region("Hazy Maze Cave", player, world)
create_default_locs(regHMC, locHMC_table, player)
if (world.EnableCoinStars[player].value):
regHMC.locations.append(SM64Location(player, "HMC: 100 Coins", location_table["HMC: 100 Coins"], regHMC))
world.regions.append(regHMC)
regLLL = Region("Lethal Lava Land", RegionType.Generic, "Lethal Lava Land", player, world)
locLLL_names = [name for name, id in locLLL_table.items()]
regLLL.locations += [SM64Location(player, loc_name, location_table[loc_name], regLLL) for loc_name in locLLL_names]
regLLL = create_region("Lethal Lava Land", player, world)
create_default_locs(regLLL, locLLL_table, player)
if (world.EnableCoinStars[player].value):
regLLL.locations.append(SM64Location(player, "LLL: 100 Coins", location_table["LLL: 100 Coins"], regLLL))
world.regions.append(regLLL)
regSSL = Region("Shifting Sand Land", RegionType.Generic, "Shifting Sand Land", player, world)
locSSL_names = [name for name, id in locSSL_table.items()]
regSSL.locations += [SM64Location(player, loc_name, location_table[loc_name], regSSL) for loc_name in locSSL_names]
regSSL = create_region("Shifting Sand Land", player, world)
create_default_locs(regSSL, locSSL_table, player)
if (world.EnableCoinStars[player].value):
regSSL.locations.append(SM64Location(player, "SSL: 100 Coins", location_table["SSL: 100 Coins"], regSSL))
world.regions.append(regSSL)
regDDD = Region("Dire, Dire Docks", RegionType.Generic, "Dire, Dire Docks", player, world)
locDDD_names = [name for name, id in locDDD_table.items()]
regDDD.locations += [SM64Location(player, loc_name, location_table[loc_name], regDDD) for loc_name in locDDD_names]
regDDD = create_region("Dire, Dire Docks", player, world)
create_default_locs(regDDD, locDDD_table, player)
if (world.EnableCoinStars[player].value):
regDDD.locations.append(SM64Location(player, "DDD: 100 Coins", location_table["DDD: 100 Coins"], regDDD))
world.regions.append(regDDD)
regBitFS = Region("Bowser in the Fire Sea", RegionType.Generic, "Bowser in the Fire Sea", player, world)
locBitFS_names = [name for name, id in locBitFS_table.items()]
regBitFS.locations += [SM64Location(player, loc_name, location_table[loc_name], regBitFS) for loc_name in
locBitFS_names]
regCotMC = create_region("Cavern of the Metal Cap", player, world)
create_default_locs(regCotMC, locCotMC_table, player)
world.regions.append(regCotMC)
regVCutM = create_region("Vanish Cap under the Moat", player, world)
create_default_locs(regVCutM, locVCutM_table, player)
world.regions.append(regVCutM)
regBitFS = create_region("Bowser in the Fire Sea", player, world)
create_default_locs(regBitFS, locBitFS_table, player)
world.regions.append(regBitFS)
regFloor2 = Region("Second Floor", RegionType.Generic, "Second Floor", player, world)
regFloor2 = create_region("Second Floor", player, world)
world.regions.append(regFloor2)
regSL = Region("Snowman's Land", RegionType.Generic, "Snowman's Land", player, world)
locSL_names = [name for name, id in locSL_table.items()]
regSL.locations += [SM64Location(player, loc_name, location_table[loc_name], regSL) for loc_name in locSL_names]
regSL = create_region("Snowman's Land", player, world)
create_default_locs(regSL, locSL_table, player)
if (world.EnableCoinStars[player].value):
regSL.locations.append(SM64Location(player, "SL: 100 Coins", location_table["SL: 100 Coins"], regSL))
world.regions.append(regSL)
regWDW = Region("Wet-Dry World", RegionType.Generic, "Wet-Dry World", player, world)
locWDW_names = [name for name, id in locWDW_table.items()]
regWDW.locations += [SM64Location(player, loc_name, location_table[loc_name], regWDW) for loc_name in locWDW_names]
regWDW = create_region("Wet-Dry World", player, world)
create_default_locs(regWDW, locWDW_table, player)
if (world.EnableCoinStars[player].value):
regWDW.locations.append(SM64Location(player, "WDW: 100 Coins", location_table["WDW: 100 Coins"], regWDW))
world.regions.append(regWDW)
regTTM = Region("Tall, Tall Mountain", RegionType.Generic, "Tall, Tall Mountain", player, world)
locTTM_names = [name for name, id in locTTM_table.items()]
regTTM.locations += [SM64Location(player, loc_name, location_table[loc_name], regTTM) for loc_name in locTTM_names]
regTTM = create_region("Tall, Tall Mountain", player, world)
create_default_locs(regTTM, locTTM_table, player)
if (world.EnableCoinStars[player].value):
regTTM.locations.append(SM64Location(player, "TTM: 100 Coins", location_table["TTM: 100 Coins"], regTTM))
world.regions.append(regTTM)
regTHI = Region("Tiny-Huge Island", RegionType.Generic, "Tiny-Huge Island", player, world)
locTHI_names = [name for name, id in locTHI_table.items()]
regTHI.locations += [SM64Location(player, loc_name, location_table[loc_name], regTHI) for loc_name in locTHI_names]
regTHI = create_region("Tiny-Huge Island", player, world)
create_default_locs(regTHI, locTHI_table, player)
if (world.EnableCoinStars[player].value):
regTHI.locations.append(SM64Location(player, "THI: 100 Coins", location_table["THI: 100 Coins"], regTHI))
world.regions.append(regTHI)
regFloor3 = Region("Third Floor", RegionType.Generic, "Third Floor", player, world)
regFloor3 = create_region("Third Floor", player, world)
world.regions.append(regFloor3)
regTTC = Region("Tick Tock Clock", RegionType.Generic, "Tick Tock Clock", player, world)
locTTC_names = [name for name, id in locTTC_table.items()]
regTTC.locations += [SM64Location(player, loc_name, location_table[loc_name], regTTC) for loc_name in locTTC_names]
regTTC = create_region("Tick Tock Clock", player, world)
create_default_locs(regTTC, locTTC_table, player)
if (world.EnableCoinStars[player].value):
regTTC.locations.append(SM64Location(player, "TTC: 100 Coins", location_table["TTC: 100 Coins"], regTTC))
world.regions.append(regTTC)
regRR = Region("Rainbow Ride", RegionType.Generic, "Rainbow Ride", player, world)
locRR_names = [name for name, id in locRR_table.items()]
regRR.locations += [SM64Location(player, loc_name, location_table[loc_name], regRR) for loc_name in locRR_names]
regRR = create_region("Rainbow Ride", player, world)
create_default_locs(regRR, locRR_table, player)
if (world.EnableCoinStars[player].value):
regRR.locations.append(SM64Location(player, "RR: 100 Coins", location_table["RR: 100 Coins"], regRR))
world.regions.append(regRR)
regWMotR = create_region("Wing Mario over the Rainbow", player, world)
create_default_locs(regWMotR, locWMotR_table, player)
world.regions.append(regWMotR)
regBitS = create_region("Bowser in the Sky", player, world)
create_default_locs(regBitS, locBitS_table, player)
world.regions.append(regBitS)
def connect_regions(world: MultiWorld, player: int, source: str, target: str, rule=None):
sourceRegion = world.get_region(source, player)
@@ -159,3 +177,10 @@ def connect_regions(world: MultiWorld, player: int, source: str, target: str, ru
sourceRegion.exits.append(connection)
connection.connect(targetRegion)
def create_region(name: str, player: int, world: MultiWorld) -> Region:
return Region(name, RegionType.Generic, name, player, world)
def create_default_locs(reg: Region, locs, player):
reg_names = [name for name, id in locs.items()]
reg.locations += [SM64Location(player, loc_name, location_table[loc_name], reg) for loc_name in locs]

View File

@@ -1,56 +1,76 @@
from ..generic.Rules import add_rule
from .Regions import connect_regions, sm64courses, sm64paintings
from .Regions import connect_regions, sm64courses, sm64paintings, sm64secrets, sm64entrances
def fix_reg(entrance_ids, reg, invalidspot, swaplist, world):
if entrance_ids.index(reg) == invalidspot: # Unlucky :C
swaplist.remove(invalidspot)
rand = world.random.choice(swaplist)
entrance_ids[invalidspot], entrance_ids[rand] = entrance_ids[rand], entrance_ids[invalidspot]
swaplist.append(invalidspot)
swaplist.remove(rand)
def set_rules(world, player: int, area_connections):
entrance_ids = list(range(len(sm64paintings)))
destination_courses = list(range(13)) + [12,13,14] # Two instances of Destination Course THI
if world.AreaRandomizer[player]:
destination_regions = list(range(13)) + [12,13,14] + list(range(15,15+len(sm64secrets))) # Two instances of Destination Course THI. Past normal course idx are secret regions
if world.AreaRandomizer[player].value == 0:
entrance_ids = list(range(len(sm64paintings + sm64secrets)))
if world.AreaRandomizer[player].value >= 1: # Some randomization is happening, randomize Courses
entrance_ids = list(range(len(sm64paintings)))
world.random.shuffle(entrance_ids)
temp_assign = dict(zip(entrance_ids,destination_courses)) # Used for Rules only
entrance_ids = entrance_ids + list(range(len(sm64paintings), len(sm64paintings) + len(sm64secrets)))
if world.AreaRandomizer[player].value == 2: # Secret Regions as well
world.random.shuffle(entrance_ids)
# Guarantee first entrance is a course
swaplist = list(range(len(entrance_ids)))
if entrance_ids.index(0) > 15: # Unlucky :C
rand = world.random.randint(0,15)
entrance_ids[entrance_ids.index(0)], entrance_ids[rand] = entrance_ids[rand], entrance_ids[entrance_ids.index(0)]
swaplist.remove(entrance_ids.index(0))
# Guarantee COTMC is not mapped to HMC, cuz thats impossible
fix_reg(entrance_ids, 20, 5, swaplist, world)
# Guarantee BITFS is not mapped to DDD
fix_reg(entrance_ids, 22, 8, swaplist, world)
temp_assign = dict(zip(entrance_ids,destination_regions)) # Used for Rules only
# Destination Format: LVL | AREA with LVL = Course ID, 0-indexed, AREA = Area as used in sm64 code
area_connections.update({entrance: (destination_course*10 + 1) for entrance, destination_course in temp_assign.items()})
for i in range(len(area_connections)):
if (int(area_connections[i]/10) == 12):
# Change first occurence of course 12 (THI) to Area 2 (THI Tiny)
area_connections[i] = 12*10 + 2
break
connect_regions(world, player, "Menu", sm64courses[temp_assign[0]])
connect_regions(world, player, "Menu", sm64courses[temp_assign[1]], lambda state: state.has("Power Star", player, 1))
connect_regions(world, player, "Menu", sm64courses[temp_assign[2]], lambda state: state.has("Power Star", player, 3))
connect_regions(world, player, "Menu", sm64courses[temp_assign[3]], lambda state: state.has("Power Star", player, 3))
connect_regions(world, player, "Menu", "Bowser in the Dark World", lambda state: state.has("Power Star", player, world.FirstBowserStarDoorCost[player].value))
connect_regions(world, player, "Menu", sm64courses[temp_assign[4]], lambda state: state.has("Power Star", player, 12))
# Destination Format: LVL | AREA with LVL = LEVEL_x, AREA = Area as used in sm64 code
area_connections.update({sm64entrances[entrance]: destination for entrance, destination in zip(entrance_ids,sm64entrances)})
connect_regions(world, player, "Menu", sm64courses[temp_assign[0]]) # BOB
connect_regions(world, player, "Menu", sm64courses[temp_assign[1]], lambda state: state.has("Power Star", player, 1)) # WF
connect_regions(world, player, "Menu", sm64courses[temp_assign[2]], lambda state: state.has("Power Star", player, 3)) # JRB
connect_regions(world, player, "Menu", sm64courses[temp_assign[3]], lambda state: state.has("Power Star", player, 3)) # CCM
connect_regions(world, player, "Menu", sm64courses[temp_assign[4]], lambda state: state.has("Power Star", player, 12)) # BBH
connect_regions(world, player, "Menu", sm64courses[temp_assign[16]], lambda state: state.has("Power Star", player, 1)) # PSS
connect_regions(world, player, "Menu", sm64courses[temp_assign[17]], lambda state: state.has("Power Star", player, 3)) # SA
connect_regions(world, player, "Menu", sm64courses[temp_assign[19]], lambda state: state.has("Power Star", player, 10)) # TOTWC
connect_regions(world, player, "Menu", sm64courses[temp_assign[18]], lambda state: state.has("Power Star", player, world.FirstBowserStarDoorCost[player].value)) # BITDW
connect_regions(world, player, "Menu", "Basement", lambda state: state.has("Basement Key", player) or state.has("Progressive Key", player, 1))
connect_regions(world, player, "Basement", sm64courses[temp_assign[5]])
connect_regions(world, player, "Basement", sm64courses[temp_assign[6]])
connect_regions(world, player, "Basement", sm64courses[temp_assign[7]])
connect_regions(world, player, "Basement", sm64courses[temp_assign[8]], lambda state: state.has("Power Star", player, world.BasementStarDoorCost[player].value))
connect_regions(world, player, "Basement", "Bowser in the Fire Sea", lambda state: state.has("Power Star", player, world.BasementStarDoorCost[player].value) and
state.can_reach("DDD: Board Bowser's Sub", 'Location', player))
connect_regions(world, player, "Basement", sm64courses[temp_assign[5]]) # HMC
connect_regions(world, player, "Basement", sm64courses[temp_assign[6]]) # LLL
connect_regions(world, player, "Basement", sm64courses[temp_assign[7]]) # SSL
connect_regions(world, player, "Basement", sm64courses[temp_assign[8]], lambda state: state.has("Power Star", player, world.BasementStarDoorCost[player].value)) # DDD
connect_regions(world, player, "Hazy Maze Cave", sm64courses[temp_assign[20]]) # COTMC
connect_regions(world, player, "Basement", sm64courses[temp_assign[21]]) # VCUTM
connect_regions(world, player, "Basement", sm64courses[temp_assign[22]], lambda state: state.has("Power Star", player, world.BasementStarDoorCost[player].value) and
state.can_reach("DDD: Board Bowser's Sub", 'Location', player)) # BITFS
connect_regions(world, player, "Menu", "Second Floor", lambda state: state.has("Second Floor Key", player) or state.has("Progressive Key", player, 2))
connect_regions(world, player, "Second Floor", sm64courses[temp_assign[9]])
connect_regions(world, player, "Second Floor", sm64courses[temp_assign[10]])
connect_regions(world, player, "Second Floor", sm64courses[temp_assign[11]])
connect_regions(world, player, "Second Floor", sm64courses[temp_assign[9]]) # SL
connect_regions(world, player, "Second Floor", sm64courses[temp_assign[10]]) # WDW
connect_regions(world, player, "Second Floor", sm64courses[temp_assign[11]]) # TTM
connect_regions(world, player, "Second Floor", sm64courses[temp_assign[12]]) # THI Tiny
connect_regions(world, player, "Second Floor", sm64courses[temp_assign[13]]) # THI Huge
connect_regions(world, player, "Second Floor", "Third Floor", lambda state: state.has("Power Star", player, world.SecondFloorStarDoorCost[player].value))
connect_regions(world, player, "Third Floor", sm64courses[temp_assign[14]])
connect_regions(world, player, "Third Floor", sm64courses[temp_assign[15]])
connect_regions(world, player, "Third Floor", sm64courses[temp_assign[14]]) # TTC
connect_regions(world, player, "Third Floor", sm64courses[temp_assign[15]]) # RR
connect_regions(world, player, "Third Floor", sm64courses[temp_assign[23]]) # WMOTR
connect_regions(world, player, "Third Floor", "Bowser in the Sky", lambda state: state.has("Power Star", player, world.StarsToFinish[player].value)) # BITS
#Special Rules for some Locations
add_rule(world.get_location("Tower of the Wing Cap Switch", player), lambda state: state.has("Power Star", player, 10))
add_rule(world.get_location("Cavern of the Metal Cap Switch", player), lambda state: state.can_reach("Hazy Maze Cave", 'Region', player))
add_rule(world.get_location("Vanish Cap Under the Moat Switch", player), lambda state: state.can_reach("Basement", 'Region', player))
add_rule(world.get_location("BoB: Mario Wings to the Sky", player), lambda state: state.has("Cannon Unlock BoB", player))
add_rule(world.get_location("BBH: Eye to Eye in the Secret Room", player), lambda state: state.has("Vanish Cap", player))
add_rule(world.get_location("DDD: Collect the Caps...", player), lambda state: state.has("Vanish Cap", player))
@@ -89,18 +109,14 @@ def set_rules(world, player: int, area_connections):
add_rule(world.get_location("BoB: 100 Coins", player), lambda state: state.has("Cannon Unlock BoB", player) or state.has("Wing Cap", player))
#Rules for Secret Stars
add_rule(world.get_location("Bowser in the Sky Red Coins", player), lambda state: state.can_reach("Third Floor", 'Region',player) and state.has("Power Star", player, world.StarsToFinish[player].value))
add_rule(world.get_location("The Princess's Secret Slide Block", player), lambda state: state.has("Power Star", player, 1))
add_rule(world.get_location("The Princess's Secret Slide Fast", player), lambda state: state.has("Power Star", player, 1))
add_rule(world.get_location("Cavern of the Metal Cap Red Coins", player), lambda state: state.can_reach("Cavern of the Metal Cap Switch", 'Location', player))
add_rule(world.get_location("Tower of the Wing Cap Red Coins", player), lambda state: state.can_reach("Tower of the Wing Cap Switch", 'Location', player))
add_rule(world.get_location("Vanish Cap Under the Moat Red Coins", player), lambda state: state.can_reach("Vanish Cap Under the Moat Switch", 'Location', player))
add_rule(world.get_location("Wing Mario Over the Rainbow", player), lambda state: state.can_reach("Third Floor", 'Region', player) and state.has("Wing Cap", player))
add_rule(world.get_location("The Secret Aquarium", player), lambda state: state.has("Power Star", player, 3))
add_rule(world.get_location("Wing Mario Over the Rainbow", player), lambda state: state.has("Wing Cap", player))
add_rule(world.get_location("Toad (Basement)", player), lambda state: state.can_reach("Basement", 'Region', player) and state.has("Power Star", player, 12))
add_rule(world.get_location("Toad (Second Floor)", player), lambda state: state.can_reach("Second Floor", 'Region', player) and state.has("Power Star", player, 25))
add_rule(world.get_location("Toad (Third Floor)", player), lambda state: state.can_reach("Third Floor", 'Region', player) and state.has("Power Star", player, 35))
add_rule(world.get_location("MIPS 1", player), lambda state: state.can_reach("Basement", 'Region', player) and state.has("Power Star", player, 15))
add_rule(world.get_location("MIPS 2", player), lambda state: state.can_reach("Basement", 'Region', player) and state.has("Power Star", player, 50))
world.completion_condition[player] = lambda state: state.can_reach("Third Floor", 'Region', player) and state.has("Power Star", player, world.StarsToFinish[player].value)
if world.MIPS1Cost[player].value > world.MIPS2Cost[player].value:
world.MIPS2Cost[player].value = world.MIPS1Cost[player].value
add_rule(world.get_location("MIPS 1", player), lambda state: state.can_reach("Basement", 'Region', player) and state.has("Power Star", player, world.MIPS1Cost[player].value))
add_rule(world.get_location("MIPS 2", player), lambda state: state.can_reach("Basement", 'Region', player) and state.has("Power Star", player, world.MIPS2Cost[player].value))
world.completion_condition[player] = lambda state: state.can_reach("Bowser in the Sky", 'Region', player)

View File

@@ -5,7 +5,7 @@ from .Items import item_table, cannon_item_table, SM64Item
from .Locations import location_table, SM64Location
from .Options import sm64_options
from .Rules import set_rules
from .Regions import create_regions, sm64courses, sm64paintings
from .Regions import create_regions, sm64courses, sm64entrances_s, sm64_internalloc_to_string, sm64_internalloc_to_regionid
from BaseClasses import Item, Tutorial, ItemClassification
from ..AutoWorld import World, WebWorld
@@ -54,10 +54,10 @@ class SM64World(World):
set_rules(self.world, self.player, self.area_connections)
if self.topology_present:
# Write area_connections to spoiler log
for painting_id, destination in self.area_connections.items():
for entrance, destination in self.area_connections.items():
self.world.spoiler.set_entrance(
sm64paintings[painting_id] + " Painting",
sm64courses[destination // 10],
sm64_internalloc_to_string[entrance] + " Entrance",
sm64_internalloc_to_string[destination],
'entrance', self.player)
def create_item(self, name: str) -> Item:
@@ -74,9 +74,13 @@ class SM64World(World):
def generate_basic(self):
staritem = self.create_item("Power Star")
starcount = min(self.world.StarsToFinish[self.player].value + self.world.ExtraStars[self.player].value,120)
starcount = self.world.AmountOfStars[self.player].value
if (not self.world.EnableCoinStars[self.player].value):
starcount = max(starcount - 15,self.world.StarsToFinish[self.player].value)
starcount = max(35,self.world.AmountOfStars[self.player].value-15)
starcount = max(starcount, self.world.FirstBowserStarDoorCost[self.player].value,
self.world.BasementStarDoorCost[self.player].value, self.world.SecondFloorStarDoorCost[self.player].value,
self.world.MIPS1Cost[self.player].value, self.world.MIPS2Cost[self.player].value,
self.world.StarsToFinish[self.player].value)
self.world.itempool += [staritem for i in range(0,starcount)]
mushroomitem = self.create_item("1Up Mushroom")
self.world.itempool += [mushroomitem for i in range(starcount,120 - (15 if not self.world.EnableCoinStars[self.player].value else 0))]
@@ -117,6 +121,8 @@ class SM64World(World):
"FirstBowserDoorCost": self.world.FirstBowserStarDoorCost[self.player].value,
"BasementDoorCost": self.world.BasementStarDoorCost[self.player].value,
"SecondFloorCost": self.world.SecondFloorStarDoorCost[self.player].value,
"MIPS1Cost": self.world.MIPS1Cost[self.player].value,
"MIPS2Cost": self.world.MIPS2Cost[self.player].value,
"StarsToFinish": self.world.StarsToFinish[self.player].value,
"DeathLink": self.world.death_link[self.player].value,
}
@@ -145,8 +151,9 @@ class SM64World(World):
def modify_multidata(self, multidata):
if self.topology_present:
er_hint_data = {}
for painting_id, destination in self.area_connections.items():
region = self.world.get_region(sm64courses[destination // 10], self.player)
for entrance, destination in self.area_connections.items():
regionid = sm64_internalloc_to_regionid[destination]
region = self.world.get_region(sm64courses[regionid], self.player)
for location in region.locations:
er_hint_data[location.address] = sm64paintings[painting_id]
er_hint_data[location.address] = sm64_internalloc_to_string[entrance]
multidata['er_hint_data'][self.player] = er_hint_data

View File

@@ -115,7 +115,7 @@ item_table: Dict[int, ItemDict] = {
'name': 'Light Stick Fragment',
'tech_type': 'TechlightFragment'},
35026: {'classification': ItemClassification.progression,
'count': 3,
'count': 4,
'name': 'Mobile Vehicle Bay Fragment',
'tech_type': 'ConstructorFragment'},
35027: {'classification': ItemClassification.progression,
@@ -222,10 +222,10 @@ item_table: Dict[int, ItemDict] = {
'count': 2,
'name': 'Observatory Fragment',
'tech_type': 'BaseObservatoryFragment'},
35053: {'classification': ItemClassification.filler,
35053: {'classification': ItemClassification.useful,
'count': 2,
'name': 'Multipurpose Room Fragment',
'tech_type': 'BaseRoomFragment'},
'name': 'Multipurpose Room',
'tech_type': 'BaseRoom'},
35054: {'classification': ItemClassification.useful,
'count': 2,
'name': 'Bulkhead Fragment',

View File

@@ -560,8 +560,12 @@ location_table: Dict[int, LocationDict] = {
33129: {'can_slip_through': False,
'name': 'Floating Island - Cave Entrance PDA',
'need_laser_cutter': False,
'position': {'x': -748.9, 'y': 14.4, 'z': -1179.5}}}
'position': {'x': -748.9, 'y': 14.4, 'z': -1179.5}},
33130: {'can_slip_through': False,
'name': 'Degasi Seabase - Jellyshroom Cave - Outside PDA',
'need_laser_cutter': False,
'position': {'x': -83.2, 'y': -276.4, 'z': -345.5}},
}
if False: # turn to True to export for Subnautica mod
payload = {location_id: location_data["position"] for location_id, location_data in location_table.items()}
import json

View File

@@ -1,4 +1,4 @@
from Options import Choice, Range
from Options import Choice, Range, DeathLink
from .Creatures import all_creatures
@@ -39,8 +39,14 @@ class CreatureScans(Range):
range_end = len(all_creatures)
class SubnauticaDeathLink(DeathLink):
"""When you die, everyone dies. Of course the reverse is true too.
Note: can be toggled via in-game console command "deathlink"."""
options = {
"item_pool": ItemPool,
"goal": Goal,
"creature_scans": CreatureScans
"creature_scans": CreatureScans,
"death_link": SubnauticaDeathLink,
}

View File

@@ -41,7 +41,7 @@ class SubnauticaWorld(World):
location_name_to_id = all_locations
options = Options.options
data_version = 3
data_version = 5
required_client_version = (0, 3, 3)
prefill_items: List[Item]
@@ -115,7 +115,8 @@ class SubnauticaWorld(World):
slot_data: Dict[str, Any] = {
"goal": goal.current_key,
"vanilla_tech": vanilla_tech,
"creatures_to_scan": self.creatures_to_scan
"creatures_to_scan": self.creatures_to_scan,
"death_link": self.world.death_link[self.player].value,
}
return slot_data

View File

@@ -36,7 +36,8 @@ class ShuffleLasers(Toggle):
class ShuffleDoors(Choice):
"""If on, opening doors will require their respective "keys".
If set to "panels", those keys will unlock the panels on doors.
In "doors_simple" and "doors_complex", the doors will magically open by themselves upon receiving the key."""
In "doors_simple" and "doors_complex", the doors will magically open by themselves upon receiving the key.
The last option, "max", is a combination of "doors_complex" and "panels"."""
display_name = "Shuffle Doors"
option_none = 0
option_panels = 1
@@ -124,10 +125,10 @@ the_witness_options: Dict[str, type] = {
"shuffle_uncommon": ShuffleUncommonLocations,
"shuffle_postgame": ShufflePostgame,
"victory_condition": VictoryCondition,
"trap_percentage": TrapPercentage,
"early_secret_area": EarlySecretArea,
"mountain_lasers": MountainLasers,
"challenge_lasers": ChallengeLasers,
"early_secret_area": EarlySecretArea,
"trap_percentage": TrapPercentage,
"puzzle_skip_amount": PuzzleSkipAmount,
}

View File

@@ -173,8 +173,8 @@ Doors:
1906 - Symmetry Island Doors - 0x17F3E,0x18269
1909 - Orchard Gates - 0x03313,0x03307
1912 - Desert Doors - 0x09FEE,0x0C2C3,0x0A24B,0x0C316
1915 - Quarry Main Entry - 0x09D6F
1918 - Quarry Mill Shortcuts - 0x17C07,0x17CE8,0x0368A
1915 - Quarry Main Entry - 0x09D6F,0x17C07
1918 - Quarry Mill Shortcuts - 0x17CE8,0x0368A,0x275FF
1921 - Quarry Boathouse Barriers - 0x17C50,0x3865F
1924 - Shadows Laser Room Door - 0x194B2,0x19665
1927 - Shadows Barriers - 0x19865,0x0A2DF,0x1855B,0x19ADE

View File

@@ -354,7 +354,7 @@ Shipwreck (Shipwreck) - Keep 3rd Pressure Plate - True:
Keep Tower (Keep) - Keep - 0x04F8F:
158206 - 0x0361B (Tower Shortcut to Keep Panel) - True - True
Door - 0x04F8F (Tower Shortcut to Keep) - 0x0361B
158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F - Environment & Sound
158704 - 0x0360E (Laser Panel Hedges) - 0x01A0F & 0x019E7 & 0x019DC & 0x00139 - Environment & Sound
158705 - 0x03317 (Laser Panel Pressure Plates) - 0x01D3F - Shapers & Squares & Black/White Squares & Colored Squares & Stars & Stars + Same Colored Symbol & Dots
Laser - 0x014BB (Laser) - 0x0360E | 0x03317

View File

@@ -47,6 +47,7 @@ class WitnessWorld(World):
name: data.code for name, data in static_items.ALL_ITEM_TABLE.items()
}
location_name_to_id = StaticWitnessLocations.ALL_LOCATIONS_TO_ID
item_name_groups = StaticWitnessItems.ITEM_NAME_GROUPS
def _get_slot_data(self):
return {

View File

@@ -2,7 +2,7 @@
Defines progression, junk and event items for The Witness
"""
import copy
from typing import Dict, NamedTuple, Optional
from typing import Dict, NamedTuple, Optional, Set
from BaseClasses import Item, MultiWorld
from . import StaticWitnessLogic, WitnessPlayerLocations, WitnessPlayerLogic
@@ -35,6 +35,8 @@ class StaticWitnessItems:
ALL_ITEM_TABLE: Dict[str, ItemData] = {}
ITEM_NAME_GROUPS: Dict[str, Set[str]] = dict()
# These should always add up to 1!!!
BONUS_WEIGHTS = {
"Speed Boost": Fraction(1, 1),
@@ -57,9 +59,17 @@ class StaticWitnessItems:
item_tab[item[0]] = ItemData(158000 + item[1], True, False)
self.ITEM_NAME_GROUPS.setdefault("Symbols", set()).add(item[0])
for item in StaticWitnessLogic.ALL_DOOR_ITEMS:
item_tab[item[0]] = ItemData(158000 + item[1], True, False)
# 1500 - 1510 are the laser items, which are handled like doors but should be their own separate group.
if item[1] in range(1500, 1511):
self.ITEM_NAME_GROUPS.setdefault("Lasers", set()).add(item[0])
else:
self.ITEM_NAME_GROUPS.setdefault("Doors", set()).add(item[0])
for item in StaticWitnessLogic.ALL_TRAPS:
item_tab[item[0]] = ItemData(
158000 + item[1], False, False, True

View File

@@ -82,7 +82,8 @@ Disabled Locations:
0x002C7 (Waves 7)
0x15ADD (River Rhombic Avoid Vault)
0x03702 (River Vault Box)
0x17C2E (Door to Bunker) - True - Squares & Black/White Squares
0x17CAA (Rhombic Avoid to Monastery Garden)
0x17C2E (Door to Bunker)
0x09F7D (Bunker Drawn Squares 1)
0x09FDC (Bunker Drawn Squares 2)
0x09FF7 (Bunker Drawn Squares 3)