mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-05-27 05:39:57 -07:00
Merge branch 'main' into allow_collect
This commit is contained in:
@@ -5,7 +5,7 @@ name: Build
|
||||
on: workflow_dispatch
|
||||
|
||||
env:
|
||||
SNI_VERSION: v0.0.84
|
||||
SNI_VERSION: v0.0.88
|
||||
ENEMIZER_VERSION: 7.1
|
||||
APPIMAGETOOL_VERSION: 13
|
||||
|
||||
@@ -78,9 +78,10 @@ jobs:
|
||||
- name: Build
|
||||
run: |
|
||||
# pygobject is an optional dependency for kivy that's not in requirements
|
||||
"${{ env.PYTHON }}" -m pip install --upgrade pip virtualenv PyGObject setuptools
|
||||
# charset-normalizer was somehow incomplete in the github runner
|
||||
"${{ env.PYTHON }}" -m venv venv
|
||||
source venv/bin/activate
|
||||
"${{ env.PYTHON }}" -m pip install --upgrade pip PyGObject setuptools charset-normalizer
|
||||
pip install -r requirements.txt
|
||||
python setup.py build_exe --yes bdist_appimage --yes
|
||||
echo -e "setup.py build output:\n `ls build`"
|
||||
|
||||
@@ -8,7 +8,7 @@ on:
|
||||
- '*.*.*'
|
||||
|
||||
env:
|
||||
SNI_VERSION: v0.0.84
|
||||
SNI_VERSION: v0.0.88
|
||||
ENEMIZER_VERSION: 7.1
|
||||
APPIMAGETOOL_VERSION: 13
|
||||
|
||||
@@ -65,9 +65,10 @@ jobs:
|
||||
- name: Build
|
||||
run: |
|
||||
# pygobject is an optional dependency for kivy that's not in requirements
|
||||
"${{ env.PYTHON }}" -m pip install --upgrade pip virtualenv PyGObject setuptools
|
||||
# charset-normalizer was somehow incomplete in the github runner
|
||||
"${{ env.PYTHON }}" -m venv venv
|
||||
source venv/bin/activate
|
||||
"${{ env.PYTHON }}" -m pip install --upgrade pip PyGObject setuptools charset-normalizer
|
||||
pip install -r requirements.txt
|
||||
python setup.py build --yes bdist_appimage --yes
|
||||
echo -e "setup.py build output:\n `ls build`"
|
||||
|
||||
@@ -50,6 +50,7 @@ Output Logs/
|
||||
/Archipelago.zip
|
||||
/setup.ini
|
||||
/installdelete.iss
|
||||
/data/user.kv
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
|
||||
+46
-31
@@ -29,6 +29,20 @@ class Group(TypedDict, total=False):
|
||||
link_replacement: bool
|
||||
|
||||
|
||||
class ThreadBarrierProxy():
|
||||
"""Passes through getattr while passthrough is True"""
|
||||
def __init__(self, obj: Any):
|
||||
self.passthrough = True
|
||||
self.obj = obj
|
||||
|
||||
def __getattr__(self, item):
|
||||
if self.passthrough:
|
||||
return getattr(self.obj, item)
|
||||
else:
|
||||
raise RuntimeError("You are in a threaded context and global random state was removed for your safety. "
|
||||
"Please use multiworld.per_slot_randoms[player] or randomize ahead of output.")
|
||||
|
||||
|
||||
class MultiWorld():
|
||||
debug_types = False
|
||||
player_name: Dict[int, str]
|
||||
@@ -62,6 +76,9 @@ class MultiWorld():
|
||||
|
||||
game: Dict[int, str]
|
||||
|
||||
random: random.Random
|
||||
per_slot_randoms: Dict[int, random.Random]
|
||||
|
||||
class AttributeProxy():
|
||||
def __init__(self, rule):
|
||||
self.rule = rule
|
||||
@@ -70,7 +87,8 @@ class MultiWorld():
|
||||
return self.rule(player)
|
||||
|
||||
def __init__(self, players: int):
|
||||
self.random = random.Random() # world-local random state is saved for multiple generations running concurrently
|
||||
# world-local random state is saved for multiple generations running concurrently
|
||||
self.random = ThreadBarrierProxy(random.Random())
|
||||
self.players = players
|
||||
self.player_types = {player: NetUtils.SlotType.player for player in self.player_ids}
|
||||
self.glitch_triforce = False
|
||||
@@ -161,7 +179,7 @@ class MultiWorld():
|
||||
set_player_attr('completion_condition', lambda state: True)
|
||||
self.custom_data = {}
|
||||
self.worlds = {}
|
||||
self.slot_seeds = {}
|
||||
self.per_slot_randoms = {}
|
||||
self.plando_options = PlandoOptions.none
|
||||
|
||||
def get_all_ids(self) -> Tuple[int, ...]:
|
||||
@@ -207,8 +225,8 @@ class MultiWorld():
|
||||
else:
|
||||
self.random.seed(self.seed)
|
||||
self.seed_name = name if name else str(self.seed)
|
||||
self.slot_seeds = {player: random.Random(self.random.getrandbits(64)) for player in
|
||||
range(1, self.players + 1)}
|
||||
self.per_slot_randoms = {player: random.Random(self.random.getrandbits(64)) for player in
|
||||
range(1, self.players + 1)}
|
||||
|
||||
def set_options(self, args: Namespace) -> None:
|
||||
for option_key in Options.common_options:
|
||||
@@ -292,7 +310,7 @@ class MultiWorld():
|
||||
self.state = CollectionState(self)
|
||||
|
||||
def secure(self):
|
||||
self.random = secrets.SystemRandom()
|
||||
self.random = ThreadBarrierProxy(secrets.SystemRandom())
|
||||
self.is_race = True
|
||||
|
||||
@functools.cached_property
|
||||
@@ -932,24 +950,9 @@ class CollectionState():
|
||||
self.stale[item.player] = True
|
||||
|
||||
|
||||
@unique
|
||||
class RegionType(IntEnum):
|
||||
Generic = 0
|
||||
LightWorld = 1
|
||||
DarkWorld = 2
|
||||
Cave = 3 # Also includes Houses
|
||||
Dungeon = 4
|
||||
|
||||
@property
|
||||
def is_indoors(self) -> bool:
|
||||
"""Shorthand for checking if Cave or Dungeon"""
|
||||
return self in (RegionType.Cave, RegionType.Dungeon)
|
||||
|
||||
|
||||
class Region:
|
||||
name: str
|
||||
type: RegionType
|
||||
hint_text: str
|
||||
_hint_text: str
|
||||
player: int
|
||||
multiworld: Optional[MultiWorld]
|
||||
entrances: List[Entrance]
|
||||
@@ -963,14 +966,13 @@ class Region:
|
||||
is_light_world: bool = False
|
||||
is_dark_world: bool = False
|
||||
|
||||
def __init__(self, name: str, type_: RegionType, hint: str, player: int, world: Optional[MultiWorld] = None):
|
||||
def __init__(self, name: str, player: int, multiworld: MultiWorld, hint: Optional[str] = None):
|
||||
self.name = name
|
||||
self.type = type_
|
||||
self.entrances = []
|
||||
self.exits = []
|
||||
self.locations = []
|
||||
self.multiworld = world
|
||||
self.hint_text = hint
|
||||
self.multiworld = multiworld
|
||||
self._hint_text = hint
|
||||
self.player = player
|
||||
|
||||
def can_reach(self, state: CollectionState) -> bool:
|
||||
@@ -986,6 +988,10 @@ class Region:
|
||||
return True
|
||||
return False
|
||||
|
||||
@property
|
||||
def hint_text(self) -> str:
|
||||
return self._hint_text if self._hint_text else self.name
|
||||
|
||||
def get_connecting_entrance(self, is_main_entrance: typing.Callable[[Entrance], bool]) -> Entrance:
|
||||
for entrance in self.entrances:
|
||||
if is_main_entrance(entrance):
|
||||
@@ -1272,6 +1278,7 @@ class Spoiler():
|
||||
[('player', player), ('entrance', entrance), ('exit', exit_), ('direction', direction)])
|
||||
|
||||
def parse_data(self):
|
||||
from worlds.alttp.SubClasses import LTTPRegionType
|
||||
self.medallions = OrderedDict()
|
||||
for player in self.multiworld.get_game_players("A Link to the Past"):
|
||||
self.medallions[f'Misery Mire ({self.multiworld.get_player_name(player)})'] = \
|
||||
@@ -1281,23 +1288,31 @@ class Spoiler():
|
||||
|
||||
self.locations = OrderedDict()
|
||||
listed_locations = set()
|
||||
lw_locations = []
|
||||
dw_locations = []
|
||||
cave_locations = []
|
||||
for loc in self.multiworld.get_locations():
|
||||
if loc.game == "A Link to the Past":
|
||||
if loc not in listed_locations and loc.parent_region and \
|
||||
loc.parent_region.type == LTTPRegionType.LightWorld and loc.show_in_spoiler:
|
||||
lw_locations.append(loc)
|
||||
elif loc not in listed_locations and loc.parent_region and \
|
||||
loc.parent_region.type == LTTPRegionType.DarkWorld and loc.show_in_spoiler:
|
||||
dw_locations.append(loc)
|
||||
elif loc not in listed_locations and loc.parent_region and \
|
||||
loc.parent_region.type == LTTPRegionType.Cave and loc.show_in_spoiler:
|
||||
cave_locations.append(loc)
|
||||
|
||||
lw_locations = [loc for loc in self.multiworld.get_locations() if
|
||||
loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.LightWorld and loc.show_in_spoiler]
|
||||
self.locations['Light World'] = OrderedDict(
|
||||
[(str(location), str(location.item) if location.item is not None else 'Nothing') for location in
|
||||
lw_locations])
|
||||
listed_locations.update(lw_locations)
|
||||
|
||||
dw_locations = [loc for loc in self.multiworld.get_locations() if
|
||||
loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.DarkWorld and loc.show_in_spoiler]
|
||||
self.locations['Dark World'] = OrderedDict(
|
||||
[(str(location), str(location.item) if location.item is not None else 'Nothing') for location in
|
||||
dw_locations])
|
||||
listed_locations.update(dw_locations)
|
||||
|
||||
cave_locations = [loc for loc in self.multiworld.get_locations() if
|
||||
loc not in listed_locations and loc.parent_region and loc.parent_region.type == RegionType.Cave and loc.show_in_spoiler]
|
||||
self.locations['Caves'] = OrderedDict(
|
||||
[(str(location), str(location.item) if location.item is not None else 'Nothing') for location in
|
||||
cave_locations])
|
||||
|
||||
@@ -341,6 +341,11 @@ class CommonContext:
|
||||
return self.slot in self.slot_info[slot].group_members
|
||||
return False
|
||||
|
||||
def is_echoed_chat(self, print_json_packet: dict) -> bool:
|
||||
return print_json_packet.get("type", "") == "Chat" \
|
||||
and print_json_packet.get("team", None) == self.team \
|
||||
and print_json_packet.get("slot", None) == self.slot
|
||||
|
||||
def is_uninteresting_item_send(self, print_json_packet: dict) -> bool:
|
||||
"""Helper function for filtering out ItemSend prints that do not concern the local player."""
|
||||
return print_json_packet.get("type", "") == "ItemSend" \
|
||||
|
||||
+3
-2
@@ -109,9 +109,10 @@ class FactorioContext(CommonContext):
|
||||
|
||||
def on_print_json(self, args: dict):
|
||||
if self.rcon_client:
|
||||
if not self.filter_item_sends or not self.is_uninteresting_item_send(args):
|
||||
if (not self.filter_item_sends or not self.is_uninteresting_item_send(args)) \
|
||||
and not self.is_echoed_chat(args):
|
||||
text = self.factorio_json_text_parser(copy.deepcopy(args["data"]))
|
||||
if not text.startswith(self.player_names[self.slot] + ":"):
|
||||
if not text.startswith(self.player_names[self.slot] + ":"): # TODO: Remove string heuristic in the future.
|
||||
self.print_to_game(text)
|
||||
super(FactorioContext, self).on_print_json(args)
|
||||
|
||||
|
||||
+1
-1
@@ -35,7 +35,7 @@ class AdjusterWorld(object):
|
||||
def __init__(self, sprite_pool):
|
||||
import random
|
||||
self.sprite_pool = {1: sprite_pool}
|
||||
self.slot_seeds = {1: random}
|
||||
self.per_slot_randoms = {1: random}
|
||||
|
||||
|
||||
class ArgumentDefaultsHelpFormatter(argparse.RawTextHelpFormatter):
|
||||
|
||||
@@ -9,11 +9,12 @@ import tempfile
|
||||
import zipfile
|
||||
from typing import Dict, List, Tuple, Optional, Set
|
||||
|
||||
from BaseClasses import Item, MultiWorld, CollectionState, Region, RegionType, LocationProgressType, Location
|
||||
from BaseClasses import Item, MultiWorld, CollectionState, Region, LocationProgressType, Location
|
||||
import worlds
|
||||
from worlds.alttp.SubClasses import LTTPRegionType
|
||||
from worlds.alttp.Regions import is_main_entrance
|
||||
from Fill import distribute_items_restrictive, flood_items, balance_multiworld_progression, distribute_planned
|
||||
from worlds.alttp.Shops import SHOP_ID_START, total_shop_slots, FillDisabledShopSlots
|
||||
from worlds.alttp.Shops import FillDisabledShopSlots
|
||||
from Utils import output_path, get_options, __version__, version_tuple
|
||||
from worlds.generic.Rules import locality_rules, exclusion_rules
|
||||
from worlds import AutoWorld
|
||||
@@ -191,7 +192,7 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
||||
new_item.classification |= classifications[item_name]
|
||||
new_itempool.append(new_item)
|
||||
|
||||
region = Region("Menu", RegionType.Generic, "ItemLink", group_id, world)
|
||||
region = Region("Menu", group_id, world, "ItemLink")
|
||||
world.regions.append(region)
|
||||
locations = region.locations = []
|
||||
for item in world.itempool:
|
||||
@@ -251,6 +252,10 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
||||
balance_multiworld_progression(world)
|
||||
|
||||
logger.info(f'Beginning output...')
|
||||
|
||||
# we're about to output using multithreading, so we're removing the global random state to prevent accidental use
|
||||
world.random.passthrough = False
|
||||
|
||||
outfilebase = 'AP_' + world.seed_name
|
||||
|
||||
output = tempfile.TemporaryDirectory()
|
||||
@@ -286,13 +291,13 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
||||
'Inverted Ganons Tower': 'Ganons Tower'} \
|
||||
.get(location.parent_region.dungeon.name, location.parent_region.dungeon.name)
|
||||
checks_in_area[location.player][dungeonname].append(location.address)
|
||||
elif location.parent_region.type == RegionType.LightWorld:
|
||||
elif location.parent_region.type == LTTPRegionType.LightWorld:
|
||||
checks_in_area[location.player]["Light World"].append(location.address)
|
||||
elif location.parent_region.type == RegionType.DarkWorld:
|
||||
elif location.parent_region.type == LTTPRegionType.DarkWorld:
|
||||
checks_in_area[location.player]["Dark World"].append(location.address)
|
||||
elif main_entrance.parent_region.type == RegionType.LightWorld:
|
||||
elif main_entrance.parent_region.type == LTTPRegionType.LightWorld:
|
||||
checks_in_area[location.player]["Light World"].append(location.address)
|
||||
elif main_entrance.parent_region.type == RegionType.DarkWorld:
|
||||
elif main_entrance.parent_region.type == LTTPRegionType.DarkWorld:
|
||||
checks_in_area[location.player]["Dark World"].append(location.address)
|
||||
checks_in_area[location.player]["Total"] += 1
|
||||
|
||||
@@ -361,8 +366,7 @@ def main(args, seed=None, baked_server_options: Optional[Dict[str, object]] = No
|
||||
multidata = {
|
||||
"slot_data": slot_data,
|
||||
"slot_info": slot_info,
|
||||
"names": names, # TODO: remove around 0.2.5 in favor of slot_info
|
||||
"games": games, # TODO: remove around 0.2.5 in favor of slot_info
|
||||
"names": names, # TODO: remove after 0.3.9
|
||||
"connect_names": {name: (0, player) for player, name in world.player_name.items()},
|
||||
"locations": locations_data,
|
||||
"checks_in_area": checks_in_area,
|
||||
|
||||
+20
-4
@@ -2,6 +2,7 @@ import os
|
||||
import sys
|
||||
import subprocess
|
||||
import pkg_resources
|
||||
import warnings
|
||||
|
||||
local_dir = os.path.dirname(__file__)
|
||||
requirements_files = {os.path.join(local_dir, 'requirements.txt')}
|
||||
@@ -39,6 +40,8 @@ def update(yes=False, force=False):
|
||||
path = os.path.join(os.path.dirname(__file__), req_file)
|
||||
with open(path) as requirementsfile:
|
||||
for line in requirementsfile:
|
||||
if not line or line[0] == "#":
|
||||
continue # ignore comments
|
||||
if line.startswith(("https://", "git+https://")):
|
||||
# extract name and version for url
|
||||
rest = line.split('/')[-1]
|
||||
@@ -46,8 +49,10 @@ def update(yes=False, force=False):
|
||||
if "#egg=" in rest:
|
||||
# from egg info
|
||||
rest, egg = rest.split("#egg=", 1)
|
||||
egg = egg.split(";", 1)[0]
|
||||
egg = egg.split(";", 1)[0].rstrip()
|
||||
if any(compare in egg for compare in ("==", ">=", ">", "<", "<=", "!=")):
|
||||
warnings.warn(f"Specifying version as #egg={egg} will become unavailable in pip 25.0. "
|
||||
"Use name @ url#version instead.", DeprecationWarning)
|
||||
line = egg
|
||||
else:
|
||||
egg = ""
|
||||
@@ -58,16 +63,27 @@ def update(yes=False, force=False):
|
||||
rest = rest.replace(".zip", "-").replace(".tar.gz", "-")
|
||||
name, version, _ = rest.split("-", 2)
|
||||
line = f'{egg or name}=={version}'
|
||||
elif "@" in line and "#" in line:
|
||||
# PEP 508 does not allow us to specify a version, so we use custom syntax
|
||||
# name @ url#version ; marker
|
||||
name, rest = line.split("@", 1)
|
||||
version = rest.split("#", 1)[1].split(";", 1)[0].rstrip()
|
||||
line = f"{name.rstrip()}=={version}"
|
||||
if ";" in rest: # keep marker
|
||||
line += rest[rest.find(";"):]
|
||||
requirements = pkg_resources.parse_requirements(line)
|
||||
for requirement in requirements:
|
||||
requirement = str(requirement)
|
||||
for requirement in map(str, requirements):
|
||||
try:
|
||||
pkg_resources.require(requirement)
|
||||
except pkg_resources.ResolutionError:
|
||||
if not yes:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
input(f'Requirement {requirement} is not satisfied, press enter to install it')
|
||||
try:
|
||||
input(f"\nRequirement {requirement} is not satisfied, press enter to install it")
|
||||
except KeyboardInterrupt:
|
||||
print("\nAborting")
|
||||
sys.exit(1)
|
||||
update_command()
|
||||
return
|
||||
|
||||
|
||||
+75
-99
@@ -41,7 +41,6 @@ from NetUtils import Endpoint, ClientStatus, NetworkItem, decode, encode, Networ
|
||||
SlotType
|
||||
|
||||
min_client_version = Version(0, 1, 6)
|
||||
print_command_compatability_threshold = Version(0, 3, 5) # Remove backwards compatibility around 0.3.7
|
||||
colorama.init()
|
||||
|
||||
|
||||
@@ -159,6 +158,7 @@ class Context:
|
||||
stored_data: typing.Dict[str, object]
|
||||
read_data: typing.Dict[str, object]
|
||||
stored_data_notification_clients: typing.Dict[str, typing.Set[Client]]
|
||||
slot_info: typing.Dict[int, NetworkSlot]
|
||||
|
||||
item_names: typing.Dict[int, str] = Utils.KeyedDefaultDict(lambda code: f'Unknown item (ID:{code})')
|
||||
item_name_groups: typing.Dict[str, typing.Dict[str, typing.Set[str]]]
|
||||
@@ -171,7 +171,7 @@ class Context:
|
||||
remaining_mode: str = "disabled", auto_shutdown: typing.SupportsFloat = 0, compatibility: int = 2,
|
||||
log_network: bool = False):
|
||||
super(Context, self).__init__()
|
||||
self.slot_info: typing.Dict[int, NetworkSlot] = {}
|
||||
self.slot_info = {}
|
||||
self.log_network = log_network
|
||||
self.endpoints = []
|
||||
self.clients = {}
|
||||
@@ -310,6 +310,10 @@ class Context:
|
||||
endpoints = (endpoint for endpoint in self.endpoints if endpoint.auth)
|
||||
async_start(self.broadcast_send_encoded_msgs(endpoints, msgs))
|
||||
|
||||
def broadcast_text_all(self, text: str, additional_arguments: dict = {}):
|
||||
logging.info("Notice (all): %s" % text)
|
||||
self.broadcast_all([{**{"cmd": "PrintJSON", "data": [{ "text": text }]}, **additional_arguments}])
|
||||
|
||||
def broadcast_team(self, team: int, msgs: typing.List[dict]):
|
||||
msgs = self.dumper(msgs)
|
||||
endpoints = (endpoint for endpoint in itertools.chain.from_iterable(self.clients[team].values()))
|
||||
@@ -326,29 +330,18 @@ class Context:
|
||||
self.clients[endpoint.team][endpoint.slot].remove(endpoint)
|
||||
await on_client_disconnected(self, endpoint)
|
||||
|
||||
# text
|
||||
|
||||
def notify_all(self, text: str):
|
||||
logging.info("Notice (all): %s" % text)
|
||||
broadcast_text_all(self, text)
|
||||
|
||||
def notify_client(self, client: Client, text: str):
|
||||
def notify_client(self, client: Client, text: str, additional_arguments: dict = {}):
|
||||
if not client.auth:
|
||||
return
|
||||
logging.info("Notice (Player %s in team %d): %s" % (client.name, client.team + 1, text))
|
||||
if client.version >= print_command_compatability_threshold:
|
||||
async_start(self.send_msgs(client, [{"cmd": "PrintJSON", "data": [{ "text": text }]}]))
|
||||
else:
|
||||
async_start(self.send_msgs(client, [{"cmd": "Print", "text": text}]))
|
||||
async_start(self.send_msgs(client, [{"cmd": "PrintJSON", "data": [{ "text": text }], **additional_arguments}]))
|
||||
|
||||
def notify_client_multiple(self, client: Client, texts: typing.List[str]):
|
||||
def notify_client_multiple(self, client: Client, texts: typing.List[str], additional_arguments: dict = {}):
|
||||
if not client.auth:
|
||||
return
|
||||
if client.version >= print_command_compatability_threshold:
|
||||
async_start(self.send_msgs(client,
|
||||
[{"cmd": "PrintJSON", "data": [{ "text": text }]} for text in texts]))
|
||||
else:
|
||||
async_start(self.send_msgs(client, [{"cmd": "Print", "text": text} for text in texts]))
|
||||
async_start(self.send_msgs(client,
|
||||
[{"cmd": "PrintJSON", "data": [{ "text": text }], **additional_arguments}
|
||||
for text in texts]))
|
||||
|
||||
# loading
|
||||
|
||||
@@ -387,15 +380,25 @@ class Context:
|
||||
for player, version in clients_ver.items():
|
||||
self.minimum_client_versions[player] = max(Utils.Version(*version), min_client_version)
|
||||
|
||||
self.clients = {}
|
||||
for team, names in enumerate(decoded_obj['names']):
|
||||
self.clients[team] = {}
|
||||
for player, name in enumerate(names, 1):
|
||||
self.clients[team][player] = []
|
||||
self.player_names[team, player] = name
|
||||
self.player_name_lookup[name] = team, player
|
||||
self.read_data[f"hints_{team}_{player}"] = lambda local_team=team, local_player=player: \
|
||||
list(self.get_rechecked_hints(local_team, local_player))
|
||||
self.slot_info = decoded_obj["slot_info"]
|
||||
self.games = {slot: slot_info.game for slot, slot_info in self.slot_info.items()}
|
||||
self.groups = {slot: slot_info.group_members for slot, slot_info in self.slot_info.items()
|
||||
if slot_info.type == SlotType.group}
|
||||
self.allow_collect = {slot: slot_info.allow_collect if type(slot_info.allow_collect) is bool else True
|
||||
for slot, slot_info in self.slot_info.items()}
|
||||
|
||||
self.clients = {0: {}}
|
||||
slot_info: NetworkSlot
|
||||
slot_id: int
|
||||
|
||||
team_0 = self.clients[0]
|
||||
for slot_id, slot_info in self.slot_info.items():
|
||||
team_0[slot_id] = []
|
||||
self.player_names[0, slot_id] = slot_info.name
|
||||
self.player_name_lookup[slot_info.name] = 0, slot_id
|
||||
self.read_data[f"hints_{0}_{slot_id}"] = lambda local_team=0, local_player=slot_id: \
|
||||
list(self.get_rechecked_hints(local_team, local_player))
|
||||
|
||||
self.seed_name = decoded_obj["seed_name"]
|
||||
self.random.seed(self.seed_name)
|
||||
self.connect_names = decoded_obj['connect_names']
|
||||
@@ -410,32 +413,9 @@ class Context:
|
||||
for slot, item_codes in decoded_obj["precollected_items"].items():
|
||||
self.start_inventory[slot] = [NetworkItem(item_code, -2, 0) for item_code in item_codes]
|
||||
|
||||
for team in range(len(decoded_obj['names'])):
|
||||
for slot, hints in decoded_obj["precollected_hints"].items():
|
||||
self.hints[team, slot].update(hints)
|
||||
if "slot_info" in decoded_obj:
|
||||
self.slot_info = decoded_obj["slot_info"]
|
||||
self.games = {slot: slot_info.game for slot, slot_info in self.slot_info.items()}
|
||||
self.groups = {slot: slot_info.group_members for slot, slot_info in self.slot_info.items()
|
||||
if slot_info.type == SlotType.group}
|
||||
self.allow_collect = {slot: slot_info.allow_collect if type(slot_info.allow_collect) is bool else True
|
||||
for slot, slot_info in self.slot_info.items()}
|
||||
else:
|
||||
self.games = decoded_obj["games"]
|
||||
self.groups = {}
|
||||
self.slot_info = {
|
||||
slot: NetworkSlot(
|
||||
self.player_names[0, slot],
|
||||
self.games[slot],
|
||||
SlotType(int(bool(locations))))
|
||||
for slot, locations in self.locations.items()
|
||||
}
|
||||
# locations may need converting
|
||||
for slot, locations in self.locations.items():
|
||||
self.allow_collect[slot] = True
|
||||
for location, item_data in locations.items():
|
||||
if len(item_data) < 3:
|
||||
locations[location] = (*item_data, 0)
|
||||
for slot, hints in decoded_obj["precollected_hints"].items():
|
||||
self.hints[0, slot].update(hints)
|
||||
|
||||
# declare slots that aren't players as done
|
||||
for slot, slot_info in self.slot_info.items():
|
||||
if slot_info.type.always_goal:
|
||||
@@ -578,7 +558,7 @@ class Context:
|
||||
self.location_check_points = savedata["game_options"]["location_check_points"]
|
||||
self.server_password = savedata["game_options"]["server_password"]
|
||||
self.password = savedata["game_options"]["password"]
|
||||
self.release_mode = savedata["game_options"]["release_mode"]
|
||||
self.release_mode = savedata["game_options"].get("release_mode", savedata["game_options"].get("forfeit_mode", "goal"))
|
||||
self.remaining_mode = savedata["game_options"]["remaining_mode"]
|
||||
self.collect_mode = savedata["game_options"]["collect_mode"]
|
||||
self.item_cheat = savedata["game_options"]["item_cheat"]
|
||||
@@ -689,7 +669,7 @@ class Context:
|
||||
def on_goal_achieved(self, client: Client):
|
||||
finished_msg = f'{self.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1})' \
|
||||
f' has completed their goal.'
|
||||
self.notify_all(finished_msg)
|
||||
self.broadcast_text_all(finished_msg, {"type": "Goal", "team": client.team, "slot": client.slot})
|
||||
if "auto" in self.collect_mode:
|
||||
collect_player(self, client.team, client.slot)
|
||||
if "auto" in self.release_mode:
|
||||
@@ -782,55 +762,42 @@ async def on_client_joined(ctx: Context, client: Client):
|
||||
update_client_status(ctx, client, ClientStatus.CLIENT_CONNECTED)
|
||||
version_str = '.'.join(str(x) for x in client.version)
|
||||
verb = "tracking" if "Tracker" in client.tags else "playing"
|
||||
ctx.notify_all(
|
||||
ctx.broadcast_text_all(
|
||||
f"{ctx.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1}) "
|
||||
f"{verb} {ctx.games[client.slot]} has joined. "
|
||||
f"Client({version_str}), {client.tags}).")
|
||||
f"Client({version_str}), {client.tags}).",
|
||||
{"type": "Join", "team": client.team, "slot": client.slot, "tags": client.tags})
|
||||
ctx.notify_client(client, "Now that you are connected, "
|
||||
"you can use !help to list commands to run via the server. "
|
||||
"If your client supports it, "
|
||||
"you may have additional local commands you can list with /help.")
|
||||
"you may have additional local commands you can list with /help.",
|
||||
{"type": "Tutorial"})
|
||||
ctx.client_connection_timers[client.team, client.slot] = datetime.datetime.now(datetime.timezone.utc)
|
||||
|
||||
|
||||
async def on_client_left(ctx: Context, client: Client):
|
||||
update_client_status(ctx, client, ClientStatus.CLIENT_UNKNOWN)
|
||||
ctx.notify_all(
|
||||
"%s (Team #%d) has left the game" % (ctx.get_aliased_name(client.team, client.slot), client.team + 1))
|
||||
ctx.broadcast_text_all(
|
||||
"%s (Team #%d) has left the game" % (ctx.get_aliased_name(client.team, client.slot), client.team + 1),
|
||||
{"type": "Part", "team": client.team, "slot": client.slot})
|
||||
ctx.client_connection_timers[client.team, client.slot] = datetime.datetime.now(datetime.timezone.utc)
|
||||
|
||||
|
||||
async def countdown(ctx: Context, timer: int):
|
||||
broadcast_countdown(ctx, timer, f"[Server]: Starting countdown of {timer}s")
|
||||
ctx.broadcast_text_all(f"[Server]: Starting countdown of {timer}s", {"type": "Countdown", "countdown": timer})
|
||||
if ctx.countdown_timer:
|
||||
ctx.countdown_timer = timer # timer is already running, set it to a different time
|
||||
else:
|
||||
ctx.countdown_timer = timer
|
||||
while ctx.countdown_timer > 0:
|
||||
broadcast_countdown(ctx, ctx.countdown_timer, f"[Server]: {ctx.countdown_timer}")
|
||||
ctx.broadcast_text_all(f"[Server]: {ctx.countdown_timer}",
|
||||
{"type": "Countdown", "countdown": ctx.countdown_timer})
|
||||
ctx.countdown_timer -= 1
|
||||
await asyncio.sleep(1)
|
||||
broadcast_countdown(ctx, 0, f"[Server]: GO")
|
||||
ctx.broadcast_text_all(f"[Server]: GO", {"type": "Countdown", "countdown": 0})
|
||||
ctx.countdown_timer = 0
|
||||
|
||||
|
||||
def broadcast_text_all(ctx: Context, text: str, additional_arguments: dict = {}):
|
||||
old_clients, new_clients = [], []
|
||||
|
||||
for teams in ctx.clients.values():
|
||||
for clients in teams.values():
|
||||
for client in clients:
|
||||
new_clients.append(client) if client.version >= print_command_compatability_threshold \
|
||||
else old_clients.append(client)
|
||||
|
||||
ctx.broadcast(old_clients, [{"cmd": "Print", "text": text }])
|
||||
ctx.broadcast(new_clients, [{**{"cmd": "PrintJSON", "data": [{ "text": text }]}, **additional_arguments}])
|
||||
|
||||
|
||||
def broadcast_countdown(ctx: Context, timer: int, message: str):
|
||||
broadcast_text_all(ctx, message, {"type": "Countdown", "countdown": timer})
|
||||
|
||||
|
||||
def get_players_string(ctx: Context):
|
||||
auth_clients = {(c.team, c.slot) for c in ctx.endpoints if c.auth}
|
||||
|
||||
@@ -898,7 +865,9 @@ def update_checked_locations(ctx: Context, team: int, slot: int):
|
||||
def release_player(ctx: Context, team: int, slot: int):
|
||||
"""register any locations that are in the multidata"""
|
||||
all_locations = set(ctx.locations[slot])
|
||||
ctx.notify_all("%s (Team #%d) has released all remaining items from their world." % (ctx.player_names[(team, slot)], team + 1))
|
||||
ctx.broadcast_text_all("%s (Team #%d) has released all remaining items from their world."
|
||||
% (ctx.player_names[(team, slot)], team + 1),
|
||||
{"type": "Release", "team": team, "slot": slot})
|
||||
register_location_checks(ctx, team, slot, all_locations)
|
||||
update_checked_locations(ctx, team, slot)
|
||||
|
||||
@@ -913,7 +882,9 @@ def collect_player(ctx: Context, team: int, slot: int, is_group: bool = False):
|
||||
if values[1] == slot:
|
||||
all_locations[source_slot].add(location_id)
|
||||
|
||||
ctx.notify_all("%s (Team #%d) has collected their items from other worlds." % (ctx.player_names[(team, slot)], team + 1))
|
||||
ctx.broadcast_text_all("%s (Team #%d) has collected their items from other worlds."
|
||||
% (ctx.player_names[(team, slot)], team + 1),
|
||||
{"type": "Collect", "team": team, "slot": slot})
|
||||
for source_player, location_ids in all_locations.items():
|
||||
register_location_checks(ctx, team, source_player, location_ids, count_activity=False)
|
||||
update_checked_locations(ctx, team, source_player)
|
||||
@@ -1183,11 +1154,15 @@ class ClientMessageProcessor(CommonCommandProcessor):
|
||||
|
||||
def __call__(self, raw: str) -> typing.Optional[bool]:
|
||||
if not raw.startswith("!admin"):
|
||||
self.ctx.notify_all(self.ctx.get_aliased_name(self.client.team, self.client.slot) + ': ' + raw)
|
||||
self.ctx.broadcast_text_all(self.ctx.get_aliased_name(self.client.team, self.client.slot) + ': ' + raw,
|
||||
{"type": "Chat", "team": self.client.team, "slot": self.client.slot, "message": raw})
|
||||
return super(ClientMessageProcessor, self).__call__(raw)
|
||||
|
||||
def output(self, text):
|
||||
self.ctx.notify_client(self.client, text)
|
||||
def output(self, text: str):
|
||||
self.ctx.notify_client(self.client, text, {"type": "CommandResult"})
|
||||
|
||||
def output_multiple(self, texts: typing.List[str]):
|
||||
self.ctx.notify_client_multiple(self.client, texts, {"type": "CommandResult"})
|
||||
|
||||
def default(self, raw: str):
|
||||
pass # default is client sending just text
|
||||
@@ -1210,9 +1185,8 @@ class ClientMessageProcessor(CommonCommandProcessor):
|
||||
# disallow others from knowing what the new remote administration password is.
|
||||
"!admin /option server_password"):
|
||||
output = f"!admin /option server_password {('*' * random.randint(4, 16))}"
|
||||
# Otherwise notify the others what is happening.
|
||||
self.ctx.notify_all(self.ctx.get_aliased_name(self.client.team,
|
||||
self.client.slot) + ': ' + output)
|
||||
self.ctx.broadcast_text_all(self.ctx.get_aliased_name(self.client.team, self.client.slot) + ': ' + output,
|
||||
{"type": "Chat", "team": self.client.team, "slot": self.client.slot, "message": output})
|
||||
|
||||
if not self.ctx.server_password:
|
||||
self.output("Sorry, Remote administration is disabled")
|
||||
@@ -1249,7 +1223,7 @@ class ClientMessageProcessor(CommonCommandProcessor):
|
||||
def _cmd_players(self) -> bool:
|
||||
"""Get information about connected and missing players."""
|
||||
if len(self.ctx.player_names) < 10:
|
||||
self.ctx.notify_all(get_players_string(self.ctx))
|
||||
self.ctx.broadcast_text_all(get_players_string(self.ctx), {"type": "CommandResult"})
|
||||
else:
|
||||
self.output(get_players_string(self.ctx))
|
||||
return True
|
||||
@@ -1338,7 +1312,7 @@ class ClientMessageProcessor(CommonCommandProcessor):
|
||||
if locations:
|
||||
texts = [f'Missing: {self.ctx.location_names[location]}' for location in locations]
|
||||
texts.append(f"Found {len(locations)} missing location checks")
|
||||
self.ctx.notify_client_multiple(self.client, texts)
|
||||
self.output_multiple(texts)
|
||||
else:
|
||||
self.output("No missing location checks found.")
|
||||
return True
|
||||
@@ -1351,7 +1325,7 @@ class ClientMessageProcessor(CommonCommandProcessor):
|
||||
if locations:
|
||||
texts = [f'Checked: {self.ctx.location_names[location]}' for location in locations]
|
||||
texts.append(f"Found {len(locations)} done location checks")
|
||||
self.ctx.notify_client_multiple(self.client, texts)
|
||||
self.output_multiple(texts)
|
||||
else:
|
||||
self.output("No done location checks found.")
|
||||
return True
|
||||
@@ -1387,9 +1361,10 @@ class ClientMessageProcessor(CommonCommandProcessor):
|
||||
new_item = NetworkItem(names[item_name], -1, self.client.slot)
|
||||
get_received_items(self.ctx, self.client.team, self.client.slot, False).append(new_item)
|
||||
get_received_items(self.ctx, self.client.team, self.client.slot, True).append(new_item)
|
||||
self.ctx.notify_all(
|
||||
self.ctx.broadcast_text_all(
|
||||
'Cheat console: sending "' + item_name + '" to ' + self.ctx.get_aliased_name(self.client.team,
|
||||
self.client.slot))
|
||||
self.client.slot),
|
||||
{"type": "ItemCheat", "team": self.client.team, "receiving": self.client.slot, "item": new_item})
|
||||
send_new_items(self.ctx)
|
||||
return True
|
||||
else:
|
||||
@@ -1688,9 +1663,10 @@ async def process_client_cmd(ctx: Context, client: Client, args: dict):
|
||||
client.tags = args["tags"]
|
||||
if set(old_tags) != set(client.tags):
|
||||
client.no_locations = 'TextOnly' in client.tags or 'Tracker' in client.tags
|
||||
ctx.notify_all(
|
||||
ctx.broadcast_text_all(
|
||||
f"{ctx.get_aliased_name(client.team, client.slot)} (Team #{client.team + 1}) has changed tags "
|
||||
f"from {old_tags} to {client.tags}.")
|
||||
f"from {old_tags} to {client.tags}.",
|
||||
{"type": "TagsChanged", "team": client.team, "slot": client.slot, "tags": client.tags})
|
||||
|
||||
elif cmd == 'Sync':
|
||||
start_inventory = get_start_inventory(ctx, client.slot, client.remote_start_inventory)
|
||||
@@ -1808,11 +1784,11 @@ class ServerCommandProcessor(CommonCommandProcessor):
|
||||
|
||||
def output(self, text: str):
|
||||
if self.client:
|
||||
self.ctx.notify_client(self.client, text)
|
||||
self.ctx.notify_client(self.client, text, {"type": "AdminCommandResult"})
|
||||
super(ServerCommandProcessor, self).output(text)
|
||||
|
||||
def default(self, raw: str):
|
||||
self.ctx.notify_all('[Server]: ' + raw)
|
||||
self.ctx.broadcast_text_all('[Server]: ' + raw, {"type": "ServerChat", "message": raw})
|
||||
|
||||
def _cmd_save(self) -> bool:
|
||||
"""Save current state to multidata"""
|
||||
@@ -1953,7 +1929,7 @@ class ServerCommandProcessor(CommonCommandProcessor):
|
||||
send_items_to(self.ctx, team, slot, *new_items)
|
||||
|
||||
send_new_items(self.ctx)
|
||||
self.ctx.notify_all(
|
||||
self.ctx.broadcast_text_all(
|
||||
'Cheat console: sending ' + ('' if amount == 1 else f'{amount} of ') +
|
||||
f'"{item_name}" to {self.ctx.get_aliased_name(team, slot)}')
|
||||
return True
|
||||
|
||||
+1
-1
@@ -197,7 +197,7 @@ def set_icon(window):
|
||||
def adjust(args):
|
||||
# Create a fake world and OOTWorld to use as a base
|
||||
world = MultiWorld(1)
|
||||
world.slot_seeds = {1: random}
|
||||
world.per_slot_randoms = {1: random}
|
||||
ootworld = OOTWorld(world, 1)
|
||||
# Set options in the fake OOTWorld
|
||||
for name, option in chain(cosmetic_options.items(), sfx_options.items()):
|
||||
|
||||
+3
-5
@@ -79,9 +79,6 @@ class AssembleOptions(abc.ABCMeta):
|
||||
|
||||
return super(AssembleOptions, mcs).__new__(mcs, name, bases, attrs)
|
||||
|
||||
@abc.abstractclassmethod
|
||||
def from_any(cls, value: typing.Any) -> "Option[typing.Any]": ...
|
||||
|
||||
|
||||
T = typing.TypeVar('T')
|
||||
|
||||
@@ -129,8 +126,9 @@ class Option(typing.Generic[T], metaclass=AssembleOptions):
|
||||
return bool(self.value)
|
||||
|
||||
@classmethod
|
||||
@abc.abstractmethod
|
||||
def from_any(cls, data: typing.Any) -> Option[T]:
|
||||
raise NotImplementedError
|
||||
...
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from Generate import PlandoOptions
|
||||
@@ -168,7 +166,7 @@ class FreeText(Option):
|
||||
return value
|
||||
|
||||
|
||||
class NumericOption(Option[int], numbers.Integral):
|
||||
class NumericOption(Option[int], numbers.Integral, abc.ABC):
|
||||
default = 0
|
||||
# note: some of the `typing.Any`` here is a result of unresolved issue in python standards
|
||||
# `int` is not a `numbers.Integral` according to the official typestubs
|
||||
|
||||
+17
-13
@@ -56,7 +56,9 @@ class SNIClientCommandProcessor(ClientCommandProcessor):
|
||||
"""Connect to a snes. Optionally include network address of a snes to connect to,
|
||||
otherwise show available devices; and a SNES device number if more than one SNES is detected.
|
||||
Examples: "/snes", "/snes 1", "/snes localhost:23074 1" """
|
||||
|
||||
if self.ctx.snes_state in {SNESState.SNES_ATTACHED, SNESState.SNES_CONNECTED, SNESState.SNES_CONNECTING}:
|
||||
self.output("Already connected to SNES. Disconnecting first.")
|
||||
self._cmd_snes_close()
|
||||
return self.connect_to_snes(snes_options)
|
||||
|
||||
def connect_to_snes(self, snes_options: str = "") -> bool:
|
||||
@@ -84,7 +86,7 @@ class SNIClientCommandProcessor(ClientCommandProcessor):
|
||||
"""Close connection to a currently connected snes"""
|
||||
self.ctx.snes_reconnect_address = None
|
||||
self.ctx.cancel_snes_autoreconnect()
|
||||
if self.ctx.snes_socket is not None and not self.ctx.snes_socket.closed:
|
||||
if self.ctx.snes_socket and not self.ctx.snes_socket.closed:
|
||||
async_start(self.ctx.snes_socket.close())
|
||||
return True
|
||||
else:
|
||||
@@ -442,7 +444,8 @@ async def snes_connect(ctx: SNIContext, address: str, deviceIndex: int = -1) ->
|
||||
recv_task = asyncio.create_task(snes_recv_loop(ctx))
|
||||
|
||||
except Exception as e:
|
||||
if recv_task is not None:
|
||||
ctx.snes_state = SNESState.SNES_DISCONNECTED
|
||||
if task_alive(recv_task):
|
||||
if not ctx.snes_socket.closed:
|
||||
await ctx.snes_socket.close()
|
||||
else:
|
||||
@@ -450,15 +453,9 @@ async def snes_connect(ctx: SNIContext, address: str, deviceIndex: int = -1) ->
|
||||
if not ctx.snes_socket.closed:
|
||||
await ctx.snes_socket.close()
|
||||
ctx.snes_socket = None
|
||||
ctx.snes_state = SNESState.SNES_DISCONNECTED
|
||||
if not ctx.snes_reconnect_address:
|
||||
snes_logger.error("Error connecting to snes (%s)" % e)
|
||||
else:
|
||||
snes_logger.error(f"Error connecting to snes, retrying in {_global_snes_reconnect_delay} seconds")
|
||||
assert ctx.snes_autoreconnect_task is None
|
||||
ctx.snes_autoreconnect_task = asyncio.create_task(snes_autoreconnect(ctx), name="snes auto-reconnect")
|
||||
snes_logger.error(f"Error connecting to snes ({e}), retrying in {_global_snes_reconnect_delay} seconds")
|
||||
ctx.snes_autoreconnect_task = asyncio.create_task(snes_autoreconnect(ctx), name="snes auto-reconnect")
|
||||
_global_snes_reconnect_delay *= 2
|
||||
|
||||
else:
|
||||
_global_snes_reconnect_delay = ctx.starting_reconnect_delay
|
||||
snes_logger.info(f"Attached to {device}")
|
||||
@@ -471,10 +468,17 @@ async def snes_disconnect(ctx: SNIContext) -> None:
|
||||
ctx.snes_socket = None
|
||||
|
||||
|
||||
def task_alive(task: typing.Optional[asyncio.Task]) -> bool:
|
||||
if task:
|
||||
return not task.done()
|
||||
return False
|
||||
|
||||
|
||||
async def snes_autoreconnect(ctx: SNIContext) -> None:
|
||||
await asyncio.sleep(_global_snes_reconnect_delay)
|
||||
if ctx.snes_reconnect_address and not ctx.snes_socket and not ctx.snes_connect_task:
|
||||
ctx.snes_connect_task = asyncio.create_task(snes_connect(ctx, ctx.snes_reconnect_address), name="SNES Connect")
|
||||
if not ctx.snes_socket and not task_alive(ctx.snes_connect_task):
|
||||
address = ctx.snes_reconnect_address if ctx.snes_reconnect_address else ctx.snes_address
|
||||
ctx.snes_connect_task = asyncio.create_task(snes_connect(ctx, address), name="SNES Connect")
|
||||
|
||||
|
||||
async def snes_recv_loop(ctx: SNIContext) -> None:
|
||||
|
||||
@@ -12,7 +12,7 @@ import io
|
||||
import collections
|
||||
import importlib
|
||||
import logging
|
||||
from typing import BinaryIO, ClassVar, Coroutine, Optional, Set
|
||||
from typing import BinaryIO, Coroutine, Optional, Set, Dict, Any, Union
|
||||
|
||||
from yaml import load, load_all, dump, SafeLoader
|
||||
|
||||
@@ -38,7 +38,7 @@ class Version(typing.NamedTuple):
|
||||
build: int
|
||||
|
||||
|
||||
__version__ = "0.3.8"
|
||||
__version__ = "0.3.9"
|
||||
version_tuple = tuplize_version(__version__)
|
||||
|
||||
is_linux = sys.platform.startswith("linux")
|
||||
@@ -662,7 +662,10 @@ def messagebox(title: str, text: str, error: bool = False) -> None:
|
||||
|
||||
def title_sorted(data: typing.Sequence, key=None, ignore: typing.Set = frozenset(("a", "the"))):
|
||||
"""Sorts a sequence of text ignoring typical articles like "a" or "the" in the beginning."""
|
||||
def sorter(element: str) -> str:
|
||||
def sorter(element: Union[str, Dict[str, Any]]) -> str:
|
||||
if (not isinstance(element, str)):
|
||||
element = element["title"]
|
||||
|
||||
parts = element.split(maxsplit=1)
|
||||
if parts[0].lower() in ignore:
|
||||
return parts[1].lower()
|
||||
|
||||
+4
-2
@@ -1,5 +1,6 @@
|
||||
import datetime
|
||||
import os
|
||||
from typing import List, Dict, Union
|
||||
|
||||
import jinja2.exceptions
|
||||
from flask import request, redirect, url_for, render_template, Response, session, abort, send_from_directory
|
||||
@@ -163,8 +164,9 @@ def get_datapackage():
|
||||
@app.route('/index')
|
||||
@app.route('/sitemap')
|
||||
def get_sitemap():
|
||||
available_games = []
|
||||
available_games: List[Dict[str, Union[str, bool]]] = []
|
||||
for game, world in AutoWorldRegister.world_types.items():
|
||||
if not world.hidden:
|
||||
available_games.append(game)
|
||||
has_settings: bool = isinstance(world.web.settings_page, bool) and world.web.settings_page
|
||||
available_games.append({ 'title': game, 'has_settings': has_settings })
|
||||
return render_template("siteMap.html", games=available_games)
|
||||
|
||||
@@ -20,7 +20,7 @@ comfortable exploiting certain glitches in the game.
|
||||
## What is a multi-world?
|
||||
|
||||
While a randomizer shuffles a game, a multi-world randomizer shuffles that game for multiple players. For example, in a
|
||||
two player multi-world, players A and B each get their own randomized version of a game, called seeds. In each player's
|
||||
two player multi-world, players A and B each get their own randomized version of a game, called a world. In each player's
|
||||
game, they may find items which belong to the other player. If player A finds an item which belongs to player B, the
|
||||
item will be sent to player B's world over the internet.
|
||||
|
||||
|
||||
@@ -18,7 +18,8 @@ window.addEventListener('load', () => {
|
||||
info: false,
|
||||
dom: "t",
|
||||
stateSave: true,
|
||||
stateSaveCallback: function(settings,data) {
|
||||
stateSaveCallback: function(settings, data) {
|
||||
delete data.search;
|
||||
localStorage.setItem(`DataTables_${settings.sInstance}_/tracker`, JSON.stringify(data));
|
||||
},
|
||||
stateLoadCallback: function(settings) {
|
||||
@@ -70,10 +71,30 @@ window.addEventListener('load', () => {
|
||||
// the tbody and render two separate tables.
|
||||
});
|
||||
|
||||
document.getElementById('search').addEventListener('keyup', (event) => {
|
||||
tables.search(event.target.value);
|
||||
console.info(tables.search());
|
||||
const searchBox = document.getElementById("search");
|
||||
searchBox.value = tables.search();
|
||||
searchBox.focus();
|
||||
searchBox.select();
|
||||
const doSearch = () => {
|
||||
tables.search(searchBox.value);
|
||||
tables.draw();
|
||||
};
|
||||
searchBox.addEventListener("keyup", doSearch);
|
||||
window.addEventListener("keydown", (event) => {
|
||||
if (!event.ctrlKey && !event.altKey && event.key.length === 1 && document.activeElement !== searchBox) {
|
||||
searchBox.focus();
|
||||
searchBox.select();
|
||||
}
|
||||
if (!event.ctrlKey && !event.altKey && !event.shiftKey && event.key === "Escape") {
|
||||
if (searchBox.value !== "") {
|
||||
searchBox.value = "";
|
||||
doSearch();
|
||||
}
|
||||
searchBox.blur();
|
||||
if (!document.getElementById("tables-container"))
|
||||
window.scroll(0, 0);
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
const tracker = document.getElementById('tracker-wrapper').getAttribute('data-tracker');
|
||||
const target_second = document.getElementById('tracker-wrapper').getAttribute('data-second') + 3;
|
||||
|
||||
@@ -9,19 +9,54 @@
|
||||
border-top-left-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
padding: 3px 3px 10px;
|
||||
width: 384px;
|
||||
width: 374px;
|
||||
background-color: #8d60a7;
|
||||
}
|
||||
|
||||
#inventory-table td{
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
display: grid;
|
||||
grid-template-rows: repeat(5, 48px);
|
||||
}
|
||||
|
||||
#inventory-table img{
|
||||
display: block;
|
||||
}
|
||||
|
||||
#inventory-table div.table-row{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, 1fr);
|
||||
}
|
||||
|
||||
#inventory-table div.C1{
|
||||
grid-column: 1;
|
||||
place-content: center;
|
||||
place-items: center;
|
||||
display: flex;
|
||||
}
|
||||
#inventory-table div.C2{
|
||||
grid-column: 2;
|
||||
place-content: center;
|
||||
place-items: center;
|
||||
display: flex;
|
||||
}
|
||||
#inventory-table div.C3{
|
||||
grid-column: 3;
|
||||
place-content: center;
|
||||
place-items: center;
|
||||
display: flex;
|
||||
}
|
||||
#inventory-table div.C4{
|
||||
grid-column: 4;
|
||||
place-content: center;
|
||||
place-items: center;
|
||||
display: flex;
|
||||
}
|
||||
#inventory-table div.C5{
|
||||
grid-column: 5;
|
||||
place-content: center;
|
||||
place-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#inventory-table img{
|
||||
height: 100%;
|
||||
max-width: 40px;
|
||||
max-height: 40px;
|
||||
filter: grayscale(100%) contrast(75%) brightness(30%);
|
||||
@@ -31,11 +66,70 @@
|
||||
filter: none;
|
||||
}
|
||||
|
||||
#inventory-table div.counted-item {
|
||||
#inventory-table img.acquired.purple{ /*00FFFF*/
|
||||
filter: hue-rotate(270deg) saturate(6) brightness(0.8);
|
||||
}
|
||||
#inventory-table img.acquired.cyan{ /*FF00FF*/
|
||||
filter: hue-rotate(138deg) saturate(10) brightness(0.8);
|
||||
}
|
||||
#inventory-table img.acquired.green{ /*32CD32*/
|
||||
filter: hue-rotate(84deg) saturate(10) brightness(0.7);
|
||||
}
|
||||
|
||||
#inventory-table div.image-stack{
|
||||
display: grid;
|
||||
position: relative;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
|
||||
#inventory-table div.image-stack div.stack-back{
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
}
|
||||
|
||||
#inventory-table div.image-stack div.stack-front{
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
display: grid;
|
||||
grid-template-columns: 20px 20px;
|
||||
grid-template-rows: 20px 20px;
|
||||
}
|
||||
|
||||
#inventory-table div.image-stack div.stack-top-left{
|
||||
grid-column: 1;
|
||||
grid-row: 1;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#inventory-table div.image-stack div.stack-top-right{
|
||||
grid-column: 2;
|
||||
grid-row: 1;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#inventory-table div.image-stack div.stack-bottum-left{
|
||||
grid-column: 1;
|
||||
grid-row: 2;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#inventory-table div.image-stack div.stack-bottum-right{
|
||||
grid-column: 2;
|
||||
grid-row: 2;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#inventory-table div.image-stack div.stack-front img{
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
#inventory-table div.counted-item{
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#inventory-table div.item-count {
|
||||
#inventory-table div.item-count{
|
||||
position: absolute;
|
||||
color: white;
|
||||
font-family: "Minecraftia", monospace;
|
||||
@@ -69,16 +163,16 @@
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
#location-table td.counter {
|
||||
#location-table td.counter{
|
||||
text-align: right;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#location-table td.toggle-arrow {
|
||||
#location-table td.toggle-arrow{
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#location-table tr#Total-header {
|
||||
#location-table tr#Total-header{
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
@@ -88,14 +182,14 @@
|
||||
max-height: 30px;
|
||||
}
|
||||
|
||||
#location-table tbody.locations {
|
||||
#location-table tbody.locations{
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
#location-table td.location-name {
|
||||
#location-table td.location-name{
|
||||
padding-left: 16px;
|
||||
}
|
||||
|
||||
.hide {
|
||||
.hide{
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -29,17 +29,30 @@
|
||||
<li><a href="/glossary/en">Glossary</a></li>
|
||||
</ul>
|
||||
|
||||
<h2>Tutorials</h2>
|
||||
<ul>
|
||||
<li><a href="/tutorial/Archipelago/setup/en">Multiworld Setup Tutorial</a></li>
|
||||
<li><a href="/tutorial/Archipelago/using_website/en">Website User Guide</a></li>
|
||||
<li><a href="/tutorial/Archipelago/mac/en">Setup Guide for Mac</a></li>
|
||||
<li><a href="/tutorial/Archipelago/commands/en">Server and Client Commands</a></li>
|
||||
<li><a href="/tutorial/Archipelago/advanced_settings/en">Advanced YAML Guide</a></li>
|
||||
<li><a href="/tutorial/Archipelago/triggers/en">Triggers Guide</a></li>
|
||||
<li><a href="/tutorial/Archipelago/plando/en">Plando Guide</a></li>
|
||||
</ul>
|
||||
|
||||
<h2>Game Info Pages</h2>
|
||||
<ul>
|
||||
{% for game in games | title_sorted %}
|
||||
<li><a href="{{ url_for('game_info', game=game, lang='en') }}">{{ game }}</a></li>
|
||||
<li><a href="{{ url_for('game_info', game=game['title'], lang='en') }}">{{ game['title'] }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
|
||||
<h2>Game Settings Pages</h2>
|
||||
<ul>
|
||||
{% for game in games | title_sorted %}
|
||||
<li><a href="{{ url_for('player_settings', game=game) }}">{{ game }}</a></li>
|
||||
{% if game['has_settings'] %}
|
||||
<li><a href="{{ url_for('player_settings', game=game['title']) }}">{{ game['title'] }}</a></li>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@@ -8,79 +8,94 @@
|
||||
|
||||
<body>
|
||||
<div id="player-tracker-wrapper" data-tracker="{{ room.tracker|suuid }}">
|
||||
<table id="inventory-table">
|
||||
<tr>
|
||||
<td><img src="{{ icons['Timespinner Wheel'] }}" class="{{ 'acquired' if 'Timespinner Wheel' in acquired_items }}" title="Timespinner Wheel" /></td>
|
||||
<td><img src="{{ icons['Timespinner Spindle'] }}" class="{{ 'acquired' if 'Timespinner Spindle' in acquired_items }}" title="Timespinner Spindle" /></td>
|
||||
<td><img src="{{ icons['Timespinner Gear 1'] }}" class="{{ 'acquired' if 'Timespinner Gear 1' in acquired_items }}" title="Timespinner Gear 1" /></td>
|
||||
<td><img src="{{ icons['Timespinner Gear 2'] }}" class="{{ 'acquired' if 'Timespinner Gear 2' in acquired_items }}" title="Timespinner Gear 2" /></td>
|
||||
<td><img src="{{ icons['Timespinner Gear 3'] }}" class="{{ 'acquired' if 'Timespinner Gear 3' in acquired_items }}" title="Timespinner Gear 3" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="{{ icons['Talaria Attachment'] }}" class="{{ 'acquired' if 'Talaria Attachment' in acquired_items or 'QuickSeed' in options }}" title="Talaria Attachment" /></td>
|
||||
<td><img src="{{ icons['Succubus Hairpin'] }}" class="{{ 'acquired' if 'Succubus Hairpin' in acquired_items }}" title="Succubus Hairpin" /></td>
|
||||
<td><img src="{{ icons['Lightwall'] }}" class="{{ 'acquired' if 'Lightwall' in acquired_items }}" title="Lightwall" /></td>
|
||||
<td><img src="{{ icons['Celestial Sash'] }}" class="{{ 'acquired' if 'Celestial Sash' in acquired_items }}" title="Celestial Sash" /></td>
|
||||
<td><img src="{{ icons['Twin Pyramid Key'] }}" class="{{ 'acquired' if 'Twin Pyramid Key' in acquired_items }}" title="Twin Pyramid Key" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><img src="{{ icons['Security Keycard A'] }}" class="{{ 'acquired' if 'Security Keycard A' in acquired_items }}" title="Security Keycard A" /></td>
|
||||
<td><img src="{{ icons['Security Keycard B'] }}" class="{{ 'acquired' if 'Security Keycard B' in acquired_items }}" title="Security Keycard B" /></td>
|
||||
<td><img src="{{ icons['Security Keycard C'] }}" class="{{ 'acquired' if 'Security Keycard C' in acquired_items }}" title="Security Keycard C" /></td>
|
||||
<td><img src="{{ icons['Security Keycard D'] }}" class="{{ 'acquired' if 'Security Keycard D' in acquired_items }}" title="Security Keycard D" /></td>
|
||||
{% if 'DownloadableItems' in options %}
|
||||
<td><img src="{{ icons['Library Keycard V'] }}" class="{{ 'acquired' if 'Library Keycard V' in acquired_items }}" title="Library Keycard V" /></td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
<tr>
|
||||
{% if 'DownloadableItems' in options %}
|
||||
<td><img src="{{ icons['Tablet'] }}" class="{{ 'acquired' if 'Tablet' in acquired_items }}" title="Tablet" /></td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
<td><img src="{{ icons['Elevator Keycard'] }}" class="{{ 'acquired' if 'Elevator Keycard' in acquired_items }}" title="Elevator Keycard" /></td>
|
||||
{% if 'EyeSpy' in options %}
|
||||
<td><img src="{{ icons['Oculus Ring'] }}" class="{{ 'acquired' if 'Oculus Ring' in acquired_items }}" title="Oculus Ring" /></td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
{% endif %}
|
||||
<td><img src="{{ icons['Water Mask'] }}" class="{{ 'acquired' if 'Water Mask' in acquired_items }}" title="Water Mask" /></td>
|
||||
<td><img src="{{ icons['Gas Mask'] }}" class="{{ 'acquired' if 'Gas Mask' in acquired_items }}" title="Gas Mask" /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
{% if 'GyreArchives' in options %}
|
||||
<td><img src="{{ icons['Kobo'] }}" class="{{ 'acquired' if 'Kobo' in acquired_items }}" title="Kobo" /></td>
|
||||
<td><img src="{{ icons['Merchant Crow'] }}" class="{{ 'acquired' if 'Merchant Crow' in acquired_items }}" title="Merchant Crow" /></td>
|
||||
{% else %}
|
||||
<td></td>
|
||||
<td></td>
|
||||
{% endif %}
|
||||
|
||||
<div id="inventory-table">
|
||||
<div class="table-row">
|
||||
<div class="C1"><img src="{{ icons['Timespinner Wheel'] }}" class="{{ 'acquired' if 'Timespinner Wheel' in acquired_items }}" title="Timespinner Wheel" /></div>
|
||||
<div class="C2"><img src="{{ icons['Timespinner Spindle'] }}" class="{{ 'acquired' if 'Timespinner Spindle' in acquired_items }}" title="Timespinner Spindle" /></div>
|
||||
<div class="C3"><img src="{{ icons['Timespinner Gear 1'] }}" class="{{ 'acquired' if 'Timespinner Gear 1' in acquired_items }}" title="Timespinner Gear 1" /></div>
|
||||
<div class="C4"><img src="{{ icons['Timespinner Gear 2'] }}" class="{{ 'acquired' if 'Timespinner Gear 2' in acquired_items }}" title="Timespinner Gear 2" /></div>
|
||||
<div class="C5"><img src="{{ icons['Timespinner Gear 3'] }}" class="{{ 'acquired' if 'Timespinner Gear 3' in acquired_items }}" title="Timespinner Gear 3" /></div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="C1"><img src="{{ icons['Talaria Attachment'] }}" class="{{ 'acquired' if 'Talaria Attachment' in acquired_items or 'QuickSeed' in options }}" title="Talaria Attachment" /></div>
|
||||
<div class="C2"><img src="{{ icons['Succubus Hairpin'] }}" class="{{ 'acquired' if 'Succubus Hairpin' in acquired_items }}" title="Succubus Hairpin" /></div>
|
||||
<div class="C3"><img src="{{ icons['Lightwall'] }}" class="{{ 'acquired' if 'Lightwall' in acquired_items }}" title="Lightwall" /></div>
|
||||
<div class="C4"><img src="{{ icons['Celestial Sash'] }}" class="{{ 'acquired' if 'Celestial Sash' in acquired_items }}" title="Celestial Sash" /></div>
|
||||
<div class="C5">
|
||||
<div class="image-stack">
|
||||
<div class="stack-back">
|
||||
<img src="{{ icons['Twin Pyramid Key'] }}" class="{{ 'acquired' if 'Twin Pyramid Key' in acquired_items or 'UnchainedKeys' in options }}" title="Twin Pyramid Key" />
|
||||
</div>
|
||||
<div class="stack-front">
|
||||
{% if 'UnchainedKeys' in options %}
|
||||
{% if 'EnterSandman' in options %}
|
||||
<div class="stack-top-right">
|
||||
<img src="{{ icons['Twin Pyramid Key'] }}" class="green {{ 'acquired' if 'Mysterious Warp Beacon' in acquired_items }}" title="Mysterious Warp Beacon" />
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="stack-bottum-left">
|
||||
<img src="{{ icons['Twin Pyramid Key'] }}" class="cyan {{ 'acquired' if 'Timeworn Warp Beacon' in acquired_items }}" title="Timeworn Warp Beacon" />
|
||||
</div>
|
||||
<div class="stack-bottum-right">
|
||||
<img src="{{ icons['Twin Pyramid Key'] }}" class="purple {{ 'acquired' if 'Modern Warp Beacon' in acquired_items }}" title="Modern Warp Beacon" />
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
<div class="C1"><img src="{{ icons['Security Keycard A'] }}" class="{{ 'acquired' if 'Security Keycard A' in acquired_items }}" title="Security Keycard A" /></div>
|
||||
<div class="C2"><img src="{{ icons['Security Keycard B'] }}" class="{{ 'acquired' if 'Security Keycard B' in acquired_items }}" title="Security Keycard B" /></div>
|
||||
<div class="C3"><img src="{{ icons['Security Keycard C'] }}" class="{{ 'acquired' if 'Security Keycard C' in acquired_items }}" title="Security Keycard C" /></div>
|
||||
<div class="C4"><img src="{{ icons['Security Keycard D'] }}" class="{{ 'acquired' if 'Security Keycard D' in acquired_items }}" title="Security Keycard D" /></div>
|
||||
{% if 'DownloadableItems' in options %}
|
||||
<div class="C5"><img src="{{ icons['Library Keycard V'] }}" class="{{ 'acquired' if 'Library Keycard V' in acquired_items }}" title="Library Keycard V" /></div>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="table-row">
|
||||
{% if 'DownloadableItems' in options %}
|
||||
<div class="C1"><img src="{{ icons['Tablet'] }}" class="{{ 'acquired' if 'Tablet' in acquired_items }}" title="Tablet" /></div>
|
||||
{% endif %}
|
||||
<div class="C2"><img src="{{ icons['Elevator Keycard'] }}" class="{{ 'acquired' if 'Elevator Keycard' in acquired_items }}" title="Elevator Keycard" /></div>
|
||||
{% if 'EyeSpy' in options %}
|
||||
<div class="C3"><img src="{{ icons['Oculus Ring'] }}" class="{{ 'acquired' if 'Oculus Ring' in acquired_items }}" title="Oculus Ring" /></div>
|
||||
{% endif %}
|
||||
<div class="C4"><img src="{{ icons['Water Mask'] }}" class="{{ 'acquired' if 'Water Mask' in acquired_items }}" title="Water Mask" /></div>
|
||||
<div class="C5"><img src="{{ icons['Gas Mask'] }}" class="{{ 'acquired' if 'Gas Mask' in acquired_items }}" title="Gas Mask" /></div>
|
||||
</div>
|
||||
<div class="table-row">
|
||||
{% if 'GyreArchives' in options %}
|
||||
<div class="C1"><img src="{{ icons['Kobo'] }}" class="{{ 'acquired' if 'Kobo' in acquired_items }}" title="Kobo" /></div>
|
||||
<div class="C2"><img src="{{ icons['Merchant Crow'] }}" class="{{ 'acquired' if 'Merchant Crow' in acquired_items }}" title="Merchant Crow" /></div>
|
||||
{% endif %}
|
||||
<div class="C3">
|
||||
{% if 'Djinn Inferno' in acquired_items %}
|
||||
<td><img src="{{ icons['Djinn Inferno'] }}" class="acquired" title="Djinn Inferno" /></td>
|
||||
<img src="{{ icons['Djinn Inferno'] }}" class="acquired" title="Djinn Inferno" />
|
||||
{% elif 'Pyro Ring' in acquired_items %}
|
||||
<td><img src="{{ icons['Pyro Ring'] }}" class="acquired" title="Pyro Ring" /></td>
|
||||
<img src="{{ icons['Pyro Ring'] }}" class="acquired" title="Pyro Ring" />
|
||||
{% elif 'Fire Orb' in acquired_items %}
|
||||
<td><img src="{{ icons['Fire Orb'] }}" class="acquired" title="Fire Orb" /></td>
|
||||
<img src="{{ icons['Fire Orb'] }}" class="acquired" title="Fire Orb" />
|
||||
{% elif 'Infernal Flames' in acquired_items %}
|
||||
<td><img src="{{ icons['Infernal Flames'] }}" class="acquired" title="Infernal Flames" /></td>
|
||||
<img src="{{ icons['Infernal Flames'] }}" class="acquired" title="Infernal Flames" />
|
||||
{% else %}
|
||||
<td><img src="{{ icons['Djinn Inferno'] }}" title="Djinn Inferno" /></td>
|
||||
<img src="{{ icons['Djinn Inferno'] }}" title="Djinn Inferno" />
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
<div class="C4">
|
||||
{% if 'Royal Ring' in acquired_items %}
|
||||
<td><img src="{{ icons['Royal Ring'] }}" class="acquired" title="Royal Ring" /></td>
|
||||
<img src="{{ icons['Royal Ring'] }}" class="acquired" title="Royal Ring" />
|
||||
{% elif 'Plasma Geyser' in acquired_items %}
|
||||
<td><img src="{{ icons['Plasma Geyser'] }}" class="acquired" title="Plasma Geyser" /></td>
|
||||
<img src="{{ icons['Plasma Geyser'] }}" class="acquired" title="Plasma Geyser" />
|
||||
{% elif 'Plasma Orb' in acquired_items %}
|
||||
<td><img src="{{ icons['Plasma Orb'] }}" class="acquired" title="Plasma Orb" /></td>
|
||||
<img src="{{ icons['Plasma Orb'] }}" class="acquired" title="Plasma Orb" />
|
||||
{% else %}
|
||||
<td><img src="{{ icons['Royal Ring'] }}" title="Royal Ring" /></td>
|
||||
<img src="{{ icons['Royal Ring'] }}" title="Royal Ring" />
|
||||
{% endif %}
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table id="location-table">
|
||||
{% for area in checks_done %}
|
||||
<tr class="location-category" id="{{area}}-header">
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
<th colspan="{{ colspan }}" class="center-column">{{ area }}</th>
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
<th rowspan="2" class="center-column">%</th>
|
||||
<th rowspan="2" class="center-column hours">Last<br>Activity</th>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -140,6 +141,7 @@
|
||||
<td class="center-column">{% if inventory[team][player][big_key_ids[area]] %}✔️{% endif %}</td>
|
||||
{%- endif -%}
|
||||
{%- endfor -%}
|
||||
<td class="center-column">{{ percent_total_checks_done[team][player] }}</td>
|
||||
{%- if activity_timers[(team, player)] -%}
|
||||
<td class="center-column">{{ activity_timers[(team, player)].total_seconds() }}</td>
|
||||
{%- else -%}
|
||||
|
||||
@@ -1232,6 +1232,11 @@ def getTracker(tracker: UUID):
|
||||
for playernumber in range(1, len(team) + 1) if playernumber not in groups}
|
||||
for teamnumber, team in enumerate(names)}
|
||||
|
||||
percent_total_checks_done = {teamnumber: {playernumber: 0
|
||||
for playernumber in range(1, len(team) + 1) if playernumber not in groups}
|
||||
for teamnumber, team in enumerate(names)}
|
||||
|
||||
|
||||
hints = {team: set() for team in range(len(names))}
|
||||
if room.multisave:
|
||||
multisave = restricted_loads(room.multisave)
|
||||
@@ -1259,6 +1264,7 @@ def getTracker(tracker: UUID):
|
||||
attribute_item(inventory, team, recipient, item)
|
||||
checks_done[team][player][player_location_to_area[player][location]] += 1
|
||||
checks_done[team][player]["Total"] += 1
|
||||
percent_total_checks_done[team][player] = int(checks_done[team][player]["Total"] / seed_checks_in_area[player]["Total"] * 100) if seed_checks_in_area[player]["Total"] else 100
|
||||
|
||||
for (team, player), game_state in multisave.get("client_game_state", {}).items():
|
||||
if player in groups:
|
||||
@@ -1303,8 +1309,8 @@ def getTracker(tracker: UUID):
|
||||
return render_template("tracker.html", inventory=inventory, get_item_name_from_id=lookup_any_item_id_to_name,
|
||||
lookup_id_to_name=Items.lookup_id_to_name, player_names=player_names,
|
||||
tracking_names=tracking_names, tracking_ids=tracking_ids, room=room, icons=alttp_icons,
|
||||
multi_items=multi_items, checks_done=checks_done, ordered_areas=ordered_areas,
|
||||
checks_in_area=seed_checks_in_area, activity_timers=activity_timers,
|
||||
multi_items=multi_items, checks_done=checks_done, percent_total_checks_done=percent_total_checks_done,
|
||||
ordered_areas=ordered_areas, checks_in_area=seed_checks_in_area, activity_timers=activity_timers,
|
||||
key_locations=group_key_locations, small_key_ids=small_key_ids, big_key_ids=big_key_ids,
|
||||
video=video, big_key_locations=group_big_key_locations,
|
||||
hints=hints, long_player_names=long_player_names)
|
||||
|
||||
+20
-1
@@ -1,4 +1,21 @@
|
||||
<TabbedPanel>
|
||||
<TextColors>:
|
||||
# Hex-format RGB colors used in clients. Resets after an update/install.
|
||||
# To avoid, you can copy the TextColors section into a new "user.kv" next to this file
|
||||
# and it will read from there instead.
|
||||
black: "000000"
|
||||
red: "EE0000"
|
||||
green: "00FF7F" # typically a location
|
||||
yellow: "FAFAD2" # typically other slots/players
|
||||
blue: "6495ED" # typically extra info (such as entrance)
|
||||
magenta: "EE00EE" # typically your slot/player
|
||||
cyan: "00EEEE" # typically regular item
|
||||
slateblue: "6D8BE8" # typically useful item
|
||||
plum: "AF99EF" # typically progression item
|
||||
salmon: "FA8072" # typically trap item
|
||||
white: "FFFFFF" # not used, if you want to change the generic text color change color in Label
|
||||
<Label>:
|
||||
color: "FFFFFF"
|
||||
<TabbedPanel>:
|
||||
tab_width: root.width / app.tab_count
|
||||
<SelectableLabel>:
|
||||
canvas.before:
|
||||
@@ -13,6 +30,8 @@
|
||||
font_size: dp(20)
|
||||
markup: True
|
||||
<UILog>:
|
||||
messages: 1000 # amount of messages stored in client logs.
|
||||
cols: 1
|
||||
viewclass: 'SelectableLabel'
|
||||
scroll_y: 0
|
||||
scroll_type: ["content", "bars"]
|
||||
|
||||
+34
-26
@@ -9,7 +9,7 @@ These steps should be followed in order to establish a gameplay connection with
|
||||
5. Client sends [Connect](#Connect) packet in order to authenticate with the server.
|
||||
6. Server validates the client's packet and responds with [Connected](#Connected) or [ConnectionRefused](#ConnectionRefused).
|
||||
7. Server may send [ReceivedItems](#ReceivedItems) to the client, in the case that the client is missing items that are queued up for it.
|
||||
8. Server sends [Print](#Print) to all players to notify them of the new client connection.
|
||||
8. Server sends [PrintJSON](#PrintJSON) to all players to notify them of the new client connection.
|
||||
|
||||
In the case that the client does not authenticate properly and receives a [ConnectionRefused](#ConnectionRefused) then the server will maintain the connection and allow for follow-up [Connect](#Connect) packet.
|
||||
|
||||
@@ -54,7 +54,6 @@ These packets are are sent from the multiworld server to the client. They are no
|
||||
* [ReceivedItems](#ReceivedItems)
|
||||
* [LocationInfo](#LocationInfo)
|
||||
* [RoomUpdate](#RoomUpdate)
|
||||
* [Print](#Print)
|
||||
* [PrintJSON](#PrintJSON)
|
||||
* [DataPackage](#DataPackage)
|
||||
* [Bounced](#Bounced)
|
||||
@@ -160,35 +159,44 @@ The arguments for RoomUpdate are identical to [RoomInfo](#RoomInfo) barring:
|
||||
|
||||
All arguments for this packet are optional, only changes are sent.
|
||||
|
||||
### Print
|
||||
Sent to clients purely to display a message to the player.
|
||||
* *Deprecation warning: clients that connect with version 0.3.5 or higher will nolonger recieve Print packets, instead all messsages are send as [PrintJSON](#PrintJSON)*
|
||||
#### Arguments
|
||||
| Name | Type | Notes |
|
||||
| ---- | ---- | ----- |
|
||||
| text | str | Message to display to player. |
|
||||
|
||||
### PrintJSON
|
||||
Sent to clients purely to display a message to the player. This packet differs from [Print](#Print) in that the data being sent with this packet allows for more configurable or specific messaging.
|
||||
Sent to clients purely to display a message to the player. While various message types provide additional arguments, clients only need to evaluate the `data` argument to construct the human-readable message text. All other arguments may be ignored safely.
|
||||
#### Arguments
|
||||
| Name | Type | Notes |
|
||||
| ---- | ---- | ----- |
|
||||
| data | list\[[JSONMessagePart](#JSONMessagePart)\] | Type of this part of the message. |
|
||||
| type | str | May be present to indicate the [PrintJsonType](#PrintJsonType) of this message. |
|
||||
| receiving | int | Is present if type is Hint or ItemSend and marks the destination player's ID. |
|
||||
| item | [NetworkItem](#NetworkItem) | Is present if type is Hint or ItemSend and marks the source player id, location id, item id and item flags. |
|
||||
| found | bool | Is present if type is Hint, denotes whether the location hinted for was checked. |
|
||||
| countdown | int | Is present if type is `Countdown`, denotes the amount of seconds remaining on the countdown. |
|
||||
| Name | Type | Message Types | Contents |
|
||||
| ---- | ---- | ------------- | -------- |
|
||||
| data | list\[[JSONMessagePart](#JSONMessagePart)\] | (all) | Textual content of this message |
|
||||
| type | str | (any) | [PrintJsonType](#PrintJsonType) of this message (optional) |
|
||||
| receiving | int | ItemSend, ItemCheat, Hint | Destination player's ID |
|
||||
| item | [NetworkItem](#NetworkItem) | ItemSend, ItemCheat, Hint | Source player's ID, location ID, item ID and item flags |
|
||||
| found | bool | Hint | Whether the location hinted for was checked |
|
||||
| team | int | Join, Part, Chat, TagsChanged, Goal, Release, Collect, ItemCheat | Team of the triggering player |
|
||||
| slot | int | Join, Part, Chat, TagsChanged, Goal, Release, Collect | Slot of the triggering player |
|
||||
| message | str | Chat, ServerChat | Original chat message without sender prefix |
|
||||
| tags | list\[str\] | Join, TagsChanged | Tags of the triggering player |
|
||||
| countdown | int | Countdown | Amount of seconds remaining on the countdown |
|
||||
|
||||
##### PrintJsonType
|
||||
PrintJsonType indicates the type of [PrintJson](#PrintJson) packet, different types can be handled differently by the client and can also contain additional arguments. When receiving an unknown type the data's list\[[JSONMessagePart](#JSONMessagePart)\] should still be printed as normal.
|
||||
#### PrintJsonType
|
||||
PrintJsonType indicates the type of a [PrintJSON](#PrintJSON) packet. Different types can be handled differently by the client and can also contain additional arguments. When receiving an unknown or missing type, the `data`'s list\[[JSONMessagePart](#JSONMessagePart)\] should still be displayed to the player as normal text.
|
||||
|
||||
Currently defined types are:
|
||||
| Type | Notes |
|
||||
| ---- | ----- |
|
||||
| ItemSend | The message is in response to a player receiving an item. |
|
||||
| Hint | The message is in response to a player hinting. |
|
||||
| Countdown | The message contains information about the current server Countdown. |
|
||||
|
||||
| Type | Subject |
|
||||
| ---- | ------- |
|
||||
| ItemSend | A player received an item. |
|
||||
| ItemCheat | A player used the `!getitem` command. |
|
||||
| Hint | A player hinted. |
|
||||
| Join | A player connected. |
|
||||
| Part | A player disconnected. |
|
||||
| Chat | A player sent a chat message. |
|
||||
| ServerChat | The server broadcasted a message. |
|
||||
| Tutorial | The client has triggered a tutorial message, such as when first connecting. |
|
||||
| TagsChanged | A player changed their tags. |
|
||||
| CommandResult | Someone (usually the client) entered an `!` command. |
|
||||
| AdminCommandResult | The client entered an `!admin` command. |
|
||||
| Goal | A player reached their goal. |
|
||||
| Release | A player released the remaining items in their world. |
|
||||
| Collect | A player collected the remaining items for their world. |
|
||||
| Countdown | The current server countdown has progressed. |
|
||||
|
||||
### DataPackage
|
||||
Sent to clients to provide what is known as a 'data package' which contains information to enable a client to most easily communicate with the Archipelago server. Contents include things like location id to name mappings, among others; see [Data Package Contents](#Data-Package-Contents) for more info.
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
* Use type annotations where possible for function signatures and class members.
|
||||
* Use type annotations where appropriate for local variables (e.g. `var: List[int] = []`, or when the
|
||||
type is hard or impossible to deduce.) Clear annotations help developers look up and validate API calls.
|
||||
* Worlds that do not follow PEP8 should still have a consistent style across its files to make reading easier.
|
||||
|
||||
|
||||
## Markdown
|
||||
|
||||
+81
-21
@@ -402,40 +402,43 @@ The world has to provide the following things for generation
|
||||
* additions to the regions list: at least one called "Menu"
|
||||
* locations placed inside those regions
|
||||
* a `def create_item(self, item: str) -> MyGameItem` to create any item on demand
|
||||
* applying `self.world.push_precollected` for start inventory
|
||||
* a `def generate_output(self, output_directory: str)` that creates the output
|
||||
files if there is output to be generated. When this is
|
||||
called, `self.world.get_locations(self.player)` has all locations for the player, with
|
||||
attribute `item` pointing to the item.
|
||||
`location.item.player` can be used to see if it's a local item.
|
||||
* applying `self.multiworld.push_precollected` for start inventory
|
||||
* `required_client_version: Tuple(int, int, int)`
|
||||
Optional client version as tuple of 3 ints to make sure the client is compatible to
|
||||
this world (e.g. implements all required features) when connecting.
|
||||
|
||||
In addition, the following methods can be implemented and attributes can be set
|
||||
In addition, the following methods can be implemented and are called in this order during generation
|
||||
|
||||
* `stage_assert_generate(cls, multiworld)` is a class method called at the start of
|
||||
generation to check the existence of prerequisite files, usually a ROM for
|
||||
games which require one.
|
||||
* `def generate_early(self)`
|
||||
called per player before any items or locations are created. You can set
|
||||
properties on your world here. Already has access to player options and RNG.
|
||||
* `def create_regions(self)`
|
||||
called to place player's regions and their locations into the MultiWorld's regions list. If it's
|
||||
hard to separate, this can be done during `generate_early` or `basic` as well.
|
||||
hard to separate, this can be done during `generate_early` or `create_items` as well.
|
||||
* `def create_items(self)`
|
||||
called to place player's items into the MultiWorld's itempool.
|
||||
called to place player's items into the MultiWorld's itempool. After this step all regions and items have to be in
|
||||
the MultiWorld's regions and itempool, and these lists should not be modified afterwards.
|
||||
* `def set_rules(self)`
|
||||
called to set access and item rules on locations and entrances.
|
||||
Locations have to be defined before this, or rule application can miss them.
|
||||
* `def generate_basic(self)`
|
||||
called after the previous steps. Some placement and player specific
|
||||
randomizations can be done here. After this step all regions and items have
|
||||
to be in the MultiWorld's regions and itempool.
|
||||
randomizations can be done here.
|
||||
* `pre_fill`, `fill_hook` and `post_fill` are called to modify item placement
|
||||
before, during and after the regular fill process, before `generate_output`.
|
||||
If items need to be placed during pre_fill, these items can be determined
|
||||
and created using `get_prefill_items`
|
||||
* `def generate_output(self, output_directory: str)` that creates the output
|
||||
files if there is output to be generated. When this is
|
||||
called, `self.multiworld.get_locations(self.player)` has all locations for the player, with
|
||||
attribute `item` pointing to the item.
|
||||
`location.item.player` can be used to see if it's a local item.
|
||||
* `fill_slot_data` and `modify_multidata` can be used to modify the data that
|
||||
will be used by the server to host the MultiWorld.
|
||||
* `required_client_version: Tuple(int, int, int)`
|
||||
Client version as tuple of 3 ints to make sure the client is compatible to
|
||||
this world (e.g. implements all required features) when connecting.
|
||||
* `assert_generate(cls, world)` is a class method called at the start of
|
||||
generation to check the existence of prerequisite files, usually a ROM for
|
||||
games which require one.
|
||||
|
||||
|
||||
#### generate_early
|
||||
|
||||
@@ -497,21 +500,21 @@ def create_items(self) -> None:
|
||||
```python
|
||||
def create_regions(self) -> None:
|
||||
# Add regions to the multiworld. "Menu" is the required starting point.
|
||||
# Arguments to Region() are name, type, human_readable_name, player, world
|
||||
r = Region("Menu", RegionType.Generic, "Menu", self.player, self.multiworld)
|
||||
# Arguments to Region() are name, player, world, and optionally hint_text
|
||||
r = Region("Menu", self.player, self.multiworld)
|
||||
# Set Region.exits to a list of entrances that are reachable from region
|
||||
r.exits = [Entrance(self.player, "New game", r)] # or use r.exits.append
|
||||
# Append region to MultiWorld's regions
|
||||
self.multiworld.regions.append(r) # or use += [r...]
|
||||
|
||||
r = Region("Main Area", RegionType.Generic, "Main Area", self.player, self.multiworld)
|
||||
r = Region("Main Area", self.player, self.multiworld)
|
||||
# Add main area's locations to main area (all but final boss)
|
||||
r.locations = [MyGameLocation(self.player, location.name,
|
||||
self.location_name_to_id[location.name], r)]
|
||||
r.exits = [Entrance(self.player, "Boss Door", r)]
|
||||
self.multiworld.regions.append(r)
|
||||
|
||||
r = Region("Boss Room", RegionType.Generic, "Boss Room", self.player, self.multiworld)
|
||||
r = Region("Boss Room", self.player, self.multiworld)
|
||||
# add event to Boss Room
|
||||
r.locations = [MyGameLocation(self.player, "Final Boss", None, r)]
|
||||
self.multiworld.regions.append(r)
|
||||
@@ -680,3 +683,60 @@ def generate_output(self, output_directory: str):
|
||||
generate_mod(src, out_file, data)
|
||||
```
|
||||
|
||||
### Documentation
|
||||
|
||||
Each world implementation should have a tutorial and a game info page. These are both rendered on the website by reading
|
||||
the `.md` files in your world's `/docs` directory.
|
||||
|
||||
#### Game Info
|
||||
The game info page is for a short breakdown of what your game is and how it works in Archipelago. Any additional
|
||||
information that may be useful to the player when learning your randomizer should also go here. The file name format
|
||||
is `<language key>_<game name>.md`. While you can write these docs for multiple languages, currently only the english
|
||||
version is displayed on the website.
|
||||
|
||||
#### Tutorials
|
||||
Your game can have as many tutorials in as many languages as you like, with each one having a relevant `Tutorial`
|
||||
defined in the `WebWorld`. The file name you use aren't particularly important, but it should be descriptive of what
|
||||
the tutorial is covering, and the name of the file must match the relative URL provided in the `Tutorial`. Currently,
|
||||
the JS that determines this ignores the provided file name and will search for `game/document_lang.md`, where
|
||||
`game/document/lang` is the provided URL.
|
||||
|
||||
### Tests
|
||||
|
||||
Each world is expected to include unit tests that cover its logic, to ensure no logic bug regressions occur. This can be
|
||||
done by creating a `/test` package within your world package. The `__init__.py` within this folder is where the world's
|
||||
TestBase should be defined. This can be inherited from the main TestBase, which will automatically set up a solo
|
||||
multiworld for each test written using it. Within subsequent modules, classes should be defined which inherit the world
|
||||
TestBase, and can then define options to test in the class body, and run tests in each test method.
|
||||
|
||||
Example `__init__.py`
|
||||
```python
|
||||
from test.TestBase import WorldTestBase
|
||||
|
||||
|
||||
class MyGameTestBase(WorldTestBase):
|
||||
game = "My Game"
|
||||
```
|
||||
|
||||
Next using the rules defined in the above `set_rules` we can test that the chests have the correct access rules.
|
||||
|
||||
Example `testChestAccess.py`
|
||||
```python
|
||||
from . import MyGameTestBase
|
||||
|
||||
|
||||
class TestChestAccess(MyGameTestBase):
|
||||
def testSwordChests(self):
|
||||
"""Test locations that require a sword"""
|
||||
locations = ["Chest1", "Chest2"]
|
||||
items = [["Sword"]]
|
||||
# this will test that each location can't be accessed without the "Sword", but can be accessed once obtained.
|
||||
self.assertAccessDependency(locations, items)
|
||||
|
||||
def testAnyWeaponChests(self):
|
||||
"""Test locations that require any weapon"""
|
||||
locations = [f"Chest{i}" for i in range(3, 6)]
|
||||
items = [["Sword"], ["Axe"], ["Spear"]]
|
||||
# this will test that chests 3-5 can't be accessed without any weapon, but can be with just one of them.
|
||||
self.assertAccessDependency(locations, items)
|
||||
```
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import os
|
||||
import logging
|
||||
import typing
|
||||
import asyncio
|
||||
|
||||
os.environ["KIVY_NO_CONSOLELOG"] = "1"
|
||||
os.environ["KIVY_NO_FILELOG"] = "1"
|
||||
@@ -26,6 +25,7 @@ from kivy.base import ExceptionHandler, ExceptionManager
|
||||
from kivy.clock import Clock
|
||||
from kivy.factory import Factory
|
||||
from kivy.properties import BooleanProperty, ObjectProperty
|
||||
from kivy.uix.widget import Widget
|
||||
from kivy.uix.button import Button
|
||||
from kivy.uix.gridlayout import GridLayout
|
||||
from kivy.uix.layout import Layout
|
||||
@@ -508,7 +508,7 @@ class LogtoUI(logging.Handler):
|
||||
|
||||
|
||||
class UILog(RecycleView):
|
||||
cols = 1
|
||||
messages: typing.ClassVar[int] # comes from kv file
|
||||
|
||||
def __init__(self, *loggers_to_handle, **kwargs):
|
||||
super(UILog, self).__init__(**kwargs)
|
||||
@@ -518,9 +518,15 @@ class UILog(RecycleView):
|
||||
|
||||
def on_log(self, record: str) -> None:
|
||||
self.data.append({"text": escape_markup(record)})
|
||||
self.clean_old()
|
||||
|
||||
def on_message_markup(self, text):
|
||||
self.data.append({"text": text})
|
||||
self.clean_old()
|
||||
|
||||
def clean_old(self):
|
||||
if len(self.data) > self.messages:
|
||||
self.data.pop(0)
|
||||
|
||||
def fix_heights(self):
|
||||
"""Workaround fix for divergent texture and layout heights"""
|
||||
@@ -538,6 +544,19 @@ class E(ExceptionHandler):
|
||||
|
||||
|
||||
class KivyJSONtoTextParser(JSONtoTextParser):
|
||||
# dummy class to absorb kvlang definitions
|
||||
class TextColors(Widget):
|
||||
pass
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
# we grab the color definitions from the .kv file, then overwrite the JSONtoTextParser default entries
|
||||
colors = self.TextColors()
|
||||
color_codes = self.color_codes.copy()
|
||||
for name, code in color_codes.items():
|
||||
color_codes[name] = getattr(colors, name, code)
|
||||
self.color_codes = color_codes
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
def __call__(self, *args, **kwargs):
|
||||
self.ref_count = 0
|
||||
return super(KivyJSONtoTextParser, self).__call__(*args, **kwargs)
|
||||
@@ -587,3 +606,8 @@ class KivyJSONtoTextParser(JSONtoTextParser):
|
||||
ExceptionManager.add_handler(E())
|
||||
|
||||
Builder.load_file(Utils.local_path("data", "client.kv"))
|
||||
user_file = Utils.local_path("data", "user.kv")
|
||||
if os.path.exists(user_file):
|
||||
logging.info("Loading user.kv into builder.")
|
||||
Builder.load_file(user_file)
|
||||
|
||||
|
||||
@@ -10,29 +10,12 @@ import zipfile
|
||||
from collections.abc import Iterable
|
||||
from hashlib import sha3_512
|
||||
from pathlib import Path
|
||||
|
||||
import setuptools
|
||||
import setuptools.command.build
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ModuleUpdate
|
||||
ModuleUpdate.update()
|
||||
|
||||
from Launcher import components, icon_paths
|
||||
from Utils import version_tuple, is_windows, is_linux
|
||||
|
||||
# On Python < 3.10 LogicMixin is not currently supported.
|
||||
apworlds: set = {
|
||||
"Subnautica",
|
||||
"Factorio",
|
||||
"Rogue Legacy",
|
||||
}
|
||||
|
||||
# This is a bit jank. We need cx-Freeze to be able to run anything from this script, so install it
|
||||
import subprocess
|
||||
import pkg_resources
|
||||
requirement = 'cx-Freeze>=6.14.1'
|
||||
|
||||
# This is a bit jank. We need cx-Freeze to be able to run anything from this script, so install it
|
||||
try:
|
||||
requirement = 'cx-Freeze>=6.14.1'
|
||||
pkg_resources.require(requirement)
|
||||
import cx_Freeze
|
||||
except pkg_resources.ResolutionError:
|
||||
@@ -41,12 +24,36 @@ except pkg_resources.ResolutionError:
|
||||
subprocess.call([sys.executable, '-m', 'pip', 'install', requirement, '--upgrade'])
|
||||
import cx_Freeze
|
||||
|
||||
# .build only exists if cx-Freeze is the right version, so we have to update/install that first before this line
|
||||
import setuptools.command.build
|
||||
|
||||
if __name__ == "__main__":
|
||||
# need to run this early to import from Utils and Launcher
|
||||
# TODO: move stuff to not require this
|
||||
import ModuleUpdate
|
||||
ModuleUpdate.update(yes="--yes" in sys.argv or "-y" in sys.argv)
|
||||
ModuleUpdate.update_ran = False # restore for later
|
||||
|
||||
from Launcher import components, icon_paths
|
||||
from Utils import version_tuple, is_windows, is_linux
|
||||
|
||||
|
||||
# On Python < 3.10 LogicMixin is not currently supported.
|
||||
apworlds: set = {
|
||||
"Subnautica",
|
||||
"Factorio",
|
||||
"Rogue Legacy",
|
||||
"Donkey Kong Country 3",
|
||||
"Super Mario World",
|
||||
"Timespinner",
|
||||
}
|
||||
|
||||
if os.path.exists("X:/pw.txt"):
|
||||
print("Using signtool")
|
||||
with open("X:/pw.txt", encoding="utf-8-sig") as f:
|
||||
pw = f.read()
|
||||
signtool = r'signtool sign /f X:/_SITS_Zertifikat_.pfx /p "' + pw + r'" /fd sha256 /tr http://timestamp.digicert.com/ '
|
||||
signtool = r'signtool sign /f X:/_SITS_Zertifikat_.pfx /p "' + pw + \
|
||||
r'" /fd sha256 /tr http://timestamp.digicert.com/ '
|
||||
else:
|
||||
signtool = None
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ import unittest
|
||||
from worlds.AutoWorld import World
|
||||
from Fill import FillError, balance_multiworld_progression, fill_restrictive, \
|
||||
distribute_early_items, distribute_items_restrictive
|
||||
from BaseClasses import Entrance, LocationProgressType, MultiWorld, Region, RegionType, Item, Location, \
|
||||
from BaseClasses import Entrance, LocationProgressType, MultiWorld, Region, Item, Location, \
|
||||
ItemClassification
|
||||
from worlds.generic.Rules import CollectionRule, add_item_rule, locality_rules, set_rule
|
||||
|
||||
@@ -17,8 +17,7 @@ def generate_multi_world(players: int = 1) -> MultiWorld:
|
||||
multi_world.game[player_id] = f"Game {player_id}"
|
||||
multi_world.worlds[player_id] = world
|
||||
multi_world.player_name[player_id] = "Test Player " + str(player_id)
|
||||
region = Region("Menu", RegionType.Generic,
|
||||
"Menu Region Hint", player_id, multi_world)
|
||||
region = Region("Menu", player_id, multi_world, "Menu Region Hint")
|
||||
multi_world.regions.append(region)
|
||||
|
||||
multi_world.set_seed(0)
|
||||
@@ -48,8 +47,7 @@ class PlayerDefinition(object):
|
||||
def generate_region(self, parent: Region, size: int, access_rule: CollectionRule = lambda state: True) -> Region:
|
||||
region_tag = "_region" + str(len(self.regions))
|
||||
region_name = "player" + str(self.id) + region_tag
|
||||
region = Region("player" + str(self.id) + region_tag, RegionType.Generic,
|
||||
"Region Hint", self.id, self.multiworld)
|
||||
region = Region("player" + str(self.id) + region_tag, self.id, self.multiworld)
|
||||
self.locations += generate_locations(size, self.id, None, region, region_tag)
|
||||
|
||||
entrance = Entrance(self.id, region_name + "_entrance", parent)
|
||||
|
||||
@@ -1,14 +1,33 @@
|
||||
import unittest
|
||||
from worlds.AutoWorld import AutoWorldRegister
|
||||
|
||||
from . import setup_default_world
|
||||
from . import setup_solo_multiworld
|
||||
|
||||
|
||||
class TestImplemented(unittest.TestCase):
|
||||
def testCompletionCondition(self):
|
||||
"""Ensure a completion condition is set that has requirements."""
|
||||
for gamename, world_type in AutoWorldRegister.world_types.items():
|
||||
if not world_type.hidden and gamename not in {"ArchipIDLE", "Final Fantasy", "Sudoku"}:
|
||||
with self.subTest(gamename):
|
||||
world = setup_default_world(world_type)
|
||||
self.assertFalse(world.completion_condition[1](world.state))
|
||||
for game_name, world_type in AutoWorldRegister.world_types.items():
|
||||
if not world_type.hidden and game_name not in {"ArchipIDLE", "Sudoku"}:
|
||||
with self.subTest(game_name):
|
||||
multiworld = setup_solo_multiworld(world_type)
|
||||
self.assertFalse(multiworld.completion_condition[1](multiworld.state))
|
||||
|
||||
def testEntranceParents(self):
|
||||
"""Tests that the parents of created Entrances match the exiting Region."""
|
||||
for game_name, world_type in AutoWorldRegister.world_types.items():
|
||||
if not world_type.hidden:
|
||||
with self.subTest(game_name):
|
||||
multiworld = setup_solo_multiworld(world_type)
|
||||
for region in multiworld.regions:
|
||||
for exit in region.exits:
|
||||
self.assertEqual(exit.parent_region, region)
|
||||
|
||||
def testStageMethods(self):
|
||||
"""Tests that worlds don't try to implement certain steps that are only ever called as stage."""
|
||||
for game_name, world_type in AutoWorldRegister.world_types.items():
|
||||
if not world_type.hidden:
|
||||
with self.subTest(game_name):
|
||||
for method in ("assert_generate",):
|
||||
self.assertFalse(hasattr(world_type, method),
|
||||
f"{method} must be implemented as a @classmethod named stage_{method}.")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import unittest
|
||||
from worlds.AutoWorld import AutoWorldRegister
|
||||
from . import setup_default_world
|
||||
from . import setup_solo_multiworld
|
||||
|
||||
|
||||
class TestBase(unittest.TestCase):
|
||||
@@ -43,14 +43,18 @@ class TestBase(unittest.TestCase):
|
||||
|
||||
def testItemCountGreaterEqualLocations(self):
|
||||
for game_name, world_type in AutoWorldRegister.world_types.items():
|
||||
|
||||
if game_name in {"Final Fantasy"}:
|
||||
continue
|
||||
with self.subTest("Game", game=game_name):
|
||||
world = setup_default_world(world_type)
|
||||
location_count = sum(0 if location.event or location.item else 1 for location in world.get_locations())
|
||||
multiworld = setup_solo_multiworld(world_type)
|
||||
self.assertGreaterEqual(
|
||||
len(world.itempool),
|
||||
location_count,
|
||||
f"{game_name} Item count MUST meet or exceede the number of locations",
|
||||
len(multiworld.itempool),
|
||||
len(multiworld.get_unfilled_locations()),
|
||||
f"{game_name} Item count MUST meet or exceed the number of locations",
|
||||
)
|
||||
|
||||
def testItemsInDatapackage(self):
|
||||
"""Test that any created items in the itempool are in the datapackage"""
|
||||
for game_name, world_type in AutoWorldRegister.world_types.items():
|
||||
with self.subTest("Game", game=game_name):
|
||||
multiworld = setup_solo_multiworld(world_type)
|
||||
for item in multiworld.itempool:
|
||||
self.assertIn(item.name, world_type.item_name_to_id)
|
||||
|
||||
@@ -1,16 +1,25 @@
|
||||
import unittest
|
||||
from collections import Counter
|
||||
from worlds.AutoWorld import AutoWorldRegister
|
||||
from . import setup_default_world
|
||||
from . import setup_solo_multiworld
|
||||
|
||||
|
||||
class TestBase(unittest.TestCase):
|
||||
def testCreateDuplicateLocations(self):
|
||||
"""Tests that no two Locations share a name."""
|
||||
for game_name, world_type in AutoWorldRegister.world_types.items():
|
||||
if game_name in {"Final Fantasy"}:
|
||||
continue
|
||||
multiworld = setup_default_world(world_type)
|
||||
multiworld = setup_solo_multiworld(world_type)
|
||||
locations = Counter(multiworld.get_locations())
|
||||
if locations:
|
||||
self.assertLessEqual(locations.most_common(1)[0][1], 1,
|
||||
f"{world_type.game} has duplicate of location {locations.most_common(1)}")
|
||||
|
||||
def testLocationsInDatapackage(self):
|
||||
"""Tests that created locations not filled before fill starts exist in the datapackage."""
|
||||
for game_name, world_type in AutoWorldRegister.world_types.items():
|
||||
with self.subTest("Game", game_name=game_name):
|
||||
multiworld = setup_solo_multiworld(world_type)
|
||||
locations = multiworld.get_unfilled_locations() # do unfilled locations to avoid Events
|
||||
for location in locations:
|
||||
self.assertIn(location.name, world_type.location_name_to_id)
|
||||
self.assertEqual(location.address, world_type.location_name_to_id[location.name])
|
||||
|
||||
@@ -3,18 +3,41 @@ import unittest
|
||||
from BaseClasses import CollectionState
|
||||
from worlds.AutoWorld import AutoWorldRegister
|
||||
|
||||
from . import setup_default_world
|
||||
from . import setup_solo_multiworld
|
||||
|
||||
|
||||
class TestBase(unittest.TestCase):
|
||||
gen_steps = ["generate_early", "create_regions", "create_items", "set_rules", "generate_basic", "pre_fill"]
|
||||
|
||||
default_settings_unreachable_regions = {
|
||||
"A Link to the Past": {
|
||||
"Chris Houlihan Room", # glitch room by definition
|
||||
"Desert Northern Cliffs", # on top of mountain, only reachable via OWG
|
||||
"Dark Death Mountain Bunny Descent Area" # OWG Mountain descent
|
||||
},
|
||||
"Ocarina of Time": {
|
||||
"Prelude of Light Warp", # Prelude is not progression by default
|
||||
"Serenade of Water Warp", # Serenade is not progression by default
|
||||
"Lost Woods Mushroom Timeout", # trade quest starts after this item
|
||||
"ZD Eyeball Frog Timeout", # trade quest starts after this item
|
||||
"ZR Top of Waterfall", # dummy region used for entrance shuffle
|
||||
},
|
||||
# The following SM regions are only used when the corresponding StartLocation option is selected (so not with default settings).
|
||||
# Also, those dont have any entrances as they serve as starting Region (that's why they have to be excluded for testAllStateCanReachEverything).
|
||||
"Super Metroid": {
|
||||
"Ceres",
|
||||
"Gauntlet Top",
|
||||
"Mama Turtle"
|
||||
}
|
||||
}
|
||||
|
||||
def testAllStateCanReachEverything(self):
|
||||
for game_name, world_type in AutoWorldRegister.world_types.items():
|
||||
# Final Fantasy logic is controlled by finalfantasyrandomizer.com
|
||||
if game_name != "Ori and the Blind Forest" and game_name != "Final Fantasy": # TODO: fix Ori Logic
|
||||
if game_name not in {"Ori and the Blind Forest"}: # TODO: fix Ori Logic
|
||||
unreachable_regions = self.default_settings_unreachable_regions.get(game_name, set())
|
||||
with self.subTest("Game", game=game_name):
|
||||
world = setup_default_world(world_type)
|
||||
world = setup_solo_multiworld(world_type)
|
||||
excluded = world.exclude_locations[1].value
|
||||
state = world.get_all_state(False)
|
||||
for location in world.get_locations():
|
||||
@@ -22,15 +45,20 @@ class TestBase(unittest.TestCase):
|
||||
with self.subTest("Location should be reached", location=location):
|
||||
self.assertTrue(location.can_reach(state), f"{location.name} unreachable")
|
||||
|
||||
for region in world.get_regions():
|
||||
if region.name not in unreachable_regions:
|
||||
with self.subTest("Region should be reached", region=region):
|
||||
self.assertTrue(region.can_reach(state))
|
||||
|
||||
with self.subTest("Completion Condition"):
|
||||
self.assertTrue(world.can_beat_game(state))
|
||||
|
||||
def testEmptyStateCanReachSomething(self):
|
||||
for game_name, world_type in AutoWorldRegister.world_types.items():
|
||||
# Final Fantasy logic is controlled by finalfantasyrandomizer.com
|
||||
if game_name not in {"Archipelago", "Final Fantasy", "Sudoku"}:
|
||||
if game_name not in {"Archipelago", "Sudoku"}:
|
||||
with self.subTest("Game", game=game_name):
|
||||
world = setup_default_world(world_type)
|
||||
world = setup_solo_multiworld(world_type)
|
||||
state = CollectionState(world)
|
||||
locations = set()
|
||||
for location in world.get_locations():
|
||||
|
||||
@@ -6,7 +6,7 @@ from worlds.AutoWorld import call_all
|
||||
gen_steps = ["generate_early", "create_regions", "create_items", "set_rules", "generate_basic", "pre_fill"]
|
||||
|
||||
|
||||
def setup_default_world(world_type) -> MultiWorld:
|
||||
def setup_solo_multiworld(world_type) -> MultiWorld:
|
||||
multiworld = MultiWorld(1)
|
||||
multiworld.game[1] = world_type.game
|
||||
multiworld.player_name = {1: "Tester"}
|
||||
|
||||
+6
-7
@@ -188,10 +188,11 @@ class World(metaclass=AutoWorldRegister):
|
||||
# can also be implemented as a classmethod and called "stage_<original_name>",
|
||||
# in that case the MultiWorld object is passed as an argument and it gets called once for the entire multiworld.
|
||||
# An example of this can be found in alttp as stage_pre_fill
|
||||
|
||||
@classmethod
|
||||
def assert_generate(cls) -> None:
|
||||
def stage_assert_generate(cls, multiworld: "MultiWorld") -> None:
|
||||
"""Checks that a game is capable of generating, usually checks for some base file like a ROM.
|
||||
Not run for unittests since they don't produce output"""
|
||||
This gets called once per present world type. Not run for unittests since they don't produce output"""
|
||||
pass
|
||||
|
||||
def generate_early(self) -> None:
|
||||
@@ -213,14 +214,12 @@ class World(metaclass=AutoWorldRegister):
|
||||
"""Optional method that is supposed to be used for special fill stages. This is run *after* plando."""
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
def fill_hook(cls,
|
||||
def fill_hook(self,
|
||||
progitempool: List["Item"],
|
||||
usefulitempool: List["Item"],
|
||||
filleritempool: List["Item"],
|
||||
fill_locations: List["Location"]) -> None:
|
||||
"""Special method that gets called as part of distribute_items_restrictive (main fill).
|
||||
This gets called once per present world type."""
|
||||
"""Special method that gets called as part of distribute_items_restrictive (main fill)."""
|
||||
pass
|
||||
|
||||
def post_fill(self) -> None:
|
||||
@@ -229,7 +228,7 @@ class World(metaclass=AutoWorldRegister):
|
||||
|
||||
def generate_output(self, output_directory: str) -> None:
|
||||
"""This method gets called from a threadpool, do not use world.random here.
|
||||
If you need any last-second randomization, use MultiWorld.slot_seeds[slot] instead."""
|
||||
If you need any last-second randomization, use MultiWorld.per_slot_randoms[slot] instead."""
|
||||
pass
|
||||
|
||||
def fill_slot_data(self) -> Dict[str, Any]: # json of WebHostLib.models.Slot
|
||||
|
||||
+513
-295
@@ -1,313 +1,531 @@
|
||||
import collections
|
||||
from BaseClasses import RegionType
|
||||
from worlds.alttp.Regions import create_lw_region, create_dw_region, create_cave_region, create_dungeon_region
|
||||
from worlds.alttp.SubClasses import LTTPRegionType
|
||||
|
||||
|
||||
def create_inverted_regions(world, player):
|
||||
|
||||
world.regions += [
|
||||
create_dw_region(player, 'Menu', None, ['Links House S&Q', 'Dark Sanctuary S&Q', 'Old Man S&Q', 'Castle Ledge S&Q']),
|
||||
create_lw_region(player, 'Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest', 'Bombos Tablet'],
|
||||
create_dw_region(world, player, 'Menu', None,
|
||||
['Links House S&Q', 'Dark Sanctuary S&Q', 'Old Man S&Q', 'Castle Ledge S&Q']),
|
||||
create_lw_region(world, player, 'Light World',
|
||||
['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest',
|
||||
'Bombos Tablet'],
|
||||
["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Kings Grave Outer Rocks', 'Dam',
|
||||
'Inverted Big Bomb Shop', 'Tavern North', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave',
|
||||
'Blacksmiths Hut', 'Bat Cave Drop Ledge', 'Bat Cave Cave', 'Sick Kids House', 'Hobo Bridge', 'Lost Woods Hideout Drop', 'Lost Woods Hideout Stump',
|
||||
'Lumberjack Tree Tree', 'Lumberjack Tree Cave', 'Mini Moldorm Cave', 'Ice Rod Cave', 'Lake Hylia Central Island Pier', 'Lake Hylia Island Pier', 'Lake Hylia Warp',
|
||||
'Bonk Rock Cave', 'Library', 'Two Brothers House (East)', 'Desert Palace Stairs', 'Eastern Palace', 'Master Sword Meadow',
|
||||
'Inverted Big Bomb Shop', 'Tavern North', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut',
|
||||
'Kakariko Well Drop', 'Kakariko Well Cave',
|
||||
'Blacksmiths Hut', 'Bat Cave Drop Ledge', 'Bat Cave Cave', 'Sick Kids House', 'Hobo Bridge',
|
||||
'Lost Woods Hideout Drop', 'Lost Woods Hideout Stump',
|
||||
'Lumberjack Tree Tree', 'Lumberjack Tree Cave', 'Mini Moldorm Cave', 'Ice Rod Cave',
|
||||
'Lake Hylia Central Island Pier', 'Lake Hylia Island Pier', 'Lake Hylia Warp',
|
||||
'Bonk Rock Cave', 'Library', 'Two Brothers House (East)', 'Desert Palace Stairs',
|
||||
'Eastern Palace', 'Master Sword Meadow',
|
||||
'Sanctuary', 'Sanctuary Grave', 'Death Mountain Entrance Rock', 'Light World River Drop',
|
||||
'Elder House (East)', 'Elder House (West)', 'North Fairy Cave', 'North Fairy Cave Drop', 'Lost Woods Gamble', 'Snitch Lady (East)', 'Snitch Lady (West)', 'Tavern (Front)',
|
||||
'Kakariko Shop', 'Long Fairy Cave', 'Good Bee Cave', '20 Rupee Cave', 'Cave Shop (Lake Hylia)',
|
||||
'Bonk Fairy (Light)', '50 Rupee Cave', 'Fortune Teller (Light)', 'Lake Hylia Fairy', 'Light Hype Fairy', 'Desert Fairy', 'Lumberjack House', 'Lake Hylia Fortune Teller', 'Kakariko Gamble Game',
|
||||
'East Dark World Mirror Spot', 'West Dark World Mirror Spot', 'South Dark World Mirror Spot', 'Cave 45', 'Checkerboard Cave', 'Mire Mirror Spot', 'Hammer Peg Area Mirror Spot',
|
||||
'Shopping Mall Mirror Spot', 'Skull Woods Mirror Spot', 'Inverted Pyramid Entrance','Hyrule Castle Entrance (South)', 'Secret Passage Outer Bushes', 'Bush Covered Lawn Outer Bushes',
|
||||
'Elder House (East)', 'Elder House (West)', 'North Fairy Cave', 'North Fairy Cave Drop',
|
||||
'Lost Woods Gamble', 'Snitch Lady (East)', 'Snitch Lady (West)', 'Tavern (Front)',
|
||||
'Kakariko Shop', 'Long Fairy Cave', 'Good Bee Cave', '20 Rupee Cave',
|
||||
'Cave Shop (Lake Hylia)',
|
||||
'Bonk Fairy (Light)', '50 Rupee Cave', 'Fortune Teller (Light)', 'Lake Hylia Fairy',
|
||||
'Light Hype Fairy', 'Desert Fairy', 'Lumberjack House', 'Lake Hylia Fortune Teller',
|
||||
'Kakariko Gamble Game',
|
||||
'East Dark World Mirror Spot', 'West Dark World Mirror Spot', 'South Dark World Mirror Spot',
|
||||
'Cave 45', 'Checkerboard Cave', 'Mire Mirror Spot', 'Hammer Peg Area Mirror Spot',
|
||||
'Shopping Mall Mirror Spot', 'Skull Woods Mirror Spot', 'Inverted Pyramid Entrance',
|
||||
'Hyrule Castle Entrance (South)', 'Secret Passage Outer Bushes',
|
||||
'Bush Covered Lawn Outer Bushes',
|
||||
'Potion Shop Outer Bushes', 'Graveyard Cave Outer Bushes', 'Bomb Hut Outer Bushes']),
|
||||
create_lw_region(player, 'Bush Covered Lawn', None, ['Bush Covered House', 'Bush Covered Lawn Inner Bushes', 'Bush Covered Lawn Mirror Spot']),
|
||||
create_lw_region(player, 'Bomb Hut Area', None, ['Light World Bomb Hut', 'Bomb Hut Inner Bushes', 'Bomb Hut Mirror Spot']),
|
||||
create_lw_region(player, 'Hyrule Castle Secret Entrance Area', None, ['Hyrule Castle Secret Entrance Stairs', 'Secret Passage Inner Bushes']),
|
||||
create_lw_region(player, 'Death Mountain Entrance', None, ['Old Man Cave (West)', 'Death Mountain Entrance Drop', 'Bumper Cave Entrance Mirror Spot']),
|
||||
create_lw_region(player, 'Lake Hylia Central Island', None, ['Capacity Upgrade', 'Lake Hylia Central Island Mirror Spot']),
|
||||
create_cave_region(player, 'Blinds Hideout', 'a bounty of five items', ["Blind\'s Hideout - Top",
|
||||
"Blind\'s Hideout - Left",
|
||||
"Blind\'s Hideout - Right",
|
||||
"Blind\'s Hideout - Far Left",
|
||||
"Blind\'s Hideout - Far Right"]),
|
||||
create_lw_region(player, 'Northeast Light World', None, ['Zoras River', 'Waterfall of Wishing Cave', 'Potion Shop Outer Rock', 'Catfish Mirror Spot', 'Northeast Light World Warp']),
|
||||
create_lw_region(player, 'Waterfall of Wishing Cave', None, ['Waterfall of Wishing', 'Northeast Light World Return']),
|
||||
create_lw_region(player, 'Potion Shop Area', None, ['Potion Shop', 'Potion Shop Inner Bushes', 'Potion Shop Inner Rock', 'Potion Shop Mirror Spot', 'Potion Shop River Drop']),
|
||||
create_lw_region(player, 'Graveyard Cave Area', None, ['Graveyard Cave', 'Graveyard Cave Inner Bushes', 'Graveyard Cave Mirror Spot']),
|
||||
create_lw_region(player, 'River', None, ['Light World Pier', 'Potion Shop Pier']),
|
||||
create_cave_region(player, 'Hyrule Castle Secret Entrance', 'a drop\'s exit', ['Link\'s Uncle', 'Secret Passage'], ['Hyrule Castle Secret Entrance Exit']),
|
||||
create_lw_region(player, 'Zoras River', ['King Zora', 'Zora\'s Ledge']),
|
||||
create_cave_region(player, 'Waterfall of Wishing', 'a cave with two chests', ['Waterfall Fairy - Left', 'Waterfall Fairy - Right']),
|
||||
create_lw_region(player, 'Kings Grave Area', None, ['Kings Grave', 'Kings Grave Inner Rocks']),
|
||||
create_cave_region(player, 'Kings Grave', 'a cave with a chest', ['King\'s Tomb']),
|
||||
create_cave_region(player, 'North Fairy Cave', 'a drop\'s exit', None, ['North Fairy Cave Exit']),
|
||||
create_cave_region(player, 'Dam', 'the dam', ['Floodgate', 'Floodgate Chest']),
|
||||
create_cave_region(player, 'Inverted Links House', 'your house', ['Link\'s House'], ['Inverted Links House Exit']),
|
||||
create_cave_region(player, 'Chris Houlihan Room', 'I AM ERROR', None, ['Chris Houlihan Room Exit']),
|
||||
create_cave_region(player, 'Tavern', 'the tavern', ['Kakariko Tavern']),
|
||||
create_cave_region(player, 'Elder House', 'a connector', None, ['Elder House Exit (East)', 'Elder House Exit (West)']),
|
||||
create_cave_region(player, 'Snitch Lady (East)', 'a boring house'),
|
||||
create_cave_region(player, 'Snitch Lady (West)', 'a boring house'),
|
||||
create_cave_region(player, 'Bush Covered House', 'the grass man'),
|
||||
create_cave_region(player, 'Tavern (Front)', 'the tavern'),
|
||||
create_cave_region(player, 'Light World Bomb Hut', 'a restock room'),
|
||||
create_cave_region(player, 'Kakariko Shop', 'a common shop'),
|
||||
create_cave_region(player, 'Fortune Teller (Light)', 'a fortune teller'),
|
||||
create_cave_region(player, 'Lake Hylia Fortune Teller', 'a fortune teller'),
|
||||
create_cave_region(player, 'Lumberjack House', 'a boring house'),
|
||||
create_cave_region(player, 'Bonk Fairy (Light)', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Bonk Fairy (Dark)', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Lake Hylia Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Swamp Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Desert Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Dark Lake Hylia Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Dark Lake Hylia Ledge Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Dark Desert Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Dark Death Mountain Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Chicken House', 'a house with a chest', ['Chicken House']),
|
||||
create_cave_region(player, 'Aginahs Cave', 'a cave with a chest', ['Aginah\'s Cave']),
|
||||
create_cave_region(player, 'Sahasrahlas Hut', 'Sahasrahla', ['Sahasrahla\'s Hut - Left', 'Sahasrahla\'s Hut - Middle', 'Sahasrahla\'s Hut - Right', 'Sahasrahla']),
|
||||
create_cave_region(player, 'Kakariko Well (top)', 'a drop\'s exit', ['Kakariko Well - Top', 'Kakariko Well - Left', 'Kakariko Well - Middle',
|
||||
'Kakariko Well - Right', 'Kakariko Well - Bottom'], ['Kakariko Well (top to bottom)']),
|
||||
create_cave_region(player, 'Kakariko Well (bottom)', 'a drop\'s exit', None, ['Kakariko Well Exit']),
|
||||
create_cave_region(player, 'Blacksmiths Hut', 'the smith', ['Blacksmith', 'Missing Smith']),
|
||||
create_lw_region(player, 'Bat Cave Drop Ledge', None, ['Bat Cave Drop']),
|
||||
create_cave_region(player, 'Bat Cave (right)', 'a drop\'s exit', ['Magic Bat'], ['Bat Cave Door']),
|
||||
create_cave_region(player, 'Bat Cave (left)', 'a drop\'s exit', None, ['Bat Cave Exit']),
|
||||
create_cave_region(player, 'Sick Kids House', 'the sick kid', ['Sick Kid']),
|
||||
create_lw_region(player, 'Hobo Bridge', ['Hobo']),
|
||||
create_cave_region(player, 'Lost Woods Hideout (top)', 'a drop\'s exit', ['Lost Woods Hideout'], ['Lost Woods Hideout (top to bottom)']),
|
||||
create_cave_region(player, 'Lost Woods Hideout (bottom)', 'a drop\'s exit', None, ['Lost Woods Hideout Exit']),
|
||||
create_cave_region(player, 'Lumberjack Tree (top)', 'a drop\'s exit', ['Lumberjack Tree'], ['Lumberjack Tree (top to bottom)']),
|
||||
create_cave_region(player, 'Lumberjack Tree (bottom)', 'a drop\'s exit', None, ['Lumberjack Tree Exit']),
|
||||
create_cave_region(player, 'Cave 45', 'a cave with an item', ['Cave 45']),
|
||||
create_cave_region(player, 'Graveyard Cave', 'a cave with an item', ['Graveyard Cave']),
|
||||
create_cave_region(player, 'Checkerboard Cave', 'a cave with an item', ['Checkerboard Cave']),
|
||||
create_cave_region(player, 'Long Fairy Cave', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Mini Moldorm Cave', 'a bounty of five items', ['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Left', 'Mini Moldorm Cave - Right',
|
||||
'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Generous Guy']),
|
||||
create_cave_region(player, 'Ice Rod Cave', 'a cave with a chest', ['Ice Rod Cave']),
|
||||
create_cave_region(player, 'Good Bee Cave', 'a cold bee'),
|
||||
create_cave_region(player, '20 Rupee Cave', 'a cave with some cash'),
|
||||
create_cave_region(player, 'Cave Shop (Lake Hylia)', 'a common shop'),
|
||||
create_cave_region(player, 'Cave Shop (Dark Death Mountain)', 'a common shop'),
|
||||
create_cave_region(player, 'Bonk Rock Cave', 'a cave with a chest', ['Bonk Rock Cave']),
|
||||
create_cave_region(player, 'Library', 'the library', ['Library']),
|
||||
create_cave_region(player, 'Kakariko Gamble Game', 'a game of chance'),
|
||||
create_cave_region(player, 'Potion Shop', 'the potion shop', ['Potion Shop']),
|
||||
create_lw_region(player, 'Lake Hylia Island', ['Lake Hylia Island']),
|
||||
create_cave_region(player, 'Capacity Upgrade', 'the queen of fairies'),
|
||||
create_cave_region(player, 'Two Brothers House', 'a connector', None, ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']),
|
||||
create_lw_region(player, 'Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)', 'Maze Race Mirror Spot']),
|
||||
create_cave_region(player, '50 Rupee Cave', 'a cave with some cash'),
|
||||
create_lw_region(player, 'Desert Ledge', ['Desert Ledge'], ['Desert Palace Entrance (North) Rocks', 'Desert Palace Entrance (West)', 'Desert Ledge Drop']),
|
||||
create_lw_region(player, 'Desert Palace Stairs', None, ['Desert Palace Entrance (South)', 'Desert Palace Stairs Mirror Spot']),
|
||||
create_lw_region(player, 'Desert Palace Lone Stairs', None, ['Desert Palace Stairs Drop', 'Desert Palace Entrance (East)']),
|
||||
create_lw_region(player, 'Desert Palace Entrance (North) Spot', None, ['Desert Palace Entrance (North)', 'Desert Ledge Return Rocks', 'Desert Palace North Mirror Spot']),
|
||||
create_dungeon_region(player, 'Desert Palace Main (Outer)', 'Desert Palace', ['Desert Palace - Big Chest', 'Desert Palace - Torch', 'Desert Palace - Map Chest'],
|
||||
['Desert Palace Pots (Outer)', 'Desert Palace Exit (West)', 'Desert Palace Exit (East)', 'Desert Palace East Wing']),
|
||||
create_dungeon_region(player, 'Desert Palace Main (Inner)', 'Desert Palace', None, ['Desert Palace Exit (South)', 'Desert Palace Pots (Inner)']),
|
||||
create_dungeon_region(player, 'Desert Palace East', 'Desert Palace', ['Desert Palace - Compass Chest', 'Desert Palace - Big Key Chest']),
|
||||
create_dungeon_region(player, 'Desert Palace North', 'Desert Palace', ['Desert Palace - Boss', 'Desert Palace - Prize'], ['Desert Palace Exit (North)']),
|
||||
create_dungeon_region(player, 'Eastern Palace', 'Eastern Palace', ['Eastern Palace - Compass Chest', 'Eastern Palace - Big Chest', 'Eastern Palace - Cannonball Chest',
|
||||
'Eastern Palace - Big Key Chest', 'Eastern Palace - Map Chest', 'Eastern Palace - Boss', 'Eastern Palace - Prize'], ['Eastern Palace Exit']),
|
||||
create_lw_region(player, 'Master Sword Meadow', ['Master Sword Pedestal']),
|
||||
create_cave_region(player, 'Lost Woods Gamble', 'a game of chance'),
|
||||
create_lw_region(player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Inverted Ganons Tower', 'Hyrule Castle Ledge Courtyard Drop', 'Inverted Pyramid Hole']),
|
||||
create_dungeon_region(player, 'Hyrule Castle', 'Hyrule Castle', ['Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest'],
|
||||
['Hyrule Castle Exit (East)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (South)', 'Throne Room']),
|
||||
create_dungeon_region(player, 'Sewer Drop', 'a drop\'s exit', None, ['Sewer Drop']), # This exists only to be referenced for access checks
|
||||
create_dungeon_region(player, 'Sewers (Dark)', 'a drop\'s exit', ['Sewers - Dark Cross'], ['Sewers Door']),
|
||||
create_dungeon_region(player, 'Sewers', 'a drop\'s exit', ['Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle',
|
||||
'Sewers - Secret Room - Right'], ['Sanctuary Push Door', 'Sewers Back Door']),
|
||||
create_dungeon_region(player, 'Sanctuary', 'a drop\'s exit', ['Sanctuary'], ['Sanctuary Exit']),
|
||||
create_dungeon_region(player, 'Inverted Agahnims Tower', 'Castle Tower', ['Castle Tower - Room 03', 'Castle Tower - Dark Maze'], ['Agahnim 1', 'Inverted Agahnims Tower Exit']),
|
||||
create_dungeon_region(player, 'Agahnim 1', 'Castle Tower', ['Agahnim 1'], None),
|
||||
create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)', 'Old Man Cave Exit (West)']),
|
||||
create_cave_region(player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
|
||||
create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']),
|
||||
create_lw_region(player, 'Death Mountain', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave',
|
||||
'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Mirror Spot']),
|
||||
create_cave_region(player, 'Death Mountain Return Cave', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']),
|
||||
create_lw_region(player, 'Death Mountain Return Ledge', None, ['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)', 'Bumper Cave Ledge Mirror Spot']),
|
||||
create_cave_region(player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']),
|
||||
create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit']),
|
||||
create_cave_region(player, 'Spectacle Rock Cave (Peak)', 'a connector', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']),
|
||||
create_lw_region(player, 'East Death Mountain (Bottom)', None, ['Broken Bridge (East)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'East Death Mountain Mirror Spot (Bottom)', 'Hookshot Fairy',
|
||||
'Fairy Ascension Rocks', 'Spiral Cave (Bottom)']),
|
||||
create_cave_region(player, 'Hookshot Fairy', 'fairies deep in a cave'),
|
||||
create_cave_region(player, 'Paradox Cave Front', 'a connector', None, ['Paradox Cave Push Block Reverse', 'Paradox Cave Exit (Bottom)', 'Light World Death Mountain Shop']),
|
||||
create_cave_region(player, 'Paradox Cave Chest Area', 'a connector', ['Paradox Cave Lower - Far Left',
|
||||
'Paradox Cave Lower - Left',
|
||||
'Paradox Cave Lower - Right',
|
||||
'Paradox Cave Lower - Far Right',
|
||||
'Paradox Cave Lower - Middle',
|
||||
'Paradox Cave Upper - Left',
|
||||
'Paradox Cave Upper - Right'],
|
||||
create_lw_region(world, player, 'Bush Covered Lawn', None,
|
||||
['Bush Covered House', 'Bush Covered Lawn Inner Bushes', 'Bush Covered Lawn Mirror Spot']),
|
||||
create_lw_region(world, player, 'Bomb Hut Area', None,
|
||||
['Light World Bomb Hut', 'Bomb Hut Inner Bushes', 'Bomb Hut Mirror Spot']),
|
||||
create_lw_region(world, player, 'Hyrule Castle Secret Entrance Area', None,
|
||||
['Hyrule Castle Secret Entrance Stairs', 'Secret Passage Inner Bushes']),
|
||||
create_lw_region(world, player, 'Death Mountain Entrance', None,
|
||||
['Old Man Cave (West)', 'Death Mountain Entrance Drop', 'Bumper Cave Entrance Mirror Spot']),
|
||||
create_lw_region(world, player, 'Lake Hylia Central Island', None,
|
||||
['Capacity Upgrade', 'Lake Hylia Central Island Mirror Spot']),
|
||||
create_cave_region(world, player, 'Blinds Hideout', 'a bounty of five items', ["Blind\'s Hideout - Top",
|
||||
"Blind\'s Hideout - Left",
|
||||
"Blind\'s Hideout - Right",
|
||||
"Blind\'s Hideout - Far Left",
|
||||
"Blind\'s Hideout - Far Right"]),
|
||||
create_lw_region(world, player, 'Northeast Light World', None,
|
||||
['Zoras River', 'Waterfall of Wishing Cave', 'Potion Shop Outer Rock', 'Catfish Mirror Spot',
|
||||
'Northeast Light World Warp']),
|
||||
create_lw_region(world, player, 'Waterfall of Wishing Cave', None,
|
||||
['Waterfall of Wishing', 'Northeast Light World Return']),
|
||||
create_lw_region(world, player, 'Potion Shop Area', None,
|
||||
['Potion Shop', 'Potion Shop Inner Bushes', 'Potion Shop Inner Rock',
|
||||
'Potion Shop Mirror Spot', 'Potion Shop River Drop']),
|
||||
create_lw_region(world, player, 'Graveyard Cave Area', None,
|
||||
['Graveyard Cave', 'Graveyard Cave Inner Bushes', 'Graveyard Cave Mirror Spot']),
|
||||
create_lw_region(world, player, 'River', None, ['Light World Pier', 'Potion Shop Pier']),
|
||||
create_cave_region(world, player, 'Hyrule Castle Secret Entrance', 'a drop\'s exit',
|
||||
['Link\'s Uncle', 'Secret Passage'], ['Hyrule Castle Secret Entrance Exit']),
|
||||
create_lw_region(world, player, 'Zoras River', ['King Zora', 'Zora\'s Ledge']),
|
||||
create_cave_region(world, player, 'Waterfall of Wishing', 'a cave with two chests',
|
||||
['Waterfall Fairy - Left', 'Waterfall Fairy - Right']),
|
||||
create_lw_region(world, player, 'Kings Grave Area', None, ['Kings Grave', 'Kings Grave Inner Rocks']),
|
||||
create_cave_region(world, player, 'Kings Grave', 'a cave with a chest', ['King\'s Tomb']),
|
||||
create_cave_region(world, player, 'North Fairy Cave', 'a drop\'s exit', None, ['North Fairy Cave Exit']),
|
||||
create_cave_region(world, player, 'Dam', 'the dam', ['Floodgate', 'Floodgate Chest']),
|
||||
create_cave_region(world, player, 'Inverted Links House', 'your house', ['Link\'s House'],
|
||||
['Inverted Links House Exit']),
|
||||
create_cave_region(world, player, 'Chris Houlihan Room', 'I AM ERROR', None, ['Chris Houlihan Room Exit']),
|
||||
create_cave_region(world, player, 'Tavern', 'the tavern', ['Kakariko Tavern']),
|
||||
create_cave_region(world, player, 'Elder House', 'a connector', None,
|
||||
['Elder House Exit (East)', 'Elder House Exit (West)']),
|
||||
create_cave_region(world, player, 'Snitch Lady (East)', 'a boring house'),
|
||||
create_cave_region(world, player, 'Snitch Lady (West)', 'a boring house'),
|
||||
create_cave_region(world, player, 'Bush Covered House', 'the grass man'),
|
||||
create_cave_region(world, player, 'Tavern (Front)', 'the tavern'),
|
||||
create_cave_region(world, player, 'Light World Bomb Hut', 'a restock room'),
|
||||
create_cave_region(world, player, 'Kakariko Shop', 'a common shop'),
|
||||
create_cave_region(world, player, 'Fortune Teller (Light)', 'a fortune teller'),
|
||||
create_cave_region(world, player, 'Lake Hylia Fortune Teller', 'a fortune teller'),
|
||||
create_cave_region(world, player, 'Lumberjack House', 'a boring house'),
|
||||
create_cave_region(world, player, 'Bonk Fairy (Light)', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Bonk Fairy (Dark)', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Lake Hylia Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Swamp Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Desert Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Dark Lake Hylia Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Dark Lake Hylia Ledge Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Dark Desert Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Dark Death Mountain Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Chicken House', 'a house with a chest', ['Chicken House']),
|
||||
create_cave_region(world, player, 'Aginahs Cave', 'a cave with a chest', ['Aginah\'s Cave']),
|
||||
create_cave_region(world, player, 'Sahasrahlas Hut', 'Sahasrahla',
|
||||
['Sahasrahla\'s Hut - Left', 'Sahasrahla\'s Hut - Middle', 'Sahasrahla\'s Hut - Right',
|
||||
'Sahasrahla']),
|
||||
create_cave_region(world, player, 'Kakariko Well (top)', 'a drop\'s exit',
|
||||
['Kakariko Well - Top', 'Kakariko Well - Left', 'Kakariko Well - Middle',
|
||||
'Kakariko Well - Right', 'Kakariko Well - Bottom'], ['Kakariko Well (top to bottom)']),
|
||||
create_cave_region(world, player, 'Kakariko Well (bottom)', 'a drop\'s exit', None, ['Kakariko Well Exit']),
|
||||
create_cave_region(world, player, 'Blacksmiths Hut', 'the smith', ['Blacksmith', 'Missing Smith']),
|
||||
create_lw_region(world, player, 'Bat Cave Drop Ledge', None, ['Bat Cave Drop']),
|
||||
create_cave_region(world, player, 'Bat Cave (right)', 'a drop\'s exit', ['Magic Bat'], ['Bat Cave Door']),
|
||||
create_cave_region(world, player, 'Bat Cave (left)', 'a drop\'s exit', None, ['Bat Cave Exit']),
|
||||
create_cave_region(world, player, 'Sick Kids House', 'the sick kid', ['Sick Kid']),
|
||||
create_lw_region(world, player, 'Hobo Bridge', ['Hobo']),
|
||||
create_cave_region(world, player, 'Lost Woods Hideout (top)', 'a drop\'s exit', ['Lost Woods Hideout'],
|
||||
['Lost Woods Hideout (top to bottom)']),
|
||||
create_cave_region(world, player, 'Lost Woods Hideout (bottom)', 'a drop\'s exit', None,
|
||||
['Lost Woods Hideout Exit']),
|
||||
create_cave_region(world, player, 'Lumberjack Tree (top)', 'a drop\'s exit', ['Lumberjack Tree'],
|
||||
['Lumberjack Tree (top to bottom)']),
|
||||
create_cave_region(world, player, 'Lumberjack Tree (bottom)', 'a drop\'s exit', None, ['Lumberjack Tree Exit']),
|
||||
create_cave_region(world, player, 'Cave 45', 'a cave with an item', ['Cave 45']),
|
||||
create_cave_region(world, player, 'Graveyard Cave', 'a cave with an item', ['Graveyard Cave']),
|
||||
create_cave_region(world, player, 'Checkerboard Cave', 'a cave with an item', ['Checkerboard Cave']),
|
||||
create_cave_region(world, player, 'Long Fairy Cave', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Mini Moldorm Cave', 'a bounty of five items',
|
||||
['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Left', 'Mini Moldorm Cave - Right',
|
||||
'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Generous Guy']),
|
||||
create_cave_region(world, player, 'Ice Rod Cave', 'a cave with a chest', ['Ice Rod Cave']),
|
||||
create_cave_region(world, player, 'Good Bee Cave', 'a cold bee'),
|
||||
create_cave_region(world, player, '20 Rupee Cave', 'a cave with some cash'),
|
||||
create_cave_region(world, player, 'Cave Shop (Lake Hylia)', 'a common shop'),
|
||||
create_cave_region(world, player, 'Cave Shop (Dark Death Mountain)', 'a common shop'),
|
||||
create_cave_region(world, player, 'Bonk Rock Cave', 'a cave with a chest', ['Bonk Rock Cave']),
|
||||
create_cave_region(world, player, 'Library', 'the library', ['Library']),
|
||||
create_cave_region(world, player, 'Kakariko Gamble Game', 'a game of chance'),
|
||||
create_cave_region(world, player, 'Potion Shop', 'the potion shop', ['Potion Shop']),
|
||||
create_lw_region(world, player, 'Lake Hylia Island', ['Lake Hylia Island']),
|
||||
create_cave_region(world, player, 'Capacity Upgrade', 'the queen of fairies'),
|
||||
create_cave_region(world, player, 'Two Brothers House', 'a connector', None,
|
||||
['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']),
|
||||
create_lw_region(world, player, 'Maze Race Ledge', ['Maze Race'],
|
||||
['Two Brothers House (West)', 'Maze Race Mirror Spot']),
|
||||
create_cave_region(world, player, '50 Rupee Cave', 'a cave with some cash'),
|
||||
create_lw_region(world, player, 'Desert Ledge', ['Desert Ledge'],
|
||||
['Desert Palace Entrance (North) Rocks', 'Desert Palace Entrance (West)',
|
||||
'Desert Ledge Drop']),
|
||||
create_lw_region(world, player, 'Desert Palace Stairs', None,
|
||||
['Desert Palace Entrance (South)', 'Desert Palace Stairs Mirror Spot']),
|
||||
create_lw_region(world, player, 'Desert Palace Lone Stairs', None,
|
||||
['Desert Palace Stairs Drop', 'Desert Palace Entrance (East)']),
|
||||
create_lw_region(world, player, 'Desert Palace Entrance (North) Spot', None,
|
||||
['Desert Palace Entrance (North)', 'Desert Ledge Return Rocks',
|
||||
'Desert Palace North Mirror Spot']),
|
||||
create_dungeon_region(world, player, 'Desert Palace Main (Outer)', 'Desert Palace',
|
||||
['Desert Palace - Big Chest', 'Desert Palace - Torch', 'Desert Palace - Map Chest'],
|
||||
['Desert Palace Pots (Outer)', 'Desert Palace Exit (West)', 'Desert Palace Exit (East)',
|
||||
'Desert Palace East Wing']),
|
||||
create_dungeon_region(world, player, 'Desert Palace Main (Inner)', 'Desert Palace', None,
|
||||
['Desert Palace Exit (South)', 'Desert Palace Pots (Inner)']),
|
||||
create_dungeon_region(world, player, 'Desert Palace East', 'Desert Palace',
|
||||
['Desert Palace - Compass Chest', 'Desert Palace - Big Key Chest']),
|
||||
create_dungeon_region(world, player, 'Desert Palace North', 'Desert Palace',
|
||||
['Desert Palace - Boss', 'Desert Palace - Prize'], ['Desert Palace Exit (North)']),
|
||||
create_dungeon_region(world, player, 'Eastern Palace', 'Eastern Palace',
|
||||
['Eastern Palace - Compass Chest', 'Eastern Palace - Big Chest',
|
||||
'Eastern Palace - Cannonball Chest',
|
||||
'Eastern Palace - Big Key Chest', 'Eastern Palace - Map Chest', 'Eastern Palace - Boss',
|
||||
'Eastern Palace - Prize'], ['Eastern Palace Exit']),
|
||||
create_lw_region(world, player, 'Master Sword Meadow', ['Master Sword Pedestal']),
|
||||
create_cave_region(world, player, 'Lost Woods Gamble', 'a game of chance'),
|
||||
create_lw_region(world, player, 'Hyrule Castle Ledge', None,
|
||||
['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Inverted Ganons Tower',
|
||||
'Hyrule Castle Ledge Courtyard Drop', 'Inverted Pyramid Hole']),
|
||||
create_dungeon_region(world, player, 'Hyrule Castle', 'Hyrule Castle',
|
||||
['Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest',
|
||||
'Hyrule Castle - Zelda\'s Chest'],
|
||||
['Hyrule Castle Exit (East)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (South)',
|
||||
'Throne Room']),
|
||||
create_dungeon_region(world, player, 'Sewer Drop', 'a drop\'s exit', None, ['Sewer Drop']), # This exists only to be referenced for access checks
|
||||
create_dungeon_region(world, player, 'Sewers (Dark)', 'a drop\'s exit', ['Sewers - Dark Cross'],
|
||||
['Sewers Door']),
|
||||
create_dungeon_region(world, player, 'Sewers', 'a drop\'s exit',
|
||||
['Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle',
|
||||
'Sewers - Secret Room - Right'], ['Sanctuary Push Door', 'Sewers Back Door']),
|
||||
create_dungeon_region(world, player, 'Sanctuary', 'a drop\'s exit', ['Sanctuary'], ['Sanctuary Exit']),
|
||||
create_dungeon_region(world, player, 'Inverted Agahnims Tower', 'Castle Tower',
|
||||
['Castle Tower - Room 03', 'Castle Tower - Dark Maze'],
|
||||
['Agahnim 1', 'Inverted Agahnims Tower Exit']),
|
||||
create_dungeon_region(world, player, 'Agahnim 1', 'Castle Tower', ['Agahnim 1'], None),
|
||||
create_cave_region(world, player, 'Old Man Cave', 'a connector', ['Old Man'],
|
||||
['Old Man Cave Exit (East)', 'Old Man Cave Exit (West)']),
|
||||
create_cave_region(world, player, 'Old Man House', 'a connector', None,
|
||||
['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
|
||||
create_cave_region(world, player, 'Old Man House Back', 'a connector', None,
|
||||
['Old Man House Exit (Top)', 'Old Man House Back to Front']),
|
||||
create_lw_region(world, player, 'Death Mountain', None,
|
||||
['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)',
|
||||
'Death Mountain Return Cave (East)', 'Spectacle Rock Cave',
|
||||
'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)',
|
||||
'Death Mountain Mirror Spot']),
|
||||
create_cave_region(world, player, 'Death Mountain Return Cave', 'a connector', None,
|
||||
['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']),
|
||||
create_lw_region(world, player, 'Death Mountain Return Ledge', None,
|
||||
['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)',
|
||||
'Bumper Cave Ledge Mirror Spot']),
|
||||
create_cave_region(world, player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'],
|
||||
['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']),
|
||||
create_cave_region(world, player, 'Spectacle Rock Cave (Bottom)', 'a connector', None,
|
||||
['Spectacle Rock Cave Exit']),
|
||||
create_cave_region(world, player, 'Spectacle Rock Cave (Peak)', 'a connector', None,
|
||||
['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']),
|
||||
create_lw_region(world, player, 'East Death Mountain (Bottom)', None,
|
||||
['Broken Bridge (East)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)',
|
||||
'East Death Mountain Mirror Spot (Bottom)', 'Hookshot Fairy',
|
||||
'Fairy Ascension Rocks', 'Spiral Cave (Bottom)']),
|
||||
create_cave_region(world, player, 'Hookshot Fairy', 'fairies deep in a cave'),
|
||||
create_cave_region(world, player, 'Paradox Cave Front', 'a connector', None,
|
||||
['Paradox Cave Push Block Reverse', 'Paradox Cave Exit (Bottom)',
|
||||
'Light World Death Mountain Shop']),
|
||||
create_cave_region(world, player, 'Paradox Cave Chest Area', 'a connector', ['Paradox Cave Lower - Far Left',
|
||||
'Paradox Cave Lower - Left',
|
||||
'Paradox Cave Lower - Right',
|
||||
'Paradox Cave Lower - Far Right',
|
||||
'Paradox Cave Lower - Middle',
|
||||
'Paradox Cave Upper - Left',
|
||||
'Paradox Cave Upper - Right'],
|
||||
['Paradox Cave Push Block', 'Paradox Cave Bomb Jump']),
|
||||
create_cave_region(player, 'Paradox Cave', 'a connector', None, ['Paradox Cave Exit (Middle)', 'Paradox Cave Exit (Top)', 'Paradox Cave Drop']),
|
||||
create_cave_region(player, 'Light World Death Mountain Shop', 'a common shop'),
|
||||
create_lw_region(player, 'East Death Mountain (Top)', ['Floating Island'], ['Paradox Cave (Top)', 'Death Mountain (Top)', 'Spiral Cave Ledge Access', 'East Death Mountain Drop', 'East Death Mountain Mirror Spot (Top)', 'Fairy Ascension Ledge Access', 'Mimic Cave Ledge Access',
|
||||
'Floating Island Mirror Spot']),
|
||||
create_lw_region(player, 'Spiral Cave Ledge', None, ['Spiral Cave', 'Spiral Cave Ledge Drop', 'Dark Death Mountain Ledge Mirror Spot (West)']),
|
||||
create_lw_region(player, 'Mimic Cave Ledge', None, ['Mimic Cave', 'Mimic Cave Ledge Drop', 'Dark Death Mountain Ledge Mirror Spot (East)']),
|
||||
create_cave_region(player, 'Spiral Cave (Top)', 'a connector', ['Spiral Cave'], ['Spiral Cave (top to bottom)', 'Spiral Cave Exit (Top)']),
|
||||
create_cave_region(player, 'Spiral Cave (Bottom)', 'a connector', None, ['Spiral Cave Exit']),
|
||||
create_lw_region(player, 'Fairy Ascension Plateau', None, ['Fairy Ascension Drop', 'Fairy Ascension Cave (Bottom)']),
|
||||
create_cave_region(player, 'Fairy Ascension Cave (Bottom)', 'a connector', None, ['Fairy Ascension Cave Climb', 'Fairy Ascension Cave Exit (Bottom)']),
|
||||
create_cave_region(player, 'Fairy Ascension Cave (Drop)', 'a connector', None, ['Fairy Ascension Cave Pots']),
|
||||
create_cave_region(player, 'Fairy Ascension Cave (Top)', 'a connector', None, ['Fairy Ascension Cave Exit (Top)', 'Fairy Ascension Cave Drop']),
|
||||
create_lw_region(player, 'Fairy Ascension Ledge', None, ['Fairy Ascension Ledge Drop', 'Fairy Ascension Cave (Top)', 'Laser Bridge Mirror Spot']),
|
||||
create_lw_region(player, 'Death Mountain (Top)', ['Ether Tablet', 'Spectacle Rock'], ['East Death Mountain (Top)', 'Tower of Hera', 'Death Mountain Drop', 'Death Mountain (Top) Mirror Spot']),
|
||||
create_dw_region(player, 'Bumper Cave Ledge', ['Bumper Cave Ledge'], ['Bumper Cave Ledge Drop', 'Bumper Cave (Top)']),
|
||||
create_dungeon_region(player, 'Tower of Hera (Bottom)', 'Tower of Hera', ['Tower of Hera - Basement Cage', 'Tower of Hera - Map Chest'], ['Tower of Hera Small Key Door', 'Tower of Hera Big Key Door', 'Tower of Hera Exit']),
|
||||
create_dungeon_region(player, 'Tower of Hera (Basement)', 'Tower of Hera', ['Tower of Hera - Big Key Chest']),
|
||||
create_dungeon_region(player, 'Tower of Hera (Top)', 'Tower of Hera', ['Tower of Hera - Compass Chest', 'Tower of Hera - Big Chest', 'Tower of Hera - Boss', 'Tower of Hera - Prize']),
|
||||
create_cave_region(world, player, 'Paradox Cave', 'a connector', None,
|
||||
['Paradox Cave Exit (Middle)', 'Paradox Cave Exit (Top)', 'Paradox Cave Drop']),
|
||||
create_cave_region(world, player, 'Light World Death Mountain Shop', 'a common shop'),
|
||||
create_lw_region(world, player, 'East Death Mountain (Top)', ['Floating Island'],
|
||||
['Paradox Cave (Top)', 'Death Mountain (Top)', 'Spiral Cave Ledge Access',
|
||||
'East Death Mountain Drop', 'East Death Mountain Mirror Spot (Top)',
|
||||
'Fairy Ascension Ledge Access', 'Mimic Cave Ledge Access',
|
||||
'Floating Island Mirror Spot']),
|
||||
create_lw_region(world, player, 'Spiral Cave Ledge', None,
|
||||
['Spiral Cave', 'Spiral Cave Ledge Drop', 'Dark Death Mountain Ledge Mirror Spot (West)']),
|
||||
create_lw_region(world, player, 'Mimic Cave Ledge', None,
|
||||
['Mimic Cave', 'Mimic Cave Ledge Drop', 'Dark Death Mountain Ledge Mirror Spot (East)']),
|
||||
create_cave_region(world, player, 'Spiral Cave (Top)', 'a connector', ['Spiral Cave'],
|
||||
['Spiral Cave (top to bottom)', 'Spiral Cave Exit (Top)']),
|
||||
create_cave_region(world, player, 'Spiral Cave (Bottom)', 'a connector', None, ['Spiral Cave Exit']),
|
||||
create_lw_region(world, player, 'Fairy Ascension Plateau', None,
|
||||
['Fairy Ascension Drop', 'Fairy Ascension Cave (Bottom)']),
|
||||
create_cave_region(world, player, 'Fairy Ascension Cave (Bottom)', 'a connector', None,
|
||||
['Fairy Ascension Cave Climb', 'Fairy Ascension Cave Exit (Bottom)']),
|
||||
create_cave_region(world, player, 'Fairy Ascension Cave (Drop)', 'a connector', None,
|
||||
['Fairy Ascension Cave Pots']),
|
||||
create_cave_region(world, player, 'Fairy Ascension Cave (Top)', 'a connector', None,
|
||||
['Fairy Ascension Cave Exit (Top)', 'Fairy Ascension Cave Drop']),
|
||||
create_lw_region(world, player, 'Fairy Ascension Ledge', None,
|
||||
['Fairy Ascension Ledge Drop', 'Fairy Ascension Cave (Top)', 'Laser Bridge Mirror Spot']),
|
||||
create_lw_region(world, player, 'Death Mountain (Top)', ['Ether Tablet', 'Spectacle Rock'],
|
||||
['East Death Mountain (Top)', 'Tower of Hera', 'Death Mountain Drop',
|
||||
'Death Mountain (Top) Mirror Spot']),
|
||||
create_dw_region(world, player, 'Bumper Cave Ledge', ['Bumper Cave Ledge'],
|
||||
['Bumper Cave Ledge Drop', 'Bumper Cave (Top)']),
|
||||
create_dungeon_region(world, player, 'Tower of Hera (Bottom)', 'Tower of Hera',
|
||||
['Tower of Hera - Basement Cage', 'Tower of Hera - Map Chest'],
|
||||
['Tower of Hera Small Key Door', 'Tower of Hera Big Key Door', 'Tower of Hera Exit']),
|
||||
create_dungeon_region(world, player, 'Tower of Hera (Basement)', 'Tower of Hera',
|
||||
['Tower of Hera - Big Key Chest']),
|
||||
create_dungeon_region(world, player, 'Tower of Hera (Top)', 'Tower of Hera',
|
||||
['Tower of Hera - Compass Chest', 'Tower of Hera - Big Chest', 'Tower of Hera - Boss',
|
||||
'Tower of Hera - Prize']),
|
||||
|
||||
create_dw_region(player, 'East Dark World', ['Pyramid'], ['Pyramid Fairy', 'South Dark World Bridge', 'Palace of Darkness', 'Dark Lake Hylia Drop (East)',
|
||||
'Dark Lake Hylia Fairy', 'Palace of Darkness Hint', 'East Dark World Hint', 'Northeast Dark World Broken Bridge Pass', 'East Dark World Teleporter', 'EDW Flute']),
|
||||
create_dw_region(player, 'Catfish', ['Catfish'], ['Catfish Exit Rock']),
|
||||
create_dw_region(player, 'Northeast Dark World', None, ['West Dark World Gap', 'Dark World Potion Shop', 'East Dark World Broken Bridge Pass', 'NEDW Flute', 'Dark Lake Hylia Teleporter', 'Catfish Entrance Rock']),
|
||||
create_cave_region(player, 'Palace of Darkness Hint', 'a storyteller'),
|
||||
create_cave_region(player, 'East Dark World Hint', 'a storyteller'),
|
||||
create_dw_region(player, 'South Dark World', ['Stumpy', 'Digging Game'], ['Dark Lake Hylia Drop (South)', 'Hype Cave', 'Swamp Palace', 'Village of Outcasts Heavy Rock', 'East Dark World Bridge', 'Inverted Links House', 'Archery Game', 'Bonk Fairy (Dark)',
|
||||
'Dark Lake Hylia Shop', 'South Dark World Teleporter', 'Post Aga Teleporter', 'SDW Flute']),
|
||||
create_cave_region(player, 'Inverted Big Bomb Shop', 'the bomb shop'),
|
||||
create_cave_region(player, 'Archery Game', 'a game of skill'),
|
||||
create_dw_region(player, 'Dark Lake Hylia', None, ['East Dark World Pier', 'Dark Lake Hylia Ledge Pier', 'Ice Palace Missing Wall']),
|
||||
create_dw_region(player, 'Dark Lake Hylia Central Island', None, ['Dark Lake Hylia Shallows', 'Ice Palace', 'Dark Lake Hylia Central Island Teleporter']),
|
||||
create_dw_region(player, 'Dark Lake Hylia Ledge', None, ['Dark Lake Hylia Ledge Drop', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Spike Cave', 'DLHL Flute']),
|
||||
create_cave_region(player, 'Dark Lake Hylia Ledge Hint', 'a storyteller'),
|
||||
create_cave_region(player, 'Dark Lake Hylia Ledge Spike Cave', 'a spiky hint'),
|
||||
create_cave_region(player, 'Hype Cave', 'a bounty of five items', ['Hype Cave - Top', 'Hype Cave - Middle Right', 'Hype Cave - Middle Left',
|
||||
'Hype Cave - Bottom', 'Hype Cave - Generous Guy']),
|
||||
create_dw_region(player, 'West Dark World', ['Frog', 'Flute Activation Spot'], ['Village of Outcasts Drop', 'East Dark World River Pier', 'Brewery', 'C-Shaped House', 'Chest Game', 'Thieves Town', 'Bumper Cave Entrance Rock',
|
||||
'Skull Woods Forest', 'Village of Outcasts Pegs', 'Village of Outcasts Eastern Rocks', 'Red Shield Shop', 'Inverted Dark Sanctuary', 'Fortune Teller (Dark)', 'Dark World Lumberjack Shop',
|
||||
'West Dark World Teleporter', 'WDW Flute']),
|
||||
create_dw_region(player, 'Dark Grassy Lawn', None, ['Grassy Lawn Pegs', 'Village of Outcasts Shop', 'Dark Grassy Lawn Flute']),
|
||||
create_dw_region(player, 'Hammer Peg Area', ['Dark Blacksmith Ruins'], ['Dark World Hammer Peg Cave', 'Peg Area Rocks', 'Hammer Peg Area Flute']),
|
||||
create_dw_region(player, 'Bumper Cave Entrance', None, ['Bumper Cave (Bottom)', 'Bumper Cave Entrance Drop']),
|
||||
create_cave_region(player, 'Fortune Teller (Dark)', 'a fortune teller'),
|
||||
create_cave_region(player, 'Village of Outcasts Shop', 'a common shop'),
|
||||
create_cave_region(player, 'Dark Lake Hylia Shop', 'a common shop'),
|
||||
create_cave_region(player, 'Dark World Lumberjack Shop', 'a common shop'),
|
||||
create_cave_region(player, 'Dark World Potion Shop', 'a common shop'),
|
||||
create_cave_region(player, 'Dark World Hammer Peg Cave', 'a cave with an item', ['Peg Cave']),
|
||||
create_cave_region(player, 'Pyramid Fairy', 'a cave with two chests', ['Pyramid Fairy - Left', 'Pyramid Fairy - Right']),
|
||||
create_cave_region(player, 'Brewery', 'a house with a chest', ['Brewery']),
|
||||
create_cave_region(player, 'C-Shaped House', 'a house with a chest', ['C-Shaped House']),
|
||||
create_cave_region(player, 'Chest Game', 'a game of 16 chests', ['Chest Game']),
|
||||
create_cave_region(player, 'Red Shield Shop', 'the rare shop'),
|
||||
create_cave_region(player, 'Inverted Dark Sanctuary', 'a storyteller', None, ['Inverted Dark Sanctuary Exit']),
|
||||
create_cave_region(player, 'Bumper Cave', 'a connector', None, ['Bumper Cave Exit (Bottom)', 'Bumper Cave Exit (Top)']),
|
||||
create_dw_region(player, 'Skull Woods Forest', None, ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)',
|
||||
'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)']),
|
||||
create_dw_region(player, 'Skull Woods Forest (West)', None, ['Skull Woods Second Section Hole', 'Skull Woods Second Section Door (West)', 'Skull Woods Final Section']),
|
||||
create_dw_region(player, 'Dark Desert', None, ['Misery Mire', 'Mire Shed', 'Dark Desert Hint', 'Dark Desert Fairy', 'DD Flute']),
|
||||
create_dw_region(player, 'Dark Desert Ledge', None, ['Dark Desert Drop', 'Dark Desert Teleporter']),
|
||||
create_cave_region(player, 'Mire Shed', 'a cave with two chests', ['Mire Shed - Left', 'Mire Shed - Right']),
|
||||
create_cave_region(player, 'Dark Desert Hint', 'a storyteller'),
|
||||
create_dw_region(player, 'Dark Death Mountain', None, ['Dark Death Mountain Drop (East)', 'Inverted Agahnims Tower', 'Superbunny Cave (Top)', 'Hookshot Cave', 'Turtle Rock',
|
||||
'Spike Cave', 'Dark Death Mountain Fairy', 'Dark Death Mountain Teleporter (West)', 'Turtle Rock Tail Drop', 'DDM Flute']),
|
||||
create_dw_region(player, 'Dark Death Mountain Ledge', None, ['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)']),
|
||||
create_dw_region(player, 'Turtle Rock (Top)', None, ['Dark Death Mountain Teleporter (East)', 'Turtle Rock Drop']),
|
||||
create_dw_region(player, 'Dark Death Mountain Isolated Ledge', None, ['Turtle Rock Isolated Ledge Entrance']),
|
||||
create_dw_region(player, 'Dark Death Mountain (East Bottom)', None, ['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)', 'Dark Death Mountain Teleporter (East Bottom)', 'EDDM Flute']),
|
||||
create_cave_region(player, 'Superbunny Cave (Top)', 'a connector', ['Superbunny Cave - Top', 'Superbunny Cave - Bottom'], ['Superbunny Cave Exit (Top)']),
|
||||
create_cave_region(player, 'Superbunny Cave (Bottom)', 'a connector', None, ['Superbunny Cave Climb', 'Superbunny Cave Exit (Bottom)']),
|
||||
create_cave_region(player, 'Spike Cave', 'Spike Cave', ['Spike Cave']),
|
||||
create_cave_region(player, 'Hookshot Cave', 'a connector', ['Hookshot Cave - Top Right', 'Hookshot Cave - Top Left', 'Hookshot Cave - Bottom Right', 'Hookshot Cave - Bottom Left'],
|
||||
create_dw_region(world, player, 'East Dark World', ['Pyramid'],
|
||||
['Pyramid Fairy', 'South Dark World Bridge', 'Palace of Darkness',
|
||||
'Dark Lake Hylia Drop (East)',
|
||||
'Dark Lake Hylia Fairy', 'Palace of Darkness Hint', 'East Dark World Hint',
|
||||
'Northeast Dark World Broken Bridge Pass', 'East Dark World Teleporter', 'EDW Flute']),
|
||||
create_dw_region(world, player, 'Catfish', ['Catfish'], ['Catfish Exit Rock']),
|
||||
create_dw_region(world, player, 'Northeast Dark World', None,
|
||||
['West Dark World Gap', 'Dark World Potion Shop', 'East Dark World Broken Bridge Pass',
|
||||
'NEDW Flute', 'Dark Lake Hylia Teleporter', 'Catfish Entrance Rock']),
|
||||
create_cave_region(world, player, 'Palace of Darkness Hint', 'a storyteller'),
|
||||
create_cave_region(world, player, 'East Dark World Hint', 'a storyteller'),
|
||||
create_dw_region(world, player, 'South Dark World', ['Stumpy', 'Digging Game'],
|
||||
['Dark Lake Hylia Drop (South)', 'Hype Cave', 'Swamp Palace', 'Village of Outcasts Heavy Rock',
|
||||
'East Dark World Bridge', 'Inverted Links House', 'Archery Game', 'Bonk Fairy (Dark)',
|
||||
'Dark Lake Hylia Shop', 'South Dark World Teleporter', 'Post Aga Teleporter', 'SDW Flute']),
|
||||
create_cave_region(world, player, 'Inverted Big Bomb Shop', 'the bomb shop'),
|
||||
create_cave_region(world, player, 'Archery Game', 'a game of skill'),
|
||||
create_dw_region(world, player, 'Dark Lake Hylia', None,
|
||||
['East Dark World Pier', 'Dark Lake Hylia Ledge Pier', 'Ice Palace Missing Wall']),
|
||||
create_dw_region(world, player, 'Dark Lake Hylia Central Island', None,
|
||||
['Dark Lake Hylia Shallows', 'Ice Palace', 'Dark Lake Hylia Central Island Teleporter']),
|
||||
create_dw_region(world, player, 'Dark Lake Hylia Ledge', None,
|
||||
['Dark Lake Hylia Ledge Drop', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Hint',
|
||||
'Dark Lake Hylia Ledge Spike Cave', 'DLHL Flute']),
|
||||
create_cave_region(world, player, 'Dark Lake Hylia Ledge Hint', 'a storyteller'),
|
||||
create_cave_region(world, player, 'Dark Lake Hylia Ledge Spike Cave', 'a spiky hint'),
|
||||
create_cave_region(world, player, 'Hype Cave', 'a bounty of five items',
|
||||
['Hype Cave - Top', 'Hype Cave - Middle Right', 'Hype Cave - Middle Left',
|
||||
'Hype Cave - Bottom', 'Hype Cave - Generous Guy']),
|
||||
create_dw_region(world, player, 'West Dark World', ['Frog', 'Flute Activation Spot'],
|
||||
['Village of Outcasts Drop', 'East Dark World River Pier', 'Brewery', 'C-Shaped House',
|
||||
'Chest Game', 'Thieves Town', 'Bumper Cave Entrance Rock',
|
||||
'Skull Woods Forest', 'Village of Outcasts Pegs', 'Village of Outcasts Eastern Rocks',
|
||||
'Red Shield Shop', 'Inverted Dark Sanctuary', 'Fortune Teller (Dark)',
|
||||
'Dark World Lumberjack Shop',
|
||||
'West Dark World Teleporter', 'WDW Flute']),
|
||||
create_dw_region(world, player, 'Dark Grassy Lawn', None,
|
||||
['Grassy Lawn Pegs', 'Village of Outcasts Shop', 'Dark Grassy Lawn Flute']),
|
||||
create_dw_region(world, player, 'Hammer Peg Area', ['Dark Blacksmith Ruins'],
|
||||
['Dark World Hammer Peg Cave', 'Peg Area Rocks', 'Hammer Peg Area Flute']),
|
||||
create_dw_region(world, player, 'Bumper Cave Entrance', None,
|
||||
['Bumper Cave (Bottom)', 'Bumper Cave Entrance Drop']),
|
||||
create_cave_region(world, player, 'Fortune Teller (Dark)', 'a fortune teller'),
|
||||
create_cave_region(world, player, 'Village of Outcasts Shop', 'a common shop'),
|
||||
create_cave_region(world, player, 'Dark Lake Hylia Shop', 'a common shop'),
|
||||
create_cave_region(world, player, 'Dark World Lumberjack Shop', 'a common shop'),
|
||||
create_cave_region(world, player, 'Dark World Potion Shop', 'a common shop'),
|
||||
create_cave_region(world, player, 'Dark World Hammer Peg Cave', 'a cave with an item', ['Peg Cave']),
|
||||
create_cave_region(world, player, 'Pyramid Fairy', 'a cave with two chests',
|
||||
['Pyramid Fairy - Left', 'Pyramid Fairy - Right']),
|
||||
create_cave_region(world, player, 'Brewery', 'a house with a chest', ['Brewery']),
|
||||
create_cave_region(world, player, 'C-Shaped House', 'a house with a chest', ['C-Shaped House']),
|
||||
create_cave_region(world, player, 'Chest Game', 'a game of 16 chests', ['Chest Game']),
|
||||
create_cave_region(world, player, 'Red Shield Shop', 'the rare shop'),
|
||||
create_cave_region(world, player, 'Inverted Dark Sanctuary', 'a storyteller', None,
|
||||
['Inverted Dark Sanctuary Exit']),
|
||||
create_cave_region(world, player, 'Bumper Cave', 'a connector', None,
|
||||
['Bumper Cave Exit (Bottom)', 'Bumper Cave Exit (Top)']),
|
||||
create_dw_region(world, player, 'Skull Woods Forest', None,
|
||||
['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)',
|
||||
'Skull Woods First Section Hole (North)',
|
||||
'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)']),
|
||||
create_dw_region(world, player, 'Skull Woods Forest (West)', None,
|
||||
['Skull Woods Second Section Hole', 'Skull Woods Second Section Door (West)',
|
||||
'Skull Woods Final Section']),
|
||||
create_dw_region(world, player, 'Dark Desert', None,
|
||||
['Misery Mire', 'Mire Shed', 'Dark Desert Hint', 'Dark Desert Fairy', 'DD Flute']),
|
||||
create_dw_region(world, player, 'Dark Desert Ledge', None, ['Dark Desert Drop', 'Dark Desert Teleporter']),
|
||||
create_cave_region(world, player, 'Mire Shed', 'a cave with two chests',
|
||||
['Mire Shed - Left', 'Mire Shed - Right']),
|
||||
create_cave_region(world, player, 'Dark Desert Hint', 'a storyteller'),
|
||||
create_dw_region(world, player, 'Dark Death Mountain', None,
|
||||
['Dark Death Mountain Drop (East)', 'Inverted Agahnims Tower', 'Superbunny Cave (Top)',
|
||||
'Hookshot Cave', 'Turtle Rock',
|
||||
'Spike Cave', 'Dark Death Mountain Fairy', 'Dark Death Mountain Teleporter (West)',
|
||||
'Turtle Rock Tail Drop', 'DDM Flute']),
|
||||
create_dw_region(world, player, 'Dark Death Mountain Ledge', None,
|
||||
['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)']),
|
||||
create_dw_region(world, player, 'Turtle Rock (Top)', None,
|
||||
['Dark Death Mountain Teleporter (East)', 'Turtle Rock Drop']),
|
||||
create_dw_region(world, player, 'Dark Death Mountain Isolated Ledge', None,
|
||||
['Turtle Rock Isolated Ledge Entrance']),
|
||||
create_dw_region(world, player, 'Dark Death Mountain (East Bottom)', None,
|
||||
['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)',
|
||||
'Dark Death Mountain Teleporter (East Bottom)', 'EDDM Flute']),
|
||||
create_cave_region(world, player, 'Superbunny Cave (Top)', 'a connector',
|
||||
['Superbunny Cave - Top', 'Superbunny Cave - Bottom'], ['Superbunny Cave Exit (Top)']),
|
||||
create_cave_region(world, player, 'Superbunny Cave (Bottom)', 'a connector', None,
|
||||
['Superbunny Cave Climb', 'Superbunny Cave Exit (Bottom)']),
|
||||
create_cave_region(world, player, 'Spike Cave', 'Spike Cave', ['Spike Cave']),
|
||||
create_cave_region(world, player, 'Hookshot Cave', 'a connector',
|
||||
['Hookshot Cave - Top Right', 'Hookshot Cave - Top Left', 'Hookshot Cave - Bottom Right',
|
||||
'Hookshot Cave - Bottom Left'],
|
||||
['Hookshot Cave Exit (South)', 'Hookshot Cave Exit (North)']),
|
||||
create_dw_region(player, 'Death Mountain Floating Island (Dark World)', None, ['Floating Island Drop', 'Hookshot Cave Back Entrance']),
|
||||
create_cave_region(player, 'Mimic Cave', 'Mimic Cave', ['Mimic Cave']),
|
||||
create_dw_region(world, player, 'Death Mountain Floating Island (Dark World)', None,
|
||||
['Floating Island Drop', 'Hookshot Cave Back Entrance']),
|
||||
create_cave_region(world, player, 'Mimic Cave', 'Mimic Cave', ['Mimic Cave']),
|
||||
|
||||
create_dungeon_region(player, 'Swamp Palace (Entrance)', 'Swamp Palace', None, ['Swamp Palace Moat', 'Swamp Palace Exit']),
|
||||
create_dungeon_region(player, 'Swamp Palace (First Room)', 'Swamp Palace', ['Swamp Palace - Entrance'], ['Swamp Palace Small Key Door']),
|
||||
create_dungeon_region(player, 'Swamp Palace (Starting Area)', 'Swamp Palace', ['Swamp Palace - Map Chest'], ['Swamp Palace (Center)']),
|
||||
create_dungeon_region(player, 'Swamp Palace (Center)', 'Swamp Palace', ['Swamp Palace - Big Chest', 'Swamp Palace - Compass Chest',
|
||||
'Swamp Palace - Big Key Chest', 'Swamp Palace - West Chest'], ['Swamp Palace (North)']),
|
||||
create_dungeon_region(player, 'Swamp Palace (North)', 'Swamp Palace', ['Swamp Palace - Flooded Room - Left', 'Swamp Palace - Flooded Room - Right',
|
||||
'Swamp Palace - Waterfall Room', 'Swamp Palace - Boss', 'Swamp Palace - Prize']),
|
||||
create_dungeon_region(player, 'Thieves Town (Entrance)', 'Thieves\' Town', ['Thieves\' Town - Big Key Chest',
|
||||
'Thieves\' Town - Map Chest',
|
||||
'Thieves\' Town - Compass Chest',
|
||||
'Thieves\' Town - Ambush Chest'], ['Thieves Town Big Key Door', 'Thieves Town Exit']),
|
||||
create_dungeon_region(player, 'Thieves Town (Deep)', 'Thieves\' Town', ['Thieves\' Town - Attic',
|
||||
'Thieves\' Town - Big Chest',
|
||||
'Thieves\' Town - Blind\'s Cell'], ['Blind Fight']),
|
||||
create_dungeon_region(player, 'Blind Fight', 'Thieves\' Town', ['Thieves\' Town - Boss', 'Thieves\' Town - Prize']),
|
||||
create_dungeon_region(player, 'Skull Woods First Section', 'Skull Woods', ['Skull Woods - Map Chest'], ['Skull Woods First Section Exit', 'Skull Woods First Section Bomb Jump', 'Skull Woods First Section South Door', 'Skull Woods First Section West Door']),
|
||||
create_dungeon_region(player, 'Skull Woods First Section (Right)', 'Skull Woods', ['Skull Woods - Pinball Room'], ['Skull Woods First Section (Right) North Door']),
|
||||
create_dungeon_region(player, 'Skull Woods First Section (Left)', 'Skull Woods', ['Skull Woods - Compass Chest', 'Skull Woods - Pot Prison'], ['Skull Woods First Section (Left) Door to Exit', 'Skull Woods First Section (Left) Door to Right']),
|
||||
create_dungeon_region(player, 'Skull Woods First Section (Top)', 'Skull Woods', ['Skull Woods - Big Chest'], ['Skull Woods First Section (Top) One-Way Path']),
|
||||
create_dungeon_region(player, 'Skull Woods Second Section (Drop)', 'Skull Woods', None, ['Skull Woods Second Section (Drop)']),
|
||||
create_dungeon_region(player, 'Skull Woods Second Section', 'Skull Woods', ['Skull Woods - Big Key Chest'], ['Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)']),
|
||||
create_dungeon_region(player, 'Skull Woods Final Section (Entrance)', 'Skull Woods', ['Skull Woods - Bridge Room'], ['Skull Woods Torch Room', 'Skull Woods Final Section Exit']),
|
||||
create_dungeon_region(player, 'Skull Woods Final Section (Mothula)', 'Skull Woods', ['Skull Woods - Boss', 'Skull Woods - Prize']),
|
||||
create_dungeon_region(player, 'Ice Palace (Entrance)', 'Ice Palace', None, ['Ice Palace Entrance Room', 'Ice Palace Exit']),
|
||||
create_dungeon_region(player, 'Ice Palace (Main)', 'Ice Palace', ['Ice Palace - Compass Chest', 'Ice Palace - Freezor Chest',
|
||||
'Ice Palace - Big Chest', 'Ice Palace - Iced T Room'], ['Ice Palace (East)', 'Ice Palace (Kholdstare)']),
|
||||
create_dungeon_region(player, 'Ice Palace (East)', 'Ice Palace', ['Ice Palace - Spike Room'], ['Ice Palace (East Top)']),
|
||||
create_dungeon_region(player, 'Ice Palace (East Top)', 'Ice Palace', ['Ice Palace - Big Key Chest', 'Ice Palace - Map Chest']),
|
||||
create_dungeon_region(player, 'Ice Palace (Kholdstare)', 'Ice Palace', ['Ice Palace - Boss', 'Ice Palace - Prize']),
|
||||
create_dungeon_region(player, 'Misery Mire (Entrance)', 'Misery Mire', None, ['Misery Mire Entrance Gap', 'Misery Mire Exit']),
|
||||
create_dungeon_region(player, 'Misery Mire (Main)', 'Misery Mire', ['Misery Mire - Big Chest', 'Misery Mire - Map Chest', 'Misery Mire - Main Lobby',
|
||||
'Misery Mire - Bridge Chest', 'Misery Mire - Spike Chest'], ['Misery Mire (West)', 'Misery Mire Big Key Door']),
|
||||
create_dungeon_region(player, 'Misery Mire (West)', 'Misery Mire', ['Misery Mire - Compass Chest', 'Misery Mire - Big Key Chest']),
|
||||
create_dungeon_region(player, 'Misery Mire (Final Area)', 'Misery Mire', None, ['Misery Mire (Vitreous)']),
|
||||
create_dungeon_region(player, 'Misery Mire (Vitreous)', 'Misery Mire', ['Misery Mire - Boss', 'Misery Mire - Prize']),
|
||||
create_dungeon_region(player, 'Turtle Rock (Entrance)', 'Turtle Rock', None, ['Turtle Rock Entrance Gap', 'Turtle Rock Exit (Front)']),
|
||||
create_dungeon_region(player, 'Turtle Rock (First Section)', 'Turtle Rock', ['Turtle Rock - Compass Chest', 'Turtle Rock - Roller Room - Left',
|
||||
'Turtle Rock - Roller Room - Right'], ['Turtle Rock Pokey Room', 'Turtle Rock Entrance Gap Reverse']),
|
||||
create_dungeon_region(player, 'Turtle Rock (Chain Chomp Room)', 'Turtle Rock', ['Turtle Rock - Chain Chomps'], ['Turtle Rock (Chain Chomp Room) (North)', 'Turtle Rock (Chain Chomp Room) (South)']),
|
||||
create_dungeon_region(player, 'Turtle Rock (Second Section)', 'Turtle Rock', ['Turtle Rock - Big Key Chest'], ['Turtle Rock Ledge Exit (West)', 'Turtle Rock Chain Chomp Staircase', 'Turtle Rock Big Key Door']),
|
||||
create_dungeon_region(player, 'Turtle Rock (Big Chest)', 'Turtle Rock', ['Turtle Rock - Big Chest'], ['Turtle Rock (Big Chest) (North)', 'Turtle Rock Ledge Exit (East)']),
|
||||
create_dungeon_region(player, 'Turtle Rock (Crystaroller Room)', 'Turtle Rock', ['Turtle Rock - Crystaroller Room'], ['Turtle Rock Dark Room Staircase', 'Turtle Rock Big Key Door Reverse']),
|
||||
create_dungeon_region(player, 'Turtle Rock (Dark Room)', 'Turtle Rock', None, ['Turtle Rock (Dark Room) (North)', 'Turtle Rock (Dark Room) (South)']),
|
||||
create_dungeon_region(player, 'Turtle Rock (Eye Bridge)', 'Turtle Rock', ['Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right',
|
||||
'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'],
|
||||
['Turtle Rock Dark Room (South)', 'Turtle Rock (Trinexx)', 'Turtle Rock Isolated Ledge Exit']),
|
||||
create_dungeon_region(player, 'Turtle Rock (Trinexx)', 'Turtle Rock', ['Turtle Rock - Boss', 'Turtle Rock - Prize']),
|
||||
create_dungeon_region(player, 'Palace of Darkness (Entrance)', 'Palace of Darkness', ['Palace of Darkness - Shooter Room'], ['Palace of Darkness Bridge Room', 'Palace of Darkness Bonk Wall', 'Palace of Darkness Exit']),
|
||||
create_dungeon_region(player, 'Palace of Darkness (Center)', 'Palace of Darkness', ['Palace of Darkness - The Arena - Bridge', 'Palace of Darkness - Stalfos Basement'],
|
||||
['Palace of Darkness Big Key Chest Staircase', 'Palace of Darkness (North)', 'Palace of Darkness Big Key Door']),
|
||||
create_dungeon_region(player, 'Palace of Darkness (Big Key Chest)', 'Palace of Darkness', ['Palace of Darkness - Big Key Chest']),
|
||||
create_dungeon_region(player, 'Palace of Darkness (Bonk Section)', 'Palace of Darkness', ['Palace of Darkness - The Arena - Ledge', 'Palace of Darkness - Map Chest'], ['Palace of Darkness Hammer Peg Drop']),
|
||||
create_dungeon_region(player, 'Palace of Darkness (North)', 'Palace of Darkness', ['Palace of Darkness - Compass Chest', 'Palace of Darkness - Dark Basement - Left', 'Palace of Darkness - Dark Basement - Right'],
|
||||
create_dungeon_region(world, player, 'Swamp Palace (Entrance)', 'Swamp Palace', None,
|
||||
['Swamp Palace Moat', 'Swamp Palace Exit']),
|
||||
create_dungeon_region(world, player, 'Swamp Palace (First Room)', 'Swamp Palace', ['Swamp Palace - Entrance'],
|
||||
['Swamp Palace Small Key Door']),
|
||||
create_dungeon_region(world, player, 'Swamp Palace (Starting Area)', 'Swamp Palace',
|
||||
['Swamp Palace - Map Chest'], ['Swamp Palace (Center)']),
|
||||
create_dungeon_region(world, player, 'Swamp Palace (Center)', 'Swamp Palace',
|
||||
['Swamp Palace - Big Chest', 'Swamp Palace - Compass Chest',
|
||||
'Swamp Palace - Big Key Chest', 'Swamp Palace - West Chest'], ['Swamp Palace (North)']),
|
||||
create_dungeon_region(world, player, 'Swamp Palace (North)', 'Swamp Palace',
|
||||
['Swamp Palace - Flooded Room - Left', 'Swamp Palace - Flooded Room - Right',
|
||||
'Swamp Palace - Waterfall Room', 'Swamp Palace - Boss', 'Swamp Palace - Prize']),
|
||||
create_dungeon_region(world, player, 'Thieves Town (Entrance)', 'Thieves\' Town',
|
||||
['Thieves\' Town - Big Key Chest',
|
||||
'Thieves\' Town - Map Chest',
|
||||
'Thieves\' Town - Compass Chest',
|
||||
'Thieves\' Town - Ambush Chest'], ['Thieves Town Big Key Door', 'Thieves Town Exit']),
|
||||
create_dungeon_region(world, player, 'Thieves Town (Deep)', 'Thieves\' Town', ['Thieves\' Town - Attic',
|
||||
'Thieves\' Town - Big Chest',
|
||||
'Thieves\' Town - Blind\'s Cell'],
|
||||
['Blind Fight']),
|
||||
create_dungeon_region(world, player, 'Blind Fight', 'Thieves\' Town',
|
||||
['Thieves\' Town - Boss', 'Thieves\' Town - Prize']),
|
||||
create_dungeon_region(world, player, 'Skull Woods First Section', 'Skull Woods', ['Skull Woods - Map Chest'],
|
||||
['Skull Woods First Section Exit', 'Skull Woods First Section Bomb Jump',
|
||||
'Skull Woods First Section South Door', 'Skull Woods First Section West Door']),
|
||||
create_dungeon_region(world, player, 'Skull Woods First Section (Right)', 'Skull Woods',
|
||||
['Skull Woods - Pinball Room'], ['Skull Woods First Section (Right) North Door']),
|
||||
create_dungeon_region(world, player, 'Skull Woods First Section (Left)', 'Skull Woods',
|
||||
['Skull Woods - Compass Chest', 'Skull Woods - Pot Prison'],
|
||||
['Skull Woods First Section (Left) Door to Exit',
|
||||
'Skull Woods First Section (Left) Door to Right']),
|
||||
create_dungeon_region(world, player, 'Skull Woods First Section (Top)', 'Skull Woods',
|
||||
['Skull Woods - Big Chest'], ['Skull Woods First Section (Top) One-Way Path']),
|
||||
create_dungeon_region(world, player, 'Skull Woods Second Section (Drop)', 'Skull Woods', None,
|
||||
['Skull Woods Second Section (Drop)']),
|
||||
create_dungeon_region(world, player, 'Skull Woods Second Section', 'Skull Woods',
|
||||
['Skull Woods - Big Key Chest'],
|
||||
['Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)']),
|
||||
create_dungeon_region(world, player, 'Skull Woods Final Section (Entrance)', 'Skull Woods',
|
||||
['Skull Woods - Bridge Room'],
|
||||
['Skull Woods Torch Room', 'Skull Woods Final Section Exit']),
|
||||
create_dungeon_region(world, player, 'Skull Woods Final Section (Mothula)', 'Skull Woods',
|
||||
['Skull Woods - Boss', 'Skull Woods - Prize']),
|
||||
create_dungeon_region(world, player, 'Ice Palace (Entrance)', 'Ice Palace', None,
|
||||
['Ice Palace Entrance Room', 'Ice Palace Exit']),
|
||||
create_dungeon_region(world, player, 'Ice Palace (Main)', 'Ice Palace',
|
||||
['Ice Palace - Compass Chest', 'Ice Palace - Freezor Chest',
|
||||
'Ice Palace - Big Chest', 'Ice Palace - Iced T Room'],
|
||||
['Ice Palace (East)', 'Ice Palace (Kholdstare)']),
|
||||
create_dungeon_region(world, player, 'Ice Palace (East)', 'Ice Palace', ['Ice Palace - Spike Room'],
|
||||
['Ice Palace (East Top)']),
|
||||
create_dungeon_region(world, player, 'Ice Palace (East Top)', 'Ice Palace',
|
||||
['Ice Palace - Big Key Chest', 'Ice Palace - Map Chest']),
|
||||
create_dungeon_region(world, player, 'Ice Palace (Kholdstare)', 'Ice Palace',
|
||||
['Ice Palace - Boss', 'Ice Palace - Prize']),
|
||||
create_dungeon_region(world, player, 'Misery Mire (Entrance)', 'Misery Mire', None,
|
||||
['Misery Mire Entrance Gap', 'Misery Mire Exit']),
|
||||
create_dungeon_region(world, player, 'Misery Mire (Main)', 'Misery Mire',
|
||||
['Misery Mire - Big Chest', 'Misery Mire - Map Chest', 'Misery Mire - Main Lobby',
|
||||
'Misery Mire - Bridge Chest', 'Misery Mire - Spike Chest'],
|
||||
['Misery Mire (West)', 'Misery Mire Big Key Door']),
|
||||
create_dungeon_region(world, player, 'Misery Mire (West)', 'Misery Mire',
|
||||
['Misery Mire - Compass Chest', 'Misery Mire - Big Key Chest']),
|
||||
create_dungeon_region(world, player, 'Misery Mire (Final Area)', 'Misery Mire', None,
|
||||
['Misery Mire (Vitreous)']),
|
||||
create_dungeon_region(world, player, 'Misery Mire (Vitreous)', 'Misery Mire',
|
||||
['Misery Mire - Boss', 'Misery Mire - Prize']),
|
||||
create_dungeon_region(world, player, 'Turtle Rock (Entrance)', 'Turtle Rock', None,
|
||||
['Turtle Rock Entrance Gap', 'Turtle Rock Exit (Front)']),
|
||||
create_dungeon_region(world, player, 'Turtle Rock (First Section)', 'Turtle Rock',
|
||||
['Turtle Rock - Compass Chest', 'Turtle Rock - Roller Room - Left',
|
||||
'Turtle Rock - Roller Room - Right'],
|
||||
['Turtle Rock Pokey Room', 'Turtle Rock Entrance Gap Reverse']),
|
||||
create_dungeon_region(world, player, 'Turtle Rock (Chain Chomp Room)', 'Turtle Rock',
|
||||
['Turtle Rock - Chain Chomps'],
|
||||
['Turtle Rock (Chain Chomp Room) (North)', 'Turtle Rock (Chain Chomp Room) (South)']),
|
||||
create_dungeon_region(world, player, 'Turtle Rock (Second Section)', 'Turtle Rock',
|
||||
['Turtle Rock - Big Key Chest'],
|
||||
['Turtle Rock Ledge Exit (West)', 'Turtle Rock Chain Chomp Staircase',
|
||||
'Turtle Rock Big Key Door']),
|
||||
create_dungeon_region(world, player, 'Turtle Rock (Big Chest)', 'Turtle Rock', ['Turtle Rock - Big Chest'],
|
||||
['Turtle Rock (Big Chest) (North)', 'Turtle Rock Ledge Exit (East)']),
|
||||
create_dungeon_region(world, player, 'Turtle Rock (Crystaroller Room)', 'Turtle Rock',
|
||||
['Turtle Rock - Crystaroller Room'],
|
||||
['Turtle Rock Dark Room Staircase', 'Turtle Rock Big Key Door Reverse']),
|
||||
create_dungeon_region(world, player, 'Turtle Rock (Dark Room)', 'Turtle Rock', None,
|
||||
['Turtle Rock (Dark Room) (North)', 'Turtle Rock (Dark Room) (South)']),
|
||||
create_dungeon_region(world, player, 'Turtle Rock (Eye Bridge)', 'Turtle Rock',
|
||||
['Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right',
|
||||
'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'],
|
||||
['Turtle Rock Dark Room (South)', 'Turtle Rock (Trinexx)',
|
||||
'Turtle Rock Isolated Ledge Exit']),
|
||||
create_dungeon_region(world, player, 'Turtle Rock (Trinexx)', 'Turtle Rock',
|
||||
['Turtle Rock - Boss', 'Turtle Rock - Prize']),
|
||||
create_dungeon_region(world, player, 'Palace of Darkness (Entrance)', 'Palace of Darkness',
|
||||
['Palace of Darkness - Shooter Room'],
|
||||
['Palace of Darkness Bridge Room', 'Palace of Darkness Bonk Wall',
|
||||
'Palace of Darkness Exit']),
|
||||
create_dungeon_region(world, player, 'Palace of Darkness (Center)', 'Palace of Darkness',
|
||||
['Palace of Darkness - The Arena - Bridge', 'Palace of Darkness - Stalfos Basement'],
|
||||
['Palace of Darkness Big Key Chest Staircase', 'Palace of Darkness (North)',
|
||||
'Palace of Darkness Big Key Door']),
|
||||
create_dungeon_region(world, player, 'Palace of Darkness (Big Key Chest)', 'Palace of Darkness',
|
||||
['Palace of Darkness - Big Key Chest']),
|
||||
create_dungeon_region(world, player, 'Palace of Darkness (Bonk Section)', 'Palace of Darkness',
|
||||
['Palace of Darkness - The Arena - Ledge', 'Palace of Darkness - Map Chest'],
|
||||
['Palace of Darkness Hammer Peg Drop']),
|
||||
create_dungeon_region(world, player, 'Palace of Darkness (North)', 'Palace of Darkness',
|
||||
['Palace of Darkness - Compass Chest', 'Palace of Darkness - Dark Basement - Left',
|
||||
'Palace of Darkness - Dark Basement - Right'],
|
||||
['Palace of Darkness Spike Statue Room Door', 'Palace of Darkness Maze Door']),
|
||||
create_dungeon_region(player, 'Palace of Darkness (Maze)', 'Palace of Darkness', ['Palace of Darkness - Dark Maze - Top', 'Palace of Darkness - Dark Maze - Bottom', 'Palace of Darkness - Big Chest']),
|
||||
create_dungeon_region(player, 'Palace of Darkness (Harmless Hellway)', 'Palace of Darkness', ['Palace of Darkness - Harmless Hellway']),
|
||||
create_dungeon_region(player, 'Palace of Darkness (Final Section)', 'Palace of Darkness', ['Palace of Darkness - Boss', 'Palace of Darkness - Prize']),
|
||||
create_dungeon_region(player, 'Inverted Ganons Tower (Entrance)', 'Ganon\'s Tower', ['Ganons Tower - Bob\'s Torch', 'Ganons Tower - Hope Room - Left', 'Ganons Tower - Hope Room - Right'],
|
||||
['Ganons Tower (Tile Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower Big Key Door', 'Inverted Ganons Tower Exit']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Tile Room)', 'Ganon\'s Tower', ['Ganons Tower - Tile Room'], ['Ganons Tower (Tile Room) Key Door']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Compass Room)', 'Ganon\'s Tower', ['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right',
|
||||
'Ganons Tower - Compass Room - Bottom Left', 'Ganons Tower - Compass Room - Bottom Right'],
|
||||
['Ganons Tower (Bottom) (East)']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Hookshot Room)', 'Ganon\'s Tower', ['Ganons Tower - DMs Room - Top Left', 'Ganons Tower - DMs Room - Top Right',
|
||||
'Ganons Tower - DMs Room - Bottom Left', 'Ganons Tower - DMs Room - Bottom Right'],
|
||||
create_dungeon_region(world, player, 'Palace of Darkness (Maze)', 'Palace of Darkness',
|
||||
['Palace of Darkness - Dark Maze - Top', 'Palace of Darkness - Dark Maze - Bottom',
|
||||
'Palace of Darkness - Big Chest']),
|
||||
create_dungeon_region(world, player, 'Palace of Darkness (Harmless Hellway)', 'Palace of Darkness',
|
||||
['Palace of Darkness - Harmless Hellway']),
|
||||
create_dungeon_region(world, player, 'Palace of Darkness (Final Section)', 'Palace of Darkness',
|
||||
['Palace of Darkness - Boss', 'Palace of Darkness - Prize']),
|
||||
create_dungeon_region(world, player, 'Inverted Ganons Tower (Entrance)', 'Ganon\'s Tower',
|
||||
['Ganons Tower - Bob\'s Torch', 'Ganons Tower - Hope Room - Left',
|
||||
'Ganons Tower - Hope Room - Right'],
|
||||
['Ganons Tower (Tile Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower Big Key Door',
|
||||
'Inverted Ganons Tower Exit']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Tile Room)', 'Ganon\'s Tower', ['Ganons Tower - Tile Room'],
|
||||
['Ganons Tower (Tile Room) Key Door']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Compass Room)', 'Ganon\'s Tower',
|
||||
['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right',
|
||||
'Ganons Tower - Compass Room - Bottom Left',
|
||||
'Ganons Tower - Compass Room - Bottom Right'], ['Ganons Tower (Bottom) (East)']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Hookshot Room)', 'Ganon\'s Tower',
|
||||
['Ganons Tower - DMs Room - Top Left', 'Ganons Tower - DMs Room - Top Right',
|
||||
'Ganons Tower - DMs Room - Bottom Left', 'Ganons Tower - DMs Room - Bottom Right'],
|
||||
['Ganons Tower (Map Room)', 'Ganons Tower (Double Switch Room)']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Map Room)', 'Ganon\'s Tower', ['Ganons Tower - Map Chest']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Firesnake Room)', 'Ganon\'s Tower', ['Ganons Tower - Firesnake Room'], ['Ganons Tower (Firesnake Room)']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Teleport Room)', 'Ganon\'s Tower', ['Ganons Tower - Randomizer Room - Top Left', 'Ganons Tower - Randomizer Room - Top Right',
|
||||
'Ganons Tower - Randomizer Room - Bottom Left', 'Ganons Tower - Randomizer Room - Bottom Right'],
|
||||
['Ganons Tower (Bottom) (West)']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Bottom)', 'Ganon\'s Tower', ['Ganons Tower - Bob\'s Chest', 'Ganons Tower - Big Chest', 'Ganons Tower - Big Key Room - Left',
|
||||
'Ganons Tower - Big Key Room - Right', 'Ganons Tower - Big Key Chest']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Top)', 'Ganon\'s Tower', None, ['Ganons Tower Torch Rooms']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Before Moldorm)', 'Ganon\'s Tower', ['Ganons Tower - Mini Helmasaur Room - Left', 'Ganons Tower - Mini Helmasaur Room - Right',
|
||||
'Ganons Tower - Pre-Moldorm Chest'], ['Ganons Tower Moldorm Door']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Moldorm)', 'Ganon\'s Tower', None, ['Ganons Tower Moldorm Gap']),
|
||||
create_dungeon_region(player, 'Agahnim 2', 'Ganon\'s Tower', ['Ganons Tower - Validation Chest', 'Agahnim 2'], None),
|
||||
create_cave_region(player, 'Pyramid', 'a drop\'s exit', ['Ganon'], ['Ganon Drop']),
|
||||
create_cave_region(player, 'Bottom of Pyramid', 'a drop\'s exit', None, ['Pyramid Exit']),
|
||||
create_dw_region(player, 'Pyramid Ledge', None, ['Pyramid Drop']), # houlihan room exits here in inverted
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Map Room)', 'Ganon\'s Tower', ['Ganons Tower - Map Chest']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Firesnake Room)', 'Ganon\'s Tower',
|
||||
['Ganons Tower - Firesnake Room'], ['Ganons Tower (Firesnake Room)']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Teleport Room)', 'Ganon\'s Tower',
|
||||
['Ganons Tower - Randomizer Room - Top Left',
|
||||
'Ganons Tower - Randomizer Room - Top Right',
|
||||
'Ganons Tower - Randomizer Room - Bottom Left',
|
||||
'Ganons Tower - Randomizer Room - Bottom Right'], ['Ganons Tower (Bottom) (West)']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Bottom)', 'Ganon\'s Tower',
|
||||
['Ganons Tower - Bob\'s Chest', 'Ganons Tower - Big Chest',
|
||||
'Ganons Tower - Big Key Room - Left',
|
||||
'Ganons Tower - Big Key Room - Right', 'Ganons Tower - Big Key Chest']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Top)', 'Ganon\'s Tower', None,
|
||||
['Ganons Tower Torch Rooms']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Before Moldorm)', 'Ganon\'s Tower',
|
||||
['Ganons Tower - Mini Helmasaur Room - Left',
|
||||
'Ganons Tower - Mini Helmasaur Room - Right',
|
||||
'Ganons Tower - Pre-Moldorm Chest'], ['Ganons Tower Moldorm Door']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Moldorm)', 'Ganon\'s Tower', None,
|
||||
['Ganons Tower Moldorm Gap']),
|
||||
create_dungeon_region(world, player, 'Agahnim 2', 'Ganon\'s Tower',
|
||||
['Ganons Tower - Validation Chest', 'Agahnim 2'], None),
|
||||
create_cave_region(world, player, 'Pyramid', 'a drop\'s exit', ['Ganon'], ['Ganon Drop']),
|
||||
create_cave_region(world, player, 'Bottom of Pyramid', 'a drop\'s exit', None, ['Pyramid Exit']),
|
||||
create_dw_region(world, player, 'Pyramid Ledge', None, ['Pyramid Drop']), # houlihan room exits here in inverted
|
||||
|
||||
# to simplify flute connections
|
||||
create_cave_region(player, 'The Sky', 'A Dark Sky', None, ['DDM Landing','NEDW Landing', 'WDW Landing', 'SDW Landing', 'EDW Landing', 'DD Landing', 'DLHL Landing']),
|
||||
create_cave_region(world, player, 'The Sky', 'A Dark Sky', None,
|
||||
['DDM Landing', 'NEDW Landing', 'WDW Landing', 'SDW Landing', 'EDW Landing', 'DD Landing',
|
||||
'DLHL Landing']),
|
||||
|
||||
create_lw_region(player, 'Desert Northern Cliffs'),
|
||||
create_lw_region(player, 'Death Mountain Bunny Descent Area')
|
||||
create_lw_region(world, player, 'Desert Northern Cliffs'),
|
||||
create_lw_region(world, player, 'Death Mountain Bunny Descent Area')
|
||||
]
|
||||
|
||||
world.initialize_regions()
|
||||
@@ -316,26 +534,26 @@ def create_inverted_regions(world, player):
|
||||
def mark_dark_world_regions(world, player):
|
||||
# cross world caves may have some sections marked as both in_light_world, and in_dark_work.
|
||||
# That is ok. the bunny logic will check for this case and incorporate special rules.
|
||||
queue = collections.deque(region for region in world.get_regions(player) if region.type == RegionType.DarkWorld)
|
||||
queue = collections.deque(region for region in world.get_regions(player) if region.type == LTTPRegionType.DarkWorld)
|
||||
seen = set(queue)
|
||||
while queue:
|
||||
current = queue.popleft()
|
||||
current.is_dark_world = True
|
||||
for exit in current.exits:
|
||||
if exit.connected_region.type == RegionType.LightWorld:
|
||||
if exit.connected_region.type == LTTPRegionType.LightWorld:
|
||||
# Don't venture into the dark world
|
||||
continue
|
||||
if exit.connected_region not in seen:
|
||||
seen.add(exit.connected_region)
|
||||
queue.append(exit.connected_region)
|
||||
|
||||
queue = collections.deque(region for region in world.get_regions(player) if region.type == RegionType.LightWorld)
|
||||
queue = collections.deque(region for region in world.get_regions(player) if region.type == LTTPRegionType.LightWorld)
|
||||
seen = set(queue)
|
||||
while queue:
|
||||
current = queue.popleft()
|
||||
current.is_light_world = True
|
||||
for exit in current.exits:
|
||||
if exit.connected_region.type == RegionType.DarkWorld:
|
||||
if exit.connected_region.type == LTTPRegionType.DarkWorld:
|
||||
# Don't venture into the light world
|
||||
continue
|
||||
if exit.connected_region not in seen:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
from collections import namedtuple
|
||||
import logging
|
||||
|
||||
from BaseClasses import Region, RegionType, ItemClassification
|
||||
from worlds.alttp.SubClasses import ALttPLocation
|
||||
from BaseClasses import ItemClassification
|
||||
from worlds.alttp.SubClasses import ALttPLocation, LTTPRegion, LTTPRegionType
|
||||
from worlds.alttp.Shops import TakeAny, total_shop_slots, set_up_shops, shuffle_shops, create_dynamic_shop_locations
|
||||
from worlds.alttp.Bosses import place_bosses
|
||||
from worlds.alttp.Dungeons import get_dungeon_item_pool_player
|
||||
@@ -471,7 +471,7 @@ def set_up_take_anys(world, player):
|
||||
|
||||
regions = world.random.sample(take_any_locs, 5)
|
||||
|
||||
old_man_take_any = Region("Old Man Sword Cave", RegionType.Cave, 'the sword cave', player)
|
||||
old_man_take_any = LTTPRegion("Old Man Sword Cave", LTTPRegionType.Cave, 'the sword cave', player, world)
|
||||
world.regions.append(old_man_take_any)
|
||||
|
||||
reg = regions.pop()
|
||||
@@ -491,7 +491,7 @@ def set_up_take_anys(world, player):
|
||||
old_man_take_any.shop.add_inventory(0, 'Rupees (300)', 0, 0, create_location=True)
|
||||
|
||||
for num in range(4):
|
||||
take_any = Region("Take-Any #{}".format(num+1), RegionType.Cave, 'a cave of choice', player)
|
||||
take_any = LTTPRegion("Take-Any #{}".format(num+1), LTTPRegionType.Cave, 'a cave of choice', player, world)
|
||||
world.regions.append(take_any)
|
||||
|
||||
target, room_id = world.random.choice([(0x58, 0x0112), (0x60, 0x010F), (0x46, 0x011F)])
|
||||
|
||||
+510
-313
@@ -1,369 +1,566 @@
|
||||
import collections
|
||||
import typing
|
||||
|
||||
from BaseClasses import Region, Entrance, RegionType
|
||||
from BaseClasses import Entrance, MultiWorld
|
||||
from .SubClasses import LTTPRegion, LTTPRegionType
|
||||
|
||||
|
||||
def is_main_entrance(entrance: Entrance) -> bool:
|
||||
return entrance.parent_region.type in {RegionType.DarkWorld, RegionType.LightWorld, RegionType.Generic}
|
||||
return entrance.parent_region.type in {LTTPRegionType.DarkWorld, LTTPRegionType.LightWorld} if entrance.parent_region.type else True
|
||||
|
||||
|
||||
def create_regions(world, player):
|
||||
|
||||
world.regions += [
|
||||
create_lw_region(player, 'Menu', None, ['Links House S&Q', 'Sanctuary S&Q', 'Old Man S&Q']),
|
||||
create_lw_region(player, 'Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure',
|
||||
'Purple Chest', 'Flute Activation Spot'],
|
||||
["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Zoras River', 'Kings Grave Outer Rocks', 'Dam',
|
||||
'Links House', 'Tavern North', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave',
|
||||
'Blacksmiths Hut', 'Bat Cave Drop Ledge', 'Bat Cave Cave', 'Sick Kids House', 'Hobo Bridge', 'Lost Woods Hideout Drop', 'Lost Woods Hideout Stump',
|
||||
'Lumberjack Tree Tree', 'Lumberjack Tree Cave', 'Mini Moldorm Cave', 'Ice Rod Cave', 'Lake Hylia Central Island Pier',
|
||||
'Bonk Rock Cave', 'Library', 'Potion Shop', 'Two Brothers House (East)', 'Desert Palace Stairs', 'Eastern Palace', 'Master Sword Meadow',
|
||||
'Sanctuary', 'Sanctuary Grave', 'Death Mountain Entrance Rock', 'Flute Spot 1', 'Dark Desert Teleporter', 'East Hyrule Teleporter', 'South Hyrule Teleporter', 'Kakariko Teleporter',
|
||||
'Elder House (East)', 'Elder House (West)', 'North Fairy Cave', 'North Fairy Cave Drop', 'Lost Woods Gamble', 'Snitch Lady (East)', 'Snitch Lady (West)', 'Tavern (Front)',
|
||||
'Bush Covered House', 'Light World Bomb Hut', 'Kakariko Shop', 'Long Fairy Cave', 'Good Bee Cave', '20 Rupee Cave', 'Cave Shop (Lake Hylia)', 'Waterfall of Wishing', 'Hyrule Castle Main Gate',
|
||||
'Bonk Fairy (Light)', '50 Rupee Cave', 'Fortune Teller (Light)', 'Lake Hylia Fairy', 'Light Hype Fairy', 'Desert Fairy', 'Lumberjack House', 'Lake Hylia Fortune Teller', 'Kakariko Gamble Game', 'Top of Pyramid']),
|
||||
create_lw_region(player, 'Death Mountain Entrance', None, ['Old Man Cave (West)', 'Death Mountain Entrance Drop']),
|
||||
create_lw_region(player, 'Lake Hylia Central Island', None, ['Capacity Upgrade', 'Lake Hylia Central Island Teleporter']),
|
||||
create_cave_region(player, 'Blinds Hideout', 'a bounty of five items', ["Blind\'s Hideout - Top",
|
||||
"Blind\'s Hideout - Left",
|
||||
"Blind\'s Hideout - Right",
|
||||
"Blind\'s Hideout - Far Left",
|
||||
"Blind\'s Hideout - Far Right"]),
|
||||
create_cave_region(player, 'Hyrule Castle Secret Entrance', 'a drop\'s exit', ['Link\'s Uncle', 'Secret Passage'], ['Hyrule Castle Secret Entrance Exit']),
|
||||
create_lw_region(player, 'Zoras River', ['King Zora', 'Zora\'s Ledge']),
|
||||
create_cave_region(player, 'Waterfall of Wishing', 'a cave with two chests', ['Waterfall Fairy - Left', 'Waterfall Fairy - Right']),
|
||||
create_lw_region(player, 'Kings Grave Area', None, ['Kings Grave', 'Kings Grave Inner Rocks']),
|
||||
create_cave_region(player, 'Kings Grave', 'a cave with a chest', ['King\'s Tomb']),
|
||||
create_cave_region(player, 'North Fairy Cave', 'a drop\'s exit', None, ['North Fairy Cave Exit']),
|
||||
create_cave_region(player, 'Dam', 'the dam', ['Floodgate', 'Floodgate Chest']),
|
||||
create_cave_region(player, 'Links House', 'your house', ['Link\'s House'], ['Links House Exit']),
|
||||
create_cave_region(player, 'Chris Houlihan Room', 'I AM ERROR', None, ['Chris Houlihan Room Exit']),
|
||||
create_cave_region(player, 'Tavern', 'the tavern', ['Kakariko Tavern']),
|
||||
create_cave_region(player, 'Elder House', 'a connector', None, ['Elder House Exit (East)', 'Elder House Exit (West)']),
|
||||
create_cave_region(player, 'Snitch Lady (East)', 'a boring house'),
|
||||
create_cave_region(player, 'Snitch Lady (West)', 'a boring house'),
|
||||
create_cave_region(player, 'Bush Covered House', 'the grass man'),
|
||||
create_cave_region(player, 'Tavern (Front)', 'the tavern'),
|
||||
create_cave_region(player, 'Light World Bomb Hut', 'a restock room'),
|
||||
create_cave_region(player, 'Kakariko Shop', 'a common shop'),
|
||||
create_cave_region(player, 'Fortune Teller (Light)', 'a fortune teller'),
|
||||
create_cave_region(player, 'Lake Hylia Fortune Teller', 'a fortune teller'),
|
||||
create_cave_region(player, 'Lumberjack House', 'a boring house'),
|
||||
create_cave_region(player, 'Bonk Fairy (Light)', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Bonk Fairy (Dark)', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Lake Hylia Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Swamp Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Desert Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Dark Lake Hylia Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Dark Lake Hylia Ledge Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Dark Desert Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Dark Death Mountain Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Chicken House', 'a house with a chest', ['Chicken House']),
|
||||
create_cave_region(player, 'Aginahs Cave', 'a cave with a chest', ['Aginah\'s Cave']),
|
||||
create_cave_region(player, 'Sahasrahlas Hut', 'Sahasrahla', ['Sahasrahla\'s Hut - Left', 'Sahasrahla\'s Hut - Middle', 'Sahasrahla\'s Hut - Right', 'Sahasrahla']),
|
||||
create_cave_region(player, 'Kakariko Well (top)', 'a drop\'s exit', ['Kakariko Well - Top', 'Kakariko Well - Left', 'Kakariko Well - Middle',
|
||||
'Kakariko Well - Right', 'Kakariko Well - Bottom'], ['Kakariko Well (top to bottom)']),
|
||||
create_cave_region(player, 'Kakariko Well (bottom)', 'a drop\'s exit', None, ['Kakariko Well Exit']),
|
||||
create_cave_region(player, 'Blacksmiths Hut', 'the smith', ['Blacksmith', 'Missing Smith']),
|
||||
create_lw_region(player, 'Bat Cave Drop Ledge', None, ['Bat Cave Drop']),
|
||||
create_cave_region(player, 'Bat Cave (right)', 'a drop\'s exit', ['Magic Bat'], ['Bat Cave Door']),
|
||||
create_cave_region(player, 'Bat Cave (left)', 'a drop\'s exit', None, ['Bat Cave Exit']),
|
||||
create_cave_region(player, 'Sick Kids House', 'the sick kid', ['Sick Kid']),
|
||||
create_lw_region(player, 'Hobo Bridge', ['Hobo']),
|
||||
create_cave_region(player, 'Lost Woods Hideout (top)', 'a drop\'s exit', ['Lost Woods Hideout'], ['Lost Woods Hideout (top to bottom)']),
|
||||
create_cave_region(player, 'Lost Woods Hideout (bottom)', 'a drop\'s exit', None, ['Lost Woods Hideout Exit']),
|
||||
create_cave_region(player, 'Lumberjack Tree (top)', 'a drop\'s exit', ['Lumberjack Tree'], ['Lumberjack Tree (top to bottom)']),
|
||||
create_cave_region(player, 'Lumberjack Tree (bottom)', 'a drop\'s exit', None, ['Lumberjack Tree Exit']),
|
||||
create_lw_region(player, 'Cave 45 Ledge', None, ['Cave 45']),
|
||||
create_cave_region(player, 'Cave 45', 'a cave with an item', ['Cave 45']),
|
||||
create_lw_region(player, 'Graveyard Ledge', None, ['Graveyard Cave']),
|
||||
create_cave_region(player, 'Graveyard Cave', 'a cave with an item', ['Graveyard Cave']),
|
||||
create_cave_region(player, 'Checkerboard Cave', 'a cave with an item', ['Checkerboard Cave']),
|
||||
create_cave_region(player, 'Long Fairy Cave', 'a fairy fountain'),
|
||||
create_cave_region(player, 'Mini Moldorm Cave', 'a bounty of five items', ['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Left', 'Mini Moldorm Cave - Right',
|
||||
'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Generous Guy']),
|
||||
create_cave_region(player, 'Ice Rod Cave', 'a cave with a chest', ['Ice Rod Cave']),
|
||||
create_cave_region(player, 'Good Bee Cave', 'a cold bee'),
|
||||
create_cave_region(player, '20 Rupee Cave', 'a cave with some cash'),
|
||||
create_cave_region(player, 'Cave Shop (Lake Hylia)', 'a common shop'),
|
||||
create_cave_region(player, 'Cave Shop (Dark Death Mountain)', 'a common shop'),
|
||||
create_cave_region(player, 'Bonk Rock Cave', 'a cave with a chest', ['Bonk Rock Cave']),
|
||||
create_cave_region(player, 'Library', 'the library', ['Library']),
|
||||
create_cave_region(player, 'Kakariko Gamble Game', 'a game of chance'),
|
||||
create_cave_region(player, 'Potion Shop', 'the potion shop', ['Potion Shop']),
|
||||
create_lw_region(player, 'Lake Hylia Island', ['Lake Hylia Island']),
|
||||
create_cave_region(player, 'Capacity Upgrade', 'the queen of fairies'),
|
||||
create_cave_region(player, 'Two Brothers House', 'a connector', None, ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']),
|
||||
create_lw_region(player, 'Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)']),
|
||||
create_cave_region(player, '50 Rupee Cave', 'a cave with some cash'),
|
||||
create_lw_region(player, 'Desert Ledge', ['Desert Ledge'], ['Desert Palace Entrance (North) Rocks', 'Desert Palace Entrance (West)']),
|
||||
create_lw_region(player, 'Desert Ledge (Northeast)', None, ['Checkerboard Cave']),
|
||||
create_lw_region(player, 'Desert Palace Stairs', None, ['Desert Palace Entrance (South)']),
|
||||
create_lw_region(player, 'Desert Palace Lone Stairs', None, ['Desert Palace Stairs Drop', 'Desert Palace Entrance (East)']),
|
||||
create_lw_region(player, 'Desert Palace Entrance (North) Spot', None, ['Desert Palace Entrance (North)', 'Desert Ledge Return Rocks']),
|
||||
create_dungeon_region(player, 'Desert Palace Main (Outer)', 'Desert Palace', ['Desert Palace - Big Chest', 'Desert Palace - Torch', 'Desert Palace - Map Chest'],
|
||||
['Desert Palace Pots (Outer)', 'Desert Palace Exit (West)', 'Desert Palace Exit (East)', 'Desert Palace East Wing']),
|
||||
create_dungeon_region(player, 'Desert Palace Main (Inner)', 'Desert Palace', None, ['Desert Palace Exit (South)', 'Desert Palace Pots (Inner)']),
|
||||
create_dungeon_region(player, 'Desert Palace East', 'Desert Palace', ['Desert Palace - Compass Chest', 'Desert Palace - Big Key Chest']),
|
||||
create_dungeon_region(player, 'Desert Palace North', 'Desert Palace', ['Desert Palace - Boss', 'Desert Palace - Prize'], ['Desert Palace Exit (North)']),
|
||||
create_dungeon_region(player, 'Eastern Palace', 'Eastern Palace', ['Eastern Palace - Compass Chest', 'Eastern Palace - Big Chest', 'Eastern Palace - Cannonball Chest',
|
||||
'Eastern Palace - Big Key Chest', 'Eastern Palace - Map Chest', 'Eastern Palace - Boss', 'Eastern Palace - Prize'], ['Eastern Palace Exit']),
|
||||
create_lw_region(player, 'Master Sword Meadow', ['Master Sword Pedestal']),
|
||||
create_cave_region(player, 'Lost Woods Gamble', 'a game of chance'),
|
||||
create_lw_region(player, 'Hyrule Castle Courtyard', None, ['Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Entrance (South)']),
|
||||
create_lw_region(player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Agahnims Tower', 'Hyrule Castle Ledge Courtyard Drop']),
|
||||
create_dungeon_region(player, 'Hyrule Castle', 'Hyrule Castle', ['Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest', 'Hyrule Castle - Zelda\'s Chest'],
|
||||
['Hyrule Castle Exit (East)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (South)', 'Throne Room']),
|
||||
create_dungeon_region(player, 'Sewer Drop', 'a drop\'s exit', None, ['Sewer Drop']), # This exists only to be referenced for access checks
|
||||
create_dungeon_region(player, 'Sewers (Dark)', 'a drop\'s exit', ['Sewers - Dark Cross'], ['Sewers Door']),
|
||||
create_dungeon_region(player, 'Sewers', 'a drop\'s exit', ['Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle',
|
||||
'Sewers - Secret Room - Right'], ['Sanctuary Push Door', 'Sewers Back Door']),
|
||||
create_dungeon_region(player, 'Sanctuary', 'a drop\'s exit', ['Sanctuary'], ['Sanctuary Exit']),
|
||||
create_dungeon_region(player, 'Agahnims Tower', 'Castle Tower', ['Castle Tower - Room 03', 'Castle Tower - Dark Maze'], ['Agahnim 1', 'Agahnims Tower Exit']),
|
||||
create_dungeon_region(player, 'Agahnim 1', 'Castle Tower', ['Agahnim 1'], None),
|
||||
create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)', 'Old Man Cave Exit (West)']),
|
||||
create_cave_region(player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
|
||||
create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']),
|
||||
create_lw_region(player, 'Death Mountain', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Teleporter']),
|
||||
create_cave_region(player, 'Death Mountain Return Cave', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']),
|
||||
create_lw_region(player, 'Death Mountain Return Ledge', None, ['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)']),
|
||||
create_cave_region(player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']),
|
||||
create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit']),
|
||||
create_cave_region(player, 'Spectacle Rock Cave (Peak)', 'a connector', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']),
|
||||
create_lw_region(player, 'East Death Mountain (Bottom)', None, ['Broken Bridge (East)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'East Death Mountain Teleporter', 'Hookshot Fairy', 'Fairy Ascension Rocks', 'Spiral Cave (Bottom)']),
|
||||
create_cave_region(player, 'Hookshot Fairy', 'fairies deep in a cave'),
|
||||
create_cave_region(player, 'Paradox Cave Front', 'a connector', None, ['Paradox Cave Push Block Reverse', 'Paradox Cave Exit (Bottom)', 'Light World Death Mountain Shop']),
|
||||
create_cave_region(player, 'Paradox Cave Chest Area', 'a connector', ['Paradox Cave Lower - Far Left',
|
||||
'Paradox Cave Lower - Left',
|
||||
'Paradox Cave Lower - Right',
|
||||
'Paradox Cave Lower - Far Right',
|
||||
'Paradox Cave Lower - Middle',
|
||||
'Paradox Cave Upper - Left',
|
||||
'Paradox Cave Upper - Right'],
|
||||
create_lw_region(world, player, 'Menu', None, ['Links House S&Q', 'Sanctuary S&Q', 'Old Man S&Q']),
|
||||
create_lw_region(world, player, 'Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure',
|
||||
'Purple Chest', 'Flute Activation Spot'],
|
||||
["Blinds Hideout", "Hyrule Castle Secret Entrance Drop", 'Zoras River',
|
||||
'Kings Grave Outer Rocks', 'Dam',
|
||||
'Links House', 'Tavern North', 'Chicken House', 'Aginahs Cave', 'Sahasrahlas Hut',
|
||||
'Kakariko Well Drop', 'Kakariko Well Cave',
|
||||
'Blacksmiths Hut', 'Bat Cave Drop Ledge', 'Bat Cave Cave', 'Sick Kids House', 'Hobo Bridge',
|
||||
'Lost Woods Hideout Drop', 'Lost Woods Hideout Stump',
|
||||
'Lumberjack Tree Tree', 'Lumberjack Tree Cave', 'Mini Moldorm Cave', 'Ice Rod Cave',
|
||||
'Lake Hylia Central Island Pier',
|
||||
'Bonk Rock Cave', 'Library', 'Potion Shop', 'Two Brothers House (East)',
|
||||
'Desert Palace Stairs', 'Eastern Palace', 'Master Sword Meadow',
|
||||
'Sanctuary', 'Sanctuary Grave', 'Death Mountain Entrance Rock', 'Flute Spot 1',
|
||||
'Dark Desert Teleporter', 'East Hyrule Teleporter', 'South Hyrule Teleporter',
|
||||
'Kakariko Teleporter',
|
||||
'Elder House (East)', 'Elder House (West)', 'North Fairy Cave', 'North Fairy Cave Drop',
|
||||
'Lost Woods Gamble', 'Snitch Lady (East)', 'Snitch Lady (West)', 'Tavern (Front)',
|
||||
'Bush Covered House', 'Light World Bomb Hut', 'Kakariko Shop', 'Long Fairy Cave',
|
||||
'Good Bee Cave', '20 Rupee Cave', 'Cave Shop (Lake Hylia)', 'Waterfall of Wishing',
|
||||
'Hyrule Castle Main Gate',
|
||||
'Bonk Fairy (Light)', '50 Rupee Cave', 'Fortune Teller (Light)', 'Lake Hylia Fairy',
|
||||
'Light Hype Fairy', 'Desert Fairy', 'Lumberjack House', 'Lake Hylia Fortune Teller',
|
||||
'Kakariko Gamble Game', 'Top of Pyramid']),
|
||||
create_lw_region(world, player, 'Death Mountain Entrance', None,
|
||||
['Old Man Cave (West)', 'Death Mountain Entrance Drop']),
|
||||
create_lw_region(world, player, 'Lake Hylia Central Island', None,
|
||||
['Capacity Upgrade', 'Lake Hylia Central Island Teleporter']),
|
||||
create_cave_region(world, player, 'Blinds Hideout', 'a bounty of five items', ["Blind\'s Hideout - Top",
|
||||
"Blind\'s Hideout - Left",
|
||||
"Blind\'s Hideout - Right",
|
||||
"Blind\'s Hideout - Far Left",
|
||||
"Blind\'s Hideout - Far Right"]),
|
||||
create_cave_region(world, player, 'Hyrule Castle Secret Entrance', 'a drop\'s exit',
|
||||
['Link\'s Uncle', 'Secret Passage'], ['Hyrule Castle Secret Entrance Exit']),
|
||||
create_lw_region(world, player, 'Zoras River', ['King Zora', 'Zora\'s Ledge']),
|
||||
create_cave_region(world, player, 'Waterfall of Wishing', 'a cave with two chests',
|
||||
['Waterfall Fairy - Left', 'Waterfall Fairy - Right']),
|
||||
create_lw_region(world, player, 'Kings Grave Area', None, ['Kings Grave', 'Kings Grave Inner Rocks']),
|
||||
create_cave_region(world, player, 'Kings Grave', 'a cave with a chest', ['King\'s Tomb']),
|
||||
create_cave_region(world, player, 'North Fairy Cave', 'a drop\'s exit', None, ['North Fairy Cave Exit']),
|
||||
create_cave_region(world, player, 'Dam', 'the dam', ['Floodgate', 'Floodgate Chest']),
|
||||
create_cave_region(world, player, 'Links House', 'your house', ['Link\'s House'], ['Links House Exit']),
|
||||
create_cave_region(world, player, 'Chris Houlihan Room', 'I AM ERROR', None, ['Chris Houlihan Room Exit']),
|
||||
create_cave_region(world, player, 'Tavern', 'the tavern', ['Kakariko Tavern']),
|
||||
create_cave_region(world, player, 'Elder House', 'a connector', None,
|
||||
['Elder House Exit (East)', 'Elder House Exit (West)']),
|
||||
create_cave_region(world, player, 'Snitch Lady (East)', 'a boring house'),
|
||||
create_cave_region(world, player, 'Snitch Lady (West)', 'a boring house'),
|
||||
create_cave_region(world, player, 'Bush Covered House', 'the grass man'),
|
||||
create_cave_region(world, player, 'Tavern (Front)', 'the tavern'),
|
||||
create_cave_region(world, player, 'Light World Bomb Hut', 'a restock room'),
|
||||
create_cave_region(world, player, 'Kakariko Shop', 'a common shop'),
|
||||
create_cave_region(world, player, 'Fortune Teller (Light)', 'a fortune teller'),
|
||||
create_cave_region(world, player, 'Lake Hylia Fortune Teller', 'a fortune teller'),
|
||||
create_cave_region(world, player, 'Lumberjack House', 'a boring house'),
|
||||
create_cave_region(world, player, 'Bonk Fairy (Light)', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Bonk Fairy (Dark)', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Lake Hylia Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Swamp Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Desert Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Dark Lake Hylia Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Dark Lake Hylia Ledge Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Dark Desert Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Dark Death Mountain Healer Fairy', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Chicken House', 'a house with a chest', ['Chicken House']),
|
||||
create_cave_region(world, player, 'Aginahs Cave', 'a cave with a chest', ['Aginah\'s Cave']),
|
||||
create_cave_region(world, player, 'Sahasrahlas Hut', 'Sahasrahla',
|
||||
['Sahasrahla\'s Hut - Left', 'Sahasrahla\'s Hut - Middle', 'Sahasrahla\'s Hut - Right',
|
||||
'Sahasrahla']),
|
||||
create_cave_region(world, player, 'Kakariko Well (top)', 'a drop\'s exit',
|
||||
['Kakariko Well - Top', 'Kakariko Well - Left', 'Kakariko Well - Middle',
|
||||
'Kakariko Well - Right', 'Kakariko Well - Bottom'], ['Kakariko Well (top to bottom)']),
|
||||
create_cave_region(world, player, 'Kakariko Well (bottom)', 'a drop\'s exit', None, ['Kakariko Well Exit']),
|
||||
create_cave_region(world, player, 'Blacksmiths Hut', 'the smith', ['Blacksmith', 'Missing Smith']),
|
||||
create_lw_region(world, player, 'Bat Cave Drop Ledge', None, ['Bat Cave Drop']),
|
||||
create_cave_region(world, player, 'Bat Cave (right)', 'a drop\'s exit', ['Magic Bat'], ['Bat Cave Door']),
|
||||
create_cave_region(world, player, 'Bat Cave (left)', 'a drop\'s exit', None, ['Bat Cave Exit']),
|
||||
create_cave_region(world, player, 'Sick Kids House', 'the sick kid', ['Sick Kid']),
|
||||
create_lw_region(world, player, 'Hobo Bridge', ['Hobo']),
|
||||
create_cave_region(world, player, 'Lost Woods Hideout (top)', 'a drop\'s exit', ['Lost Woods Hideout'],
|
||||
['Lost Woods Hideout (top to bottom)']),
|
||||
create_cave_region(world, player, 'Lost Woods Hideout (bottom)', 'a drop\'s exit', None,
|
||||
['Lost Woods Hideout Exit']),
|
||||
create_cave_region(world, player, 'Lumberjack Tree (top)', 'a drop\'s exit', ['Lumberjack Tree'],
|
||||
['Lumberjack Tree (top to bottom)']),
|
||||
create_cave_region(world, player, 'Lumberjack Tree (bottom)', 'a drop\'s exit', None, ['Lumberjack Tree Exit']),
|
||||
create_lw_region(world, player, 'Cave 45 Ledge', None, ['Cave 45']),
|
||||
create_cave_region(world, player, 'Cave 45', 'a cave with an item', ['Cave 45']),
|
||||
create_lw_region(world, player, 'Graveyard Ledge', None, ['Graveyard Cave']),
|
||||
create_cave_region(world, player, 'Graveyard Cave', 'a cave with an item', ['Graveyard Cave']),
|
||||
create_cave_region(world, player, 'Checkerboard Cave', 'a cave with an item', ['Checkerboard Cave']),
|
||||
create_cave_region(world, player, 'Long Fairy Cave', 'a fairy fountain'),
|
||||
create_cave_region(world, player, 'Mini Moldorm Cave', 'a bounty of five items',
|
||||
['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Left', 'Mini Moldorm Cave - Right',
|
||||
'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Generous Guy']),
|
||||
create_cave_region(world, player, 'Ice Rod Cave', 'a cave with a chest', ['Ice Rod Cave']),
|
||||
create_cave_region(world, player, 'Good Bee Cave', 'a cold bee'),
|
||||
create_cave_region(world, player, '20 Rupee Cave', 'a cave with some cash'),
|
||||
create_cave_region(world, player, 'Cave Shop (Lake Hylia)', 'a common shop'),
|
||||
create_cave_region(world, player, 'Cave Shop (Dark Death Mountain)', 'a common shop'),
|
||||
create_cave_region(world, player, 'Bonk Rock Cave', 'a cave with a chest', ['Bonk Rock Cave']),
|
||||
create_cave_region(world, player, 'Library', 'the library', ['Library']),
|
||||
create_cave_region(world, player, 'Kakariko Gamble Game', 'a game of chance'),
|
||||
create_cave_region(world, player, 'Potion Shop', 'the potion shop', ['Potion Shop']),
|
||||
create_lw_region(world, player, 'Lake Hylia Island', ['Lake Hylia Island']),
|
||||
create_cave_region(world, player, 'Capacity Upgrade', 'the queen of fairies'),
|
||||
create_cave_region(world, player, 'Two Brothers House', 'a connector', None,
|
||||
['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']),
|
||||
create_lw_region(world, player, 'Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)']),
|
||||
create_cave_region(world, player, '50 Rupee Cave', 'a cave with some cash'),
|
||||
create_lw_region(world, player, 'Desert Ledge', ['Desert Ledge'],
|
||||
['Desert Palace Entrance (North) Rocks', 'Desert Palace Entrance (West)']),
|
||||
create_lw_region(world, player, 'Desert Ledge (Northeast)', None, ['Checkerboard Cave']),
|
||||
create_lw_region(world, player, 'Desert Palace Stairs', None, ['Desert Palace Entrance (South)']),
|
||||
create_lw_region(world, player, 'Desert Palace Lone Stairs', None,
|
||||
['Desert Palace Stairs Drop', 'Desert Palace Entrance (East)']),
|
||||
create_lw_region(world, player, 'Desert Palace Entrance (North) Spot', None,
|
||||
['Desert Palace Entrance (North)', 'Desert Ledge Return Rocks']),
|
||||
create_dungeon_region(world, player, 'Desert Palace Main (Outer)', 'Desert Palace',
|
||||
['Desert Palace - Big Chest', 'Desert Palace - Torch', 'Desert Palace - Map Chest'],
|
||||
['Desert Palace Pots (Outer)', 'Desert Palace Exit (West)', 'Desert Palace Exit (East)',
|
||||
'Desert Palace East Wing']),
|
||||
create_dungeon_region(world, player, 'Desert Palace Main (Inner)', 'Desert Palace', None,
|
||||
['Desert Palace Exit (South)', 'Desert Palace Pots (Inner)']),
|
||||
create_dungeon_region(world, player, 'Desert Palace East', 'Desert Palace',
|
||||
['Desert Palace - Compass Chest', 'Desert Palace - Big Key Chest']),
|
||||
create_dungeon_region(world, player, 'Desert Palace North', 'Desert Palace',
|
||||
['Desert Palace - Boss', 'Desert Palace - Prize'], ['Desert Palace Exit (North)']),
|
||||
create_dungeon_region(world, player, 'Eastern Palace', 'Eastern Palace',
|
||||
['Eastern Palace - Compass Chest', 'Eastern Palace - Big Chest',
|
||||
'Eastern Palace - Cannonball Chest',
|
||||
'Eastern Palace - Big Key Chest', 'Eastern Palace - Map Chest', 'Eastern Palace - Boss',
|
||||
'Eastern Palace - Prize'], ['Eastern Palace Exit']),
|
||||
create_lw_region(world, player, 'Master Sword Meadow', ['Master Sword Pedestal']),
|
||||
create_cave_region(world, player, 'Lost Woods Gamble', 'a game of chance'),
|
||||
create_lw_region(world, player, 'Hyrule Castle Courtyard', None,
|
||||
['Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Entrance (South)']),
|
||||
create_lw_region(world, player, 'Hyrule Castle Ledge', None,
|
||||
['Hyrule Castle Entrance (East)', 'Hyrule Castle Entrance (West)', 'Agahnims Tower',
|
||||
'Hyrule Castle Ledge Courtyard Drop']),
|
||||
create_dungeon_region(world, player, 'Hyrule Castle', 'Hyrule Castle',
|
||||
['Hyrule Castle - Boomerang Chest', 'Hyrule Castle - Map Chest',
|
||||
'Hyrule Castle - Zelda\'s Chest'],
|
||||
['Hyrule Castle Exit (East)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (South)',
|
||||
'Throne Room']),
|
||||
create_dungeon_region(world, player, 'Sewer Drop', 'a drop\'s exit', None, ['Sewer Drop']), # This exists only to be referenced for access checks
|
||||
create_dungeon_region(world, player, 'Sewers (Dark)', 'a drop\'s exit', ['Sewers - Dark Cross'],
|
||||
['Sewers Door']),
|
||||
create_dungeon_region(world, player, 'Sewers', 'a drop\'s exit',
|
||||
['Sewers - Secret Room - Left', 'Sewers - Secret Room - Middle',
|
||||
'Sewers - Secret Room - Right'], ['Sanctuary Push Door', 'Sewers Back Door']),
|
||||
create_dungeon_region(world, player, 'Sanctuary', 'a drop\'s exit', ['Sanctuary'], ['Sanctuary Exit']),
|
||||
create_dungeon_region(world, player, 'Agahnims Tower', 'Castle Tower',
|
||||
['Castle Tower - Room 03', 'Castle Tower - Dark Maze'],
|
||||
['Agahnim 1', 'Agahnims Tower Exit']),
|
||||
create_dungeon_region(world, player, 'Agahnim 1', 'Castle Tower', ['Agahnim 1'], None),
|
||||
create_cave_region(world, player, 'Old Man Cave', 'a connector', ['Old Man'],
|
||||
['Old Man Cave Exit (East)', 'Old Man Cave Exit (West)']),
|
||||
create_cave_region(world, player, 'Old Man House', 'a connector', None,
|
||||
['Old Man House Exit (Bottom)', 'Old Man House Front to Back']),
|
||||
create_cave_region(world, player, 'Old Man House Back', 'a connector', None,
|
||||
['Old Man House Exit (Top)', 'Old Man House Back to Front']),
|
||||
create_lw_region(world, player, 'Death Mountain', None,
|
||||
['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)',
|
||||
'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave Peak',
|
||||
'Spectacle Rock Cave (Bottom)', 'Broken Bridge (West)', 'Death Mountain Teleporter']),
|
||||
create_cave_region(world, player, 'Death Mountain Return Cave', 'a connector', None,
|
||||
['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave Exit (East)']),
|
||||
create_lw_region(world, player, 'Death Mountain Return Ledge', None,
|
||||
['Death Mountain Return Ledge Drop', 'Death Mountain Return Cave (West)']),
|
||||
create_cave_region(world, player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'],
|
||||
['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']),
|
||||
create_cave_region(world, player, 'Spectacle Rock Cave (Bottom)', 'a connector', None,
|
||||
['Spectacle Rock Cave Exit']),
|
||||
create_cave_region(world, player, 'Spectacle Rock Cave (Peak)', 'a connector', None,
|
||||
['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']),
|
||||
create_lw_region(world, player, 'East Death Mountain (Bottom)', None,
|
||||
['Broken Bridge (East)', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)',
|
||||
'East Death Mountain Teleporter', 'Hookshot Fairy', 'Fairy Ascension Rocks',
|
||||
'Spiral Cave (Bottom)']),
|
||||
create_cave_region(world, player, 'Hookshot Fairy', 'fairies deep in a cave'),
|
||||
create_cave_region(world, player, 'Paradox Cave Front', 'a connector', None,
|
||||
['Paradox Cave Push Block Reverse', 'Paradox Cave Exit (Bottom)',
|
||||
'Light World Death Mountain Shop']),
|
||||
create_cave_region(world, player, 'Paradox Cave Chest Area', 'a connector', ['Paradox Cave Lower - Far Left',
|
||||
'Paradox Cave Lower - Left',
|
||||
'Paradox Cave Lower - Right',
|
||||
'Paradox Cave Lower - Far Right',
|
||||
'Paradox Cave Lower - Middle',
|
||||
'Paradox Cave Upper - Left',
|
||||
'Paradox Cave Upper - Right'],
|
||||
['Paradox Cave Push Block', 'Paradox Cave Bomb Jump']),
|
||||
create_cave_region(player, 'Paradox Cave', 'a connector', None, ['Paradox Cave Exit (Middle)', 'Paradox Cave Exit (Top)', 'Paradox Cave Drop']),
|
||||
create_cave_region(player, 'Light World Death Mountain Shop', 'a common shop'),
|
||||
create_lw_region(player, 'East Death Mountain (Top)', None, ['Paradox Cave (Top)', 'Death Mountain (Top)', 'Spiral Cave Ledge Access', 'East Death Mountain Drop', 'Turtle Rock Teleporter', 'Fairy Ascension Ledge']),
|
||||
create_lw_region(player, 'Spiral Cave Ledge', None, ['Spiral Cave', 'Spiral Cave Ledge Drop']),
|
||||
create_cave_region(player, 'Spiral Cave (Top)', 'a connector', ['Spiral Cave'], ['Spiral Cave (top to bottom)', 'Spiral Cave Exit (Top)']),
|
||||
create_cave_region(player, 'Spiral Cave (Bottom)', 'a connector', None, ['Spiral Cave Exit']),
|
||||
create_lw_region(player, 'Fairy Ascension Plateau', None, ['Fairy Ascension Drop', 'Fairy Ascension Cave (Bottom)']),
|
||||
create_cave_region(player, 'Fairy Ascension Cave (Bottom)', 'a connector', None, ['Fairy Ascension Cave Climb', 'Fairy Ascension Cave Exit (Bottom)']),
|
||||
create_cave_region(player, 'Fairy Ascension Cave (Drop)', 'a connector', None, ['Fairy Ascension Cave Pots']),
|
||||
create_cave_region(player, 'Fairy Ascension Cave (Top)', 'a connector', None, ['Fairy Ascension Cave Exit (Top)', 'Fairy Ascension Cave Drop']),
|
||||
create_lw_region(player, 'Fairy Ascension Ledge', None, ['Fairy Ascension Ledge Drop', 'Fairy Ascension Cave (Top)']),
|
||||
create_lw_region(player, 'Death Mountain (Top)', ['Ether Tablet'], ['East Death Mountain (Top)', 'Tower of Hera', 'Death Mountain Drop']),
|
||||
create_lw_region(player, 'Spectacle Rock', ['Spectacle Rock'], ['Spectacle Rock Drop']),
|
||||
create_dungeon_region(player, 'Tower of Hera (Bottom)', 'Tower of Hera', ['Tower of Hera - Basement Cage', 'Tower of Hera - Map Chest'], ['Tower of Hera Small Key Door', 'Tower of Hera Big Key Door', 'Tower of Hera Exit']),
|
||||
create_dungeon_region(player, 'Tower of Hera (Basement)', 'Tower of Hera', ['Tower of Hera - Big Key Chest']),
|
||||
create_dungeon_region(player, 'Tower of Hera (Top)', 'Tower of Hera', ['Tower of Hera - Compass Chest', 'Tower of Hera - Big Chest', 'Tower of Hera - Boss', 'Tower of Hera - Prize']),
|
||||
create_cave_region(world, player, 'Paradox Cave', 'a connector', None,
|
||||
['Paradox Cave Exit (Middle)', 'Paradox Cave Exit (Top)', 'Paradox Cave Drop']),
|
||||
create_cave_region(world, player, 'Light World Death Mountain Shop', 'a common shop'),
|
||||
create_lw_region(world, player, 'East Death Mountain (Top)', None,
|
||||
['Paradox Cave (Top)', 'Death Mountain (Top)', 'Spiral Cave Ledge Access',
|
||||
'East Death Mountain Drop', 'Turtle Rock Teleporter', 'Fairy Ascension Ledge']),
|
||||
create_lw_region(world, player, 'Spiral Cave Ledge', None, ['Spiral Cave', 'Spiral Cave Ledge Drop']),
|
||||
create_cave_region(world, player, 'Spiral Cave (Top)', 'a connector', ['Spiral Cave'],
|
||||
['Spiral Cave (top to bottom)', 'Spiral Cave Exit (Top)']),
|
||||
create_cave_region(world, player, 'Spiral Cave (Bottom)', 'a connector', None, ['Spiral Cave Exit']),
|
||||
create_lw_region(world, player, 'Fairy Ascension Plateau', None,
|
||||
['Fairy Ascension Drop', 'Fairy Ascension Cave (Bottom)']),
|
||||
create_cave_region(world, player, 'Fairy Ascension Cave (Bottom)', 'a connector', None,
|
||||
['Fairy Ascension Cave Climb', 'Fairy Ascension Cave Exit (Bottom)']),
|
||||
create_cave_region(world, player, 'Fairy Ascension Cave (Drop)', 'a connector', None,
|
||||
['Fairy Ascension Cave Pots']),
|
||||
create_cave_region(world, player, 'Fairy Ascension Cave (Top)', 'a connector', None,
|
||||
['Fairy Ascension Cave Exit (Top)', 'Fairy Ascension Cave Drop']),
|
||||
create_lw_region(world, player, 'Fairy Ascension Ledge', None,
|
||||
['Fairy Ascension Ledge Drop', 'Fairy Ascension Cave (Top)']),
|
||||
create_lw_region(world, player, 'Death Mountain (Top)', ['Ether Tablet'],
|
||||
['East Death Mountain (Top)', 'Tower of Hera', 'Death Mountain Drop']),
|
||||
create_lw_region(world, player, 'Spectacle Rock', ['Spectacle Rock'], ['Spectacle Rock Drop']),
|
||||
create_dungeon_region(world, player, 'Tower of Hera (Bottom)', 'Tower of Hera',
|
||||
['Tower of Hera - Basement Cage', 'Tower of Hera - Map Chest'],
|
||||
['Tower of Hera Small Key Door', 'Tower of Hera Big Key Door', 'Tower of Hera Exit']),
|
||||
create_dungeon_region(world, player, 'Tower of Hera (Basement)', 'Tower of Hera',
|
||||
['Tower of Hera - Big Key Chest']),
|
||||
create_dungeon_region(world, player, 'Tower of Hera (Top)', 'Tower of Hera',
|
||||
['Tower of Hera - Compass Chest', 'Tower of Hera - Big Chest', 'Tower of Hera - Boss',
|
||||
'Tower of Hera - Prize']),
|
||||
|
||||
create_dw_region(player, 'East Dark World', ['Pyramid'], ['Pyramid Fairy', 'South Dark World Bridge', 'Palace of Darkness', 'Dark Lake Hylia Drop (East)',
|
||||
'Hyrule Castle Ledge Mirror Spot', 'Dark Lake Hylia Fairy', 'Palace of Darkness Hint', 'East Dark World Hint', 'Pyramid Hole', 'Northeast Dark World Broken Bridge Pass',]),
|
||||
create_dw_region(player, 'Catfish', ['Catfish'], ['Catfish Exit Rock']),
|
||||
create_dw_region(player, 'Northeast Dark World', None, ['West Dark World Gap', 'Dark World Potion Shop', 'East Dark World Broken Bridge Pass', 'Catfish Entrance Rock', 'Dark Lake Hylia Teleporter']),
|
||||
create_cave_region(player, 'Palace of Darkness Hint', 'a storyteller'),
|
||||
create_cave_region(player, 'East Dark World Hint', 'a storyteller'),
|
||||
create_dw_region(player, 'South Dark World', ['Stumpy', 'Digging Game'], ['Dark Lake Hylia Drop (South)', 'Hype Cave', 'Swamp Palace', 'Village of Outcasts Heavy Rock', 'Maze Race Mirror Spot',
|
||||
'Cave 45 Mirror Spot', 'East Dark World Bridge', 'Big Bomb Shop', 'Archery Game', 'Bonk Fairy (Dark)', 'Dark Lake Hylia Shop',
|
||||
'Bombos Tablet Mirror Spot']),
|
||||
create_lw_region(player, 'Bombos Tablet Ledge', ['Bombos Tablet']),
|
||||
create_cave_region(player, 'Big Bomb Shop', 'the bomb shop'),
|
||||
create_cave_region(player, 'Archery Game', 'a game of skill'),
|
||||
create_dw_region(player, 'Dark Lake Hylia', None, ['Lake Hylia Island Mirror Spot', 'East Dark World Pier', 'Dark Lake Hylia Ledge']),
|
||||
create_dw_region(player, 'Dark Lake Hylia Central Island', None, ['Ice Palace', 'Lake Hylia Central Island Mirror Spot']),
|
||||
create_dw_region(player, 'Dark Lake Hylia Ledge', None, ['Dark Lake Hylia Ledge Drop', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Spike Cave']),
|
||||
create_cave_region(player, 'Dark Lake Hylia Ledge Hint', 'a storyteller'),
|
||||
create_cave_region(player, 'Dark Lake Hylia Ledge Spike Cave', 'a spiky hint'),
|
||||
create_cave_region(player, 'Hype Cave', 'a bounty of five items', ['Hype Cave - Top', 'Hype Cave - Middle Right', 'Hype Cave - Middle Left',
|
||||
'Hype Cave - Bottom', 'Hype Cave - Generous Guy']),
|
||||
create_dw_region(player, 'West Dark World', ['Frog'], ['Village of Outcasts Drop', 'East Dark World River Pier', 'Brewery', 'C-Shaped House', 'Chest Game', 'Thieves Town', 'Graveyard Ledge Mirror Spot', 'Kings Grave Mirror Spot', 'Bumper Cave Entrance Rock',
|
||||
'Skull Woods Forest', 'Village of Outcasts Pegs', 'Village of Outcasts Eastern Rocks', 'Red Shield Shop', 'Dark Sanctuary Hint', 'Fortune Teller (Dark)', 'Dark World Lumberjack Shop']),
|
||||
create_dw_region(player, 'Dark Grassy Lawn', None, ['Grassy Lawn Pegs', 'Village of Outcasts Shop']),
|
||||
create_dw_region(player, 'Hammer Peg Area', ['Dark Blacksmith Ruins'], ['Bat Cave Drop Ledge Mirror Spot', 'Dark World Hammer Peg Cave', 'Peg Area Rocks']),
|
||||
create_dw_region(player, 'Bumper Cave Entrance', None, ['Bumper Cave (Bottom)', 'Bumper Cave Entrance Mirror Spot', 'Bumper Cave Entrance Drop']),
|
||||
create_cave_region(player, 'Fortune Teller (Dark)', 'a fortune teller'),
|
||||
create_cave_region(player, 'Village of Outcasts Shop', 'a common shop'),
|
||||
create_cave_region(player, 'Dark Lake Hylia Shop', 'a common shop'),
|
||||
create_cave_region(player, 'Dark World Lumberjack Shop', 'a common shop'),
|
||||
create_cave_region(player, 'Dark World Potion Shop', 'a common shop'),
|
||||
create_cave_region(player, 'Dark World Hammer Peg Cave', 'a cave with an item', ['Peg Cave']),
|
||||
create_cave_region(player, 'Pyramid Fairy', 'a cave with two chests', ['Pyramid Fairy - Left', 'Pyramid Fairy - Right']),
|
||||
create_cave_region(player, 'Brewery', 'a house with a chest', ['Brewery']),
|
||||
create_cave_region(player, 'C-Shaped House', 'a house with a chest', ['C-Shaped House']),
|
||||
create_cave_region(player, 'Chest Game', 'a game of 16 chests', ['Chest Game']),
|
||||
create_cave_region(player, 'Red Shield Shop', 'the rare shop'),
|
||||
create_cave_region(player, 'Dark Sanctuary Hint', 'a storyteller'),
|
||||
create_cave_region(player, 'Bumper Cave', 'a connector', None, ['Bumper Cave Exit (Bottom)', 'Bumper Cave Exit (Top)']),
|
||||
create_dw_region(player, 'Bumper Cave Ledge', ['Bumper Cave Ledge'], ['Bumper Cave Ledge Drop', 'Bumper Cave (Top)', 'Bumper Cave Ledge Mirror Spot']),
|
||||
create_dw_region(player, 'Skull Woods Forest', None, ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)',
|
||||
'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)']),
|
||||
create_dw_region(player, 'Skull Woods Forest (West)', None, ['Skull Woods Second Section Hole', 'Skull Woods Second Section Door (West)', 'Skull Woods Final Section']),
|
||||
create_dw_region(player, 'Dark Desert', None, ['Misery Mire', 'Mire Shed', 'Desert Ledge (Northeast) Mirror Spot', 'Desert Ledge Mirror Spot', 'Desert Palace Stairs Mirror Spot',
|
||||
'Desert Palace Entrance (North) Mirror Spot', 'Dark Desert Hint', 'Dark Desert Fairy']),
|
||||
create_cave_region(player, 'Mire Shed', 'a cave with two chests', ['Mire Shed - Left', 'Mire Shed - Right']),
|
||||
create_cave_region(player, 'Dark Desert Hint', 'a storyteller'),
|
||||
create_dw_region(player, 'Dark Death Mountain (West Bottom)', None, ['Spike Cave', 'Spectacle Rock Mirror Spot', 'Dark Death Mountain Fairy']),
|
||||
create_dw_region(player, 'Dark Death Mountain (Top)', None, ['Dark Death Mountain Drop (East)', 'Dark Death Mountain Drop (West)', 'Ganons Tower', 'Superbunny Cave (Top)',
|
||||
'Hookshot Cave', 'East Death Mountain (Top) Mirror Spot', 'Turtle Rock']),
|
||||
create_dw_region(player, 'Dark Death Mountain Ledge', None, ['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)', 'Mimic Cave Mirror Spot', 'Spiral Cave Mirror Spot']),
|
||||
create_dw_region(player, 'Dark Death Mountain Isolated Ledge', None, ['Isolated Ledge Mirror Spot', 'Turtle Rock Isolated Ledge Entrance']),
|
||||
create_dw_region(player, 'Dark Death Mountain (East Bottom)', None, ['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)', 'Fairy Ascension Mirror Spot']),
|
||||
create_cave_region(player, 'Superbunny Cave (Top)', 'a connector', ['Superbunny Cave - Top', 'Superbunny Cave - Bottom'], ['Superbunny Cave Exit (Top)']),
|
||||
create_cave_region(player, 'Superbunny Cave (Bottom)', 'a connector', None, ['Superbunny Cave Climb', 'Superbunny Cave Exit (Bottom)']),
|
||||
create_cave_region(player, 'Spike Cave', 'Spike Cave', ['Spike Cave']),
|
||||
create_cave_region(player, 'Hookshot Cave', 'a connector', ['Hookshot Cave - Top Right', 'Hookshot Cave - Top Left', 'Hookshot Cave - Bottom Right', 'Hookshot Cave - Bottom Left'],
|
||||
create_dw_region(world, player, 'East Dark World', ['Pyramid'],
|
||||
['Pyramid Fairy', 'South Dark World Bridge', 'Palace of Darkness',
|
||||
'Dark Lake Hylia Drop (East)',
|
||||
'Hyrule Castle Ledge Mirror Spot', 'Dark Lake Hylia Fairy', 'Palace of Darkness Hint',
|
||||
'East Dark World Hint', 'Pyramid Hole', 'Northeast Dark World Broken Bridge Pass', ]),
|
||||
create_dw_region(world, player, 'Catfish', ['Catfish'], ['Catfish Exit Rock']),
|
||||
create_dw_region(world, player, 'Northeast Dark World', None,
|
||||
['West Dark World Gap', 'Dark World Potion Shop', 'East Dark World Broken Bridge Pass',
|
||||
'Catfish Entrance Rock', 'Dark Lake Hylia Teleporter']),
|
||||
create_cave_region(world, player, 'Palace of Darkness Hint', 'a storyteller'),
|
||||
create_cave_region(world, player, 'East Dark World Hint', 'a storyteller'),
|
||||
create_dw_region(world, player, 'South Dark World', ['Stumpy', 'Digging Game'],
|
||||
['Dark Lake Hylia Drop (South)', 'Hype Cave', 'Swamp Palace', 'Village of Outcasts Heavy Rock',
|
||||
'Maze Race Mirror Spot',
|
||||
'Cave 45 Mirror Spot', 'East Dark World Bridge', 'Big Bomb Shop', 'Archery Game',
|
||||
'Bonk Fairy (Dark)', 'Dark Lake Hylia Shop',
|
||||
'Bombos Tablet Mirror Spot']),
|
||||
create_lw_region(world, player, 'Bombos Tablet Ledge', ['Bombos Tablet']),
|
||||
create_cave_region(world, player, 'Big Bomb Shop', 'the bomb shop'),
|
||||
create_cave_region(world, player, 'Archery Game', 'a game of skill'),
|
||||
create_dw_region(world, player, 'Dark Lake Hylia', None,
|
||||
['Lake Hylia Island Mirror Spot', 'East Dark World Pier', 'Dark Lake Hylia Ledge']),
|
||||
create_dw_region(world, player, 'Dark Lake Hylia Central Island', None,
|
||||
['Ice Palace', 'Lake Hylia Central Island Mirror Spot']),
|
||||
create_dw_region(world, player, 'Dark Lake Hylia Ledge', None,
|
||||
['Dark Lake Hylia Ledge Drop', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Hint',
|
||||
'Dark Lake Hylia Ledge Spike Cave']),
|
||||
create_cave_region(world, player, 'Dark Lake Hylia Ledge Hint', 'a storyteller'),
|
||||
create_cave_region(world, player, 'Dark Lake Hylia Ledge Spike Cave', 'a spiky hint'),
|
||||
create_cave_region(world, player, 'Hype Cave', 'a bounty of five items',
|
||||
['Hype Cave - Top', 'Hype Cave - Middle Right', 'Hype Cave - Middle Left',
|
||||
'Hype Cave - Bottom', 'Hype Cave - Generous Guy']),
|
||||
create_dw_region(world, player, 'West Dark World', ['Frog'],
|
||||
['Village of Outcasts Drop', 'East Dark World River Pier', 'Brewery', 'C-Shaped House',
|
||||
'Chest Game', 'Thieves Town', 'Graveyard Ledge Mirror Spot', 'Kings Grave Mirror Spot',
|
||||
'Bumper Cave Entrance Rock',
|
||||
'Skull Woods Forest', 'Village of Outcasts Pegs', 'Village of Outcasts Eastern Rocks',
|
||||
'Red Shield Shop', 'Dark Sanctuary Hint', 'Fortune Teller (Dark)',
|
||||
'Dark World Lumberjack Shop']),
|
||||
create_dw_region(world, player, 'Dark Grassy Lawn', None, ['Grassy Lawn Pegs', 'Village of Outcasts Shop']),
|
||||
create_dw_region(world, player, 'Hammer Peg Area', ['Dark Blacksmith Ruins'],
|
||||
['Bat Cave Drop Ledge Mirror Spot', 'Dark World Hammer Peg Cave', 'Peg Area Rocks']),
|
||||
create_dw_region(world, player, 'Bumper Cave Entrance', None,
|
||||
['Bumper Cave (Bottom)', 'Bumper Cave Entrance Mirror Spot', 'Bumper Cave Entrance Drop']),
|
||||
create_cave_region(world, player, 'Fortune Teller (Dark)', 'a fortune teller'),
|
||||
create_cave_region(world, player, 'Village of Outcasts Shop', 'a common shop'),
|
||||
create_cave_region(world, player, 'Dark Lake Hylia Shop', 'a common shop'),
|
||||
create_cave_region(world, player, 'Dark World Lumberjack Shop', 'a common shop'),
|
||||
create_cave_region(world, player, 'Dark World Potion Shop', 'a common shop'),
|
||||
create_cave_region(world, player, 'Dark World Hammer Peg Cave', 'a cave with an item', ['Peg Cave']),
|
||||
create_cave_region(world, player, 'Pyramid Fairy', 'a cave with two chests',
|
||||
['Pyramid Fairy - Left', 'Pyramid Fairy - Right']),
|
||||
create_cave_region(world, player, 'Brewery', 'a house with a chest', ['Brewery']),
|
||||
create_cave_region(world, player, 'C-Shaped House', 'a house with a chest', ['C-Shaped House']),
|
||||
create_cave_region(world, player, 'Chest Game', 'a game of 16 chests', ['Chest Game']),
|
||||
create_cave_region(world, player, 'Red Shield Shop', 'the rare shop'),
|
||||
create_cave_region(world, player, 'Dark Sanctuary Hint', 'a storyteller'),
|
||||
create_cave_region(world, player, 'Bumper Cave', 'a connector', None,
|
||||
['Bumper Cave Exit (Bottom)', 'Bumper Cave Exit (Top)']),
|
||||
create_dw_region(world, player, 'Bumper Cave Ledge', ['Bumper Cave Ledge'],
|
||||
['Bumper Cave Ledge Drop', 'Bumper Cave (Top)', 'Bumper Cave Ledge Mirror Spot']),
|
||||
create_dw_region(world, player, 'Skull Woods Forest', None,
|
||||
['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)',
|
||||
'Skull Woods First Section Hole (North)',
|
||||
'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)']),
|
||||
create_dw_region(world, player, 'Skull Woods Forest (West)', None,
|
||||
['Skull Woods Second Section Hole', 'Skull Woods Second Section Door (West)',
|
||||
'Skull Woods Final Section']),
|
||||
create_dw_region(world, player, 'Dark Desert', None,
|
||||
['Misery Mire', 'Mire Shed', 'Desert Ledge (Northeast) Mirror Spot',
|
||||
'Desert Ledge Mirror Spot', 'Desert Palace Stairs Mirror Spot',
|
||||
'Desert Palace Entrance (North) Mirror Spot', 'Dark Desert Hint', 'Dark Desert Fairy']),
|
||||
create_cave_region(world, player, 'Mire Shed', 'a cave with two chests',
|
||||
['Mire Shed - Left', 'Mire Shed - Right']),
|
||||
create_cave_region(world, player, 'Dark Desert Hint', 'a storyteller'),
|
||||
create_dw_region(world, player, 'Dark Death Mountain (West Bottom)', None,
|
||||
['Spike Cave', 'Spectacle Rock Mirror Spot', 'Dark Death Mountain Fairy']),
|
||||
create_dw_region(world, player, 'Dark Death Mountain (Top)', None,
|
||||
['Dark Death Mountain Drop (East)', 'Dark Death Mountain Drop (West)', 'Ganons Tower',
|
||||
'Superbunny Cave (Top)',
|
||||
'Hookshot Cave', 'East Death Mountain (Top) Mirror Spot', 'Turtle Rock']),
|
||||
create_dw_region(world, player, 'Dark Death Mountain Ledge', None,
|
||||
['Dark Death Mountain Ledge (East)', 'Dark Death Mountain Ledge (West)',
|
||||
'Mimic Cave Mirror Spot', 'Spiral Cave Mirror Spot']),
|
||||
create_dw_region(world, player, 'Dark Death Mountain Isolated Ledge', None,
|
||||
['Isolated Ledge Mirror Spot', 'Turtle Rock Isolated Ledge Entrance']),
|
||||
create_dw_region(world, player, 'Dark Death Mountain (East Bottom)', None,
|
||||
['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)',
|
||||
'Fairy Ascension Mirror Spot']),
|
||||
create_cave_region(world, player, 'Superbunny Cave (Top)', 'a connector',
|
||||
['Superbunny Cave - Top', 'Superbunny Cave - Bottom'], ['Superbunny Cave Exit (Top)']),
|
||||
create_cave_region(world, player, 'Superbunny Cave (Bottom)', 'a connector', None,
|
||||
['Superbunny Cave Climb', 'Superbunny Cave Exit (Bottom)']),
|
||||
create_cave_region(world, player, 'Spike Cave', 'Spike Cave', ['Spike Cave']),
|
||||
create_cave_region(world, player, 'Hookshot Cave', 'a connector',
|
||||
['Hookshot Cave - Top Right', 'Hookshot Cave - Top Left', 'Hookshot Cave - Bottom Right',
|
||||
'Hookshot Cave - Bottom Left'],
|
||||
['Hookshot Cave Exit (South)', 'Hookshot Cave Exit (North)']),
|
||||
create_dw_region(player, 'Death Mountain Floating Island (Dark World)', None, ['Floating Island Drop', 'Hookshot Cave Back Entrance', 'Floating Island Mirror Spot']),
|
||||
create_lw_region(player, 'Death Mountain Floating Island (Light World)', ['Floating Island']),
|
||||
create_dw_region(player, 'Turtle Rock (Top)', None, ['Turtle Rock Drop']),
|
||||
create_lw_region(player, 'Mimic Cave Ledge', None, ['Mimic Cave']),
|
||||
create_cave_region(player, 'Mimic Cave', 'Mimic Cave', ['Mimic Cave']),
|
||||
create_dw_region(world, player, 'Death Mountain Floating Island (Dark World)', None,
|
||||
['Floating Island Drop', 'Hookshot Cave Back Entrance', 'Floating Island Mirror Spot']),
|
||||
create_lw_region(world, player, 'Death Mountain Floating Island (Light World)', ['Floating Island']),
|
||||
create_dw_region(world, player, 'Turtle Rock (Top)', None, ['Turtle Rock Drop']),
|
||||
create_lw_region(world, player, 'Mimic Cave Ledge', None, ['Mimic Cave']),
|
||||
create_cave_region(world, player, 'Mimic Cave', 'Mimic Cave', ['Mimic Cave']),
|
||||
|
||||
create_dungeon_region(player, 'Swamp Palace (Entrance)', 'Swamp Palace', None, ['Swamp Palace Moat', 'Swamp Palace Exit']),
|
||||
create_dungeon_region(player, 'Swamp Palace (First Room)', 'Swamp Palace', ['Swamp Palace - Entrance'], ['Swamp Palace Small Key Door']),
|
||||
create_dungeon_region(player, 'Swamp Palace (Starting Area)', 'Swamp Palace', ['Swamp Palace - Map Chest'], ['Swamp Palace (Center)']),
|
||||
create_dungeon_region(player, 'Swamp Palace (Center)', 'Swamp Palace', ['Swamp Palace - Big Chest', 'Swamp Palace - Compass Chest',
|
||||
'Swamp Palace - Big Key Chest', 'Swamp Palace - West Chest'], ['Swamp Palace (North)']),
|
||||
create_dungeon_region(player, 'Swamp Palace (North)', 'Swamp Palace', ['Swamp Palace - Flooded Room - Left', 'Swamp Palace - Flooded Room - Right',
|
||||
'Swamp Palace - Waterfall Room', 'Swamp Palace - Boss', 'Swamp Palace - Prize']),
|
||||
create_dungeon_region(player, 'Thieves Town (Entrance)', 'Thieves\' Town', ['Thieves\' Town - Big Key Chest',
|
||||
'Thieves\' Town - Map Chest',
|
||||
'Thieves\' Town - Compass Chest',
|
||||
'Thieves\' Town - Ambush Chest'], ['Thieves Town Big Key Door', 'Thieves Town Exit']),
|
||||
create_dungeon_region(player, 'Thieves Town (Deep)', 'Thieves\' Town', ['Thieves\' Town - Attic',
|
||||
'Thieves\' Town - Big Chest',
|
||||
'Thieves\' Town - Blind\'s Cell'], ['Blind Fight']),
|
||||
create_dungeon_region(player, 'Blind Fight', 'Thieves\' Town', ['Thieves\' Town - Boss', 'Thieves\' Town - Prize']),
|
||||
create_dungeon_region(player, 'Skull Woods First Section', 'Skull Woods', ['Skull Woods - Map Chest'], ['Skull Woods First Section Exit', 'Skull Woods First Section Bomb Jump', 'Skull Woods First Section South Door', 'Skull Woods First Section West Door']),
|
||||
create_dungeon_region(player, 'Skull Woods First Section (Right)', 'Skull Woods', ['Skull Woods - Pinball Room'], ['Skull Woods First Section (Right) North Door']),
|
||||
create_dungeon_region(player, 'Skull Woods First Section (Left)', 'Skull Woods', ['Skull Woods - Compass Chest', 'Skull Woods - Pot Prison'], ['Skull Woods First Section (Left) Door to Exit', 'Skull Woods First Section (Left) Door to Right']),
|
||||
create_dungeon_region(player, 'Skull Woods First Section (Top)', 'Skull Woods', ['Skull Woods - Big Chest'], ['Skull Woods First Section (Top) One-Way Path']),
|
||||
create_dungeon_region(player, 'Skull Woods Second Section (Drop)', 'Skull Woods', None, ['Skull Woods Second Section (Drop)']),
|
||||
create_dungeon_region(player, 'Skull Woods Second Section', 'Skull Woods', ['Skull Woods - Big Key Chest'], ['Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)']),
|
||||
create_dungeon_region(player, 'Skull Woods Final Section (Entrance)', 'Skull Woods', ['Skull Woods - Bridge Room'], ['Skull Woods Torch Room', 'Skull Woods Final Section Exit']),
|
||||
create_dungeon_region(player, 'Skull Woods Final Section (Mothula)', 'Skull Woods', ['Skull Woods - Boss', 'Skull Woods - Prize']),
|
||||
create_dungeon_region(player, 'Ice Palace (Entrance)', 'Ice Palace', None, ['Ice Palace Entrance Room', 'Ice Palace Exit']),
|
||||
create_dungeon_region(player, 'Ice Palace (Main)', 'Ice Palace', ['Ice Palace - Compass Chest', 'Ice Palace - Freezor Chest',
|
||||
'Ice Palace - Big Chest', 'Ice Palace - Iced T Room'], ['Ice Palace (East)', 'Ice Palace (Kholdstare)']),
|
||||
create_dungeon_region(player, 'Ice Palace (East)', 'Ice Palace', ['Ice Palace - Spike Room'], ['Ice Palace (East Top)']),
|
||||
create_dungeon_region(player, 'Ice Palace (East Top)', 'Ice Palace', ['Ice Palace - Big Key Chest', 'Ice Palace - Map Chest']),
|
||||
create_dungeon_region(player, 'Ice Palace (Kholdstare)', 'Ice Palace', ['Ice Palace - Boss', 'Ice Palace - Prize']),
|
||||
create_dungeon_region(player, 'Misery Mire (Entrance)', 'Misery Mire', None, ['Misery Mire Entrance Gap', 'Misery Mire Exit']),
|
||||
create_dungeon_region(player, 'Misery Mire (Main)', 'Misery Mire', ['Misery Mire - Big Chest', 'Misery Mire - Map Chest', 'Misery Mire - Main Lobby',
|
||||
'Misery Mire - Bridge Chest', 'Misery Mire - Spike Chest'], ['Misery Mire (West)', 'Misery Mire Big Key Door']),
|
||||
create_dungeon_region(player, 'Misery Mire (West)', 'Misery Mire', ['Misery Mire - Compass Chest', 'Misery Mire - Big Key Chest']),
|
||||
create_dungeon_region(player, 'Misery Mire (Final Area)', 'Misery Mire', None, ['Misery Mire (Vitreous)']),
|
||||
create_dungeon_region(player, 'Misery Mire (Vitreous)', 'Misery Mire', ['Misery Mire - Boss', 'Misery Mire - Prize']),
|
||||
create_dungeon_region(player, 'Turtle Rock (Entrance)', 'Turtle Rock', None, ['Turtle Rock Entrance Gap', 'Turtle Rock Exit (Front)']),
|
||||
create_dungeon_region(player, 'Turtle Rock (First Section)', 'Turtle Rock', ['Turtle Rock - Compass Chest', 'Turtle Rock - Roller Room - Left',
|
||||
'Turtle Rock - Roller Room - Right'], ['Turtle Rock Pokey Room', 'Turtle Rock Entrance Gap Reverse']),
|
||||
create_dungeon_region(player, 'Turtle Rock (Chain Chomp Room)', 'Turtle Rock', ['Turtle Rock - Chain Chomps'], ['Turtle Rock (Chain Chomp Room) (North)', 'Turtle Rock (Chain Chomp Room) (South)']),
|
||||
create_dungeon_region(player, 'Turtle Rock (Second Section)', 'Turtle Rock', ['Turtle Rock - Big Key Chest'], ['Turtle Rock Ledge Exit (West)', 'Turtle Rock Chain Chomp Staircase', 'Turtle Rock Big Key Door']),
|
||||
create_dungeon_region(player, 'Turtle Rock (Big Chest)', 'Turtle Rock', ['Turtle Rock - Big Chest'], ['Turtle Rock (Big Chest) (North)', 'Turtle Rock Ledge Exit (East)']),
|
||||
create_dungeon_region(player, 'Turtle Rock (Crystaroller Room)', 'Turtle Rock', ['Turtle Rock - Crystaroller Room'], ['Turtle Rock Dark Room Staircase', 'Turtle Rock Big Key Door Reverse']),
|
||||
create_dungeon_region(player, 'Turtle Rock (Dark Room)', 'Turtle Rock', None, ['Turtle Rock (Dark Room) (North)', 'Turtle Rock (Dark Room) (South)']),
|
||||
create_dungeon_region(player, 'Turtle Rock (Eye Bridge)', 'Turtle Rock', ['Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right',
|
||||
'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'],
|
||||
['Turtle Rock Dark Room (South)', 'Turtle Rock (Trinexx)', 'Turtle Rock Isolated Ledge Exit']),
|
||||
create_dungeon_region(player, 'Turtle Rock (Trinexx)', 'Turtle Rock', ['Turtle Rock - Boss', 'Turtle Rock - Prize']),
|
||||
create_dungeon_region(player, 'Palace of Darkness (Entrance)', 'Palace of Darkness', ['Palace of Darkness - Shooter Room'], ['Palace of Darkness Bridge Room', 'Palace of Darkness Bonk Wall', 'Palace of Darkness Exit']),
|
||||
create_dungeon_region(player, 'Palace of Darkness (Center)', 'Palace of Darkness', ['Palace of Darkness - The Arena - Bridge', 'Palace of Darkness - Stalfos Basement'],
|
||||
['Palace of Darkness Big Key Chest Staircase', 'Palace of Darkness (North)', 'Palace of Darkness Big Key Door']),
|
||||
create_dungeon_region(player, 'Palace of Darkness (Big Key Chest)', 'Palace of Darkness', ['Palace of Darkness - Big Key Chest']),
|
||||
create_dungeon_region(player, 'Palace of Darkness (Bonk Section)', 'Palace of Darkness', ['Palace of Darkness - The Arena - Ledge', 'Palace of Darkness - Map Chest'], ['Palace of Darkness Hammer Peg Drop']),
|
||||
create_dungeon_region(player, 'Palace of Darkness (North)', 'Palace of Darkness', ['Palace of Darkness - Compass Chest', 'Palace of Darkness - Dark Basement - Left', 'Palace of Darkness - Dark Basement - Right'],
|
||||
create_dungeon_region(world, player, 'Swamp Palace (Entrance)', 'Swamp Palace', None,
|
||||
['Swamp Palace Moat', 'Swamp Palace Exit']),
|
||||
create_dungeon_region(world, player, 'Swamp Palace (First Room)', 'Swamp Palace', ['Swamp Palace - Entrance'],
|
||||
['Swamp Palace Small Key Door']),
|
||||
create_dungeon_region(world, player, 'Swamp Palace (Starting Area)', 'Swamp Palace',
|
||||
['Swamp Palace - Map Chest'], ['Swamp Palace (Center)']),
|
||||
create_dungeon_region(world, player, 'Swamp Palace (Center)', 'Swamp Palace',
|
||||
['Swamp Palace - Big Chest', 'Swamp Palace - Compass Chest',
|
||||
'Swamp Palace - Big Key Chest', 'Swamp Palace - West Chest'], ['Swamp Palace (North)']),
|
||||
create_dungeon_region(world, player, 'Swamp Palace (North)', 'Swamp Palace',
|
||||
['Swamp Palace - Flooded Room - Left', 'Swamp Palace - Flooded Room - Right',
|
||||
'Swamp Palace - Waterfall Room', 'Swamp Palace - Boss', 'Swamp Palace - Prize']),
|
||||
create_dungeon_region(world, player, 'Thieves Town (Entrance)', 'Thieves\' Town',
|
||||
['Thieves\' Town - Big Key Chest',
|
||||
'Thieves\' Town - Map Chest',
|
||||
'Thieves\' Town - Compass Chest',
|
||||
'Thieves\' Town - Ambush Chest'], ['Thieves Town Big Key Door', 'Thieves Town Exit']),
|
||||
create_dungeon_region(world, player, 'Thieves Town (Deep)', 'Thieves\' Town', ['Thieves\' Town - Attic',
|
||||
'Thieves\' Town - Big Chest',
|
||||
'Thieves\' Town - Blind\'s Cell'],
|
||||
['Blind Fight']),
|
||||
create_dungeon_region(world, player, 'Blind Fight', 'Thieves\' Town',
|
||||
['Thieves\' Town - Boss', 'Thieves\' Town - Prize']),
|
||||
create_dungeon_region(world, player, 'Skull Woods First Section', 'Skull Woods', ['Skull Woods - Map Chest'],
|
||||
['Skull Woods First Section Exit', 'Skull Woods First Section Bomb Jump',
|
||||
'Skull Woods First Section South Door', 'Skull Woods First Section West Door']),
|
||||
create_dungeon_region(world, player, 'Skull Woods First Section (Right)', 'Skull Woods',
|
||||
['Skull Woods - Pinball Room'], ['Skull Woods First Section (Right) North Door']),
|
||||
create_dungeon_region(world, player, 'Skull Woods First Section (Left)', 'Skull Woods',
|
||||
['Skull Woods - Compass Chest', 'Skull Woods - Pot Prison'],
|
||||
['Skull Woods First Section (Left) Door to Exit',
|
||||
'Skull Woods First Section (Left) Door to Right']),
|
||||
create_dungeon_region(world, player, 'Skull Woods First Section (Top)', 'Skull Woods',
|
||||
['Skull Woods - Big Chest'], ['Skull Woods First Section (Top) One-Way Path']),
|
||||
create_dungeon_region(world, player, 'Skull Woods Second Section (Drop)', 'Skull Woods', None,
|
||||
['Skull Woods Second Section (Drop)']),
|
||||
create_dungeon_region(world, player, 'Skull Woods Second Section', 'Skull Woods',
|
||||
['Skull Woods - Big Key Chest'],
|
||||
['Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)']),
|
||||
create_dungeon_region(world, player, 'Skull Woods Final Section (Entrance)', 'Skull Woods',
|
||||
['Skull Woods - Bridge Room'],
|
||||
['Skull Woods Torch Room', 'Skull Woods Final Section Exit']),
|
||||
create_dungeon_region(world, player, 'Skull Woods Final Section (Mothula)', 'Skull Woods',
|
||||
['Skull Woods - Boss', 'Skull Woods - Prize']),
|
||||
create_dungeon_region(world, player, 'Ice Palace (Entrance)', 'Ice Palace', None,
|
||||
['Ice Palace Entrance Room', 'Ice Palace Exit']),
|
||||
create_dungeon_region(world, player, 'Ice Palace (Main)', 'Ice Palace',
|
||||
['Ice Palace - Compass Chest', 'Ice Palace - Freezor Chest',
|
||||
'Ice Palace - Big Chest', 'Ice Palace - Iced T Room'],
|
||||
['Ice Palace (East)', 'Ice Palace (Kholdstare)']),
|
||||
create_dungeon_region(world, player, 'Ice Palace (East)', 'Ice Palace', ['Ice Palace - Spike Room'],
|
||||
['Ice Palace (East Top)']),
|
||||
create_dungeon_region(world, player, 'Ice Palace (East Top)', 'Ice Palace',
|
||||
['Ice Palace - Big Key Chest', 'Ice Palace - Map Chest']),
|
||||
create_dungeon_region(world, player, 'Ice Palace (Kholdstare)', 'Ice Palace',
|
||||
['Ice Palace - Boss', 'Ice Palace - Prize']),
|
||||
create_dungeon_region(world, player, 'Misery Mire (Entrance)', 'Misery Mire', None,
|
||||
['Misery Mire Entrance Gap', 'Misery Mire Exit']),
|
||||
create_dungeon_region(world, player, 'Misery Mire (Main)', 'Misery Mire',
|
||||
['Misery Mire - Big Chest', 'Misery Mire - Map Chest', 'Misery Mire - Main Lobby',
|
||||
'Misery Mire - Bridge Chest', 'Misery Mire - Spike Chest'],
|
||||
['Misery Mire (West)', 'Misery Mire Big Key Door']),
|
||||
create_dungeon_region(world, player, 'Misery Mire (West)', 'Misery Mire',
|
||||
['Misery Mire - Compass Chest', 'Misery Mire - Big Key Chest']),
|
||||
create_dungeon_region(world, player, 'Misery Mire (Final Area)', 'Misery Mire', None,
|
||||
['Misery Mire (Vitreous)']),
|
||||
create_dungeon_region(world, player, 'Misery Mire (Vitreous)', 'Misery Mire',
|
||||
['Misery Mire - Boss', 'Misery Mire - Prize']),
|
||||
create_dungeon_region(world, player, 'Turtle Rock (Entrance)', 'Turtle Rock', None,
|
||||
['Turtle Rock Entrance Gap', 'Turtle Rock Exit (Front)']),
|
||||
create_dungeon_region(world, player, 'Turtle Rock (First Section)', 'Turtle Rock',
|
||||
['Turtle Rock - Compass Chest', 'Turtle Rock - Roller Room - Left',
|
||||
'Turtle Rock - Roller Room - Right'],
|
||||
['Turtle Rock Pokey Room', 'Turtle Rock Entrance Gap Reverse']),
|
||||
create_dungeon_region(world, player, 'Turtle Rock (Chain Chomp Room)', 'Turtle Rock',
|
||||
['Turtle Rock - Chain Chomps'],
|
||||
['Turtle Rock (Chain Chomp Room) (North)', 'Turtle Rock (Chain Chomp Room) (South)']),
|
||||
create_dungeon_region(world, player, 'Turtle Rock (Second Section)', 'Turtle Rock',
|
||||
['Turtle Rock - Big Key Chest'],
|
||||
['Turtle Rock Ledge Exit (West)', 'Turtle Rock Chain Chomp Staircase',
|
||||
'Turtle Rock Big Key Door']),
|
||||
create_dungeon_region(world, player, 'Turtle Rock (Big Chest)', 'Turtle Rock', ['Turtle Rock - Big Chest'],
|
||||
['Turtle Rock (Big Chest) (North)', 'Turtle Rock Ledge Exit (East)']),
|
||||
create_dungeon_region(world, player, 'Turtle Rock (Crystaroller Room)', 'Turtle Rock',
|
||||
['Turtle Rock - Crystaroller Room'],
|
||||
['Turtle Rock Dark Room Staircase', 'Turtle Rock Big Key Door Reverse']),
|
||||
create_dungeon_region(world, player, 'Turtle Rock (Dark Room)', 'Turtle Rock', None,
|
||||
['Turtle Rock (Dark Room) (North)', 'Turtle Rock (Dark Room) (South)']),
|
||||
create_dungeon_region(world, player, 'Turtle Rock (Eye Bridge)', 'Turtle Rock',
|
||||
['Turtle Rock - Eye Bridge - Bottom Left', 'Turtle Rock - Eye Bridge - Bottom Right',
|
||||
'Turtle Rock - Eye Bridge - Top Left', 'Turtle Rock - Eye Bridge - Top Right'],
|
||||
['Turtle Rock Dark Room (South)', 'Turtle Rock (Trinexx)',
|
||||
'Turtle Rock Isolated Ledge Exit']),
|
||||
create_dungeon_region(world, player, 'Turtle Rock (Trinexx)', 'Turtle Rock',
|
||||
['Turtle Rock - Boss', 'Turtle Rock - Prize']),
|
||||
create_dungeon_region(world, player, 'Palace of Darkness (Entrance)', 'Palace of Darkness',
|
||||
['Palace of Darkness - Shooter Room'],
|
||||
['Palace of Darkness Bridge Room', 'Palace of Darkness Bonk Wall',
|
||||
'Palace of Darkness Exit']),
|
||||
create_dungeon_region(world, player, 'Palace of Darkness (Center)', 'Palace of Darkness',
|
||||
['Palace of Darkness - The Arena - Bridge', 'Palace of Darkness - Stalfos Basement'],
|
||||
['Palace of Darkness Big Key Chest Staircase', 'Palace of Darkness (North)',
|
||||
'Palace of Darkness Big Key Door']),
|
||||
create_dungeon_region(world, player, 'Palace of Darkness (Big Key Chest)', 'Palace of Darkness',
|
||||
['Palace of Darkness - Big Key Chest']),
|
||||
create_dungeon_region(world, player, 'Palace of Darkness (Bonk Section)', 'Palace of Darkness',
|
||||
['Palace of Darkness - The Arena - Ledge', 'Palace of Darkness - Map Chest'],
|
||||
['Palace of Darkness Hammer Peg Drop']),
|
||||
create_dungeon_region(world, player, 'Palace of Darkness (North)', 'Palace of Darkness',
|
||||
['Palace of Darkness - Compass Chest', 'Palace of Darkness - Dark Basement - Left',
|
||||
'Palace of Darkness - Dark Basement - Right'],
|
||||
['Palace of Darkness Spike Statue Room Door', 'Palace of Darkness Maze Door']),
|
||||
create_dungeon_region(player, 'Palace of Darkness (Maze)', 'Palace of Darkness', ['Palace of Darkness - Dark Maze - Top', 'Palace of Darkness - Dark Maze - Bottom', 'Palace of Darkness - Big Chest']),
|
||||
create_dungeon_region(player, 'Palace of Darkness (Harmless Hellway)', 'Palace of Darkness', ['Palace of Darkness - Harmless Hellway']),
|
||||
create_dungeon_region(player, 'Palace of Darkness (Final Section)', 'Palace of Darkness', ['Palace of Darkness - Boss', 'Palace of Darkness - Prize']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Entrance)', 'Ganon\'s Tower', ['Ganons Tower - Bob\'s Torch', 'Ganons Tower - Hope Room - Left', 'Ganons Tower - Hope Room - Right'],
|
||||
['Ganons Tower (Tile Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower Big Key Door', 'Ganons Tower Exit']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Tile Room)', 'Ganon\'s Tower', ['Ganons Tower - Tile Room'], ['Ganons Tower (Tile Room) Key Door']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Compass Room)', 'Ganon\'s Tower', ['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right',
|
||||
'Ganons Tower - Compass Room - Bottom Left', 'Ganons Tower - Compass Room - Bottom Right'],
|
||||
['Ganons Tower (Bottom) (East)']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Hookshot Room)', 'Ganon\'s Tower', ['Ganons Tower - DMs Room - Top Left', 'Ganons Tower - DMs Room - Top Right',
|
||||
'Ganons Tower - DMs Room - Bottom Left', 'Ganons Tower - DMs Room - Bottom Right'],
|
||||
create_dungeon_region(world, player, 'Palace of Darkness (Maze)', 'Palace of Darkness',
|
||||
['Palace of Darkness - Dark Maze - Top', 'Palace of Darkness - Dark Maze - Bottom',
|
||||
'Palace of Darkness - Big Chest']),
|
||||
create_dungeon_region(world, player, 'Palace of Darkness (Harmless Hellway)', 'Palace of Darkness',
|
||||
['Palace of Darkness - Harmless Hellway']),
|
||||
create_dungeon_region(world, player, 'Palace of Darkness (Final Section)', 'Palace of Darkness',
|
||||
['Palace of Darkness - Boss', 'Palace of Darkness - Prize']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Entrance)', 'Ganon\'s Tower',
|
||||
['Ganons Tower - Bob\'s Torch', 'Ganons Tower - Hope Room - Left',
|
||||
'Ganons Tower - Hope Room - Right'],
|
||||
['Ganons Tower (Tile Room)', 'Ganons Tower (Hookshot Room)', 'Ganons Tower Big Key Door',
|
||||
'Ganons Tower Exit']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Tile Room)', 'Ganon\'s Tower', ['Ganons Tower - Tile Room'],
|
||||
['Ganons Tower (Tile Room) Key Door']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Compass Room)', 'Ganon\'s Tower',
|
||||
['Ganons Tower - Compass Room - Top Left', 'Ganons Tower - Compass Room - Top Right',
|
||||
'Ganons Tower - Compass Room - Bottom Left',
|
||||
'Ganons Tower - Compass Room - Bottom Right'], ['Ganons Tower (Bottom) (East)']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Hookshot Room)', 'Ganon\'s Tower',
|
||||
['Ganons Tower - DMs Room - Top Left', 'Ganons Tower - DMs Room - Top Right',
|
||||
'Ganons Tower - DMs Room - Bottom Left', 'Ganons Tower - DMs Room - Bottom Right'],
|
||||
['Ganons Tower (Map Room)', 'Ganons Tower (Double Switch Room)']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Map Room)', 'Ganon\'s Tower', ['Ganons Tower - Map Chest']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Firesnake Room)', 'Ganon\'s Tower', ['Ganons Tower - Firesnake Room'], ['Ganons Tower (Firesnake Room)']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Teleport Room)', 'Ganon\'s Tower', ['Ganons Tower - Randomizer Room - Top Left', 'Ganons Tower - Randomizer Room - Top Right',
|
||||
'Ganons Tower - Randomizer Room - Bottom Left', 'Ganons Tower - Randomizer Room - Bottom Right'],
|
||||
['Ganons Tower (Bottom) (West)']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Bottom)', 'Ganon\'s Tower', ['Ganons Tower - Bob\'s Chest', 'Ganons Tower - Big Chest', 'Ganons Tower - Big Key Room - Left',
|
||||
'Ganons Tower - Big Key Room - Right', 'Ganons Tower - Big Key Chest']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Top)', 'Ganon\'s Tower', None, ['Ganons Tower Torch Rooms']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Before Moldorm)', 'Ganon\'s Tower', ['Ganons Tower - Mini Helmasaur Room - Left', 'Ganons Tower - Mini Helmasaur Room - Right',
|
||||
'Ganons Tower - Pre-Moldorm Chest'], ['Ganons Tower Moldorm Door']),
|
||||
create_dungeon_region(player, 'Ganons Tower (Moldorm)', 'Ganon\'s Tower', None, ['Ganons Tower Moldorm Gap']),
|
||||
create_dungeon_region(player, 'Agahnim 2', 'Ganon\'s Tower', ['Ganons Tower - Validation Chest', 'Agahnim 2'], None),
|
||||
create_cave_region(player, 'Pyramid', 'a drop\'s exit', ['Ganon'], ['Ganon Drop']),
|
||||
create_cave_region(player, 'Bottom of Pyramid', 'a drop\'s exit', None, ['Pyramid Exit']),
|
||||
create_dw_region(player, 'Pyramid Ledge', None, ['Pyramid Entrance', 'Pyramid Drop']),
|
||||
create_lw_region(player, 'Desert Northern Cliffs'),
|
||||
create_dw_region(player, 'Dark Death Mountain Bunny Descent Area')
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Map Room)', 'Ganon\'s Tower', ['Ganons Tower - Map Chest']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Firesnake Room)', 'Ganon\'s Tower',
|
||||
['Ganons Tower - Firesnake Room'], ['Ganons Tower (Firesnake Room)']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Teleport Room)', 'Ganon\'s Tower',
|
||||
['Ganons Tower - Randomizer Room - Top Left',
|
||||
'Ganons Tower - Randomizer Room - Top Right',
|
||||
'Ganons Tower - Randomizer Room - Bottom Left',
|
||||
'Ganons Tower - Randomizer Room - Bottom Right'], ['Ganons Tower (Bottom) (West)']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Bottom)', 'Ganon\'s Tower',
|
||||
['Ganons Tower - Bob\'s Chest', 'Ganons Tower - Big Chest',
|
||||
'Ganons Tower - Big Key Room - Left',
|
||||
'Ganons Tower - Big Key Room - Right', 'Ganons Tower - Big Key Chest']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Top)', 'Ganon\'s Tower', None,
|
||||
['Ganons Tower Torch Rooms']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Before Moldorm)', 'Ganon\'s Tower',
|
||||
['Ganons Tower - Mini Helmasaur Room - Left',
|
||||
'Ganons Tower - Mini Helmasaur Room - Right',
|
||||
'Ganons Tower - Pre-Moldorm Chest'], ['Ganons Tower Moldorm Door']),
|
||||
create_dungeon_region(world, player, 'Ganons Tower (Moldorm)', 'Ganon\'s Tower', None,
|
||||
['Ganons Tower Moldorm Gap']),
|
||||
create_dungeon_region(world, player, 'Agahnim 2', 'Ganon\'s Tower',
|
||||
['Ganons Tower - Validation Chest', 'Agahnim 2'], None),
|
||||
create_cave_region(world, player, 'Pyramid', 'a drop\'s exit', ['Ganon'], ['Ganon Drop']),
|
||||
create_cave_region(world, player, 'Bottom of Pyramid', 'a drop\'s exit', None, ['Pyramid Exit']),
|
||||
create_dw_region(world, player, 'Pyramid Ledge', None, ['Pyramid Entrance', 'Pyramid Drop']),
|
||||
create_lw_region(world, player, 'Desert Northern Cliffs'),
|
||||
create_dw_region(world, player, 'Dark Death Mountain Bunny Descent Area')
|
||||
]
|
||||
|
||||
world.initialize_regions()
|
||||
|
||||
|
||||
def create_lw_region(player: int, name: str, locations=None, exits=None):
|
||||
return _create_region(player, name, RegionType.LightWorld, 'Light World', locations, exits)
|
||||
def create_lw_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
||||
return _create_region(world, player, name, LTTPRegionType.LightWorld, 'Light World', locations, exits)
|
||||
|
||||
|
||||
def create_dw_region(player: int, name: str, locations=None, exits=None):
|
||||
return _create_region(player, name, RegionType.DarkWorld, 'Dark World', locations, exits)
|
||||
def create_dw_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
||||
return _create_region(world, player, name, LTTPRegionType.DarkWorld, 'Dark World', locations, exits)
|
||||
|
||||
|
||||
def create_cave_region(player: int, name: str, hint: str, locations=None, exits=None):
|
||||
return _create_region(player, name, RegionType.Cave, hint, locations, exits)
|
||||
def create_cave_region(world: MultiWorld, player: int, name: str, hint: str, locations=None, exits=None):
|
||||
return _create_region(world, player, name, LTTPRegionType.Cave, hint, locations, exits)
|
||||
|
||||
|
||||
def create_dungeon_region(player: int, name: str, hint: str, locations=None, exits=None):
|
||||
return _create_region(player, name, RegionType.Dungeon, hint, locations, exits)
|
||||
def create_dungeon_region(world: MultiWorld, player: int, name: str, hint: str, locations=None, exits=None):
|
||||
return _create_region(world, player, name, LTTPRegionType.Dungeon, hint, locations, exits)
|
||||
|
||||
|
||||
def _create_region(player: int, name: str, type: RegionType, hint: str, locations=None, exits=None):
|
||||
def _create_region(world: MultiWorld, player: int, name: str, type: LTTPRegionType, hint: str, locations=None,
|
||||
exits=None):
|
||||
from worlds.alttp.SubClasses import ALttPLocation
|
||||
ret = Region(name, type, hint, player)
|
||||
if locations is None:
|
||||
locations = []
|
||||
if exits is None:
|
||||
exits = []
|
||||
|
||||
for exit in exits:
|
||||
ret.exits.append(Entrance(player, exit, ret))
|
||||
for location in locations:
|
||||
address, player_address, crystal, hint_text = location_table[location]
|
||||
ret.locations.append(ALttPLocation(player, location, address, crystal, hint_text, ret, player_address))
|
||||
ret = LTTPRegion(name, type, hint, player, world)
|
||||
if exits:
|
||||
for exit in exits:
|
||||
ret.exits.append(Entrance(player, exit, ret))
|
||||
if locations:
|
||||
for location in locations:
|
||||
address, player_address, crystal, hint_text = location_table[location]
|
||||
ret.locations.append(ALttPLocation(player, location, address, crystal, hint_text, ret, player_address))
|
||||
return ret
|
||||
|
||||
|
||||
def mark_light_world_regions(world, player: int):
|
||||
# cross world caves may have some sections marked as both in_light_world, and in_dark_work.
|
||||
# That is ok. the bunny logic will check for this case and incorporate special rules.
|
||||
queue = collections.deque(region for region in world.get_regions(player) if region.type == RegionType.LightWorld)
|
||||
queue = collections.deque(region for region in world.get_regions(player) if region.type == LTTPRegionType.LightWorld)
|
||||
seen = set(queue)
|
||||
while queue:
|
||||
current = queue.popleft()
|
||||
current.is_light_world = True
|
||||
for exit in current.exits:
|
||||
if exit.connected_region.type == RegionType.DarkWorld:
|
||||
if exit.connected_region.type == LTTPRegionType.DarkWorld:
|
||||
# Don't venture into the dark world
|
||||
continue
|
||||
if exit.connected_region not in seen:
|
||||
seen.add(exit.connected_region)
|
||||
queue.append(exit.connected_region)
|
||||
|
||||
queue = collections.deque(region for region in world.get_regions(player) if region.type == RegionType.DarkWorld)
|
||||
queue = collections.deque(region for region in world.get_regions(player) if region.type == LTTPRegionType.DarkWorld)
|
||||
seen = set(queue)
|
||||
while queue:
|
||||
current = queue.popleft()
|
||||
current.is_dark_world = True
|
||||
for exit in current.exits:
|
||||
if exit.connected_region.type == RegionType.LightWorld:
|
||||
if exit.connected_region.type == LTTPRegionType.LightWorld:
|
||||
# Don't venture into the light world
|
||||
continue
|
||||
if exit.connected_region not in seen:
|
||||
|
||||
+10
-10
@@ -20,7 +20,7 @@ import concurrent.futures
|
||||
import bsdiff4
|
||||
from typing import Optional, List
|
||||
|
||||
from BaseClasses import CollectionState, Region, Location
|
||||
from BaseClasses import CollectionState, Region, Location, MultiWorld
|
||||
from worlds.alttp.Shops import ShopType, ShopPriceType
|
||||
from worlds.alttp.Dungeons import dungeon_music_addresses
|
||||
from worlds.alttp.Regions import location_table, old_location_address_to_new_location_address
|
||||
@@ -92,7 +92,7 @@ class LocalRom(object):
|
||||
# cause crash to provide traceback
|
||||
import xxtea
|
||||
|
||||
local_random = world.slot_seeds[player]
|
||||
local_random = world.per_slot_randoms[player]
|
||||
key = bytes(local_random.getrandbits(8 * 16).to_bytes(16, 'big'))
|
||||
self.write_bytes(0x1800B0, bytearray(key))
|
||||
self.write_int16(0x180087, 1)
|
||||
@@ -384,7 +384,7 @@ def patch_enemizer(world, player: int, rom: LocalRom, enemizercli, output_direct
|
||||
|
||||
max_enemizer_tries = 5
|
||||
for i in range(max_enemizer_tries):
|
||||
enemizer_seed = str(world.slot_seeds[player].randint(0, 999999999))
|
||||
enemizer_seed = str(world.per_slot_randoms[player].randint(0, 999999999))
|
||||
enemizer_command = [os.path.abspath(enemizercli),
|
||||
'--rom', randopatch_path,
|
||||
'--seed', enemizer_seed,
|
||||
@@ -414,7 +414,7 @@ def patch_enemizer(world, player: int, rom: LocalRom, enemizercli, output_direct
|
||||
continue
|
||||
|
||||
for j in range(i + 1, max_enemizer_tries):
|
||||
world.slot_seeds[player].randint(0, 999999999)
|
||||
world.per_slot_randoms[player].randint(0, 999999999)
|
||||
# Sacrifice all remaining random numbers that would have been used for unused enemizer tries.
|
||||
# This allows for future enemizer bug fixes to NOT affect the rest of the seed's randomness
|
||||
break
|
||||
@@ -765,8 +765,8 @@ def get_nonnative_item_sprite(item: str) -> int:
|
||||
# https://discord.com/channels/731205301247803413/827141303330406408/852102450822905886
|
||||
|
||||
|
||||
def patch_rom(world, rom, player, enemized):
|
||||
local_random = world.slot_seeds[player]
|
||||
def patch_rom(world: MultiWorld, rom: LocalRom, player: int, enemized: bool):
|
||||
local_random = world.per_slot_randoms[player]
|
||||
|
||||
# patch items
|
||||
|
||||
@@ -1646,7 +1646,7 @@ def patch_rom(world, rom, player, enemized):
|
||||
rom.write_byte(0xFEE41, 0x2A) # preopen bombable exit
|
||||
|
||||
if world.tile_shuffle[player]:
|
||||
tile_set = TileSet.get_random_tile_set(world.slot_seeds[player])
|
||||
tile_set = TileSet.get_random_tile_set(world.per_slot_randoms[player])
|
||||
rom.write_byte(0x4BA21, tile_set.get_speed())
|
||||
rom.write_byte(0x4BA1D, tile_set.get_len())
|
||||
rom.write_bytes(0x4BA2A, tile_set.get_bytes())
|
||||
@@ -1779,7 +1779,7 @@ def hud_format_text(text):
|
||||
def apply_rom_settings(rom, beep, color, quickswap, menuspeed, music: bool, sprite: str, palettes_options,
|
||||
world=None, player=1, allow_random_on_event=False, reduceflashing=False,
|
||||
triforcehud: str = None, deathlink: bool = False, allowcollect: bool = False):
|
||||
local_random = random if not world else world.slot_seeds[player]
|
||||
local_random = random if not world else world.per_slot_randoms[player]
|
||||
disable_music: bool = not music
|
||||
# enable instant item menu
|
||||
if menuspeed == 'instant':
|
||||
@@ -2105,7 +2105,7 @@ def write_string_to_rom(rom, target, string):
|
||||
|
||||
def write_strings(rom, world, player):
|
||||
from . import ALTTPWorld
|
||||
local_random = world.slot_seeds[player]
|
||||
local_random = world.per_slot_randoms[player]
|
||||
w: ALTTPWorld = world.worlds[player]
|
||||
|
||||
tt = TextTable()
|
||||
@@ -2330,7 +2330,7 @@ def write_strings(rom, world, player):
|
||||
if world.worlds[player].has_progressive_bows and (world.difficulty_requirements[player].progressive_bow_limit >= 2 or (
|
||||
world.swordless[player] or world.logic[player] == 'noglitches')):
|
||||
prog_bow_locs = world.find_item_locations('Progressive Bow', player, True)
|
||||
world.slot_seeds[player].shuffle(prog_bow_locs)
|
||||
world.per_slot_randoms[player].shuffle(prog_bow_locs)
|
||||
found_bow = False
|
||||
found_bow_alt = False
|
||||
while prog_bow_locs and not (found_bow and found_bow_alt):
|
||||
|
||||
@@ -3,7 +3,7 @@ import logging
|
||||
from typing import Iterator, Set
|
||||
|
||||
from worlds.alttp import OverworldGlitchRules
|
||||
from BaseClasses import RegionType, MultiWorld, Entrance
|
||||
from BaseClasses import MultiWorld, Entrance
|
||||
from worlds.alttp.Items import ItemFactory, progression_items, item_name_groups, item_table
|
||||
from worlds.alttp.OverworldGlitchRules import overworld_glitches_rules, no_logic_rules
|
||||
from worlds.alttp.Regions import location_table
|
||||
@@ -12,6 +12,7 @@ from worlds.alttp.Bosses import GanonDefeatRule
|
||||
from worlds.generic.Rules import set_rule, add_rule, forbid_item, add_item_rule, item_in_locations, \
|
||||
item_name
|
||||
from worlds.alttp.Options import smallkey_shuffle
|
||||
from worlds.alttp.Regions import LTTPRegionType
|
||||
|
||||
|
||||
def set_rules(world):
|
||||
@@ -1423,7 +1424,7 @@ def set_bunny_rules(world: MultiWorld, player: int, inverted: bool):
|
||||
return lambda state: state.has('Magic Mirror', player) and state.has_sword(player) or state.has('Moon Pearl', player)
|
||||
if region.name in OverworldGlitchRules.get_invalid_bunny_revival_dungeons():
|
||||
return lambda state: state.has('Magic Mirror', player) or state.has('Moon Pearl', player)
|
||||
if region.type == RegionType.Dungeon:
|
||||
if region.type == LTTPRegionType.Dungeon:
|
||||
return lambda state: True
|
||||
if (((location is None or location.name not in OverworldGlitchRules.get_superbunny_accessible_locations())
|
||||
or (connecting_entrance is not None and connecting_entrance.name in OverworldGlitchRules.get_invalid_bunny_revival_dungeons()))
|
||||
@@ -1467,7 +1468,7 @@ def set_bunny_rules(world: MultiWorld, player: int, inverted: bool):
|
||||
possible_options.append(lambda state: path_to_access_rule(new_path, entrance))
|
||||
else:
|
||||
possible_options.append(lambda state: path_to_access_rule(new_path, entrance) and state.has('Magic Mirror', player))
|
||||
if new_region.type != RegionType.Cave:
|
||||
if new_region.type != LTTPRegionType.Cave:
|
||||
continue
|
||||
else:
|
||||
continue
|
||||
@@ -1495,8 +1496,8 @@ def set_bunny_rules(world: MultiWorld, player: int, inverted: bool):
|
||||
for entrance in world.get_entrances():
|
||||
if entrance.player == player and is_bunny(entrance.connected_region):
|
||||
if world.logic[player] in ['minorglitches', 'owglitches', 'hybridglitches', 'nologic'] :
|
||||
if entrance.connected_region.type == RegionType.Dungeon:
|
||||
if entrance.parent_region.type != RegionType.Dungeon and entrance.connected_region.name in OverworldGlitchRules.get_invalid_bunny_revival_dungeons():
|
||||
if entrance.connected_region.type == LTTPRegionType.Dungeon:
|
||||
if entrance.parent_region.type != LTTPRegionType.Dungeon and entrance.connected_region.name in OverworldGlitchRules.get_invalid_bunny_revival_dungeons():
|
||||
add_rule(entrance, get_rule_to_add(entrance.connected_region, None, entrance))
|
||||
continue
|
||||
if entrance.connected_region.name == 'Turtle Rock (Entrance)':
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
"""Module extending BaseClasses.py for aLttP"""
|
||||
from typing import Optional
|
||||
from enum import IntEnum
|
||||
|
||||
from BaseClasses import Location, Item, ItemClassification
|
||||
from BaseClasses import Location, Item, ItemClassification, Region, MultiWorld
|
||||
|
||||
|
||||
class ALttPLocation(Location):
|
||||
@@ -62,4 +63,32 @@ class ALttPItem(Item):
|
||||
|
||||
@property
|
||||
def locked_dungeon_item(self):
|
||||
return self.location.locked and self.dungeon_item
|
||||
return self.location.locked and self.dungeon_item
|
||||
|
||||
|
||||
class LTTPRegionType(IntEnum):
|
||||
LightWorld = 1
|
||||
DarkWorld = 2
|
||||
Cave = 3 # also houses
|
||||
Dungeon = 4
|
||||
|
||||
@property
|
||||
def is_indoors(self) -> bool:
|
||||
"""Shorthand for checking if Cave or Dungeon"""
|
||||
return self in (LTTPRegionType.Cave, LTTPRegionType.Dungeon)
|
||||
|
||||
|
||||
class LTTPRegion(Region):
|
||||
type: LTTPRegionType
|
||||
|
||||
def __init__(self, name: str, type_: LTTPRegionType, hint: str, player: int, multiworld: MultiWorld):
|
||||
super().__init__(name, player, multiworld, hint)
|
||||
self.type = type_
|
||||
|
||||
@property
|
||||
def get_entrance(self):
|
||||
for entrance in self.entrances:
|
||||
if entrance.parent_region.type in (LTTPRegionType.DarkWorld, LTTPRegionType.LightWorld):
|
||||
return entrance
|
||||
for entrance in self.entrances:
|
||||
return entrance.parent_region.get_entrance
|
||||
|
||||
+14
-11
@@ -5,7 +5,7 @@ import threading
|
||||
import typing
|
||||
|
||||
import Utils
|
||||
from BaseClasses import Item, CollectionState, Tutorial
|
||||
from BaseClasses import Item, CollectionState, Tutorial, MultiWorld
|
||||
from .Dungeons import create_dungeons
|
||||
from .EntranceShuffle import link_entrances, link_inverted_entrances, plando_connect, \
|
||||
indirect_connections, indirect_connections_inverted, indirect_connections_not_inverted
|
||||
@@ -151,16 +151,18 @@ class ALTTPWorld(World):
|
||||
super(ALTTPWorld, self).__init__(*args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def stage_assert_generate(cls, world):
|
||||
def stage_assert_generate(cls, multiworld: MultiWorld):
|
||||
rom_file = get_base_rom_path()
|
||||
if not os.path.exists(rom_file):
|
||||
raise FileNotFoundError(rom_file)
|
||||
if world.is_race:
|
||||
if multiworld.is_race:
|
||||
import xxtea
|
||||
for player in multiworld.get_game_players(cls.game):
|
||||
if multiworld.worlds[player].use_enemizer:
|
||||
check_enemizer(multiworld.worlds[player].enemizer_path)
|
||||
break
|
||||
|
||||
def generate_early(self):
|
||||
if self.use_enemizer():
|
||||
check_enemizer(self.enemizer_path)
|
||||
|
||||
player = self.player
|
||||
world = self.multiworld
|
||||
@@ -369,19 +371,20 @@ class ALTTPWorld(World):
|
||||
def stage_post_fill(cls, world):
|
||||
ShopSlotFill(world)
|
||||
|
||||
def use_enemizer(self):
|
||||
@property
|
||||
def use_enemizer(self) -> bool:
|
||||
world = self.multiworld
|
||||
player = self.player
|
||||
return (world.boss_shuffle[player] or world.enemy_shuffle[player]
|
||||
or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default'
|
||||
or world.pot_shuffle[player] or world.bush_shuffle[player]
|
||||
or world.killable_thieves[player])
|
||||
return bool(world.boss_shuffle[player] or world.enemy_shuffle[player]
|
||||
or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default'
|
||||
or world.pot_shuffle[player] or world.bush_shuffle[player]
|
||||
or world.killable_thieves[player])
|
||||
|
||||
def generate_output(self, output_directory: str):
|
||||
world = self.multiworld
|
||||
player = self.player
|
||||
try:
|
||||
use_enemizer = self.use_enemizer()
|
||||
use_enemizer = self.use_enemizer
|
||||
|
||||
rom = LocalRom(get_base_rom_path())
|
||||
|
||||
|
||||
@@ -14,26 +14,20 @@ class ArchipIDLELogic(LogicMixin):
|
||||
|
||||
|
||||
def set_rules(world: MultiWorld, player: int):
|
||||
for i in range(1, 16):
|
||||
set_rule(
|
||||
world.get_location(f"IDLE for at least {int(i / 2)} minutes {30 if (i % 2) > 0 else 0} seconds", player),
|
||||
lambda state: state._archipidle_location_is_accessible(player, 0)
|
||||
)
|
||||
|
||||
for i in range(16, 31):
|
||||
set_rule(
|
||||
world.get_location(f"IDLE for at least {int(i / 2)} minutes {30 if (i % 2) > 0 else 0} seconds", player),
|
||||
world.get_location(f"IDLE for at least {int(i / 2)} minutes {30 if (i % 2) else 0} seconds", player),
|
||||
lambda state: state._archipidle_location_is_accessible(player, 4)
|
||||
)
|
||||
|
||||
for i in range(31, 51):
|
||||
set_rule(
|
||||
world.get_location(f"IDLE for at least {int(i / 2)} minutes {30 if (i % 2) > 0 else 0} seconds", player),
|
||||
world.get_location(f"IDLE for at least {int(i / 2)} minutes {30 if (i % 2) else 0} seconds", player),
|
||||
lambda state: state._archipidle_location_is_accessible(player, 10)
|
||||
)
|
||||
|
||||
for i in range(51, 101):
|
||||
set_rule(
|
||||
world.get_location(f"IDLE for at least {int(i / 2)} minutes {30 if (i % 2) > 0 else 0} seconds", player),
|
||||
world.get_location(f"IDLE for at least {int(i / 2)} minutes {30 if (i % 2) else 0} seconds", player),
|
||||
lambda state: state._archipidle_location_is_accessible(player, 20)
|
||||
)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from BaseClasses import Item, MultiWorld, Region, Location, Entrance, Tutorial, ItemClassification, RegionType
|
||||
from BaseClasses import Item, MultiWorld, Region, Location, Entrance, Tutorial, ItemClassification
|
||||
from .Items import item_table
|
||||
from .Rules import set_rules
|
||||
from ..AutoWorld import World, WebWorld
|
||||
@@ -38,7 +38,7 @@ class ArchipIDLEWorld(World):
|
||||
location_name_to_id = {}
|
||||
start_id = 9000
|
||||
for i in range(1, 101):
|
||||
location_name_to_id[f"IDLE for at least {int(i / 2)} minutes {30 if (i % 2) > 0 else 0} seconds"] = start_id
|
||||
location_name_to_id[f"IDLE for at least {int(i / 2)} minutes {30 if (i % 2) else 0} seconds"] = start_id
|
||||
start_id += 1
|
||||
|
||||
def generate_basic(self):
|
||||
@@ -78,8 +78,7 @@ class ArchipIDLEWorld(World):
|
||||
|
||||
|
||||
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
||||
region = Region(name, RegionType.Generic, name, player)
|
||||
region.multiworld = world
|
||||
region = Region(name, player, world)
|
||||
if locations:
|
||||
for location_name in locations.keys():
|
||||
location = ArchipIDLELocation(player, location_name, locations[location_name], region)
|
||||
@@ -98,6 +97,3 @@ class ArchipIDLEItem(Item):
|
||||
|
||||
class ArchipIDLELocation(Location):
|
||||
game: str = "ArchipIDLE"
|
||||
|
||||
def __init__(self, player: int, name: str, address=None, parent=None):
|
||||
super(ArchipIDLELocation, self).__init__(player, name, address, parent)
|
||||
|
||||
@@ -29,5 +29,5 @@ class Bk_SudokuWorld(World):
|
||||
location_name_to_id: Dict[str, int] = {}
|
||||
|
||||
@classmethod
|
||||
def stage_assert_generate(cls, world):
|
||||
def stage_assert_generate(cls, multiworld):
|
||||
raise Exception("BK Sudoku cannot be used for generating worlds, the client can instead connect to any other world")
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
|
||||
def link_checksfinder_structures(world, player):
|
||||
for (exit, region) in mandatory_connections:
|
||||
world.get_entrance(exit, player).connect(world.get_region(region, player))
|
||||
|
||||
# (Region name, list of exits)
|
||||
checksfinder_regions = [
|
||||
('Menu', ['New Board']),
|
||||
('Board',[]),
|
||||
]
|
||||
|
||||
# (Entrance, region pointed to)
|
||||
mandatory_connections = [
|
||||
('New Board', 'Board'),
|
||||
]
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from BaseClasses import Region, Entrance, Item, Tutorial, ItemClassification, RegionType
|
||||
from BaseClasses import Region, Entrance, Item, Tutorial, ItemClassification
|
||||
from .Items import ChecksFinderItem, item_table, required_items
|
||||
from .Locations import ChecksFinderAdvancement, advancement_table, exclusion_table
|
||||
from .Options import checksfinder_options
|
||||
from .Regions import checksfinder_regions, link_checksfinder_structures
|
||||
from .Rules import set_rules, set_completion_rules
|
||||
from ..AutoWorld import World, WebWorld
|
||||
|
||||
@@ -38,7 +37,7 @@ class ChecksFinderWorld(World):
|
||||
|
||||
def _get_checksfinder_data(self):
|
||||
return {
|
||||
'world_seed': self.multiworld.slot_seeds[self.player].getrandbits(32),
|
||||
'world_seed': self.multiworld.per_slot_randoms[self.player].getrandbits(32),
|
||||
'seed_name': self.multiworld.seed_name,
|
||||
'player_name': self.multiworld.get_player_name(self.player),
|
||||
'player_id': self.player,
|
||||
@@ -68,17 +67,15 @@ class ChecksFinderWorld(World):
|
||||
set_completion_rules(self.multiworld, self.player)
|
||||
|
||||
def create_regions(self):
|
||||
def ChecksFinderRegion(region_name: str, exits=[]):
|
||||
ret = Region(region_name, RegionType.Generic, region_name, self.player, self.multiworld)
|
||||
ret.locations = [ChecksFinderAdvancement(self.player, loc_name, loc_data.id, ret)
|
||||
for loc_name, loc_data in advancement_table.items()
|
||||
if loc_data.region == region_name]
|
||||
for exit in exits:
|
||||
ret.exits.append(Entrance(self.player, exit, ret))
|
||||
return ret
|
||||
menu = Region("Menu", self.player, self.multiworld)
|
||||
board = Region("Board", self.player, self.multiworld)
|
||||
board.locations = [ChecksFinderAdvancement(self.player, loc_name, loc_data.id, board)
|
||||
for loc_name, loc_data in advancement_table.items() if loc_data.region == board.name]
|
||||
|
||||
self.multiworld.regions += [ChecksFinderRegion(*r) for r in checksfinder_regions]
|
||||
link_checksfinder_structures(self.multiworld, self.player)
|
||||
connection = Entrance(self.player, "New Board", menu)
|
||||
menu.exits.append(connection)
|
||||
connection.connect(board)
|
||||
self.multiworld.regions += [menu, board]
|
||||
|
||||
def fill_slot_data(self):
|
||||
slot_data = self._get_checksfinder_data()
|
||||
|
||||
@@ -14,7 +14,7 @@ from .data.locations_data import location_dictionary, fire_link_shrine_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, progressive_locations
|
||||
from ..AutoWorld import World, WebWorld
|
||||
from BaseClasses import MultiWorld, Location, Region, Item, RegionType, Entrance, Tutorial, ItemClassification
|
||||
from BaseClasses import MultiWorld, Region, Item, Entrance, Tutorial, ItemClassification
|
||||
from ..generic.Rules import set_rule, add_item_rule
|
||||
|
||||
|
||||
@@ -77,7 +77,6 @@ class DarkSouls3World(World):
|
||||
return DarkSouls3Item(name, item_classification, data, self.player)
|
||||
|
||||
def create_regions(self):
|
||||
|
||||
menu_region = self.create_region("Menu", progressive_locations)
|
||||
|
||||
# Create all Vanilla regions of Dark Souls III
|
||||
@@ -157,7 +156,7 @@ class DarkSouls3World(World):
|
||||
|
||||
# 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, self.multiworld)
|
||||
new_region = Region(region_name, self.player, self.multiworld)
|
||||
if location_table:
|
||||
for name, address in location_table.items():
|
||||
location = DarkSouls3Location(self.player, name, self.location_name_to_id[name], new_region)
|
||||
|
||||
+89
-93
@@ -7,35 +7,35 @@ from .Names import LocationName, ItemName
|
||||
|
||||
|
||||
def create_regions(world, player: int, active_locations):
|
||||
menu_region = create_region(world, player, active_locations, 'Menu', None, None)
|
||||
menu_region = create_region(world, player, active_locations, 'Menu', 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_1_region_locations)
|
||||
|
||||
overworld_2_region_locations = {}
|
||||
overworld_2_region = create_region(world, player, active_locations, LocationName.overworld_2_region,
|
||||
overworld_2_region_locations, None)
|
||||
overworld_2_region_locations)
|
||||
|
||||
overworld_3_region_locations = {}
|
||||
overworld_3_region = create_region(world, player, active_locations, LocationName.overworld_3_region,
|
||||
overworld_3_region_locations, None)
|
||||
overworld_3_region_locations)
|
||||
|
||||
overworld_4_region_locations = {}
|
||||
overworld_4_region = create_region(world, player, active_locations, LocationName.overworld_4_region,
|
||||
overworld_4_region_locations, None)
|
||||
overworld_4_region_locations)
|
||||
|
||||
|
||||
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)
|
||||
lake_orangatanga_region = create_region(world, player, active_locations, LocationName.lake_orangatanga_region, None)
|
||||
kremwood_forest_region = create_region(world, player, active_locations, LocationName.kremwood_forest_region, None)
|
||||
cotton_top_cove_region = create_region(world, player, active_locations, LocationName.cotton_top_cove_region, None)
|
||||
mekanos_region = create_region(world, player, active_locations, LocationName.mekanos_region, None)
|
||||
k3_region = create_region(world, player, active_locations, LocationName.k3_region, None)
|
||||
razor_ridge_region = create_region(world, player, active_locations, LocationName.razor_ridge_region, None)
|
||||
kaos_kore_region = create_region(world, player, active_locations, LocationName.kaos_kore_region, None)
|
||||
krematoa_region = create_region(world, player, active_locations, LocationName.krematoa_region, None)
|
||||
|
||||
|
||||
lakeside_limbo_region_locations = {
|
||||
@@ -47,7 +47,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
lakeside_limbo_region_locations[LocationName.lakeside_limbo_kong] = []
|
||||
lakeside_limbo_region = create_region(world, player, active_locations, LocationName.lakeside_limbo_region,
|
||||
lakeside_limbo_region_locations, None)
|
||||
lakeside_limbo_region_locations)
|
||||
|
||||
doorstop_dash_region_locations = {
|
||||
LocationName.doorstop_dash_flag : [0x65A, 1],
|
||||
@@ -58,7 +58,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
doorstop_dash_region_locations[LocationName.doorstop_dash_kong] = []
|
||||
doorstop_dash_region = create_region(world, player, active_locations, LocationName.doorstop_dash_region,
|
||||
doorstop_dash_region_locations, None)
|
||||
doorstop_dash_region_locations)
|
||||
|
||||
tidal_trouble_region_locations = {
|
||||
LocationName.tidal_trouble_flag : [0x659, 1],
|
||||
@@ -69,7 +69,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
tidal_trouble_region_locations[LocationName.tidal_trouble_kong] = []
|
||||
tidal_trouble_region = create_region(world, player, active_locations, LocationName.tidal_trouble_region,
|
||||
tidal_trouble_region_locations, None)
|
||||
tidal_trouble_region_locations)
|
||||
|
||||
skiddas_row_region_locations = {
|
||||
LocationName.skiddas_row_flag : [0x65D, 1],
|
||||
@@ -80,7 +80,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
skiddas_row_region_locations[LocationName.skiddas_row_kong] = []
|
||||
skiddas_row_region = create_region(world, player, active_locations, LocationName.skiddas_row_region,
|
||||
skiddas_row_region_locations, None)
|
||||
skiddas_row_region_locations)
|
||||
|
||||
murky_mill_region_locations = {
|
||||
LocationName.murky_mill_flag : [0x65C, 1],
|
||||
@@ -91,7 +91,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
murky_mill_region_locations[LocationName.murky_mill_kong] = []
|
||||
murky_mill_region = create_region(world, player, active_locations, LocationName.murky_mill_region,
|
||||
murky_mill_region_locations, None)
|
||||
murky_mill_region_locations)
|
||||
|
||||
barrel_shield_bust_up_region_locations = {
|
||||
LocationName.barrel_shield_bust_up_flag : [0x662, 1],
|
||||
@@ -101,8 +101,9 @@ def create_regions(world, player: int, active_locations):
|
||||
}
|
||||
if world.kongsanity[player]:
|
||||
barrel_shield_bust_up_region_locations[LocationName.barrel_shield_bust_up_kong] = []
|
||||
barrel_shield_bust_up_region = create_region(world, player, active_locations, LocationName.barrel_shield_bust_up_region,
|
||||
barrel_shield_bust_up_region_locations, None)
|
||||
barrel_shield_bust_up_region = create_region(world, player, active_locations,
|
||||
LocationName.barrel_shield_bust_up_region,
|
||||
barrel_shield_bust_up_region_locations)
|
||||
|
||||
riverside_race_region_locations = {
|
||||
LocationName.riverside_race_flag : [0x664, 1],
|
||||
@@ -113,7 +114,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
riverside_race_region_locations[LocationName.riverside_race_kong] = []
|
||||
riverside_race_region = create_region(world, player, active_locations, LocationName.riverside_race_region,
|
||||
riverside_race_region_locations, None)
|
||||
riverside_race_region_locations)
|
||||
|
||||
squeals_on_wheels_region_locations = {
|
||||
LocationName.squeals_on_wheels_flag : [0x65B, 1],
|
||||
@@ -124,7 +125,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
squeals_on_wheels_region_locations[LocationName.squeals_on_wheels_kong] = []
|
||||
squeals_on_wheels_region = create_region(world, player, active_locations, LocationName.squeals_on_wheels_region,
|
||||
squeals_on_wheels_region_locations, None)
|
||||
squeals_on_wheels_region_locations)
|
||||
|
||||
springin_spiders_region_locations = {
|
||||
LocationName.springin_spiders_flag : [0x661, 1],
|
||||
@@ -135,7 +136,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
springin_spiders_region_locations[LocationName.springin_spiders_kong] = []
|
||||
springin_spiders_region = create_region(world, player, active_locations, LocationName.springin_spiders_region,
|
||||
springin_spiders_region_locations, None)
|
||||
springin_spiders_region_locations)
|
||||
|
||||
bobbing_barrel_brawl_region_locations = {
|
||||
LocationName.bobbing_barrel_brawl_flag : [0x666, 1],
|
||||
@@ -145,8 +146,9 @@ def create_regions(world, player: int, active_locations):
|
||||
}
|
||||
if world.kongsanity[player]:
|
||||
bobbing_barrel_brawl_region_locations[LocationName.bobbing_barrel_brawl_kong] = []
|
||||
bobbing_barrel_brawl_region = create_region(world, player, active_locations, LocationName.bobbing_barrel_brawl_region,
|
||||
bobbing_barrel_brawl_region_locations, None)
|
||||
bobbing_barrel_brawl_region = create_region(world, player, active_locations,
|
||||
LocationName.bobbing_barrel_brawl_region,
|
||||
bobbing_barrel_brawl_region_locations)
|
||||
|
||||
bazzas_blockade_region_locations = {
|
||||
LocationName.bazzas_blockade_flag : [0x667, 1],
|
||||
@@ -157,7 +159,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
bazzas_blockade_region_locations[LocationName.bazzas_blockade_kong] = []
|
||||
bazzas_blockade_region = create_region(world, player, active_locations, LocationName.bazzas_blockade_region,
|
||||
bazzas_blockade_region_locations, None)
|
||||
bazzas_blockade_region_locations)
|
||||
|
||||
rocket_barrel_ride_region_locations = {
|
||||
LocationName.rocket_barrel_ride_flag : [0x66A, 1],
|
||||
@@ -168,7 +170,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
rocket_barrel_ride_region_locations[LocationName.rocket_barrel_ride_kong] = []
|
||||
rocket_barrel_ride_region = create_region(world, player, active_locations, LocationName.rocket_barrel_ride_region,
|
||||
rocket_barrel_ride_region_locations, None)
|
||||
rocket_barrel_ride_region_locations)
|
||||
|
||||
kreeping_klasps_region_locations = {
|
||||
LocationName.kreeping_klasps_flag : [0x658, 1],
|
||||
@@ -179,7 +181,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
kreeping_klasps_region_locations[LocationName.kreeping_klasps_kong] = []
|
||||
kreeping_klasps_region = create_region(world, player, active_locations, LocationName.kreeping_klasps_region,
|
||||
kreeping_klasps_region_locations, None)
|
||||
kreeping_klasps_region_locations)
|
||||
|
||||
tracker_barrel_trek_region_locations = {
|
||||
LocationName.tracker_barrel_trek_flag : [0x66B, 1],
|
||||
@@ -190,7 +192,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
tracker_barrel_trek_region_locations[LocationName.tracker_barrel_trek_kong] = []
|
||||
tracker_barrel_trek_region = create_region(world, player, active_locations, LocationName.tracker_barrel_trek_region,
|
||||
tracker_barrel_trek_region_locations, None)
|
||||
tracker_barrel_trek_region_locations)
|
||||
|
||||
fish_food_frenzy_region_locations = {
|
||||
LocationName.fish_food_frenzy_flag : [0x668, 1],
|
||||
@@ -201,7 +203,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
fish_food_frenzy_region_locations[LocationName.fish_food_frenzy_kong] = []
|
||||
fish_food_frenzy_region = create_region(world, player, active_locations, LocationName.fish_food_frenzy_region,
|
||||
fish_food_frenzy_region_locations, None)
|
||||
fish_food_frenzy_region_locations)
|
||||
|
||||
fire_ball_frenzy_region_locations = {
|
||||
LocationName.fire_ball_frenzy_flag : [0x66D, 1],
|
||||
@@ -212,7 +214,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
fire_ball_frenzy_region_locations[LocationName.fire_ball_frenzy_kong] = []
|
||||
fire_ball_frenzy_region = create_region(world, player, active_locations, LocationName.fire_ball_frenzy_region,
|
||||
fire_ball_frenzy_region_locations, None)
|
||||
fire_ball_frenzy_region_locations)
|
||||
|
||||
demolition_drain_pipe_region_locations = {
|
||||
LocationName.demolition_drain_pipe_flag : [0x672, 1],
|
||||
@@ -222,8 +224,9 @@ def create_regions(world, player: int, active_locations):
|
||||
}
|
||||
if world.kongsanity[player]:
|
||||
demolition_drain_pipe_region_locations[LocationName.demolition_drain_pipe_kong] = []
|
||||
demolition_drain_pipe_region = create_region(world, player, active_locations, LocationName.demolition_drain_pipe_region,
|
||||
demolition_drain_pipe_region_locations, None)
|
||||
demolition_drain_pipe_region = create_region(world, player, active_locations,
|
||||
LocationName.demolition_drain_pipe_region,
|
||||
demolition_drain_pipe_region_locations)
|
||||
|
||||
ripsaw_rage_region_locations = {
|
||||
LocationName.ripsaw_rage_flag : [0x660, 1],
|
||||
@@ -234,7 +237,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
ripsaw_rage_region_locations[LocationName.ripsaw_rage_kong] = []
|
||||
ripsaw_rage_region = create_region(world, player, active_locations, LocationName.ripsaw_rage_region,
|
||||
ripsaw_rage_region_locations, None)
|
||||
ripsaw_rage_region_locations)
|
||||
|
||||
blazing_bazookas_region_locations = {
|
||||
LocationName.blazing_bazookas_flag : [0x66E, 1],
|
||||
@@ -245,7 +248,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
blazing_bazookas_region_locations[LocationName.blazing_bazookas_kong] = []
|
||||
blazing_bazookas_region = create_region(world, player, active_locations, LocationName.blazing_bazookas_region,
|
||||
blazing_bazookas_region_locations, None)
|
||||
blazing_bazookas_region_locations)
|
||||
|
||||
low_g_labyrinth_region_locations = {
|
||||
LocationName.low_g_labyrinth_flag : [0x670, 1],
|
||||
@@ -256,7 +259,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
low_g_labyrinth_region_locations[LocationName.low_g_labyrinth_kong] = []
|
||||
low_g_labyrinth_region = create_region(world, player, active_locations, LocationName.low_g_labyrinth_region,
|
||||
low_g_labyrinth_region_locations, None)
|
||||
low_g_labyrinth_region_locations)
|
||||
|
||||
krevice_kreepers_region_locations = {
|
||||
LocationName.krevice_kreepers_flag : [0x673, 1],
|
||||
@@ -267,7 +270,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
krevice_kreepers_region_locations[LocationName.krevice_kreepers_kong] = []
|
||||
krevice_kreepers_region = create_region(world, player, active_locations, LocationName.krevice_kreepers_region,
|
||||
krevice_kreepers_region_locations, None)
|
||||
krevice_kreepers_region_locations)
|
||||
|
||||
tearaway_toboggan_region_locations = {
|
||||
LocationName.tearaway_toboggan_flag : [0x65F, 1],
|
||||
@@ -278,7 +281,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
tearaway_toboggan_region_locations[LocationName.tearaway_toboggan_kong] = []
|
||||
tearaway_toboggan_region = create_region(world, player, active_locations, LocationName.tearaway_toboggan_region,
|
||||
tearaway_toboggan_region_locations, None)
|
||||
tearaway_toboggan_region_locations)
|
||||
|
||||
barrel_drop_bounce_region_locations = {
|
||||
LocationName.barrel_drop_bounce_flag : [0x66C, 1],
|
||||
@@ -289,7 +292,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
barrel_drop_bounce_region_locations[LocationName.barrel_drop_bounce_kong] = []
|
||||
barrel_drop_bounce_region = create_region(world, player, active_locations, LocationName.barrel_drop_bounce_region,
|
||||
barrel_drop_bounce_region_locations, None)
|
||||
barrel_drop_bounce_region_locations)
|
||||
|
||||
krack_shot_kroc_region_locations = {
|
||||
LocationName.krack_shot_kroc_flag : [0x66F, 1],
|
||||
@@ -300,7 +303,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
krack_shot_kroc_region_locations[LocationName.krack_shot_kroc_kong] = []
|
||||
krack_shot_kroc_region = create_region(world, player, active_locations, LocationName.krack_shot_kroc_region,
|
||||
krack_shot_kroc_region_locations, None)
|
||||
krack_shot_kroc_region_locations)
|
||||
|
||||
lemguin_lunge_region_locations = {
|
||||
LocationName.lemguin_lunge_flag : [0x65E, 1],
|
||||
@@ -311,7 +314,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
lemguin_lunge_region_locations[LocationName.lemguin_lunge_kong] = []
|
||||
lemguin_lunge_region = create_region(world, player, active_locations, LocationName.lemguin_lunge_region,
|
||||
lemguin_lunge_region_locations, None)
|
||||
lemguin_lunge_region_locations)
|
||||
|
||||
buzzer_barrage_region_locations = {
|
||||
LocationName.buzzer_barrage_flag : [0x676, 1],
|
||||
@@ -322,7 +325,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
buzzer_barrage_region_locations[LocationName.buzzer_barrage_kong] = []
|
||||
buzzer_barrage_region = create_region(world, player, active_locations, LocationName.buzzer_barrage_region,
|
||||
buzzer_barrage_region_locations, None)
|
||||
buzzer_barrage_region_locations)
|
||||
|
||||
kong_fused_cliffs_region_locations = {
|
||||
LocationName.kong_fused_cliffs_flag : [0x674, 1],
|
||||
@@ -333,7 +336,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
kong_fused_cliffs_region_locations[LocationName.kong_fused_cliffs_kong] = []
|
||||
kong_fused_cliffs_region = create_region(world, player, active_locations, LocationName.kong_fused_cliffs_region,
|
||||
kong_fused_cliffs_region_locations, None)
|
||||
kong_fused_cliffs_region_locations)
|
||||
|
||||
floodlit_fish_region_locations = {
|
||||
LocationName.floodlit_fish_flag : [0x669, 1],
|
||||
@@ -344,7 +347,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
floodlit_fish_region_locations[LocationName.floodlit_fish_kong] = []
|
||||
floodlit_fish_region = create_region(world, player, active_locations, LocationName.floodlit_fish_region,
|
||||
floodlit_fish_region_locations, None)
|
||||
floodlit_fish_region_locations)
|
||||
|
||||
pothole_panic_region_locations = {
|
||||
LocationName.pothole_panic_flag : [0x677, 1],
|
||||
@@ -355,7 +358,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
pothole_panic_region_locations[LocationName.pothole_panic_kong] = []
|
||||
pothole_panic_region = create_region(world, player, active_locations, LocationName.pothole_panic_region,
|
||||
pothole_panic_region_locations, None)
|
||||
pothole_panic_region_locations)
|
||||
|
||||
ropey_rumpus_region_locations = {
|
||||
LocationName.ropey_rumpus_flag : [0x675, 1],
|
||||
@@ -366,7 +369,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
ropey_rumpus_region_locations[LocationName.ropey_rumpus_kong] = []
|
||||
ropey_rumpus_region = create_region(world, player, active_locations, LocationName.ropey_rumpus_region,
|
||||
ropey_rumpus_region_locations, None)
|
||||
ropey_rumpus_region_locations)
|
||||
|
||||
konveyor_rope_clash_region_locations = {
|
||||
LocationName.konveyor_rope_clash_flag : [0x657, 1],
|
||||
@@ -377,7 +380,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
konveyor_rope_clash_region_locations[LocationName.konveyor_rope_clash_kong] = []
|
||||
konveyor_rope_clash_region = create_region(world, player, active_locations, LocationName.konveyor_rope_clash_region,
|
||||
konveyor_rope_clash_region_locations, None)
|
||||
konveyor_rope_clash_region_locations)
|
||||
|
||||
creepy_caverns_region_locations = {
|
||||
LocationName.creepy_caverns_flag : [0x678, 1],
|
||||
@@ -388,7 +391,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
creepy_caverns_region_locations[LocationName.creepy_caverns_kong] = []
|
||||
creepy_caverns_region = create_region(world, player, active_locations, LocationName.creepy_caverns_region,
|
||||
creepy_caverns_region_locations, None)
|
||||
creepy_caverns_region_locations)
|
||||
|
||||
lightning_lookout_region_locations = {
|
||||
LocationName.lightning_lookout_flag : [0x665, 1],
|
||||
@@ -399,7 +402,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
lightning_lookout_region_locations[LocationName.lightning_lookout_kong] = []
|
||||
lightning_lookout_region = create_region(world, player, active_locations, LocationName.lightning_lookout_region,
|
||||
lightning_lookout_region_locations, None)
|
||||
lightning_lookout_region_locations)
|
||||
|
||||
koindozer_klamber_region_locations = {
|
||||
LocationName.koindozer_klamber_flag : [0x679, 1],
|
||||
@@ -410,7 +413,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
koindozer_klamber_region_locations[LocationName.koindozer_klamber_kong] = []
|
||||
koindozer_klamber_region = create_region(world, player, active_locations, LocationName.koindozer_klamber_region,
|
||||
koindozer_klamber_region_locations, None)
|
||||
koindozer_klamber_region_locations)
|
||||
|
||||
poisonous_pipeline_region_locations = {
|
||||
LocationName.poisonous_pipeline_flag : [0x671, 1],
|
||||
@@ -421,7 +424,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
poisonous_pipeline_region_locations[LocationName.poisonous_pipeline_kong] = []
|
||||
poisonous_pipeline_region = create_region(world, player, active_locations, LocationName.poisonous_pipeline_region,
|
||||
poisonous_pipeline_region_locations, None)
|
||||
poisonous_pipeline_region_locations)
|
||||
|
||||
stampede_sprint_region_locations = {
|
||||
LocationName.stampede_sprint_flag : [0x67B, 1],
|
||||
@@ -433,7 +436,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
stampede_sprint_region_locations[LocationName.stampede_sprint_kong] = []
|
||||
stampede_sprint_region = create_region(world, player, active_locations, LocationName.stampede_sprint_region,
|
||||
stampede_sprint_region_locations, None)
|
||||
stampede_sprint_region_locations)
|
||||
|
||||
criss_cross_cliffs_region_locations = {
|
||||
LocationName.criss_cross_cliffs_flag : [0x67C, 1],
|
||||
@@ -444,7 +447,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
criss_cross_cliffs_region_locations[LocationName.criss_cross_cliffs_kong] = []
|
||||
criss_cross_cliffs_region = create_region(world, player, active_locations, LocationName.criss_cross_cliffs_region,
|
||||
criss_cross_cliffs_region_locations, None)
|
||||
criss_cross_cliffs_region_locations)
|
||||
|
||||
tyrant_twin_tussle_region_locations = {
|
||||
LocationName.tyrant_twin_tussle_flag : [0x67D, 1],
|
||||
@@ -456,7 +459,7 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
tyrant_twin_tussle_region_locations[LocationName.tyrant_twin_tussle_kong] = []
|
||||
tyrant_twin_tussle_region = create_region(world, player, active_locations, LocationName.tyrant_twin_tussle_region,
|
||||
tyrant_twin_tussle_region_locations, None)
|
||||
tyrant_twin_tussle_region_locations)
|
||||
|
||||
swoopy_salvo_region_locations = {
|
||||
LocationName.swoopy_salvo_flag : [0x663, 1],
|
||||
@@ -468,140 +471,140 @@ def create_regions(world, player: int, active_locations):
|
||||
if world.kongsanity[player]:
|
||||
swoopy_salvo_region_locations[LocationName.swoopy_salvo_kong] = []
|
||||
swoopy_salvo_region = create_region(world, player, active_locations, LocationName.swoopy_salvo_region,
|
||||
swoopy_salvo_region_locations, None)
|
||||
swoopy_salvo_region_locations)
|
||||
|
||||
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)
|
||||
rocket_rush_region_locations)
|
||||
|
||||
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)
|
||||
belchas_barn_region_locations)
|
||||
|
||||
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)
|
||||
arichs_ambush_region_locations)
|
||||
|
||||
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)
|
||||
squirts_showdown_region_locations)
|
||||
|
||||
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)
|
||||
kaos_karnage_region_locations)
|
||||
|
||||
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)
|
||||
bleaks_house_region_locations)
|
||||
|
||||
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)
|
||||
barboss_barrier_region_locations)
|
||||
|
||||
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)
|
||||
kastle_kaos_region_locations)
|
||||
|
||||
knautilus_region_locations = {
|
||||
LocationName.knautilus: [0x656, 1],
|
||||
}
|
||||
knautilus_region = create_region(world, player, active_locations, LocationName.knautilus_region,
|
||||
knautilus_region_locations, None)
|
||||
knautilus_region_locations)
|
||||
|
||||
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)
|
||||
belchas_burrow_region_locations)
|
||||
|
||||
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)
|
||||
kong_cave_region_locations)
|
||||
|
||||
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)
|
||||
undercover_cove_region_locations)
|
||||
|
||||
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)
|
||||
ks_cache_region_locations)
|
||||
|
||||
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)
|
||||
hill_top_hoard_region_locations)
|
||||
|
||||
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)
|
||||
bounty_beach_region_locations)
|
||||
|
||||
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)
|
||||
smugglers_cove_region_locations)
|
||||
|
||||
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)
|
||||
arichs_hoard_region_locations)
|
||||
|
||||
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)
|
||||
bounty_bay_region_locations)
|
||||
|
||||
sky_high_secret_region_locations = {}
|
||||
if False:#world.include_trade_sequence[player]:
|
||||
sky_high_secret_region_locations[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)
|
||||
sky_high_secret_region_locations)
|
||||
|
||||
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)
|
||||
glacial_grotto_region_locations)
|
||||
|
||||
cifftop_cache_region_locations = {}
|
||||
if False:#world.include_trade_sequence[player]:
|
||||
cifftop_cache_region_locations[LocationName.cifftop_cache] = [0x64D, 1]
|
||||
cifftop_cache_region = create_region(world, player, active_locations, LocationName.cifftop_cache_region,
|
||||
cifftop_cache_region_locations, None)
|
||||
cifftop_cache_region_locations)
|
||||
|
||||
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)
|
||||
sewer_stockpile_region_locations)
|
||||
|
||||
|
||||
# Set up the regions correctly.
|
||||
@@ -710,20 +713,17 @@ def create_regions(world, player: int, active_locations):
|
||||
|
||||
blizzard_region_locations[LocationName.blizzards_basecamp] = [0x625, 4, True]
|
||||
|
||||
bazaar_region = create_region(world, player, active_locations, LocationName.bazaar_region,
|
||||
bazaar_region_locations, None)
|
||||
bazaar_region = create_region(world, player, active_locations, LocationName.bazaar_region, bazaar_region_locations)
|
||||
bramble_region = create_region(world, player, active_locations, LocationName.bramble_region,
|
||||
bramble_region_locations, None)
|
||||
bramble_region_locations)
|
||||
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)
|
||||
flower_spot_region_locations)
|
||||
barter_region = create_region(world, player, active_locations, LocationName.barter_region, barter_region_locations)
|
||||
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)
|
||||
barnacle_region_locations)
|
||||
blue_region = create_region(world, player, active_locations, LocationName.blue_region, blue_region_locations)
|
||||
blizzard_region = create_region(world, player, active_locations, LocationName.blizzard_region,
|
||||
blizzard_region_locations, None)
|
||||
blizzard_region_locations)
|
||||
|
||||
world.regions += [
|
||||
bazaar_region,
|
||||
@@ -910,10 +910,9 @@ def connect_regions(world, player, level_list):
|
||||
lambda state: (state.has(ItemName.krematoa_cog, player, 5)))
|
||||
|
||||
|
||||
def create_region(world: MultiWorld, player: int, active_locations, name: str, locations=None, exits=None):
|
||||
def create_region(world: MultiWorld, player: int, active_locations, name: str, locations=None):
|
||||
# Shamelessly stolen from the ROR2 definition
|
||||
ret = Region(name, None, name, player)
|
||||
ret.multiworld = world
|
||||
ret = Region(name, player, world)
|
||||
if locations:
|
||||
for locationName, locationData in locations.items():
|
||||
loc_id = active_locations.get(locationName, 0)
|
||||
@@ -924,9 +923,6 @@ def create_region(world: MultiWorld, player: int, active_locations, name: str, l
|
||||
|
||||
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
|
||||
|
||||
|
||||
+1
-1
@@ -476,7 +476,7 @@ class LocalRom(object):
|
||||
|
||||
|
||||
def patch_rom(world, rom, player, active_level_list):
|
||||
local_random = world.slot_seeds[player]
|
||||
local_random = world.per_slot_randoms[player]
|
||||
|
||||
# Boomer Costs
|
||||
bonus_coin_cost = world.krematoa_bonus_coin_cost[player]
|
||||
|
||||
@@ -2,8 +2,8 @@ import math
|
||||
|
||||
from BaseClasses import MultiWorld
|
||||
from .Names import LocationName, ItemName
|
||||
from ..AutoWorld import LogicMixin
|
||||
from ..generic.Rules import add_rule, set_rule
|
||||
from worlds.AutoWorld import LogicMixin
|
||||
from worlds.generic.Rules import add_rule, set_rule
|
||||
|
||||
|
||||
def set_rules(world: MultiWorld, player: int):
|
||||
|
||||
@@ -12,7 +12,7 @@ from .Levels import level_list
|
||||
from .Rules import set_rules
|
||||
from .Names import ItemName, LocationName
|
||||
from .Client import DKC3SNIClient
|
||||
from ..AutoWorld import WebWorld, World
|
||||
from worlds.AutoWorld import WebWorld, World
|
||||
from .Rom import LocalRom, patch_rom, get_base_rom_path, DKC3DeltaPatch
|
||||
import Patch
|
||||
|
||||
@@ -55,7 +55,7 @@ class DKC3World(World):
|
||||
super().__init__(world, player)
|
||||
|
||||
@classmethod
|
||||
def stage_assert_generate(cls, world):
|
||||
def stage_assert_generate(cls, multiworld: MultiWorld):
|
||||
rom_file = get_base_rom_path()
|
||||
if not os.path.exists(rom_file):
|
||||
raise FileNotFoundError(rom_file)
|
||||
|
||||
@@ -99,7 +99,7 @@ def generate_mod(world: "Factorio", output_directory: str):
|
||||
for location in world.locations]
|
||||
mod_name = f"AP-{multiworld.seed_name}-P{player}-{multiworld.get_file_safe_player_name(player)}"
|
||||
|
||||
random = multiworld.slot_seeds[player]
|
||||
random = multiworld.per_slot_randoms[player]
|
||||
|
||||
def flop_random(low, high, base=None):
|
||||
"""Guarantees 50% below base and 50% above base, uniform distribution in each direction."""
|
||||
|
||||
@@ -4,7 +4,7 @@ import collections
|
||||
import logging
|
||||
import typing
|
||||
|
||||
from BaseClasses import Region, Entrance, Location, Item, RegionType, Tutorial, ItemClassification
|
||||
from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassification
|
||||
from worlds.AutoWorld import World, WebWorld
|
||||
from .Mod import generate_mod
|
||||
from .Options import factorio_options, MaxSciencePack, Silo, Satellite, TechTreeInformation, Goal, TechCostDistribution
|
||||
@@ -81,10 +81,10 @@ class Factorio(World):
|
||||
def create_regions(self):
|
||||
player = self.player
|
||||
random = self.multiworld.random
|
||||
menu = Region("Menu", RegionType.Generic, "Menu", player, self.multiworld)
|
||||
menu = Region("Menu", player, self.multiworld)
|
||||
crash = Entrance(player, "Crash Land", menu)
|
||||
menu.exits.append(crash)
|
||||
nauvis = Region("Nauvis", RegionType.Generic, "Nauvis", player, self.multiworld)
|
||||
nauvis = Region("Nauvis", player, self.multiworld)
|
||||
|
||||
location_count = len(base_tech_table) - len(useless_technologies) - self.skip_silo + \
|
||||
self.multiworld.evolution_traps[player].value + self.multiworld.attack_traps[player].value
|
||||
@@ -222,7 +222,7 @@ class Factorio(World):
|
||||
|
||||
map_basic_settings = self.multiworld.world_gen[player].value["basic"]
|
||||
if map_basic_settings.get("seed", None) is None: # allow seed 0
|
||||
map_basic_settings["seed"] = self.multiworld.slot_seeds[player].randint(0, 2 ** 32 - 1) # 32 bit uint
|
||||
map_basic_settings["seed"] = self.multiworld.per_slot_randoms[player].randint(0, 2 ** 32 - 1) # 32 bit uint
|
||||
|
||||
start_location_hints: typing.Set[str] = self.multiworld.start_location_hints[self.player].value
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ place them directly into the player's inventory.
|
||||
## What does another world's item look like in Factorio?
|
||||
|
||||
In Factorio, items which need to be sent to other worlds appear in the tech tree as new research items. They are
|
||||
represented by the Archipelago icon, and must be researched as if it were a normal technology. Upon successful
|
||||
represented by the Archipelago icon, and must be researched as if they were normal technologies. Upon successful
|
||||
completion of research, the item will be sent to its home world.
|
||||
|
||||
## When the engineer receives an item, what happens?
|
||||
@@ -32,3 +32,13 @@ completion of research, the item will be sent to its home world.
|
||||
When the player receives a technology, it is instantly learned and able to be crafted. A message will appear in the chat
|
||||
log to notify the player, and if free samples are enabled the player may also receive some items directly to their
|
||||
inventory.
|
||||
|
||||
## What is EnergyLink?
|
||||
|
||||
EnergyLink is an energy storage supported by certain games that is shared across all worlds in a multiworld.
|
||||
In Factorio, if enabled in the player settings, EnergyLink Bridge buildings can be crafted and placed, which allow
|
||||
depositing excess energy and supplementing energy deficits, much like Accumulators.
|
||||
|
||||
Each placed EnergyLink Bridge provides 1 MW of throughput. The shared storage has unlimited capacity, but 25% of energy
|
||||
is lost during depositing. The amount of energy currently in the shared storage is displayed in the Archipelago client.
|
||||
It can also be queried by typing `/energy-link` in-game.
|
||||
|
||||
@@ -2,7 +2,7 @@ import json
|
||||
from pathlib import Path
|
||||
from typing import Dict, NamedTuple, List, Optional
|
||||
|
||||
from BaseClasses import Region, RegionType, Location
|
||||
from BaseClasses import Region, Location, MultiWorld
|
||||
|
||||
EventId: Optional[int] = None
|
||||
CHAOS_TERMINATED_EVENT = 'Terminated Chaos'
|
||||
@@ -43,8 +43,8 @@ class FF1Locations:
|
||||
|
||||
@staticmethod
|
||||
def create_menu_region(player: int, locations: Dict[str, int],
|
||||
rules: Dict[str, List[List[str]]]) -> Region:
|
||||
menu_region = Region("Menu", RegionType.Generic, "Menu", player)
|
||||
rules: Dict[str, List[List[str]]], world: MultiWorld) -> Region:
|
||||
menu_region = Region("Menu", player, world)
|
||||
for name, address in locations.items():
|
||||
location = Location(player, name, address, menu_region)
|
||||
## TODO REMOVE WHEN LOGIC FOR TOFR IS CORRECT
|
||||
|
||||
+11
-10
@@ -45,14 +45,19 @@ class FF1World(World):
|
||||
self.locked_items = []
|
||||
self.locked_locations = []
|
||||
|
||||
def generate_early(self):
|
||||
return
|
||||
@classmethod
|
||||
def stage_assert_generate(cls, multiworld: MultiWorld) -> None:
|
||||
# Fail generation if there are no items in the pool
|
||||
for player in multiworld.get_game_players(cls.game):
|
||||
options = get_options(multiworld, 'items', player)
|
||||
assert options,\
|
||||
f"FFR settings submitted with no key items ({multiworld.get_player_name(player)}). Please ensure you " \
|
||||
f"generated the settings using finalfantasyrandomizer.com AND enabled the AP flag"
|
||||
|
||||
def create_regions(self):
|
||||
locations = get_options(self.multiworld, 'locations', self.player)
|
||||
rules = get_options(self.multiworld, 'rules', self.player)
|
||||
menu_region = self.ff1_locations.create_menu_region(self.player, locations, rules)
|
||||
menu_region.multiworld = self.multiworld
|
||||
menu_region = self.ff1_locations.create_menu_region(self.player, locations, rules, self.multiworld)
|
||||
terminated_event = Location(self.player, CHAOS_TERMINATED_EVENT, EventId, menu_region)
|
||||
terminated_item = Item(CHAOS_TERMINATED_EVENT, ItemClassification.progression, EventId, self.player)
|
||||
terminated_event.place_locked_item(terminated_item)
|
||||
@@ -66,8 +71,8 @@ class FF1World(World):
|
||||
terminated_event.access_rule = goal_rule_and_shards
|
||||
if "MARK" in items.keys():
|
||||
# Fail generation for Noverworld and provide link to old FFR website
|
||||
raise Exception("FFR Noverworld seeds must be generated on an older version of FFR. Please ensure you generated the settings using "
|
||||
"4-4-0.finalfantasyrandomizer.com")
|
||||
raise Exception("FFR Noverworld seeds must be generated on an older version of FFR. Please ensure you "
|
||||
"generated the settings using 4-4-0.finalfantasyrandomizer.com")
|
||||
menu_region.locations.append(terminated_event)
|
||||
self.multiworld.regions += [menu_region]
|
||||
|
||||
@@ -86,10 +91,6 @@ class FF1World(World):
|
||||
if possible_early_items:
|
||||
progression_item = self.multiworld.random.choice(possible_early_items)
|
||||
self._place_locked_item_in_sphere0(progression_item)
|
||||
else:
|
||||
# Fail generation if there are no items in the pool
|
||||
raise Exception("FFR settings submitted with no key items. Please ensure you generated the settings using "
|
||||
"finalfantasyrandomizer.com AND enabled the AP flag")
|
||||
|
||||
items = [self.create_item(name) for name, data in items.items() for x in range(data['count']) if name not in
|
||||
self.locked_items]
|
||||
|
||||
@@ -17,7 +17,7 @@ from .ExtractedData import locations, starts, multi_locations, location_to_regio
|
||||
event_names, item_effects, connectors, one_ways, vanilla_shop_costs, vanilla_location_costs
|
||||
from .Charms import names as charm_names
|
||||
|
||||
from BaseClasses import Region, Entrance, Location, MultiWorld, Item, RegionType, LocationProgressType, Tutorial, ItemClassification
|
||||
from BaseClasses import Region, Entrance, Location, MultiWorld, Item, LocationProgressType, Tutorial, ItemClassification
|
||||
from ..AutoWorld import World, LogicMixin, WebWorld
|
||||
|
||||
path_of_pain_locations = {
|
||||
@@ -446,7 +446,7 @@ class HKWorld(World):
|
||||
options[option_name] = optionvalue
|
||||
|
||||
# 32 bit int
|
||||
slot_data["seed"] = self.multiworld.slot_seeds[self.player].randint(-2147483647, 2147483646)
|
||||
slot_data["seed"] = self.multiworld.per_slot_randoms[self.player].randint(-2147483647, 2147483646)
|
||||
|
||||
# Backwards compatibility for shop cost data (HKAP < 0.1.0)
|
||||
if not self.multiworld.CostSanity[self.player]:
|
||||
@@ -585,17 +585,13 @@ class HKWorld(World):
|
||||
return self.multiworld.random.choice(self.cached_filler_items[self.player])
|
||||
|
||||
|
||||
def create_region(world: MultiWorld, player: int, name: str, location_names=None, exits=None) -> Region:
|
||||
ret = Region(name, RegionType.Generic, name, player)
|
||||
ret.multiworld = world
|
||||
def create_region(world: MultiWorld, player: int, name: str, location_names=None) -> Region:
|
||||
ret = Region(name, player, world)
|
||||
if location_names:
|
||||
for location in location_names:
|
||||
loc_id = HKWorld.location_name_to_id.get(location, None)
|
||||
location = HKLocation(player, location, loc_id, ret)
|
||||
ret.locations.append(location)
|
||||
if exits:
|
||||
for exit in exits:
|
||||
ret.exits.append(Entrance(player, exit, ret))
|
||||
return ret
|
||||
|
||||
|
||||
|
||||
+19
-19
@@ -1,6 +1,6 @@
|
||||
import random
|
||||
from typing import Dict, Any
|
||||
from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassification, RegionType
|
||||
from BaseClasses import Region, Entrance, Location, Item, Tutorial, ItemClassification
|
||||
from worlds.generic.Rules import set_rule
|
||||
from ..AutoWorld import World, WebWorld
|
||||
from . import Items, Locations, Options, Rules, Exits
|
||||
@@ -163,24 +163,24 @@ class Hylics2World(World):
|
||||
def create_regions(self) -> None:
|
||||
|
||||
region_table: Dict[int, Region] = {
|
||||
0: Region("Menu", RegionType.Generic, "Menu", self.player, self.multiworld),
|
||||
1: Region("Afterlife", RegionType.Generic, "Afterlife", self.player, self.multiworld),
|
||||
2: Region("Waynehouse", RegionType.Generic, "Waynehouse", self.player, self.multiworld),
|
||||
3: Region("World", RegionType.Generic, "World", self.player, self.multiworld),
|
||||
4: Region("New Muldul", RegionType.Generic, "New Muldul", self.player, self.multiworld),
|
||||
5: Region("New Muldul Vault", RegionType.Generic, "New Muldul Vault", self.player, self.multiworld),
|
||||
6: Region("Viewax", RegionType.Generic, "Viewax's Edifice", self.player, self.multiworld),
|
||||
7: Region("Airship", RegionType.Generic, "Airship", self.player, self.multiworld),
|
||||
8: Region("Arcade Island", RegionType.Generic, "Arcade Island", self.player, self.multiworld),
|
||||
9: Region("TV Island", RegionType.Generic, "TV Island", self.player, self.multiworld),
|
||||
10: Region("Juice Ranch", RegionType.Generic, "Juice Ranch", self.player, self.multiworld),
|
||||
11: Region("Shield Facility", RegionType.Generic, "Shield Facility", self.player, self.multiworld),
|
||||
12: Region("Worm Pod", RegionType.Generic, "Worm Pod", self.player, self.multiworld),
|
||||
13: Region("Foglast", RegionType.Generic, "Foglast", self.player, self.multiworld),
|
||||
14: Region("Drill Castle", RegionType.Generic, "Drill Castle", self.player, self.multiworld),
|
||||
15: Region("Sage Labyrinth", RegionType.Generic, "Sage Labyrinth", self.player, self.multiworld),
|
||||
16: Region("Sage Airship", RegionType.Generic, "Sage Airship", self.player, self.multiworld),
|
||||
17: Region("Hylemxylem", RegionType.Generic, "Hylemxylem", self.player, self.multiworld)
|
||||
0: Region("Menu", self.player, self.multiworld),
|
||||
1: Region("Afterlife", self.player, self.multiworld),
|
||||
2: Region("Waynehouse", self.player, self.multiworld),
|
||||
3: Region("World", self.player, self.multiworld),
|
||||
4: Region("New Muldul", self.player, self.multiworld),
|
||||
5: Region("New Muldul Vault", self.player, self.multiworld),
|
||||
6: Region("Viewax", self.player, self.multiworld, "Viewax's Edifice"),
|
||||
7: Region("Airship", self.player, self.multiworld),
|
||||
8: Region("Arcade Island", self.player, self.multiworld),
|
||||
9: Region("TV Island", self.player, self.multiworld),
|
||||
10: Region("Juice Ranch", self.player, self.multiworld),
|
||||
11: Region("Shield Facility", self.player, self.multiworld),
|
||||
12: Region("Worm Pod", self.player, self.multiworld),
|
||||
13: Region("Foglast", self.player, self.multiworld),
|
||||
14: Region("Drill Castle", self.player, self.multiworld),
|
||||
15: Region("Sage Labyrinth", self.player, self.multiworld),
|
||||
16: Region("Sage Airship", self.player, self.multiworld),
|
||||
17: Region("Hylemxylem", self.player, self.multiworld)
|
||||
}
|
||||
|
||||
# create regions from table
|
||||
|
||||
@@ -4,7 +4,7 @@ import os
|
||||
from enum import IntFlag
|
||||
from typing import Any, ClassVar, Dict, List, Optional, Set, Tuple
|
||||
|
||||
from BaseClasses import Entrance, Item, ItemClassification, MultiWorld, Region, RegionType, Tutorial
|
||||
from BaseClasses import Entrance, Item, ItemClassification, MultiWorld, Region, Tutorial
|
||||
from Main import __version__
|
||||
from Options import AssembleOptions
|
||||
from worlds.AutoWorld import WebWorld, World
|
||||
@@ -80,7 +80,7 @@ class L2ACWorld(World):
|
||||
shuffle_party_members: Optional[ShufflePartyMembers]
|
||||
|
||||
@classmethod
|
||||
def stage_assert_generate(cls, _multiworld: MultiWorld) -> None:
|
||||
def stage_assert_generate(cls, multiworld: MultiWorld) -> None:
|
||||
rom_file: str = get_base_rom_path()
|
||||
if not os.path.exists(rom_file):
|
||||
raise FileNotFoundError(f"Could not find base ROM for {cls.game}: {rom_file}")
|
||||
@@ -128,12 +128,12 @@ class L2ACWorld(World):
|
||||
self.default_party.value = DefaultParty.default
|
||||
|
||||
def create_regions(self) -> None:
|
||||
menu = Region("Menu", RegionType.Generic, "Menu", self.player, self.multiworld)
|
||||
menu = Region("Menu", self.player, self.multiworld)
|
||||
menu.exits.append(Entrance(self.player, "AncientDungeonEntrance", menu))
|
||||
self.multiworld.regions.append(menu)
|
||||
|
||||
ancient_dungeon = Region("AncientDungeon", RegionType.Generic, "Ancient Dungeon", self.player, self.multiworld)
|
||||
ancient_dungeon.exits.append(Entrance(self.player, "FinalFloorEntrance", menu))
|
||||
ancient_dungeon = Region("AncientDungeon", self.player, self.multiworld, "Ancient Dungeon")
|
||||
ancient_dungeon.exits.append(Entrance(self.player, "FinalFloorEntrance", ancient_dungeon))
|
||||
item_count: int = self.blue_chest_count
|
||||
if self.shuffle_capsule_monsters:
|
||||
item_count += len(self.item_name_groups["Capsule monsters"])
|
||||
@@ -152,7 +152,7 @@ class L2ACWorld(World):
|
||||
ancient_dungeon.locations.append(treasures)
|
||||
self.multiworld.regions.append(ancient_dungeon)
|
||||
|
||||
final_floor = Region("FinalFloor", RegionType.Generic, "Ancient Cave Final Floor", self.player, self.multiworld)
|
||||
final_floor = Region("FinalFloor", self.player, self.multiworld, "Ancient Cave Final Floor")
|
||||
ff_reached = L2ACLocation(self.player, "Final Floor reached", None, final_floor)
|
||||
ff_reached.place_locked_item(L2ACItem("Final Floor access", ItemClassification.progression, None, self.player))
|
||||
final_floor.locations.append(ff_reached)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# This software is released under the MIT License.
|
||||
# https://opensource.org/licenses/MIT
|
||||
|
||||
from BaseClasses import MultiWorld, Region, Entrance, RegionType
|
||||
from BaseClasses import MultiWorld, Region, Entrance
|
||||
from .Locations import MeritousLocation, location_table
|
||||
|
||||
meritous_regions = ["Meridian", "Ataraxia", "Merodach", "Endgame"]
|
||||
@@ -23,7 +23,7 @@ def create_regions(world: MultiWorld, player: int):
|
||||
if x == 0:
|
||||
insidename = "Menu"
|
||||
|
||||
region = Region(insidename, RegionType.Generic, fullname, player, world)
|
||||
region = Region(insidename, player, world)
|
||||
for store in ["Alpha Cache", "Beta Cache", "Gamma Cache", "Reward Chest"]:
|
||||
for y in range(1, 7):
|
||||
loc_name = f"{store} {(x * 6) + y}"
|
||||
@@ -45,7 +45,7 @@ def create_regions(world: MultiWorld, player: int):
|
||||
world.regions += [region]
|
||||
|
||||
for x, boss in enumerate(bosses):
|
||||
boss_region = Region(boss, RegionType.Generic, boss, player, world)
|
||||
boss_region = Region(boss, player, world)
|
||||
boss_region.locations += [
|
||||
MeritousLocation(player, boss, location_table[boss], boss_region),
|
||||
MeritousLocation(player, f"{boss} Defeat", None, boss_region)
|
||||
@@ -53,14 +53,12 @@ def create_regions(world: MultiWorld, player: int):
|
||||
boss_region.exits = _generate_entrances(player, [f"To {regions[x + 1]} Quarter"], boss_region)
|
||||
world.regions.append(boss_region)
|
||||
|
||||
region_final_boss = Region(
|
||||
"Final Boss", RegionType.Generic, "Final Boss", player, world)
|
||||
region_final_boss = Region("Final Boss", player, world)
|
||||
region_final_boss.locations = [MeritousLocation(
|
||||
player, "Wervyn Anixil", None, region_final_boss)]
|
||||
world.regions.append(region_final_boss)
|
||||
|
||||
region_tfb = Region("True Final Boss", RegionType.Generic,
|
||||
"True Final Boss", player, world)
|
||||
region_tfb = Region("True Final Boss", player, world)
|
||||
region_tfb.locations = [MeritousLocation(
|
||||
player, "Wervyn Anixil?", None, region_tfb)]
|
||||
world.regions.append(region_tfb)
|
||||
|
||||
@@ -70,7 +70,7 @@ class MinecraftWorld(World):
|
||||
def _get_mc_data(self):
|
||||
exits = [connection[0] for connection in default_connections]
|
||||
return {
|
||||
'world_seed': self.multiworld.slot_seeds[self.player].getrandbits(32),
|
||||
'world_seed': self.multiworld.per_slot_randoms[self.player].getrandbits(32),
|
||||
'seed_name': self.multiworld.seed_name,
|
||||
'player_name': self.multiworld.get_player_name(self.player),
|
||||
'player_id': self.player,
|
||||
@@ -137,7 +137,7 @@ class MinecraftWorld(World):
|
||||
|
||||
def create_regions(self):
|
||||
def MCRegion(region_name: str, exits=[]):
|
||||
ret = Region(region_name, None, region_name, self.player, self.multiworld)
|
||||
ret = Region(region_name, self.player, self.multiworld)
|
||||
ret.locations = [MinecraftAdvancement(self.player, loc_name, loc_data.id, ret)
|
||||
for loc_name, loc_data in advancement_table.items()
|
||||
if loc_data.region == region_name]
|
||||
|
||||
@@ -769,7 +769,7 @@ patch_sets[0x1F073FD9] = {
|
||||
|
||||
def patch_cosmetics(ootworld, rom):
|
||||
# Use the world's slot seed for cosmetics
|
||||
random.seed(ootworld.multiworld.slot_seeds[ootworld.player])
|
||||
random.seed(ootworld.multiworld.per_slot_randoms[ootworld.player])
|
||||
|
||||
# try to detect the cosmetic patch data format
|
||||
versioned_patch_set = None
|
||||
|
||||
+1
-1
@@ -460,7 +460,7 @@ def get_hint_area(spot):
|
||||
|
||||
if parent_region.dungeon:
|
||||
return parent_region.dungeon.hint_text
|
||||
elif parent_region.hint_text and (spot.parent_region.name == 'Root' or parent_region.name != 'Root'):
|
||||
elif parent_region.hint and (spot.parent_region.name == 'Root' or parent_region.name != 'Root'):
|
||||
return parent_region.hint_text
|
||||
|
||||
spot_queue.extend(list(filter(lambda ent: ent not in already_checked, parent_region.entrances)))
|
||||
|
||||
@@ -2346,7 +2346,7 @@ def patch_rom(world, rom):
|
||||
|
||||
# Write numeric seed truncated to 32 bits for rng seeding
|
||||
# Overwritten with new seed every time a new rng value is generated
|
||||
rng_seed = world.multiworld.slot_seeds[world.player].getrandbits(32)
|
||||
rng_seed = world.multiworld.per_slot_randoms[world.player].getrandbits(32)
|
||||
rom.write_int32(rom.sym('RNG_SEED_INT'), rng_seed)
|
||||
# Static initial seed value for one-time random actions like the Hylian Shield discount
|
||||
rom.write_int32(rom.sym('RANDOMIZER_RNG_SEED'), rng_seed)
|
||||
|
||||
+15
-11
@@ -1,6 +1,6 @@
|
||||
from enum import unique, Enum
|
||||
|
||||
from BaseClasses import Region
|
||||
from BaseClasses import Region, MultiWorld
|
||||
from .Hints import HintArea
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ class TimeOfDay(object):
|
||||
class OOTRegion(Region):
|
||||
game: str = "Ocarina of Time"
|
||||
|
||||
def __init__(self, name: str, type, hint, player: int):
|
||||
super(OOTRegion, self).__init__(name, type, hint, player)
|
||||
def __init__(self, name: str, player: int, multiworld: MultiWorld):
|
||||
super(OOTRegion, self).__init__(name, player, multiworld)
|
||||
self._oot_hint = None
|
||||
self.alt_hint = None
|
||||
self.price = None
|
||||
@@ -45,7 +45,17 @@ class OOTRegion(Region):
|
||||
self.font_color = None
|
||||
self.is_boss_room = False
|
||||
|
||||
def get_scene(self):
|
||||
# This is too generic of a name to risk not breaking in the future.
|
||||
# This lets us possibly switch it out later if AP starts using it.
|
||||
@property
|
||||
def hint(self):
|
||||
return self._oot_hint
|
||||
|
||||
@hint.setter
|
||||
def hint(self, value):
|
||||
self._oot_hint = value
|
||||
|
||||
def get_scene(self):
|
||||
if self.scene:
|
||||
return self.scene
|
||||
elif self.dungeon:
|
||||
@@ -70,10 +80,4 @@ class OOTRegion(Region):
|
||||
self._oot_hint = HintArea.for_dungeon(self.dungeon)
|
||||
else:
|
||||
self._oot_hint = HintArea[hint]
|
||||
self.hint_text = str(self._oot_hint)
|
||||
|
||||
# This is too generic of a name to risk not breaking in the future.
|
||||
# This lets us possibly switch it out later if AP starts using it.
|
||||
@property
|
||||
def hint(self):
|
||||
return self._oot_hint
|
||||
self._hint_text = str(self._oot_hint)
|
||||
|
||||
+16
-17
@@ -29,7 +29,7 @@ from .N64Patch import create_patch_file
|
||||
from .Cosmetics import patch_cosmetics
|
||||
|
||||
from Utils import get_options
|
||||
from BaseClasses import MultiWorld, CollectionState, RegionType, Tutorial, LocationProgressType
|
||||
from BaseClasses import MultiWorld, CollectionState, Tutorial, LocationProgressType
|
||||
from Options import Range, Toggle, VerifyKeys
|
||||
from Fill import fill_restrictive, fast_fill, FillError
|
||||
from worlds.generic.Rules import exclusion_rules, add_item_rule
|
||||
@@ -120,14 +120,14 @@ class OOTWorld(World):
|
||||
"Water Medallion", "Shadow Medallion", "Spirit Medallion",
|
||||
"Kokiri Emerald", "Goron Ruby", "Zora Sapphire"},
|
||||
"logic_bottles": {"Bottle", "Bottle with Milk", "Deliver Letter",
|
||||
"Sell Big Poe", "Bottle with Red Potion", "Bottle with Green Potion",
|
||||
"Bottle with Blue Potion", "Bottle with Fairy", "Bottle with Fish",
|
||||
"Sell Big Poe", "Bottle with Red Potion", "Bottle with Green Potion",
|
||||
"Bottle with Blue Potion", "Bottle with Fairy", "Bottle with Fish",
|
||||
"Bottle with Blue Fire", "Bottle with Bugs", "Bottle with Poe"},
|
||||
|
||||
# hint groups
|
||||
"Bottles": {"Bottle", "Bottle with Milk", "Rutos Letter",
|
||||
"Bottle with Big Poe", "Bottle with Red Potion", "Bottle with Green Potion",
|
||||
"Bottle with Blue Potion", "Bottle with Fairy", "Bottle with Fish",
|
||||
"Bottle with Big Poe", "Bottle with Red Potion", "Bottle with Green Potion",
|
||||
"Bottle with Blue Potion", "Bottle with Fairy", "Bottle with Fish",
|
||||
"Bottle with Blue Fire", "Bottle with Bugs", "Bottle with Poe"},
|
||||
"Adult Trade Item": {"Pocket Egg", "Pocket Cucco", "Odd Mushroom",
|
||||
"Odd Potion", "Poachers Saw", "Broken Sword", "Prescription",
|
||||
@@ -140,7 +140,7 @@ class OOTWorld(World):
|
||||
super(OOTWorld, self).__init__(world, player)
|
||||
|
||||
@classmethod
|
||||
def stage_assert_generate(cls, world: MultiWorld):
|
||||
def stage_assert_generate(cls, multiworld: MultiWorld):
|
||||
rom = Rom(file=get_options()['oot_options']['rom_file'])
|
||||
|
||||
def generate_early(self):
|
||||
@@ -364,7 +364,7 @@ class OOTWorld(World):
|
||||
elif self.dungeon_shortcuts_choice == 'all':
|
||||
self.dungeon_shortcuts = set(shortcut_dungeons)
|
||||
elif self.dungeon_shortcuts_choice == 'random':
|
||||
self.dungeon_shortcuts = self.multiworld.random.sample(shortcut_dungeons,
|
||||
self.dungeon_shortcuts = self.multiworld.random.sample(shortcut_dungeons,
|
||||
self.multiworld.random.randint(0, len(shortcut_dungeons)))
|
||||
# == 'choice', leave as previous
|
||||
else:
|
||||
@@ -453,8 +453,7 @@ class OOTWorld(World):
|
||||
region_json = read_json(file_path)
|
||||
|
||||
for region in region_json:
|
||||
new_region = OOTRegion(region['region_name'], RegionType.Generic, None, self.player)
|
||||
new_region.multiworld = self.multiworld
|
||||
new_region = OOTRegion(region['region_name'], self.player, self.multiworld)
|
||||
if 'pretty_name' in region:
|
||||
new_region.pretty_name = region['pretty_name']
|
||||
if 'font_color' in region:
|
||||
@@ -624,7 +623,7 @@ class OOTWorld(World):
|
||||
world_type = 'Glitched World'
|
||||
overworld_data_path = data_path(world_type, 'Overworld.json')
|
||||
bosses_data_path = data_path(world_type, 'Bosses.json')
|
||||
menu = OOTRegion('Menu', None, None, self.player)
|
||||
menu = OOTRegion('Menu', self.player, self.multiworld)
|
||||
start = OOTEntrance(self.player, self.multiworld, 'New Game', menu)
|
||||
menu.exits.append(start)
|
||||
self.multiworld.regions.append(menu)
|
||||
@@ -840,9 +839,9 @@ class OOTWorld(World):
|
||||
# Place shop items
|
||||
# fast fill will fail because there is some logic on the shop items. we'll gather them up and place the shop items
|
||||
if self.shopsanity != 'off':
|
||||
shop_prog = list(filter(lambda item: item.player == self.player and item.type == 'Shop'
|
||||
shop_prog = list(filter(lambda item: item.player == self.player and item.type == 'Shop'
|
||||
and item.advancement, self.multiworld.itempool))
|
||||
shop_junk = list(filter(lambda item: item.player == self.player and item.type == 'Shop'
|
||||
shop_junk = list(filter(lambda item: item.player == self.player and item.type == 'Shop'
|
||||
and not item.advancement, self.multiworld.itempool))
|
||||
shop_locations = list(
|
||||
filter(lambda location: location.type == 'Shop' and location.name not in self.shop_prices,
|
||||
@@ -962,10 +961,10 @@ class OOTWorld(World):
|
||||
trap_location_ids = [loc.address for loc in self.get_locations() if loc.item.trap]
|
||||
self.trap_appearances = {}
|
||||
for loc_id in trap_location_ids:
|
||||
self.trap_appearances[loc_id] = self.create_item(self.multiworld.slot_seeds[self.player].choice(self.fake_items).name)
|
||||
self.trap_appearances[loc_id] = self.create_item(self.multiworld.per_slot_randoms[self.player].choice(self.fake_items).name)
|
||||
|
||||
# Seed hint RNG, used for ganon text lines also
|
||||
self.hint_rng = self.multiworld.slot_seeds[self.player]
|
||||
self.hint_rng = self.multiworld.per_slot_randoms[self.player]
|
||||
|
||||
outfile_name = self.multiworld.get_out_file_name_base(self.player)
|
||||
rom = Rom(file=get_options()['oot_options']['rom_file'])
|
||||
@@ -1029,7 +1028,7 @@ class OOTWorld(World):
|
||||
for player in barren_hint_players:
|
||||
items_by_region[player] = {}
|
||||
for r in multiworld.worlds[player].regions:
|
||||
items_by_region[player][r.hint_text] = {'dungeon': False, 'weight': 0, 'is_barren': True}
|
||||
items_by_region[player][r._hint_text] = {'dungeon': False, 'weight': 0, 'is_barren': True}
|
||||
for d in multiworld.worlds[player].dungeons:
|
||||
items_by_region[player][d.hint_text] = {'dungeon': True, 'weight': 0, 'is_barren': True}
|
||||
del (items_by_region[player]["Link's pocket"])
|
||||
@@ -1118,7 +1117,7 @@ class OOTWorld(World):
|
||||
# If it's in a dungeon, scan all the entrances for all the regions in the dungeon.
|
||||
# This should terminate on the first region anyway, but we scan everything to be safe.
|
||||
# If it's one of the special cases, go one level deeper.
|
||||
# If it's a boss room, go one level deeper to the boss door region, which is in a dungeon.
|
||||
# If it's a boss room, go one level deeper to the boss door region, which is in a dungeon.
|
||||
# Otherwise return None.
|
||||
def get_entrance_to_region(region):
|
||||
special_case_regions = {
|
||||
@@ -1150,7 +1149,7 @@ class OOTWorld(World):
|
||||
er_hint_data[self.player][location.address] = main_entrance.name
|
||||
logger.debug(f"Set {location.name} hint data to {main_entrance.name}")
|
||||
|
||||
# Key ring handling:
|
||||
# Key ring handling:
|
||||
# Key rings are multiple items glued together into one, so we need to give
|
||||
# the appropriate number of keys in the collection state when they are
|
||||
# picked up.
|
||||
|
||||
@@ -6,7 +6,7 @@ from .Locations import lookup_name_to_id
|
||||
from .Rules import set_rules, location_rules
|
||||
from .Regions import locations_by_region, connectors
|
||||
from .Options import options
|
||||
from BaseClasses import Region, Item, Location, RegionType, Entrance, ItemClassification
|
||||
from BaseClasses import Region, Item, Location, Entrance, ItemClassification
|
||||
|
||||
|
||||
class OriBlindForest(World):
|
||||
@@ -31,7 +31,7 @@ class OriBlindForest(World):
|
||||
set_rules = set_rules
|
||||
|
||||
def create_region(self, name: str):
|
||||
return Region(name, RegionType.Generic, name, self.player, self.multiworld)
|
||||
return Region(name, self.player, self.multiworld)
|
||||
|
||||
def create_regions(self):
|
||||
world = self.multiworld
|
||||
|
||||
+51
-19
@@ -50,6 +50,14 @@ item_table: Dict[str, ItemData] = {
|
||||
"Ramp Button" : ItemData(oc2_base_id + 35),
|
||||
"Bonus Star" : ItemData(oc2_base_id + 36),
|
||||
"Calmer Unbread" : ItemData(oc2_base_id + 37),
|
||||
"Green Ramp" : ItemData(oc2_base_id + 38),
|
||||
"Yellow Ramp" : ItemData(oc2_base_id + 39),
|
||||
"Blue Ramp" : ItemData(oc2_base_id + 40),
|
||||
"Pink Ramp" : ItemData(oc2_base_id + 41),
|
||||
"Dark Green Ramp" : ItemData(oc2_base_id + 42),
|
||||
"Red Ramp" : ItemData(oc2_base_id + 43),
|
||||
"Purple Ramp" : ItemData(oc2_base_id + 44),
|
||||
"Emote Wheel" : ItemData(oc2_base_id + 45),
|
||||
}
|
||||
|
||||
item_frequencies = {
|
||||
@@ -58,28 +66,43 @@ item_frequencies = {
|
||||
"Order Lookahead": 2,
|
||||
"Progressive Dash": 2,
|
||||
"Bonus Star": 0, # Filler Item
|
||||
# default: 1
|
||||
|
||||
# Unused items
|
||||
"Ramp Button": 0,
|
||||
"Cooking Emote" : 0,
|
||||
"Curse Emote" : 0,
|
||||
"Serving Emote" : 0,
|
||||
"Preparing Emote" : 0,
|
||||
"Washing Up Emote": 0,
|
||||
"Ok Emote": 0,
|
||||
}
|
||||
|
||||
item_name_to_config_name = {
|
||||
"Wood" : "DisableWood" ,
|
||||
"Coal Bucket" : "DisableCoal" ,
|
||||
"Spare Plate" : "DisableOnePlate" ,
|
||||
"Fire Extinguisher" : "DisableFireExtinguisher" ,
|
||||
"Bellows" : "DisableBellows" ,
|
||||
"Clean Dishes" : "PlatesStartDirty" ,
|
||||
"Control Stick Batteries" : "DisableControlStick" ,
|
||||
"Wok Wheels" : "DisableWokDrag" ,
|
||||
"Dish Scrubber" : "WashTimeMultiplier" ,
|
||||
"Burn Leniency" : "BurnSpeedMultiplier" ,
|
||||
"Sharp Knife" : "ChoppingTimeScale" ,
|
||||
"Lightweight Backpack" : "BackpackMovementScale" ,
|
||||
"Faster Respawn Time" : "RespawnTime" ,
|
||||
"Faster Condiment/Drink Switch": "CarnivalDispenserRefactoryTime",
|
||||
"Guest Patience" : "CustomOrderLifetime" ,
|
||||
"Ramp Button" : "DisableRampButton" ,
|
||||
"Calmer Unbread" : "AggressiveHorde" ,
|
||||
"Coin Purse" : "DisableEarnHordeMoney" ,
|
||||
"Wood" : "DisableWood" ,
|
||||
"Coal Bucket" : "DisableCoal" ,
|
||||
"Spare Plate" : "DisableOnePlate" ,
|
||||
"Fire Extinguisher" : "DisableFireExtinguisher" ,
|
||||
"Bellows" : "DisableBellows" ,
|
||||
"Clean Dishes" : "PlatesStartDirty" ,
|
||||
"Control Stick Batteries" : "DisableControlStick" ,
|
||||
"Wok Wheels" : "DisableWokDrag" ,
|
||||
"Dish Scrubber" : "WashTimeMultiplier" ,
|
||||
"Burn Leniency" : "BurnSpeedMultiplier" ,
|
||||
"Sharp Knife" : "ChoppingTimeScale" ,
|
||||
"Lightweight Backpack" : "BackpackMovementScale" ,
|
||||
"Faster Respawn Time" : "RespawnTime" ,
|
||||
"Faster Condiment/Drink Switch" : "CarnivalDispenserRefactoryTime" ,
|
||||
"Guest Patience" : "CustomOrderLifetime" ,
|
||||
"Ramp Button" : "DisableRampButton" ,
|
||||
"Green Ramp" : "DisableGreenRampButton" ,
|
||||
"Yellow Ramp" : "DisableYellowRampButton" ,
|
||||
"Blue Ramp" : "DisableBlueRampButton" ,
|
||||
"Pink Ramp" : "DisablePinkRampButton" ,
|
||||
"Dark Green Ramp" : "DisableGreyRampButton" ,
|
||||
"Red Ramp" : "DisableRedRampButton" ,
|
||||
"Purple Ramp" : "DisablePurpleRampButton" ,
|
||||
"Calmer Unbread" : "AggressiveHorde" ,
|
||||
"Coin Purse" : "DisableEarnHordeMoney" ,
|
||||
}
|
||||
|
||||
vanilla_values = {
|
||||
@@ -101,6 +124,13 @@ vanilla_values = {
|
||||
"CustomOrderLifetime": 100.0,
|
||||
"AggressiveHorde": False,
|
||||
"DisableEarnHordeMoney": False,
|
||||
"DisableGreenRampButton" : False,
|
||||
"DisableYellowRampButton" : False,
|
||||
"DisableBlueRampButton" : False,
|
||||
"DisablePinkRampButton" : False,
|
||||
"DisableGreyRampButton" : False,
|
||||
"DisableRedRampButton" : False,
|
||||
"DisablePurpleRampButton" : False,
|
||||
}
|
||||
|
||||
item_id_to_name: Dict[int, str] = {
|
||||
@@ -124,6 +154,8 @@ def item_to_unlock_event(item_name: str) -> Dict[str, str]:
|
||||
kevin_num = int(item_name.split("-")[-1])
|
||||
action = "UNLOCK_LEVEL"
|
||||
payload = str(kevin_num + 36)
|
||||
elif item_name == "Emote Wheel":
|
||||
action = "UNLOCK_EMOTES"
|
||||
elif "Emote" in item_name:
|
||||
action = "UNLOCK_EMOTE"
|
||||
payload = str(item_table[item_name].code - item_table["Cooking Emote"].code)
|
||||
|
||||
+25
-20
@@ -8,13 +8,16 @@ def has_requirements_for_level_access(state: CollectionState, level_name: str, p
|
||||
required_star_count: int, player: int) -> bool:
|
||||
# Check if the ramps in the overworld are set correctly
|
||||
if level_name in ramp_logic:
|
||||
if not state.has("Ramp Button", player):
|
||||
return False # need the item to use ramps
|
||||
(ramp_reqs, level_reqs) = ramp_logic[level_name]
|
||||
|
||||
for req in ramp_logic[level_name]:
|
||||
for req in level_reqs:
|
||||
if not state.has(req + " Level Complete", player):
|
||||
return False # This level needs another to be beaten first
|
||||
|
||||
for req in ramp_reqs:
|
||||
if not state.has(req + " Ramp", player):
|
||||
return False # The player doesn't have the pre-requisite ramp button
|
||||
|
||||
# Kevin Levels Need to have the corresponding items
|
||||
if level_name.startswith("K"):
|
||||
return state.has(level_name, player)
|
||||
@@ -78,7 +81,7 @@ def is_item_progression(item_name, level_mapping, include_kevin):
|
||||
if item_name.endswith("Emote"):
|
||||
return False
|
||||
|
||||
if "Kevin" in item_name or item_name in ["Ramp Button"]:
|
||||
if "Kevin" in item_name or "Ramp" in item_name:
|
||||
return True # always progression
|
||||
|
||||
def item_in_logic(shortname, _item_name):
|
||||
@@ -216,23 +219,23 @@ def is_completable_no_items(level: Overcooked2GenericLevel) -> bool:
|
||||
#
|
||||
# If empty, a ramp is required to access, but the ramp button is garunteed accessible
|
||||
#
|
||||
# If populated, a ramp is required to access and the button requires all levels in the
|
||||
# If populated, ramp(s) are required to access and the button requires all levels in the
|
||||
# list to be compelted before it can be pressed
|
||||
#
|
||||
ramp_logic = {
|
||||
"1-5": [],
|
||||
"2-2": [],
|
||||
"3-1": [],
|
||||
"5-2": [],
|
||||
"6-1": [],
|
||||
"6-2": ["5-1"], # 5-1 spawns blue button, blue button gets you to red button
|
||||
"Kevin-1": [],
|
||||
"Kevin-7": ["5-1"], # 5-1 spawns blue button,
|
||||
# press blue button,
|
||||
# climb blue ramp,
|
||||
# jump the gap,
|
||||
# climb wood ramps
|
||||
"Kevin-8": ["5-1", "6-2"], # Same as above, but 6-2 spawns the ramp to K8
|
||||
"1-5": (["Yellow"], []),
|
||||
"2-2": (["Green"], []),
|
||||
"3-1": (["Blue"], []),
|
||||
"5-2": (["Purple"], []),
|
||||
"6-1": (["Pink"], []),
|
||||
"6-2": (["Red", "Purple"], ["5-1"]), # 5-1 spawns blue button, blue button gets you to red button
|
||||
"Kevin-1": (["Dark Green"], []),
|
||||
"Kevin-7": (["Purple"], ["5-1"]), # 5-1 spawns blue button,
|
||||
# press blue button,
|
||||
# climb blue ramp,
|
||||
# jump the gap,
|
||||
# climb wood ramps
|
||||
"Kevin-8": (["Red", "Blue"], ["5-1", "6-2"]), # Same as above, but 6-2 spawns the ramp to K8
|
||||
}
|
||||
|
||||
horde_logic = { # Additive
|
||||
@@ -3055,7 +3058,8 @@ level_logic = {
|
||||
"Horde H-8": (
|
||||
( # 1-star
|
||||
{ # Exclusive
|
||||
|
||||
"Coin Purse",
|
||||
"Calmer Unbread"
|
||||
},
|
||||
horde_logic,
|
||||
),
|
||||
@@ -3467,7 +3471,8 @@ level_logic = {
|
||||
"Winter H-4": (
|
||||
( # 1-star
|
||||
{ # Exclusive
|
||||
|
||||
"Coin Purse",
|
||||
"Calmer Unbread"
|
||||
},
|
||||
horde_logic,
|
||||
),
|
||||
|
||||
@@ -1,13 +1,36 @@
|
||||
from enum import Enum
|
||||
from typing import TypedDict
|
||||
from Options import DefaultOnToggle, Range, Choice
|
||||
|
||||
|
||||
class LocationBalancingMode(Enum):
|
||||
disabled = 0
|
||||
compromise = 1
|
||||
full = 2
|
||||
|
||||
|
||||
class OC2OnToggle(DefaultOnToggle):
|
||||
@property
|
||||
def result(self) -> bool:
|
||||
return bool(self.value)
|
||||
|
||||
|
||||
class LocationBalancing(Choice):
|
||||
"""Location balancing affects the density of progression items found in your world relative to other wordlds. This setting changes nothing for solo games.
|
||||
|
||||
- Disabled: Location density in your world can fluctuate greatly depending on the settings of other players. In extreme cases, your world may be entirely populated with filler items
|
||||
|
||||
- Compromise: Locations are balanced to a midpoint between "fair" and "natural"
|
||||
|
||||
- Full: Locations are balanced in an attempt to make the number of progression items sent out and received equal over the entire game"""
|
||||
auto_display_name = True
|
||||
display_name = "Location Balancing"
|
||||
option_disabled = LocationBalancingMode.disabled.value
|
||||
option_compromise = LocationBalancingMode.compromise.value
|
||||
option_full = LocationBalancingMode.full.value
|
||||
default = LocationBalancingMode.compromise.value
|
||||
|
||||
|
||||
class AlwaysServeOldestOrder(OC2OnToggle):
|
||||
"""Modifies the game so that serving an expired order doesn't target the ticket with the highest tip. This helps
|
||||
players dig out of a broken tip combo faster."""
|
||||
@@ -105,6 +128,9 @@ class StarThresholdScale(Range):
|
||||
|
||||
|
||||
overcooked_options = {
|
||||
# generator options
|
||||
"location_balancing": LocationBalancing,
|
||||
|
||||
# randomization options
|
||||
"shuffle_level_order": ShuffleLevelOrder,
|
||||
"include_horde_levels": IncludeHordeLevels,
|
||||
|
||||
+119
-23
@@ -1,12 +1,12 @@
|
||||
from enum import Enum
|
||||
from typing import Callable, Dict, Any, List, Optional
|
||||
|
||||
from BaseClasses import ItemClassification, CollectionState, Region, Entrance, Location, RegionType, Tutorial
|
||||
from BaseClasses import ItemClassification, CollectionState, Region, Entrance, Location, Tutorial, LocationProgressType
|
||||
from worlds.AutoWorld import World, WebWorld
|
||||
|
||||
from .Overcooked2Levels import Overcooked2Level, Overcooked2GenericLevel, ITEMS_TO_EXCLUDE_IF_NO_DLC
|
||||
from .Locations import Overcooked2Location, oc2_location_name_to_id, oc2_location_id_to_name
|
||||
from .Options import overcooked_options, OC2Options, OC2OnToggle
|
||||
from .Options import overcooked_options, OC2Options, OC2OnToggle, LocationBalancingMode
|
||||
from .Items import item_table, Overcooked2Item, item_name_to_id, item_id_to_name, item_to_unlock_event, item_frequencies
|
||||
from .Logic import has_requirements_for_level_star, has_requirements_for_level_access, level_shuffle_factory, is_item_progression, is_useful
|
||||
|
||||
@@ -46,10 +46,10 @@ class Overcooked2World(World):
|
||||
|
||||
game = "Overcooked! 2"
|
||||
web = Overcooked2Web()
|
||||
required_client_version = (0, 3, 7)
|
||||
required_client_version = (0, 3, 8)
|
||||
option_definitions = overcooked_options
|
||||
topology_present: bool = False
|
||||
data_version = 2
|
||||
data_version = 3
|
||||
|
||||
item_name_to_id = item_name_to_id
|
||||
item_id_to_name = item_id_to_name
|
||||
@@ -60,7 +60,6 @@ class Overcooked2World(World):
|
||||
options: Dict[str, Any]
|
||||
itempool: List[Overcooked2Item]
|
||||
|
||||
|
||||
# Helper Functions
|
||||
|
||||
def is_level_horde(self, level_id: int) -> bool:
|
||||
@@ -82,8 +81,6 @@ class Overcooked2World(World):
|
||||
|
||||
def add_region(self, region_name: str):
|
||||
region = Region(
|
||||
region_name,
|
||||
RegionType.Generic,
|
||||
region_name,
|
||||
self.player,
|
||||
self.multiworld,
|
||||
@@ -108,6 +105,7 @@ class Overcooked2World(World):
|
||||
level_id: int,
|
||||
stars: int,
|
||||
is_event: bool = False,
|
||||
priority=False,
|
||||
) -> None:
|
||||
|
||||
if is_event:
|
||||
@@ -125,6 +123,11 @@ class Overcooked2World(World):
|
||||
|
||||
location.event = is_event
|
||||
|
||||
if priority:
|
||||
location.progress_type = LocationProgressType.PRIORITY
|
||||
else:
|
||||
location.progress_type = LocationProgressType.DEFAULT
|
||||
|
||||
# if level_id is none, then it's the 6-6 edge case
|
||||
if level_id is None:
|
||||
level_id = 36
|
||||
@@ -147,6 +150,76 @@ class Overcooked2World(World):
|
||||
if issubclass(option, OC2OnToggle) else getattr(self.multiworld, name)[self.player].value
|
||||
for name, option in overcooked_options.items()})
|
||||
|
||||
def get_n_random_locations(self, n: int) -> List[int]:
|
||||
"""Return a list of n random non-repeating level locations"""
|
||||
levels = list()
|
||||
|
||||
if n == 0:
|
||||
return levels
|
||||
|
||||
for level in Overcooked2Level():
|
||||
if level.level_id == 36:
|
||||
continue
|
||||
elif not self.options["KevinLevels"] and level.level_id > 36:
|
||||
break
|
||||
|
||||
levels.append(level.level_id)
|
||||
|
||||
self.multiworld.random.shuffle(levels)
|
||||
return levels[:n]
|
||||
|
||||
def get_priority_locations(self) -> List[int]:
|
||||
"""Randomly generate list of priority locations, thus insulating this game
|
||||
from the negative effects of being shuffled with games that contain large
|
||||
ammounts of filler"""
|
||||
|
||||
if self.multiworld.players == 1:
|
||||
# random priority locations have no desirable effect on solo seeds
|
||||
return list()
|
||||
|
||||
balancing_mode = self.get_options()["LocationBalancing"]
|
||||
|
||||
if balancing_mode == LocationBalancingMode.disabled.value:
|
||||
# Location balancing is disabled, progression density is purely determined by filler
|
||||
return list()
|
||||
|
||||
# Count how many progression items are required for this overcooked instance
|
||||
game_item_count = len(self.itempool)
|
||||
game_progression_count = 0
|
||||
for item in self.itempool:
|
||||
if item.classification == ItemClassification.progression:
|
||||
game_progression_count += 1
|
||||
game_progression_density = game_progression_count/game_item_count
|
||||
|
||||
if balancing_mode == LocationBalancingMode.full.value:
|
||||
# Location balancing will be employed in an attempt to keep the number of
|
||||
# progression locations and proression items as close to equal as possible
|
||||
return self.get_n_random_locations(game_progression_count)
|
||||
|
||||
assert balancing_mode == LocationBalancingMode.compromise.value
|
||||
|
||||
# Count how many progression items are shuffled between all games
|
||||
total_item_count = len(self.multiworld.itempool)
|
||||
total_progression_count = 0
|
||||
|
||||
for item in self.multiworld.itempool:
|
||||
if item.classification == ItemClassification.progression:
|
||||
total_progression_count += 1
|
||||
total_progression_density = total_progression_count/total_item_count
|
||||
|
||||
if total_progression_density >= game_progression_density:
|
||||
# This game has more filler than the average of all other games.
|
||||
# It is not in need of location balancing
|
||||
return list()
|
||||
|
||||
# Calculate the midpoint between the two ratios
|
||||
target_progression_ratio = (game_progression_density - total_progression_density) / 2.0 + total_progression_density
|
||||
target_progression_count = int((target_progression_ratio * game_item_count) + 0.5) # I'm sorry I round like an old person
|
||||
|
||||
# Location balancing will be employed in an attempt to find a compromise at
|
||||
# the half-way point between natural probability and full artifical balancing
|
||||
return self.get_n_random_locations(target_progression_count)
|
||||
|
||||
# Helper Data
|
||||
|
||||
level_unlock_counts: Dict[int, int] # level_id, stars to purchase
|
||||
@@ -175,12 +248,20 @@ class Overcooked2World(World):
|
||||
else:
|
||||
self.level_mapping = None
|
||||
|
||||
def set_location_priority(self) -> None:
|
||||
for level in Overcooked2Level():
|
||||
if level.level_id in self.get_priority_locations():
|
||||
location: Location = self.multiworld.get_location(level.location_name_item, self.player)
|
||||
location.progress_type = LocationProgressType.PRIORITY
|
||||
|
||||
|
||||
def create_regions(self) -> None:
|
||||
# Menu -> Overworld
|
||||
self.add_region("Menu")
|
||||
self.add_region("Overworld")
|
||||
self.connect_regions("Menu", "Overworld")
|
||||
|
||||
# Create and populate "regions" (a.k.a. levels)
|
||||
for level in Overcooked2Level():
|
||||
if not self.options["KevinLevels"] and level.level_id > 36:
|
||||
break
|
||||
@@ -251,6 +332,7 @@ class Overcooked2World(World):
|
||||
state.has("Victory", self.player)
|
||||
self.multiworld.completion_condition[self.player] = completion_condition
|
||||
|
||||
|
||||
def create_items(self):
|
||||
self.itempool = []
|
||||
|
||||
@@ -258,7 +340,16 @@ class Overcooked2World(World):
|
||||
# useful = list()
|
||||
# filler = list()
|
||||
# progression = list()
|
||||
for item_name in item_table:
|
||||
for item_name in item_table:
|
||||
if item_name in item_frequencies:
|
||||
freq = item_frequencies[item_name]
|
||||
else:
|
||||
freq = 1
|
||||
|
||||
if freq <= 0:
|
||||
# not used
|
||||
continue
|
||||
|
||||
if not self.options["ShuffleLevelOrder"] and item_name in ITEMS_TO_EXCLUDE_IF_NO_DLC:
|
||||
# skip DLC items if no DLC
|
||||
continue
|
||||
@@ -266,17 +357,15 @@ class Overcooked2World(World):
|
||||
if not self.options["IncludeHordeLevels"] and item_name in ["Calmer Unbread", "Coin Purse"]:
|
||||
# skip horde-specific items if no horde levels
|
||||
continue
|
||||
|
||||
|
||||
if not self.options["KevinLevels"] and item_name.startswith("Kevin"):
|
||||
# skip kevin items if no kevin levels
|
||||
continue
|
||||
|
||||
if is_item_progression(item_name, self.level_mapping, self.options["KevinLevels"]):
|
||||
# print(f"{item_name} is progression")
|
||||
# progression.append(item_name)
|
||||
classification = ItemClassification.progression
|
||||
else:
|
||||
# print(f"{item_name} is filler")
|
||||
if (is_useful(item_name)):
|
||||
# useful.append(item_name)
|
||||
classification = ItemClassification.useful
|
||||
@@ -284,15 +373,10 @@ class Overcooked2World(World):
|
||||
# filler.append(item_name)
|
||||
classification = ItemClassification.filler
|
||||
|
||||
if item_name in item_frequencies:
|
||||
freq = item_frequencies[item_name]
|
||||
|
||||
while freq > 0:
|
||||
self.itempool.append(self.create_item(item_name, classification))
|
||||
classification = ItemClassification.useful # only the first progressive item can be progression
|
||||
freq -= 1
|
||||
else:
|
||||
while freq > 0:
|
||||
self.itempool.append(self.create_item(item_name, classification))
|
||||
classification = ItemClassification.useful # only the first progressive item can be progression
|
||||
freq -= 1
|
||||
|
||||
# print(f"progression: {progression}")
|
||||
# print(f"useful: {useful}")
|
||||
@@ -308,10 +392,8 @@ class Overcooked2World(World):
|
||||
|
||||
self.multiworld.itempool += self.itempool
|
||||
|
||||
def set_rules(self):
|
||||
pass
|
||||
|
||||
def generate_basic(self) -> None:
|
||||
def place_events(self):
|
||||
# Add Events (Star Acquisition)
|
||||
for level in Overcooked2Level():
|
||||
if not self.options["KevinLevels"] and level.level_id > 36:
|
||||
@@ -332,6 +414,13 @@ class Overcooked2World(World):
|
||||
# Add Victory Condition
|
||||
self.place_event("6-6 Completed", "Victory")
|
||||
|
||||
def set_rules(self):
|
||||
pass
|
||||
|
||||
def generate_basic(self) -> None:
|
||||
self.place_events()
|
||||
self.set_location_priority()
|
||||
|
||||
# Items get distributed to locations
|
||||
|
||||
def fill_json_data(self) -> Dict[str, Any]:
|
||||
@@ -441,7 +530,14 @@ class Overcooked2World(World):
|
||||
"DisableCatch": True,
|
||||
"DisableControlStick": True,
|
||||
"DisableWokDrag": True,
|
||||
"DisableRampButton": True,
|
||||
# "DisableRampButton": True, # Unused
|
||||
"DisableGreenRampButton" : True,
|
||||
"DisableYellowRampButton" : True,
|
||||
"DisableBlueRampButton" : True,
|
||||
"DisablePinkRampButton" : True,
|
||||
"DisableGreyRampButton" : True,
|
||||
"DisableRedRampButton" : True,
|
||||
"DisablePurpleRampButton" : True,
|
||||
"WashTimeMultiplier": 1.4,
|
||||
"BurnSpeedMultiplier": 1.43,
|
||||
"MaxOrdersOnScreenOffset": -2,
|
||||
|
||||
@@ -52,7 +52,7 @@ The following items were invented for Randomizer:
|
||||
|
||||
### Overworld
|
||||
- Unlock Kevin Level (x8)
|
||||
- Ramp Button
|
||||
- Ramp Buttons (x7)
|
||||
- Bonus Star (Filler Item*)
|
||||
|
||||
**Note: Bonus star count varies with settings*
|
||||
|
||||
@@ -17,7 +17,7 @@ class Overcooked2Test(unittest.TestCase):
|
||||
for item_name in item_table.keys():
|
||||
item: Item = item_table[item_name]
|
||||
self.assertGreaterEqual(item.code, oc2_base_id, "Overcooked Item ID out of range")
|
||||
self.assertLessEqual(item.code, item_table["Calmer Unbread"].code, "Overcooked Item ID out of range")
|
||||
self.assertLessEqual(item.code, item_table["Emote Wheel"].code, "Overcooked Item ID out of range")
|
||||
|
||||
if previous_item is not None:
|
||||
self.assertEqual(item.code, previous_item + 1,
|
||||
@@ -31,7 +31,7 @@ class Overcooked2Test(unittest.TestCase):
|
||||
self.assertIn(item_name, item_table.keys(), "Unexpected Overcooked Item in item_frequencies")
|
||||
|
||||
for item_name in item_name_to_config_name.keys():
|
||||
self.assertIn(item_name, item_table.keys(), "Unexpected Overcooked Item in config mapping")
|
||||
self.assertIn(item_name, item_table.keys(), "Unexpected config in item-config mapping")
|
||||
|
||||
for config_name in item_name_to_config_name.values():
|
||||
self.assertIn(config_name, vanilla_values.keys(), "Unexpected Overcooked Item in default config mapping")
|
||||
|
||||
@@ -65,11 +65,11 @@ class PokemonRedBlueWorld(World):
|
||||
self.traps = None
|
||||
|
||||
@classmethod
|
||||
def stage_assert_generate(cls, world):
|
||||
def stage_assert_generate(cls, multiworld: MultiWorld):
|
||||
versions = set()
|
||||
for player in world.player_ids:
|
||||
if world.worlds[player].game == "Pokemon Red and Blue":
|
||||
versions.add(world.game_version[player].current_key)
|
||||
for player in multiworld.player_ids:
|
||||
if multiworld.worlds[player].game == "Pokemon Red and Blue":
|
||||
versions.add(multiworld.game_version[player].current_key)
|
||||
for version in versions:
|
||||
if not os.path.exists(get_base_rom_path(version)):
|
||||
raise FileNotFoundError(get_base_rom_path(version))
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
|
||||
from BaseClasses import MultiWorld, Region, Entrance, RegionType, LocationProgressType
|
||||
from BaseClasses import MultiWorld, Region, Entrance, LocationProgressType
|
||||
from .locations import location_data, PokemonRBLocation
|
||||
|
||||
|
||||
def create_region(world: MultiWorld, player: int, name: str, locations_per_region=None, exits=None):
|
||||
ret = Region(name, RegionType.Generic, name, player, world)
|
||||
ret = Region(name, player, world)
|
||||
for location in locations_per_region.get(name, []):
|
||||
location.parent_region = ret
|
||||
ret.locations.append(location)
|
||||
|
||||
@@ -359,7 +359,7 @@ def process_pokemon_data(self):
|
||||
|
||||
|
||||
def generate_output(self, output_directory: str):
|
||||
random = self.multiworld.slot_seeds[self.player]
|
||||
random = self.multiworld.per_slot_randoms[self.player]
|
||||
game_version = self.multiworld.game_version[self.player].current_key
|
||||
data = bytes(get_base_rom_bytes(game_version))
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ from .Regions import create_regions, getConnectionName
|
||||
from .Rules import set_rules
|
||||
from .Options import raft_options
|
||||
|
||||
from BaseClasses import Region, RegionType, Entrance, Location, MultiWorld, Item, ItemClassification, Tutorial
|
||||
from BaseClasses import Region, Entrance, Location, MultiWorld, Item, ItemClassification, Tutorial
|
||||
from ..AutoWorld import World, WebWorld
|
||||
|
||||
|
||||
@@ -201,8 +201,7 @@ class RaftWorld(World):
|
||||
}
|
||||
|
||||
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
||||
ret = Region(name, RegionType.Generic, name, player)
|
||||
ret.multiworld = world
|
||||
ret = Region(name, player, world)
|
||||
if locations:
|
||||
for location in locations:
|
||||
loc_id = locations_lookup_name_to_id.get(location, 0)
|
||||
@@ -211,7 +210,6 @@ def create_region(world: MultiWorld, player: int, name: str, locations=None, exi
|
||||
if exits:
|
||||
for exit in exits:
|
||||
ret.exits.append(Entrance(player, getConnectionName(name, exit), ret))
|
||||
|
||||
return ret
|
||||
|
||||
class RaftLocation(Location):
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import Dict, List, NamedTuple, Optional
|
||||
|
||||
from BaseClasses import MultiWorld, Region, RegionType, Entrance
|
||||
from BaseClasses import MultiWorld, Region, Entrance
|
||||
from .Locations import RLLocation, location_table, get_locations_by_category
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ def create_regions(multiworld: MultiWorld, player: int):
|
||||
|
||||
|
||||
def create_region(multiworld: MultiWorld, player: int, name: str, data: RLRegionData):
|
||||
region = Region(name, RegionType.Generic, name, player, multiworld)
|
||||
region = Region(name, player, multiworld)
|
||||
if data.locations:
|
||||
for loc_name in data.locations:
|
||||
loc_data = location_table.get(loc_name)
|
||||
|
||||
+43
-4
@@ -1,13 +1,13 @@
|
||||
from typing import Dict
|
||||
from BaseClasses import Item
|
||||
from .Options import ItemWeights
|
||||
from .RoR2Environments import *
|
||||
|
||||
|
||||
class RiskOfRainItem(Item):
|
||||
game: str = "Risk of Rain 2"
|
||||
|
||||
|
||||
# 37000 - 38000
|
||||
# 37000 - 37699, 38000
|
||||
item_table: Dict[str, int] = {
|
||||
"Dio's Best Friend": 37001,
|
||||
"Common Item": 37002,
|
||||
@@ -19,9 +19,24 @@ item_table: Dict[str, int] = {
|
||||
"Item Scrap, White": 37008,
|
||||
"Item Scrap, Green": 37009,
|
||||
"Item Scrap, Red": 37010,
|
||||
"Item Scrap, Yellow": 37011
|
||||
"Item Scrap, Yellow": 37011,
|
||||
"Void Item": 37012
|
||||
}
|
||||
|
||||
# 37700 - 37699
|
||||
##################################################
|
||||
# environments
|
||||
|
||||
environment_offest = 37700
|
||||
|
||||
# add ALL environments into the item table
|
||||
environment_offset_table = shift_by_offset(environment_ALL_table, environment_offest)
|
||||
item_table.update(shift_by_offset(environment_ALL_table, environment_offest))
|
||||
# use the sotv dlc in the item table so that all names can be looked up regardless of use
|
||||
|
||||
# end of environments
|
||||
##################################################
|
||||
|
||||
default_weights: Dict[str, int] = {
|
||||
"Item Scrap, Green": 16,
|
||||
"Item Scrap, Red": 4,
|
||||
@@ -32,6 +47,7 @@ default_weights: Dict[str, int] = {
|
||||
"Legendary Item": 8,
|
||||
"Boss Item": 4,
|
||||
"Lunar Item": 16,
|
||||
"Void Item": 16,
|
||||
"Equipment": 32
|
||||
}
|
||||
|
||||
@@ -45,6 +61,7 @@ new_weights: Dict[str, int] = {
|
||||
"Legendary Item": 10,
|
||||
"Boss Item": 5,
|
||||
"Lunar Item": 10,
|
||||
"Void Item": 16,
|
||||
"Equipment": 20
|
||||
}
|
||||
|
||||
@@ -58,6 +75,7 @@ uncommon_weights: Dict[str, int] = {
|
||||
"Legendary Item": 10,
|
||||
"Boss Item": 5,
|
||||
"Lunar Item": 15,
|
||||
"Void Item": 16,
|
||||
"Equipment": 20
|
||||
}
|
||||
|
||||
@@ -71,6 +89,7 @@ legendary_weights: Dict[str, int] = {
|
||||
"Legendary Item": 100,
|
||||
"Boss Item": 5,
|
||||
"Lunar Item": 15,
|
||||
"Void Item": 16,
|
||||
"Equipment": 20
|
||||
}
|
||||
|
||||
@@ -84,6 +103,7 @@ lunartic_weights: Dict[str, int] = {
|
||||
"Legendary Item": 0,
|
||||
"Boss Item": 0,
|
||||
"Lunar Item": 100,
|
||||
"Void Item": 0,
|
||||
"Equipment": 0
|
||||
}
|
||||
|
||||
@@ -97,6 +117,7 @@ chaos_weights: Dict[str, int] = {
|
||||
"Legendary Item": 30,
|
||||
"Boss Item": 20,
|
||||
"Lunar Item": 60,
|
||||
"Void Item": 60,
|
||||
"Equipment": 40
|
||||
}
|
||||
|
||||
@@ -110,6 +131,7 @@ no_scraps_weights: Dict[str, int] = {
|
||||
"Legendary Item": 15,
|
||||
"Boss Item": 5,
|
||||
"Lunar Item": 10,
|
||||
"Void Item": 16,
|
||||
"Equipment": 25
|
||||
}
|
||||
|
||||
@@ -123,6 +145,7 @@ even_weights: Dict[str, int] = {
|
||||
"Legendary Item": 1,
|
||||
"Boss Item": 1,
|
||||
"Lunar Item": 1,
|
||||
"Void Item": 1,
|
||||
"Equipment": 1
|
||||
}
|
||||
|
||||
@@ -136,6 +159,21 @@ scraps_only: Dict[str, int] = {
|
||||
"Legendary Item": 0,
|
||||
"Boss Item": 0,
|
||||
"Lunar Item": 0,
|
||||
"Void Item": 0,
|
||||
"Equipment": 0
|
||||
}
|
||||
|
||||
void_weights: Dict[str, int] = {
|
||||
"Item Scrap, Green": 0,
|
||||
"Item Scrap, Red": 0,
|
||||
"Item Scrap, Yellow": 0,
|
||||
"Item Scrap, White": 0,
|
||||
"Common Item": 0,
|
||||
"Uncommon Item": 0,
|
||||
"Legendary Item": 0,
|
||||
"Boss Item": 0,
|
||||
"Lunar Item": 0,
|
||||
"Void Item": 100,
|
||||
"Equipment": 0
|
||||
}
|
||||
|
||||
@@ -148,7 +186,8 @@ item_pool_weights: Dict[int, Dict[str, int]] = {
|
||||
ItemWeights.option_chaos: chaos_weights,
|
||||
ItemWeights.option_no_scraps: no_scraps_weights,
|
||||
ItemWeights.option_even: even_weights,
|
||||
ItemWeights.option_scraps_only: scraps_only
|
||||
ItemWeights.option_scraps_only: scraps_only,
|
||||
ItemWeights.option_void: void_weights,
|
||||
}
|
||||
|
||||
lookup_id_to_name: Dict[int, str] = {id: name for name, id in item_table.items()}
|
||||
|
||||
+111
-5
@@ -1,13 +1,119 @@
|
||||
from typing import Dict
|
||||
from typing import Tuple
|
||||
from BaseClasses import Location
|
||||
from .Options import TotalLocations
|
||||
from .Options import ChestsPerEnvironment
|
||||
from .Options import ShrinesPerEnvironment
|
||||
from .Options import ScavengersPerEnvironment
|
||||
from .Options import ScannersPerEnvironment
|
||||
from .Options import AltarsPerEnvironment
|
||||
from .RoR2Environments import *
|
||||
|
||||
|
||||
class RiskOfRainLocation(Location):
|
||||
game: str = "Risk of Rain 2"
|
||||
|
||||
|
||||
# 37006 - 37506
|
||||
item_pickups: Dict[str, int] = {
|
||||
f"ItemPickup{i+1}": 37000+i for i in range(TotalLocations.range_end)
|
||||
}
|
||||
ror2_locations_start_id = 38000
|
||||
|
||||
|
||||
def get_classic_item_pickups(n: int) -> Dict[str, int]:
|
||||
"""Get n ItemPickups, capped at the max value for TotalLocations"""
|
||||
n = max(n, 0)
|
||||
n = min(n, TotalLocations.range_end)
|
||||
return { f"ItemPickup{i+1}": ror2_locations_start_id+i for i in range(n) }
|
||||
|
||||
|
||||
item_pickups = get_classic_item_pickups(TotalLocations.range_end)
|
||||
location_table = item_pickups
|
||||
|
||||
|
||||
def environment_abreviation(long_name:str) -> str:
|
||||
"""convert long environment names to initials"""
|
||||
abrev = ""
|
||||
# go through every word finding a letter (or number) for an initial
|
||||
for word in long_name.split():
|
||||
initial = word[0]
|
||||
for letter in word:
|
||||
if letter.isalnum():
|
||||
initial = letter
|
||||
break
|
||||
abrev+= initial
|
||||
return abrev
|
||||
|
||||
# highest numbered orderedstages (this is so we can treat the easily caculate the check ids based on the environment and location "offset")
|
||||
highest_orderedstage: int= max(compress_dict_list_horizontal(environment_orderedstages_table).values())
|
||||
|
||||
ror2_locations_start_orderedstage = ror2_locations_start_id + TotalLocations.range_end
|
||||
|
||||
class orderedstage_location:
|
||||
"""A class to behave like a struct for storing the offsets of location types in the allocated space per orderedstage environments."""
|
||||
# TODO is there a better, more generic way to do this?
|
||||
offset_ChestsPerEnvironment = 0
|
||||
offset_ShrinesPerEnvironment = offset_ChestsPerEnvironment + ChestsPerEnvironment.range_end
|
||||
offset_ScavengersPerEnvironment = offset_ShrinesPerEnvironment + ShrinesPerEnvironment.range_end
|
||||
offset_ScannersPerEnvironment = offset_ScavengersPerEnvironment + ScavengersPerEnvironment.range_end
|
||||
offset_AltarsPerEnvironment = offset_ScannersPerEnvironment + ScannersPerEnvironment.range_end
|
||||
|
||||
# total space allocated to the locations in a single orderedstage environment
|
||||
allocation = offset_AltarsPerEnvironment + AltarsPerEnvironment.range_end
|
||||
|
||||
def get_environment_locations(chests:int, shrines:int, scavengers:int, scanners:int, altars:int, environment: Tuple[str, int]) -> Dict[str, int]:
|
||||
"""Get the locations within a specific environment"""
|
||||
environment_name = environment[0]
|
||||
environment_index = environment[1]
|
||||
locations = {}
|
||||
|
||||
# due to this mapping, since environment ids are not consecutive, there are lots of "wasted" id numbers
|
||||
# TODO perhaps a hashing algorithm could be used to compress this range and save "wasted" ids
|
||||
environment_start_id = environment_index * orderedstage_location.allocation + ror2_locations_start_orderedstage
|
||||
for n in range(chests):
|
||||
locations.update({f"{environment_name}: Chest {n+1}": n + orderedstage_location.offset_ChestsPerEnvironment + environment_start_id})
|
||||
for n in range(shrines):
|
||||
locations.update({f"{environment_name}: Shrine {n+1}": n + orderedstage_location.offset_ShrinesPerEnvironment + environment_start_id})
|
||||
for n in range(scavengers):
|
||||
locations.update({f"{environment_name}: Scavenger {n+1}": n + orderedstage_location.offset_ScavengersPerEnvironment + environment_start_id})
|
||||
for n in range(scanners):
|
||||
locations.update({f"{environment_name}: Radio Scanner {n+1}": n + orderedstage_location.offset_ScannersPerEnvironment + environment_start_id})
|
||||
for n in range(altars):
|
||||
locations.update({f"{environment_name}: Newt Altar {n+1}": n + orderedstage_location.offset_AltarsPerEnvironment + environment_start_id})
|
||||
return locations
|
||||
|
||||
def get_locations(chests:int, shrines:int, scavengers:int, scanners:int, altars:int, dlc_sotv:bool) -> Dict[str, int]:
|
||||
"""Get a dictionary of locations for the ordedstage environments with the locations from the parameters."""
|
||||
locations = {}
|
||||
orderedstages = compress_dict_list_horizontal(environment_vanilla_orderedstages_table)
|
||||
if(dlc_sotv): orderedstages.update(compress_dict_list_horizontal(environment_sotv_orderedstages_table))
|
||||
# for every environment, generate the respective locations
|
||||
for environment_name, environment_index in orderedstages.items():
|
||||
# locations = locations | orderedstage_location.get_environment_locations(
|
||||
locations.update(orderedstage_location.get_environment_locations(
|
||||
chests=chests,
|
||||
shrines=shrines,
|
||||
scavengers=scavengers,
|
||||
scanners=scanners,
|
||||
altars=altars,
|
||||
environment=(environment_name, environment_index)
|
||||
))
|
||||
return locations
|
||||
|
||||
def getall_locations(dlc_sotv:bool=True) -> Dict[str, int]:
|
||||
"""
|
||||
Get all locations in ordered stages.
|
||||
Set dlc_sotv to true for the SOTV DLC to be included.
|
||||
"""
|
||||
# to get all locations, attempt using as many locations as possible
|
||||
return orderedstage_location.get_locations(
|
||||
chests=ChestsPerEnvironment.range_end,
|
||||
shrines=ShrinesPerEnvironment.range_end,
|
||||
scavengers=ScavengersPerEnvironment.range_end,
|
||||
scanners=ScannersPerEnvironment.range_end,
|
||||
altars=AltarsPerEnvironment.range_end,
|
||||
dlc_sotv=dlc_sotv
|
||||
)
|
||||
|
||||
|
||||
ror2_location_post_orderedstage = ror2_locations_start_orderedstage + highest_orderedstage*orderedstage_location.allocation
|
||||
location_table.update(orderedstage_location.getall_locations())
|
||||
# use the sotv dlc in the lookup table so that all ids can be looked up regardless of use
|
||||
|
||||
lookup_id_to_name: Dict[int, str] = {id: name for name, id in location_table.items()}
|
||||
|
||||
+169
-37
@@ -1,31 +1,99 @@
|
||||
from typing import Dict
|
||||
from Options import Option, DefaultOnToggle, Range, Choice
|
||||
from Options import Option, Toggle, DefaultOnToggle, DeathLink, Range, Choice
|
||||
|
||||
|
||||
# NOTE be aware that since the range of item ids that RoR2 uses is based off of the maximums of checks
|
||||
# Be careful when changing the range_end values not to go into another game's IDs
|
||||
# NOTE that these changes to range_end must also be reflected in the RoR2 client so it understands the same ids.
|
||||
|
||||
class Goal(Choice):
|
||||
"""
|
||||
Classic Mode: Every Item pickup increases fills a progress bar which gives location checks.
|
||||
|
||||
Explore Mode: Each environment will have location checks within each environment.
|
||||
environments will be locked in the item pool until received.
|
||||
"""
|
||||
display_name = "Game Mode"
|
||||
option_classic = 0
|
||||
option_explore = 1
|
||||
default = 0
|
||||
|
||||
|
||||
class TotalLocations(Range):
|
||||
"""Number of location checks which are added to the Risk of Rain playthrough."""
|
||||
"""Classic Mode: Number of location checks which are added to the Risk of Rain playthrough."""
|
||||
display_name = "Total Locations"
|
||||
range_start = 10
|
||||
range_start = 40
|
||||
range_end = 250
|
||||
default = 20
|
||||
default = 40
|
||||
|
||||
|
||||
class ChestsPerEnvironment(Range):
|
||||
"""Explore Mode: The number of chest locations per environment."""
|
||||
display_name = "Chests per Environment"
|
||||
range_start = 2
|
||||
range_end = 20
|
||||
default = 10
|
||||
|
||||
|
||||
class ShrinesPerEnvironment(Range):
|
||||
"""Explore Mode: The number of shrine locations per environment."""
|
||||
display_name = "Shrines per Environment"
|
||||
range_start = 2
|
||||
range_end = 20
|
||||
default = 5
|
||||
|
||||
|
||||
class ScavengersPerEnvironment(Range):
|
||||
"""Explore Mode: The number of scavenger locations per environment."""
|
||||
display_name = "Scavenger per Environment"
|
||||
range_start = 0
|
||||
range_end = 1
|
||||
default = 1
|
||||
|
||||
class ScannersPerEnvironment(Range):
|
||||
"""Explore Mode: The number of scanners locations per environment."""
|
||||
display_name = "Radio Scanners per Environment"
|
||||
range_start = 0
|
||||
range_end = 1
|
||||
default = 1
|
||||
|
||||
class AltarsPerEnvironment(Range):
|
||||
"""Explore Mode: The number of altars locations per environment."""
|
||||
display_name = "Newts Per Environment"
|
||||
range_start = 0
|
||||
range_end = 2
|
||||
default = 1
|
||||
|
||||
class TotalRevivals(Range):
|
||||
"""Total Percentage of `Dio's Best Friend` item put in the item pool."""
|
||||
display_name = "Total Percentage Revivals Available"
|
||||
display_name = "Total Revives"
|
||||
range_start = 0
|
||||
range_end = 10
|
||||
default = 4
|
||||
|
||||
|
||||
class ItemPickupStep(Range):
|
||||
"""Number of items to pick up before an AP Check is completed.
|
||||
"""
|
||||
Number of items to pick up before an AP Check is completed.
|
||||
Setting to 1 means every other pickup.
|
||||
Setting to 2 means every third pickup. So on..."""
|
||||
Setting to 2 means every third pickup. So on...
|
||||
"""
|
||||
display_name = "Item Pickup Step"
|
||||
range_start = 0
|
||||
range_end = 5
|
||||
default = 2
|
||||
default = 1
|
||||
|
||||
class ShrineUseStep(Range):
|
||||
"""
|
||||
Explore Mode:
|
||||
Number of shrines to use up before an AP Check is completed.
|
||||
Setting to 1 means every other pickup.
|
||||
Setting to 2 means every third pickup. So on...
|
||||
"""
|
||||
display_name = "Shrine use Step"
|
||||
range_start = 0
|
||||
range_end = 3
|
||||
default = 0
|
||||
|
||||
|
||||
class AllowLunarItems(DefaultOnToggle):
|
||||
@@ -38,13 +106,33 @@ class StartWithRevive(DefaultOnToggle):
|
||||
display_name = "Start with a Revive"
|
||||
|
||||
|
||||
class FinalStageDeath(DefaultOnToggle):
|
||||
class FinalStageDeath(Toggle):
|
||||
"""Death on the final boss stage counts as a win."""
|
||||
display_name = "Final Stage Death is Win"
|
||||
|
||||
|
||||
class BeginWithLoop(Toggle):
|
||||
"""
|
||||
Enable to precollect a full loop of environments.
|
||||
Only has an effect with Explore Mode.
|
||||
"""
|
||||
display_name = "Begin With Loop"
|
||||
|
||||
|
||||
class DLC_SOTV(Toggle):
|
||||
"""
|
||||
Enable if you are using SOTV DLC.
|
||||
Affects environment availability for Explore Mode.
|
||||
Adds Void Items into the item pool
|
||||
"""
|
||||
display_name = "Enable DLC - SOTV"
|
||||
|
||||
|
||||
|
||||
class GreenScrap(Range):
|
||||
"""Weight of Green Scraps in the item pool. (Ignored unless Item Weight Presets is 'No')"""
|
||||
"""Weight of Green Scraps in the item pool.
|
||||
|
||||
(Ignored unless Item Weight Presets is 'No')"""
|
||||
display_name = "Green Scraps"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
@@ -52,7 +140,9 @@ class GreenScrap(Range):
|
||||
|
||||
|
||||
class RedScrap(Range):
|
||||
"""Weight of Red Scraps in the item pool. (Ignored unless Item Weight Presets is 'No')"""
|
||||
"""Weight of Red Scraps in the item pool.
|
||||
|
||||
(Ignored unless Item Weight Presets is 'No')"""
|
||||
display_name = "Red Scraps"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
@@ -60,7 +150,9 @@ class RedScrap(Range):
|
||||
|
||||
|
||||
class YellowScrap(Range):
|
||||
"""Weight of yellow scraps in the item pool. (Ignored unless Item Weight Presets is 'No')"""
|
||||
"""Weight of yellow scraps in the item pool.
|
||||
|
||||
(Ignored unless Item Weight Presets is 'No')"""
|
||||
display_name = "Yellow Scraps"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
@@ -68,7 +160,9 @@ class YellowScrap(Range):
|
||||
|
||||
|
||||
class WhiteScrap(Range):
|
||||
"""Weight of white scraps in the item pool. (Ignored unless Item Weight Presets is 'No')"""
|
||||
"""Weight of white scraps in the item pool.
|
||||
|
||||
(Ignored unless Item Weight Presets is 'No')"""
|
||||
display_name = "White Scraps"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
@@ -76,7 +170,9 @@ class WhiteScrap(Range):
|
||||
|
||||
|
||||
class CommonItem(Range):
|
||||
"""Weight of common items in the item pool. (Ignored unless Item Weight Presets is 'No')"""
|
||||
"""Weight of common items in the item pool.
|
||||
|
||||
(Ignored unless Item Weight Presets is 'No')"""
|
||||
display_name = "Common Items"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
@@ -84,7 +180,9 @@ class CommonItem(Range):
|
||||
|
||||
|
||||
class UncommonItem(Range):
|
||||
"""Weight of uncommon items in the item pool. (Ignored unless Item Weight Presets is 'No')"""
|
||||
"""Weight of uncommon items in the item pool.
|
||||
|
||||
(Ignored unless Item Weight Presets is 'No')"""
|
||||
display_name = "Uncommon Items"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
@@ -92,7 +190,9 @@ class UncommonItem(Range):
|
||||
|
||||
|
||||
class LegendaryItem(Range):
|
||||
"""Weight of legendary items in the item pool. (Ignored unless Item Weight Presets is 'No')"""
|
||||
"""Weight of legendary items in the item pool.
|
||||
|
||||
(Ignored unless Item Weight Presets is 'No')"""
|
||||
display_name = "Legendary Items"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
@@ -100,7 +200,9 @@ class LegendaryItem(Range):
|
||||
|
||||
|
||||
class BossItem(Range):
|
||||
"""Weight of boss items in the item pool. (Ignored unless Item Weight Presets is 'No')"""
|
||||
"""Weight of boss items in the item pool.
|
||||
|
||||
(Ignored unless Item Weight Presets is 'No')"""
|
||||
display_name = "Boss Items"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
@@ -108,36 +210,54 @@ class BossItem(Range):
|
||||
|
||||
|
||||
class LunarItem(Range):
|
||||
"""Weight of lunar items in the item pool. (Ignored unless Item Weight Presets is 'No')"""
|
||||
"""Weight of lunar items in the item pool.
|
||||
|
||||
(Ignored unless Item Weight Presets is 'No')"""
|
||||
display_name = "Lunar Items"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
default = 16
|
||||
|
||||
|
||||
class VoidItem(Range):
|
||||
"""Weight of void items in the item pool.
|
||||
|
||||
(Ignored unless Item Weight Presets is 'No')
|
||||
|
||||
(Ignored if Enable DLC - SOTV is 'No') """
|
||||
display_name = "Void Items"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
default = 16
|
||||
|
||||
|
||||
class Equipment(Range):
|
||||
"""Weight of equipment items in the item pool. (Ignored unless Item Weight Presets is 'No')"""
|
||||
"""Weight of equipment items in the item pool.
|
||||
|
||||
(Ignored unless Item Weight Presets is 'No')"""
|
||||
display_name = "Equipment"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
default = 32
|
||||
|
||||
|
||||
class ItemPoolPresetToggle(DefaultOnToggle):
|
||||
class ItemPoolPresetToggle(Toggle):
|
||||
"""Will use the item weight presets when set to true, otherwise will use the custom set item pool weights."""
|
||||
display_name = "Use Item Weight Presets"
|
||||
|
||||
|
||||
class ItemWeights(Choice):
|
||||
"""Preset choices for determining the weights of the item pool.
|
||||
New is a test for a potential adjustment to the default weights.
|
||||
Uncommon puts a large number of uncommon items in the pool.
|
||||
Legendary puts a large number of legendary items in the pool.
|
||||
Lunartic makes everything a lunar item.
|
||||
Chaos generates the pool completely at random with rarer items having a slight cap to prevent this option being too easy.
|
||||
No Scraps removes all scrap items from the item pool.
|
||||
Even generates the item pool with every item having an even weight.
|
||||
Scraps Only will be only scrap items in the item pool."""
|
||||
"""Set item_pool_presets to true if you want to use one of these presets.
|
||||
Preset choices for determining the weights of the item pool.
|
||||
- New is a test for a potential adjustment to the default weights.
|
||||
- Uncommon puts a large number of uncommon items in the pool.
|
||||
- Legendary puts a large number of legendary items in the pool.
|
||||
- Lunartic makes everything a lunar item.
|
||||
- Chaos generates the pool completely at random with rarer items having a slight cap to prevent this option being too easy.
|
||||
- No Scraps removes all scrap items from the item pool.
|
||||
- Even generates the item pool with every item having an even weight.
|
||||
- Scraps Only will be only scrap items in the item pool.
|
||||
- Void makes everything a void item."""
|
||||
display_name = "Item Weights"
|
||||
option_default = 0
|
||||
option_new = 1
|
||||
@@ -148,6 +268,7 @@ class ItemWeights(Choice):
|
||||
option_no_scraps = 6
|
||||
option_even = 7
|
||||
option_scraps_only = 8
|
||||
option_void = 9
|
||||
|
||||
|
||||
# define a dictionary for the weights of the generated item pool.
|
||||
@@ -161,17 +282,28 @@ ror2_weights: Dict[str, type(Option)] = {
|
||||
"legendary_item": LegendaryItem,
|
||||
"boss_item": BossItem,
|
||||
"lunar_item": LunarItem,
|
||||
"void_item": VoidItem,
|
||||
"equipment": Equipment
|
||||
}
|
||||
|
||||
ror2_options: Dict[str, type(Option)] = {
|
||||
"total_locations": TotalLocations,
|
||||
"total_revivals": TotalRevivals,
|
||||
"start_with_revive": StartWithRevive,
|
||||
"final_stage_death": FinalStageDeath,
|
||||
"item_pickup_step": ItemPickupStep,
|
||||
"enable_lunar": AllowLunarItems,
|
||||
"item_weights": ItemWeights,
|
||||
"item_pool_presets": ItemPoolPresetToggle,
|
||||
"goal": Goal,
|
||||
"total_locations": TotalLocations,
|
||||
"chests_per_stage": ChestsPerEnvironment,
|
||||
"shrines_per_stage": ShrinesPerEnvironment,
|
||||
"scavengers_per_stage": ScavengersPerEnvironment,
|
||||
"scanner_per_stage": ScannersPerEnvironment,
|
||||
"altars_per_stage": AltarsPerEnvironment,
|
||||
"total_revivals": TotalRevivals,
|
||||
"start_with_revive": StartWithRevive,
|
||||
"final_stage_death": FinalStageDeath,
|
||||
"begin_with_loop": BeginWithLoop,
|
||||
"dlc_sotv": DLC_SOTV,
|
||||
"death_link": DeathLink,
|
||||
"item_pickup_step": ItemPickupStep,
|
||||
"shrine_use_step": ShrineUseStep,
|
||||
"enable_lunar": AllowLunarItems,
|
||||
"item_weights": ItemWeights,
|
||||
"item_pool_presets": ItemPoolPresetToggle,
|
||||
**ror2_weights
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
from typing import Dict, List, NamedTuple, Optional
|
||||
|
||||
from BaseClasses import MultiWorld, Region, Entrance
|
||||
from .Locations import location_table, RiskOfRainLocation
|
||||
|
||||
|
||||
class RoRRegionData(NamedTuple):
|
||||
locations: Optional[List[str]]
|
||||
region_exits: Optional[List[str]]
|
||||
|
||||
|
||||
def create_regions(multiworld: MultiWorld, player: int):
|
||||
# Default Locations
|
||||
non_dlc_regions: Dict[str, RoRRegionData] = {
|
||||
"Menu": RoRRegionData(None, ["Distant Roost", "Distant Roost (2)", "Titanic Plains", "Titanic Plains (2)"]),
|
||||
"Distant Roost": RoRRegionData([], ["OrderedStage_1"]),
|
||||
"Distant Roost (2)": RoRRegionData([], ["OrderedStage_1"]),
|
||||
"Titanic Plains": RoRRegionData([], ["OrderedStage_1"]),
|
||||
"Titanic Plains (2)": RoRRegionData([], ["OrderedStage_1"]),
|
||||
"Abandoned Aqueduct": RoRRegionData([], ["OrderedStage_2"]),
|
||||
"Wetland Aspect": RoRRegionData([], ["OrderedStage_2"]),
|
||||
"Rallypoint Delta": RoRRegionData([], ["OrderedStage_3"]),
|
||||
"Scorched Acres": RoRRegionData([], ["OrderedStage_3"]),
|
||||
"Abyssal Depths": RoRRegionData([], ["OrderedStage_4"]),
|
||||
"Siren's Call": RoRRegionData([], ["OrderedStage_4"]),
|
||||
"Sundered Grove": RoRRegionData([], ["OrderedStage_4"]),
|
||||
"Sky Meadow": RoRRegionData([], ["Hidden Realm: Bulwark's Ambry", "OrderedStage_5"]),
|
||||
}
|
||||
# SOTV Regions
|
||||
dlc_regions: Dict[str, RoRRegionData] = {
|
||||
"Siphoned Forest": RoRRegionData([], ["OrderedStage_1"]),
|
||||
"Aphelian Sanctuary": RoRRegionData([], ["OrderedStage_2"]),
|
||||
"Sulfur Pools": RoRRegionData([], ["OrderedStage_3"])
|
||||
}
|
||||
other_regions: Dict[str, RoRRegionData] = {
|
||||
"Commencement": RoRRegionData(None, ["Victory", "Petrichor V"]),
|
||||
"OrderedStage_5": RoRRegionData(None, ["Hidden Realm: A Moment, Fractured", "Commencement"]),
|
||||
"OrderedStage_1": RoRRegionData(None, ["Hidden Realm: Bazaar Between Time",
|
||||
"Hidden Realm: Gilded Coast", "Abandoned Aqueduct", "Wetland Aspect"]),
|
||||
"OrderedStage_2": RoRRegionData(None, ["Rallypoint Delta", "Scorched Acres"]),
|
||||
"OrderedStage_3": RoRRegionData(None, ["Abyssal Depths", "Siren's Call", "Sundered Grove"]),
|
||||
"OrderedStage_4": RoRRegionData(None, ["Sky Meadow"]),
|
||||
"Hidden Realm: A Moment, Fractured": RoRRegionData(None, ["Hidden Realm: A Moment, Whole"]),
|
||||
"Hidden Realm: A Moment, Whole": RoRRegionData(None, ["Victory"]),
|
||||
"Void Fields": RoRRegionData(None, []),
|
||||
"Victory": RoRRegionData(None, None),
|
||||
"Petrichor V": RoRRegionData(None, ["Victory"]),
|
||||
"Hidden Realm: Bulwark's Ambry": RoRRegionData(None, None),
|
||||
"Hidden Realm: Bazaar Between Time": RoRRegionData(None, ["Void Fields"]),
|
||||
"Hidden Realm: Gilded Coast": RoRRegionData(None, None)
|
||||
}
|
||||
dlc_other_regions: Dict[str, RoRRegionData] = {
|
||||
"The Planetarium": RoRRegionData(None, ["Victory"]),
|
||||
"Void Locus": RoRRegionData(None, ["The Planetarium"])
|
||||
}
|
||||
# Totals of each item
|
||||
chests = int(multiworld.chests_per_stage[player])
|
||||
shrines = int(multiworld.shrines_per_stage[player])
|
||||
scavengers = int(multiworld.scavengers_per_stage[player])
|
||||
scanners = int(multiworld.scanner_per_stage[player])
|
||||
newt = int(multiworld.altars_per_stage[player])
|
||||
all_location_regions = {**non_dlc_regions}
|
||||
if multiworld.dlc_sotv[player]:
|
||||
all_location_regions = {**non_dlc_regions, **dlc_regions}
|
||||
|
||||
# Locations
|
||||
for key in all_location_regions:
|
||||
if key == "Menu":
|
||||
continue
|
||||
# Chests
|
||||
for i in range(0, chests):
|
||||
all_location_regions[key].locations.append(f"{key}: Chest {i + 1}")
|
||||
# Shrines
|
||||
for i in range(0, shrines):
|
||||
all_location_regions[key].locations.append(f"{key}: Shrine {i + 1}")
|
||||
# Scavengers
|
||||
if scavengers > 0:
|
||||
for i in range(0, scavengers):
|
||||
all_location_regions[key].locations.append(f"{key}: Scavenger {i + 1}")
|
||||
# Radio Scanners
|
||||
if scanners > 0:
|
||||
for i in range(0, scanners):
|
||||
all_location_regions[key].locations.append(f"{key}: Radio Scanner {i + 1}")
|
||||
# Newt Altars
|
||||
if newt > 0:
|
||||
for i in range(0, newt):
|
||||
all_location_regions[key].locations.append(f"{key}: Newt Altar {i + 1}")
|
||||
regions_pool: Dict = {**all_location_regions, **other_regions}
|
||||
|
||||
# DLC Locations
|
||||
if multiworld.dlc_sotv[player]:
|
||||
non_dlc_regions["Menu"].region_exits.append("Siphoned Forest")
|
||||
other_regions["OrderedStage_2"].region_exits.append("Aphelian Sanctuary")
|
||||
other_regions["OrderedStage_3"].region_exits.append("Sulfur Pools")
|
||||
other_regions["Commencement"].region_exits.append("The Planetarium")
|
||||
other_regions["Void Fields"].region_exits.append("Void Locus")
|
||||
regions_pool: Dict = {**all_location_regions, **other_regions, **dlc_other_regions}
|
||||
|
||||
# Create all the regions
|
||||
for name, data in regions_pool.items():
|
||||
multiworld.regions.append(create_region(multiworld, player, name, data))
|
||||
|
||||
# Connect all the regions to their exits
|
||||
for name, data in regions_pool.items():
|
||||
create_connections_in_regions(multiworld, player, name, data)
|
||||
|
||||
|
||||
def create_region(multiworld: MultiWorld, player: int, name: str, data: RoRRegionData):
|
||||
region = Region(name, player, multiworld)
|
||||
if data.locations:
|
||||
for location_name in data.locations:
|
||||
location_data = location_table.get(location_name)
|
||||
location = RiskOfRainLocation(player, location_name, location_data, region)
|
||||
region.locations.append(location)
|
||||
|
||||
return region
|
||||
|
||||
|
||||
def create_connections_in_regions(multiworld: MultiWorld, player: int, name: str, data: RoRRegionData):
|
||||
region = multiworld.get_region(name, player)
|
||||
if data.region_exits:
|
||||
for region_exit in data.region_exits:
|
||||
r_exit_stage = Entrance(player, region_exit, region)
|
||||
exit_region = multiworld.get_region(region_exit, player)
|
||||
r_exit_stage.connect(exit_region)
|
||||
region.exits.append(r_exit_stage)
|
||||
@@ -0,0 +1,118 @@
|
||||
from typing import Dict, List, TypeVar
|
||||
|
||||
# TODO probably move to Locations
|
||||
|
||||
environment_vanilla_orderedstage_1_table: Dict[str, int] = {
|
||||
"Distant Roost": 7, # blackbeach
|
||||
"Distant Roost (2)": 8, # blackbeach2
|
||||
"Titanic Plains": 15, # golemplains
|
||||
"Titanic Plains (2)": 16, # golemplains2
|
||||
}
|
||||
environment_vanilla_orderedstage_2_table: Dict[str, int] = {
|
||||
"Abandoned Aqueduct": 17, # goolake
|
||||
"Wetland Aspect": 12, # foggyswamp
|
||||
}
|
||||
environment_vanilla_orderedstage_3_table: Dict[str, int] = {
|
||||
"Rallypoint Delta": 13, # frozenwall
|
||||
"Scorched Acres": 47, # wispgraveyard
|
||||
}
|
||||
environment_vanilla_orderedstage_4_table: Dict[str, int] = {
|
||||
"Abyssal Depths": 10, # dampcavesimple
|
||||
"Siren's Call": 37, # shipgraveyard
|
||||
"Sundered Grove": 35, # rootjungle
|
||||
}
|
||||
environment_vanilla_orderedstage_5_table: Dict[str, int] = {
|
||||
"Sky Meadow": 38, # skymeadow
|
||||
}
|
||||
|
||||
environment_vanilla_hidden_realm_table: Dict[str, int] = {
|
||||
"Hidden Realm: Bulwark's Ambry": 5, # artifactworld
|
||||
"Hidden Realm: Bazaar Between Time": 6, # bazaar
|
||||
"Hidden Realm: Gilded Coast": 14, # goldshores
|
||||
"Hidden Realm: A Moment, Whole": 27, # limbo
|
||||
"Hidden Realm: A Moment, Fractured": 33, # mysteryspace
|
||||
}
|
||||
|
||||
environment_vanilla_special_table: Dict[str, int] = {
|
||||
"Void Fields": 4, # arena
|
||||
"Commencement": 32, # moon2
|
||||
}
|
||||
|
||||
environment_sotv_orderedstage_1_table: Dict[str, int] = {
|
||||
"Siphoned Forest": 39, # snowyforest
|
||||
}
|
||||
environment_sotv_orderedstage_2_table: Dict[str, int] = {
|
||||
"Aphelian Sanctuary": 3, # ancientloft
|
||||
}
|
||||
environment_sotv_orderedstage_3_table: Dict[str, int] = {
|
||||
"Sulfur Pools": 41, # sulfurpools
|
||||
}
|
||||
environment_sotv_orderedstage_4_table: Dict[str, int] = { }
|
||||
environment_sotv_orderedstage_5_table: Dict[str, int] = { }
|
||||
|
||||
# TODO idk much and idc much about simulacrum, is there a forced order or something?
|
||||
environment_sotv_simulacrum_table: Dict[str, int] = {
|
||||
"The Simulacrum (Aphelian Sanctuary)": 20, # itancientloft
|
||||
"The Simulacrum (Abyssal Depths)": 21, # itdampcave
|
||||
"The Simulacrum (Rallypoint Delta)": 22, # itfrozenwall
|
||||
"The Simulacrum (Titanic Plains)": 23, # itgolemplains
|
||||
"The Simulacrum (Abandoned Aqueduct)": 24, # itgoolake
|
||||
"The Simulacrum (Commencement)": 25, # itmoon
|
||||
"The Simulacrum (Sky Meadow)": 26, # itskymeadow
|
||||
}
|
||||
|
||||
environment_sotv_special_table: Dict[str, int] = {
|
||||
"Void Locus": 45, # voidstage
|
||||
"The Planetarium": 46, # voidraid
|
||||
}
|
||||
|
||||
X = TypeVar("X")
|
||||
Y = TypeVar("Y")
|
||||
|
||||
|
||||
def compress_dict_list_horizontal(list_of_dict: List[Dict[X, Y]]) -> Dict[X, Y]:
|
||||
"""Combine all dictionaries in a list together into one dictionary."""
|
||||
compressed: Dict[X,Y] = {}
|
||||
for individual in list_of_dict: compressed.update(individual)
|
||||
return compressed
|
||||
|
||||
def collapse_dict_list_vertical(list_of_dict1: List[Dict[X, Y]], *args: List[Dict[X, Y]]) -> List[Dict[X, Y]]:
|
||||
"""Combine all parallel dictionaries in lists together to make a new list of dictionaries of the same length."""
|
||||
# find the length of the longest list
|
||||
length = len(list_of_dict1)
|
||||
for list_of_dictN in args:
|
||||
length = max(length, len(list_of_dictN))
|
||||
|
||||
# create a combined list with a length the same as the longest list
|
||||
collapsed = [{}] * (length)
|
||||
# The reason the list_of_dict1 is not directly used to make collapsed is
|
||||
# side effects can occur if all the dictionaries are not manually unioned.
|
||||
|
||||
# merge contents from list_of_dict1
|
||||
for i in range(len(list_of_dict1)):
|
||||
collapsed[i] = {**collapsed[i], **list_of_dict1[i]}
|
||||
|
||||
# merge contents of remaining lists_of_dicts
|
||||
for list_of_dictN in args:
|
||||
for i in range(len(list_of_dictN)):
|
||||
collapsed[i] = {**collapsed[i], **list_of_dictN[i]}
|
||||
|
||||
return collapsed
|
||||
|
||||
# TODO potentially these should only be created when they are directly referenced (unsure of the space/time cost of creating these initially)
|
||||
|
||||
environment_vanilla_orderedstages_table = [ environment_vanilla_orderedstage_1_table, environment_vanilla_orderedstage_2_table, environment_vanilla_orderedstage_3_table, environment_vanilla_orderedstage_4_table, environment_vanilla_orderedstage_5_table ]
|
||||
environment_vanilla_table = {**compress_dict_list_horizontal(environment_vanilla_orderedstages_table), **environment_vanilla_hidden_realm_table, **environment_vanilla_special_table}
|
||||
|
||||
environment_sotv_orderedstages_table = [ environment_sotv_orderedstage_1_table, environment_sotv_orderedstage_2_table, environment_sotv_orderedstage_3_table, environment_sotv_orderedstage_4_table, environment_sotv_orderedstage_5_table ]
|
||||
environment_sotv_non_simulacrum_table = {**compress_dict_list_horizontal(environment_sotv_orderedstages_table), **environment_sotv_special_table}
|
||||
environment_sotv_table = {**environment_sotv_non_simulacrum_table}
|
||||
|
||||
environment_non_orderedstages_table = {**environment_vanilla_hidden_realm_table, **environment_vanilla_special_table, **environment_sotv_simulacrum_table, **environment_sotv_special_table}
|
||||
environment_orderedstages_table = collapse_dict_list_vertical(environment_vanilla_orderedstages_table, environment_sotv_orderedstages_table)
|
||||
environment_ALL_table = {**environment_vanilla_table, **environment_sotv_table}
|
||||
|
||||
|
||||
def shift_by_offset(dictionary: Dict[str, int], offset:int) -> Dict[str, int]:
|
||||
"""Shift all indexes in a dictionary by an offset"""
|
||||
return {name:index+offset for name, index in dictionary.items()}
|
||||
+145
-26
@@ -1,33 +1,152 @@
|
||||
from BaseClasses import MultiWorld
|
||||
from BaseClasses import MultiWorld, CollectionState
|
||||
from worlds.generic.Rules import set_rule, add_rule
|
||||
from .Locations import orderedstage_location
|
||||
from .RoR2Environments import environment_vanilla_orderedstages_table, environment_sotv_orderedstages_table, \
|
||||
environment_orderedstages_table
|
||||
|
||||
|
||||
def set_rules(world: MultiWorld, player: int) -> None:
|
||||
total_locations = world.total_locations[player].value # total locations for current player
|
||||
# Rule to see if it has access to the previous stage
|
||||
def has_entrance_access_rule(multiworld: MultiWorld, stage: str, entrance: str, player: int):
|
||||
multiworld.get_entrance(entrance, player).access_rule = \
|
||||
lambda state: state.has(entrance, player) and state.has(stage, player)
|
||||
|
||||
|
||||
# Checks to see if chest/shrine are accessible
|
||||
def has_location_access_rule(multiworld: MultiWorld, environment: str, player: int, item_number: int, item_type: str):
|
||||
if item_number == 1:
|
||||
multiworld.get_location(f"{environment}: {item_type} {item_number}", player).access_rule = \
|
||||
lambda state: state.has(environment, player)
|
||||
if item_type == "Scavenger":
|
||||
multiworld.get_location(f"{environment}: {item_type} {item_number}", player).access_rule = \
|
||||
lambda state: state.has(environment, player) and state.has("Stage_4", player)
|
||||
else:
|
||||
multiworld.get_location(f"{environment}: {item_type} {item_number}", player).access_rule = \
|
||||
lambda state: check_location(state, environment, player, item_number, item_type)
|
||||
|
||||
|
||||
def check_location(state, environment: str, player: int, item_number: int, item_name: str):
|
||||
return state.can_reach(f"{environment}: {item_name} {item_number - 1}", "Location", player)
|
||||
|
||||
|
||||
# unlock event to next set of stages
|
||||
def get_stage_event(multiworld: MultiWorld, player: int, stage_number: int):
|
||||
if not multiworld.dlc_sotv[player]:
|
||||
environment_name = multiworld.random.choices(list(environment_vanilla_orderedstages_table[stage_number].keys()), k=1)
|
||||
else:
|
||||
environment_name = multiworld.random.choices(list(environment_orderedstages_table[stage_number].keys()), k=1)
|
||||
multiworld.get_location(f"Stage_{stage_number+1}", player).access_rule = \
|
||||
lambda state: get_one_of_the_stages(state, environment_name[0], player)
|
||||
|
||||
|
||||
def get_one_of_the_stages(state: CollectionState, stage: str, player: int):
|
||||
return state.has(stage, player)
|
||||
|
||||
|
||||
def set_rules(multiworld: MultiWorld, player: int) -> None:
|
||||
|
||||
if multiworld.goal[player] == "classic":
|
||||
# classic mode
|
||||
total_locations = multiworld.total_locations[player].value # total locations for current player
|
||||
else:
|
||||
# explore mode
|
||||
total_locations = len(
|
||||
orderedstage_location.get_locations(
|
||||
chests=multiworld.chests_per_stage[player].value,
|
||||
shrines=multiworld.shrines_per_stage[player].value,
|
||||
scavengers=multiworld.scavengers_per_stage[player].value,
|
||||
scanners=multiworld.scanner_per_stage[player].value,
|
||||
altars=multiworld.altars_per_stage[player].value,
|
||||
dlc_sotv=multiworld.dlc_sotv[player].value
|
||||
)
|
||||
)
|
||||
|
||||
event_location_step = 25 # set an event location at these locations for "spheres"
|
||||
divisions = total_locations // event_location_step
|
||||
total_revivals = world.worlds[player].total_revivals # pulling this info we calculated in generate_basic
|
||||
total_revivals = multiworld.worlds[player].total_revivals # pulling this info we calculated in generate_basic
|
||||
|
||||
if divisions:
|
||||
for i in range(1, divisions): # since divisions is the floor of total_locations / 25
|
||||
event_loc = world.get_location(f"Pickup{i * event_location_step}", player)
|
||||
set_rule(event_loc,
|
||||
lambda state, i=i: state.can_reach(f"ItemPickup{i * event_location_step - 1}", "Location", player))
|
||||
for n in range(i * event_location_step, (i + 1) * event_location_step): # we want to create a rule for each of the 25 locations per division
|
||||
if n == i * event_location_step:
|
||||
set_rule(world.get_location(f"ItemPickup{n}", player),
|
||||
lambda state, event_item=event_loc.item.name: state.has(event_item, player))
|
||||
else:
|
||||
set_rule(world.get_location(f"ItemPickup{n}", player),
|
||||
lambda state, n=n: state.can_reach(f"ItemPickup{n - 1}", "Location", player))
|
||||
for i in range(divisions * event_location_step, total_locations+1):
|
||||
set_rule(world.get_location(f"ItemPickup{i}", player),
|
||||
lambda state, i=i: state.can_reach(f"ItemPickup{i - 1}", "Location", player))
|
||||
set_rule(world.get_location("Victory", player),
|
||||
lambda state: state.can_reach(f"ItemPickup{total_locations}", "Location", player))
|
||||
if total_revivals or world.start_with_revive[player].value:
|
||||
add_rule(world.get_location("Victory", player),
|
||||
lambda state: state.has("Dio's Best Friend", player,
|
||||
total_revivals + world.start_with_revive[player]))
|
||||
if multiworld.goal[player] == "classic":
|
||||
# classic mode
|
||||
if divisions:
|
||||
for i in range(1, divisions): # since divisions is the floor of total_locations / 25
|
||||
event_loc = multiworld.get_location(f"Pickup{i * event_location_step}", player)
|
||||
set_rule(event_loc,
|
||||
lambda state, i=i: state.can_reach(f"ItemPickup{i * event_location_step - 1}", "Location", player))
|
||||
for n in range(i * event_location_step, (i + 1) * event_location_step): # we want to create a rule for each of the 25 locations per division
|
||||
if n == i * event_location_step:
|
||||
set_rule(multiworld.get_location(f"ItemPickup{n}", player),
|
||||
lambda state, event_item=event_loc.item.name: state.has(event_item, player))
|
||||
else:
|
||||
set_rule(multiworld.get_location(f"ItemPickup{n}", player),
|
||||
lambda state, n=n: state.can_reach(f"ItemPickup{n - 1}", "Location", player))
|
||||
for i in range(divisions * event_location_step, total_locations+1):
|
||||
set_rule(multiworld.get_location(f"ItemPickup{i}", player),
|
||||
lambda state, i=i: state.can_reach(f"ItemPickup{i - 1}", "Location", player))
|
||||
set_rule(multiworld.get_location("Victory", player),
|
||||
lambda state: state.can_reach(f"ItemPickup{total_locations}", "Location", player))
|
||||
if total_revivals or multiworld.start_with_revive[player].value:
|
||||
add_rule(multiworld.get_location("Victory", player),
|
||||
lambda state: state.has("Dio's Best Friend", player,
|
||||
total_revivals + multiworld.start_with_revive[player]))
|
||||
|
||||
world.completion_condition[player] = lambda state: state.has("Victory", player)
|
||||
elif multiworld.goal[player] == "explore":
|
||||
# When explore_mode is used,
|
||||
# scavengers need to be locked till after a full loop since that is when they are capable of spawning.
|
||||
# (While technically the requirement is just beating 5 stages, this will ensure that the player will have
|
||||
# a long enough run to have enough director credits for scavengers and
|
||||
# help prevent being stuck in the same stages until that point.)
|
||||
|
||||
for location in multiworld.get_locations():
|
||||
if location.player != player: continue # ignore all checks that don't belong to this player
|
||||
if "Scavenger" in location.name:
|
||||
add_rule(location, lambda state: state.has("Stage_5", player))
|
||||
# Regions
|
||||
chests = multiworld.chests_per_stage[player]
|
||||
shrines = multiworld.shrines_per_stage[player]
|
||||
newts = multiworld.altars_per_stage[player]
|
||||
scavengers = multiworld.scavengers_per_stage[player]
|
||||
scanners = multiworld.scanner_per_stage[player]
|
||||
for i in range(len(environment_vanilla_orderedstages_table)):
|
||||
for environment_name, _ in environment_vanilla_orderedstages_table[i].items():
|
||||
# Make sure to go through each location
|
||||
if scavengers == 1:
|
||||
has_location_access_rule(multiworld, environment_name, player, scavengers, "Scavenger")
|
||||
if scanners == 1:
|
||||
has_location_access_rule(multiworld, environment_name, player, scanners, "Radio Scanner")
|
||||
for chest in range(1, chests + 1):
|
||||
has_location_access_rule(multiworld, environment_name, player, chest, "Chest")
|
||||
for shrine in range(1, shrines + 1):
|
||||
has_location_access_rule(multiworld, environment_name, player, shrine, "Shrine")
|
||||
if newts > 0:
|
||||
for newt in range(1, newts + 1):
|
||||
has_location_access_rule(multiworld, environment_name, player, newt, "Newt Altar")
|
||||
if i > 0:
|
||||
has_entrance_access_rule(multiworld, f"Stage_{i}", environment_name, player)
|
||||
get_stage_event(multiworld, player, i)
|
||||
|
||||
if multiworld.dlc_sotv[player]:
|
||||
for i in range(len(environment_sotv_orderedstages_table)):
|
||||
for environment_name, _ in environment_sotv_orderedstages_table[i].items():
|
||||
# Make sure to go through each location
|
||||
if scavengers == 1:
|
||||
has_location_access_rule(multiworld, environment_name, player, scavengers, "Scavenger")
|
||||
if scanners == 1:
|
||||
has_location_access_rule(multiworld, environment_name, player, scanners, "Radio Scanner")
|
||||
for chest in range(1, chests + 1):
|
||||
has_location_access_rule(multiworld, environment_name, player, chest, "Chest")
|
||||
for shrine in range(1, shrines + 1):
|
||||
has_location_access_rule(multiworld, environment_name, player, shrine, "Shrine")
|
||||
if newts > 0:
|
||||
for newt in range(1, newts + 1):
|
||||
has_location_access_rule(multiworld, environment_name, player, newt, "Newt Altar")
|
||||
if i > 0:
|
||||
has_entrance_access_rule(multiworld, f"Stage_{i}", environment_name, player)
|
||||
has_entrance_access_rule(multiworld, f"Hidden Realm: A Moment, Fractured", "Hidden Realm: A Moment, Whole", player)
|
||||
has_entrance_access_rule(multiworld, f"Stage_1", "Hidden Realm: Bazaar Between Time", player)
|
||||
has_entrance_access_rule(multiworld, f"Hidden Realm: Bazaar Between Time", "Void Fields", player)
|
||||
has_entrance_access_rule(multiworld, f"Stage_5", "Commencement", player)
|
||||
has_entrance_access_rule(multiworld, f"Stage_5", "Hidden Realm: A Moment, Fractured", player)
|
||||
if multiworld.dlc_sotv[player]:
|
||||
has_entrance_access_rule(multiworld, f"Stage_5", "Void Locus", player)
|
||||
has_entrance_access_rule(multiworld, f"Void Locus", "The Planetarium", player)
|
||||
# Win Condition
|
||||
multiworld.completion_condition[player] = lambda state: state.has("Victory", player)
|
||||
|
||||
+144
-40
@@ -1,14 +1,14 @@
|
||||
import string
|
||||
from typing import Dict, List
|
||||
|
||||
from BaseClasses import Entrance, Item, ItemClassification, MultiWorld, Region, RegionType, Tutorial
|
||||
from worlds.AutoWorld import WebWorld, World
|
||||
from .Items import RiskOfRainItem, item_pool_weights, item_table
|
||||
from .Locations import RiskOfRainLocation, item_pickups
|
||||
from .Options import ItemWeights, ror2_options
|
||||
from .Items import RiskOfRainItem, item_table, item_pool_weights, environment_offest
|
||||
from .Locations import RiskOfRainLocation, get_classic_item_pickups, item_pickups, orderedstage_location
|
||||
from .Rules import set_rules
|
||||
from .RoR2Environments import *
|
||||
|
||||
client_version = 1
|
||||
from BaseClasses import Region, Entrance, Item, ItemClassification, MultiWorld, Tutorial
|
||||
from .Options import ror2_options, ItemWeights
|
||||
from worlds.AutoWorld import World, WebWorld
|
||||
from .Regions import create_regions
|
||||
|
||||
|
||||
class RiskOfWeb(WebWorld):
|
||||
@@ -35,20 +35,58 @@ class RiskOfRainWorld(World):
|
||||
item_name_to_id = item_table
|
||||
location_name_to_id = item_pickups
|
||||
|
||||
data_version = 4
|
||||
data_version = 6
|
||||
required_client_version = (0, 3, 7)
|
||||
web = RiskOfWeb()
|
||||
total_revivals: int
|
||||
|
||||
def generate_early(self) -> None:
|
||||
# figure out how many revivals should exist in the pool
|
||||
if self.multiworld.goal[self.player] == "classic":
|
||||
total_locations = self.multiworld.total_locations[self.player].value
|
||||
else:
|
||||
total_locations = len(
|
||||
orderedstage_location.get_locations(
|
||||
chests=self.multiworld.chests_per_stage[self.player].value,
|
||||
shrines=self.multiworld.shrines_per_stage[self.player].value,
|
||||
scavengers=self.multiworld.scavengers_per_stage[self.player].value,
|
||||
scanners=self.multiworld.scanner_per_stage[self.player].value,
|
||||
altars=self.multiworld.altars_per_stage[self.player].value,
|
||||
dlc_sotv=self.multiworld.dlc_sotv[self.player].value
|
||||
)
|
||||
)
|
||||
self.total_revivals = int(self.multiworld.total_revivals[self.player].value / 100 *
|
||||
self.multiworld.total_locations[self.player].value)
|
||||
|
||||
def generate_basic(self) -> None:
|
||||
# shortcut for starting_inventory... The start_with_revive option lets you start with a Dio's Best Friend
|
||||
total_locations)
|
||||
# self.total_revivals = self.multiworld.total_revivals[self.player].value
|
||||
if self.multiworld.start_with_revive[self.player].value:
|
||||
self.total_revivals -= 1
|
||||
|
||||
def create_items(self) -> None:
|
||||
# shortcut for starting_inventory... The start_with_revive option lets you start with a Dio's Best Friend
|
||||
if self.multiworld.start_with_revive[self.player]:
|
||||
self.multiworld.push_precollected(self.multiworld.create_item("Dio's Best Friend", self.player))
|
||||
|
||||
environments_pool = {}
|
||||
# only mess with the environments if they are set as items
|
||||
if self.multiworld.goal[self.player] == "explore":
|
||||
|
||||
# figure out all available ordered stages for each tier
|
||||
environment_available_orderedstages_table = environment_vanilla_orderedstages_table
|
||||
if self.multiworld.dlc_sotv[self.player]:
|
||||
environment_available_orderedstages_table = collapse_dict_list_vertical(environment_available_orderedstages_table, environment_sotv_orderedstages_table)
|
||||
|
||||
environments_pool = shift_by_offset(environment_vanilla_table, environment_offest)
|
||||
|
||||
if self.multiworld.dlc_sotv[self.player]:
|
||||
environment_offset_table = shift_by_offset(environment_sotv_table, environment_offest)
|
||||
environments_pool = {**environments_pool, **environment_offset_table}
|
||||
environments_to_precollect = 5 if self.multiworld.begin_with_loop[self.player].value else 1
|
||||
# percollect environments for each stage (or just stage 1)
|
||||
for i in range(environments_to_precollect):
|
||||
unlock = self.multiworld.random.choices(list(environment_available_orderedstages_table[i].keys()), k=1)
|
||||
self.multiworld.push_precollected(self.create_item(unlock[0]))
|
||||
environments_pool.pop(unlock[0])
|
||||
|
||||
# if presets are enabled generate junk_pool from the selected preset
|
||||
pool_option = self.multiworld.item_weights[self.player].value
|
||||
junk_pool: Dict[str, int] = {}
|
||||
@@ -70,61 +108,119 @@ class RiskOfRainWorld(World):
|
||||
"Legendary Item": self.multiworld.legendary_item[self.player].value,
|
||||
"Boss Item": self.multiworld.boss_item[self.player].value,
|
||||
"Lunar Item": self.multiworld.lunar_item[self.player].value,
|
||||
"Void Item": self.multiworld.void_item[self.player].value,
|
||||
"Equipment": self.multiworld.equipment[self.player].value
|
||||
}
|
||||
|
||||
# remove lunar items from the pool if they're disabled in the yaml unless lunartic is rolled
|
||||
if not (self.multiworld.enable_lunar[self.player] or pool_option == ItemWeights.option_lunartic):
|
||||
if not self.multiworld.enable_lunar[self.player] or pool_option == ItemWeights.option_lunartic:
|
||||
junk_pool.pop("Lunar Item")
|
||||
# remove void items from the pool
|
||||
if not self.multiworld.dlc_sotv[self.player] or pool_option == ItemWeights.option_void:
|
||||
junk_pool.pop("Void Item")
|
||||
|
||||
# Generate item pool
|
||||
itempool: List = []
|
||||
# Add revive items for the player
|
||||
itempool += ["Dio's Best Friend"] * self.total_revivals
|
||||
|
||||
for env_name, _ in environments_pool.items():
|
||||
itempool += [env_name]
|
||||
|
||||
# precollected environments are popped from the pool so counting like this is valid
|
||||
nonjunk_item_count = self.total_revivals + len(environments_pool)
|
||||
if self.multiworld.goal[self.player] == "classic":
|
||||
# classic mode
|
||||
total_locations = self.multiworld.total_locations[self.player].value
|
||||
else:
|
||||
# explore mode
|
||||
total_locations = len(
|
||||
orderedstage_location.get_locations(
|
||||
chests=self.multiworld.chests_per_stage[self.player].value,
|
||||
shrines=self.multiworld.shrines_per_stage[self.player].value,
|
||||
scavengers=self.multiworld.scavengers_per_stage[self.player].value,
|
||||
scanners=self.multiworld.scanner_per_stage[self.player].value,
|
||||
altars=self.multiworld.altars_per_stage[self.player].value,
|
||||
dlc_sotv=self.multiworld.dlc_sotv[self.player].value
|
||||
)
|
||||
)
|
||||
junk_item_count = total_locations - nonjunk_item_count
|
||||
# Fill remaining items with randomly generated junk
|
||||
itempool += self.multiworld.random.choices(list(junk_pool.keys()), weights=list(junk_pool.values()),
|
||||
k=self.multiworld.total_locations[self.player].value - self.total_revivals)
|
||||
k=junk_item_count)
|
||||
|
||||
# Convert itempool into real items
|
||||
itempool = list(map(lambda name: self.create_item(name), itempool))
|
||||
|
||||
self.multiworld.itempool += itempool
|
||||
|
||||
def set_rules(self) -> None:
|
||||
set_rules(self.multiworld, self.player)
|
||||
|
||||
def create_regions(self) -> None:
|
||||
menu = create_region(self.multiworld, self.player, "Menu")
|
||||
petrichor = create_region(self.multiworld, self.player, "Petrichor V",
|
||||
[f"ItemPickup{i + 1}" for i in range(self.multiworld.total_locations[self.player].value)])
|
||||
|
||||
connection = Entrance(self.player, "Lobby", menu)
|
||||
menu.exits.append(connection)
|
||||
connection.connect(petrichor)
|
||||
if self.multiworld.goal[self.player] == "classic":
|
||||
# classic mode
|
||||
menu = create_region(self.multiworld, self.player, "Menu")
|
||||
self.multiworld.regions.append(menu)
|
||||
# By using a victory region, we can define it as being connected to by several regions
|
||||
# which can then determine the availability of the victory.
|
||||
victory_region = create_region(self.multiworld, self.player, "Victory")
|
||||
self.multiworld.regions.append(victory_region)
|
||||
petrichor = create_region(self.multiworld, self.player, "Petrichor V",
|
||||
get_classic_item_pickups(self.multiworld.total_locations[self.player].value))
|
||||
self.multiworld.regions.append(petrichor)
|
||||
|
||||
self.multiworld.regions += [menu, petrichor]
|
||||
# classic mode can get to victory from the beginning of the game
|
||||
to_victory = Entrance(self.player, "beating game", petrichor)
|
||||
petrichor.exits.append(to_victory)
|
||||
to_victory.connect(victory_region)
|
||||
|
||||
connection = Entrance(self.player, "Lobby", menu)
|
||||
menu.exits.append(connection)
|
||||
connection.connect(petrichor)
|
||||
else:
|
||||
# explore mode
|
||||
create_regions(self.multiworld, self.player)
|
||||
|
||||
create_events(self.multiworld, self.player)
|
||||
|
||||
def fill_slot_data(self):
|
||||
return {
|
||||
"itemPickupStep": self.multiworld.item_pickup_step[self.player].value,
|
||||
"seed": "".join(self.multiworld.slot_seeds[self.player].choice(string.digits) for _ in range(16)),
|
||||
"shrineUseStep": self.multiworld.shrine_use_step[self.player].value,
|
||||
"goal": self.multiworld.goal[self.player].value,
|
||||
"seed": "".join(self.multiworld.per_slot_randoms[self.player].choice(string.digits) for _ in range(16)),
|
||||
"totalLocations": self.multiworld.total_locations[self.player].value,
|
||||
"chestsPerStage": self.multiworld.chests_per_stage[self.player].value,
|
||||
"shrinesPerStage": self.multiworld.shrines_per_stage[self.player].value,
|
||||
"scavengersPerStage": self.multiworld.scavengers_per_stage[self.player].value,
|
||||
"scannerPerStage": self.multiworld.scanner_per_stage[self.player].value,
|
||||
"altarsPerStage": self.multiworld.altars_per_stage[self.player].value,
|
||||
"totalRevivals": self.multiworld.total_revivals[self.player].value,
|
||||
"startWithDio": self.multiworld.start_with_revive[self.player].value,
|
||||
"FinalStageDeath": self.multiworld.final_stage_death[self.player].value
|
||||
"finalStageDeath": self.multiworld.final_stage_death[self.player].value,
|
||||
"deathLink": self.multiworld.death_link[self.player].value,
|
||||
}
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
item_id = item_table[name]
|
||||
classification = ItemClassification.filler
|
||||
if name == "Dio's Best Friend":
|
||||
classification = ItemClassification.progression
|
||||
elif name in {"Equipment", "Legendary Item"}:
|
||||
elif name in {"Legendary Item", "Boss Item"}:
|
||||
classification = ItemClassification.useful
|
||||
else:
|
||||
classification = ItemClassification.filler
|
||||
elif name == "Lunar Item":
|
||||
classification = ItemClassification.trap
|
||||
|
||||
# Only check for an item to be a environment unlock if those are known to be in the pool.
|
||||
# This should shave down comparions.
|
||||
|
||||
elif name in environment_ALL_table.keys():
|
||||
if name in {"Hidden Realm: Bulwark's Ambry", "Hidden Realm: Gilded Coast,"}:
|
||||
classification = ItemClassification.useful
|
||||
else:
|
||||
classification = ItemClassification.progression
|
||||
|
||||
item = RiskOfRainItem(name, classification, item_id, self.player)
|
||||
return item
|
||||
|
||||
@@ -135,23 +231,31 @@ def create_events(world: MultiWorld, player: int) -> None:
|
||||
if total_locations / 25 == num_of_events:
|
||||
num_of_events -= 1
|
||||
world_region = world.get_region("Petrichor V", player)
|
||||
if world.goal[player] == "classic":
|
||||
# only setup Pickups when using classic_mode
|
||||
for i in range(num_of_events):
|
||||
event_loc = RiskOfRainLocation(player, f"Pickup{(i + 1) * 25}", None, world_region)
|
||||
event_loc.place_locked_item(RiskOfRainItem(f"Pickup{(i + 1) * 25}", ItemClassification.progression, None, player))
|
||||
event_loc.access_rule = \
|
||||
lambda state, i=i: state.can_reach(f"ItemPickup{((i + 1) * 25) - 1}", "Location", player)
|
||||
world_region.locations.append(event_loc)
|
||||
elif world.goal[player] == "explore":
|
||||
for n in range(1, 6):
|
||||
|
||||
for i in range(num_of_events):
|
||||
event_loc = RiskOfRainLocation(player, f"Pickup{(i + 1) * 25}", None, world_region)
|
||||
event_loc.place_locked_item(RiskOfRainItem(f"Pickup{(i + 1) * 25}", ItemClassification.progression, None, player))
|
||||
event_loc.access_rule(lambda state, i=i: state.can_reach(f"ItemPickup{((i + 1) * 25) - 1}", player))
|
||||
world_region.locations.append(event_loc)
|
||||
event_region = world.get_region(f"OrderedStage_{n}", player)
|
||||
event_loc = RiskOfRainLocation(player, f"Stage_{n}", None, event_region)
|
||||
event_loc.place_locked_item(RiskOfRainItem(f"Stage_{n}", ItemClassification.progression, None, player))
|
||||
event_loc.show_in_spoiler = False
|
||||
event_region.locations.append(event_loc)
|
||||
|
||||
victory_event = RiskOfRainLocation(player, "Victory", None, world_region)
|
||||
victory_region = world.get_region("Victory", player)
|
||||
victory_event = RiskOfRainLocation(player, "Victory", None, victory_region)
|
||||
victory_event.place_locked_item(RiskOfRainItem("Victory", ItemClassification.progression, None, player))
|
||||
world_region.locations.append(victory_event)
|
||||
|
||||
|
||||
def create_region(world: MultiWorld, player: int, name: str, locations: List[str] = None) -> Region:
|
||||
ret = Region(name, RegionType.Generic, name, player, world)
|
||||
if locations:
|
||||
for location in locations:
|
||||
loc_id = item_pickups[location]
|
||||
location = RiskOfRainLocation(player, location, loc_id, ret)
|
||||
ret.locations.append(location)
|
||||
def create_region(world: MultiWorld, player: int, name: str, locations: Dict[str, int] = {}) -> Region:
|
||||
ret = Region(name, player, world)
|
||||
for location_name, location_id in locations.items():
|
||||
ret.locations.append(RiskOfRainLocation(player, location_name, location_id, ret))
|
||||
return ret
|
||||
|
||||
@@ -12,19 +12,34 @@ functionality in which certain chests (made clear via a location check progress
|
||||
multiworld. The items that _would have been_ in those chests will be returned to the Risk of Rain player via grants by
|
||||
other players in other worlds.
|
||||
|
||||
There are two modes in risk of rain. Classic Mode and Explore Mode
|
||||
|
||||
Classic Mode:
|
||||
|
||||
- Classic mode implements pure multiworld
|
||||
functionality in which certain chests (made clear via a location check progress bar) will send an item out to the
|
||||
multiworld. The items that _would have been_ in those chests will be returned to the Risk of Rain player via grants by
|
||||
other players in other worlds.
|
||||
|
||||
Explore Mode:
|
||||
|
||||
- Just like in Classic mode chests will send out an item to the multiworld. The difference is that each environment
|
||||
will have a set amount that can be sent out and shrines along with other things that will need to be checked.
|
||||
Also, each environment is an item and, you'll need it to be able to access it.
|
||||
|
||||
## What is the goal of Risk of Rain 2 in Archipelago?
|
||||
|
||||
Just like in the original game, any way to "beat the game or obliterate" counts as a win. By default, if you die while
|
||||
on a final boss stage, that also counts as a win. (You can turn this off in your player settings.) **You do not need to
|
||||
complete all the location checks** to win; any item you don't collect is automatically sent out to the multiworld when
|
||||
you meet your goal.
|
||||
Just like in the original game, any way to "beat the game or obliterate" counts as a win. There is a setting that
|
||||
if you die while on a final boss stage, that also counts as a win.(You can turn this on in your player settings.)
|
||||
**You do not need to complete all the location checks** to win; any item you don't collect may be released if the
|
||||
server options allow.
|
||||
|
||||
If you die before you accomplish your goal, you can start a new run. You will start the run with any items that you
|
||||
received from other players. Any items that you picked up the "normal" way will be lost.
|
||||
|
||||
Note, you can play Simulacrum mode as part of an Archipelago, but you can't achieve any of the victory conditions in
|
||||
Simulacrum. So you could, for example, collect most of your items through a Simulacrum run, then finish a normal mode
|
||||
run while keeping the items you received via the multiworld.
|
||||
Simulacrum. So you could, for example, collect most of your items through a Simulacrum run(only works in classic mode),
|
||||
then finish a normal mode run while keeping the items you received via the multiworld.
|
||||
|
||||
## Can you play multiplayer?
|
||||
|
||||
@@ -38,6 +53,8 @@ settings apply, so each Risk of Rain 2 player slot in the multiworld needs to be
|
||||
for example, have two players trade off hosting and making progress on each other's player slot, but a single co-op
|
||||
instance can't make progress towards multiple player slots in the multiworld.
|
||||
|
||||
Explore mode is untested in multiplayer and will likely not work until a later release.
|
||||
|
||||
## What Risk of Rain items can appear in other players' worlds?
|
||||
|
||||
The Risk of Rain items are:
|
||||
@@ -49,6 +66,7 @@ The Risk of Rain items are:
|
||||
* `Lunar Item` (Blue items)
|
||||
* `Equipment` (Orange items)
|
||||
* `Dio's Best Friend` (Used if you set the YAML setting `total_revives_available` above `0`)
|
||||
* `Void Item` (Purple items) (needs dlc_sotv: enabled)
|
||||
|
||||
Each item grants you a random in-game item from the category it belongs to.
|
||||
|
||||
@@ -57,6 +75,29 @@ in-game item of that tier will appear in the Risk of Rain player's inventory. If
|
||||
the player already has an equipment item equipped then the _item that was equipped_ will be dropped on the ground and _
|
||||
the new equipment_ will take it's place. (If you want the old one back, pick it up.)
|
||||
|
||||
Explore Mode items are:
|
||||
|
||||
* `Titanic Plains (1)`, `Titanic Plains (2)`, `Distant Roost (1)`, `Distant Roost (2)`
|
||||
* `Abandoned Aqueduct`, `Wetland Aspect`
|
||||
* `Rallypoint Delta`, `Scorched Acres`
|
||||
* `Abyssal Depths`, `Siren's Call`, `Sundered Grove`
|
||||
* `Sky Meadow`
|
||||
* `Commencement`
|
||||
* `All the Hidden Realms`
|
||||
|
||||
Dlc_Sotv items
|
||||
* `Siphoned Forest`
|
||||
* `Aphelian Sanctuary`
|
||||
* `Sulfur Pools`
|
||||
* `Void Locus`
|
||||
|
||||
When a explore item is granted it will unlock that environment and will now be accessible to progress to victory! The
|
||||
game will still pick randomly which environment is next but it will first check to see if they are available. If you have
|
||||
them unlocked it will weight the game to have a ***higher chance*** to go to one you have checks versus one you have
|
||||
already completed. You will still not be able to goto a stage 3 environment from a stage 1 environment.
|
||||
|
||||
|
||||
|
||||
### How many items are there?
|
||||
|
||||
Since a Risk of Rain 2 run can go on indefinitely, you have to configure how many collectible items (also known as
|
||||
@@ -65,6 +106,10 @@ to 250** items. The number of items will be randomized between all players, so y
|
||||
item pickup step based on how many items the other players in the multiworld have. (Around 100 seems to be a good
|
||||
ballpark if you want to have a similar number of items to most other games.)
|
||||
|
||||
In explore mode the amount of checks base on how many **chests, shrines, scavengers, radio scanners and, newt altars**
|
||||
are in the pool. With just the base game the numbers are **52 to 516** and with the dlc its **60 to 660** with
|
||||
everything on default being **216**
|
||||
|
||||
After you have completed the specified number of checks, you won't send anything else to the multiworld. You can
|
||||
receive up to the specified number of randomized items from the multiworld as the players find them. In either case,
|
||||
you can continue to collect items as normal in Risk of Rain 2 if you've already found all your location checks.
|
||||
|
||||
@@ -964,8 +964,7 @@ def connect_regions(world, player, gates: typing.List[LevelGate], cannon_core_em
|
||||
|
||||
|
||||
def create_region(world: MultiWorld, player: int, active_locations, name: str, locations=None):
|
||||
ret = Region(name, None, name, player)
|
||||
ret.multiworld = world
|
||||
ret = Region(name, player, world)
|
||||
if locations:
|
||||
for location in locations:
|
||||
loc_id = active_locations.get(location, 0)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
from typing import List, Set, Dict, Tuple, Optional, Callable
|
||||
from BaseClasses import MultiWorld, Region, Entrance, Location, RegionType
|
||||
from BaseClasses import MultiWorld, Region, Entrance, Location
|
||||
from .Locations import LocationData
|
||||
from .Options import get_option_value
|
||||
from .MissionTables import MissionInfo, mission_orders, vanilla_mission_req_table, alt_final_mission_locations
|
||||
@@ -234,8 +234,7 @@ def create_location(player: int, location_data: LocationData, region: Region,
|
||||
|
||||
def create_region(multiworld: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]],
|
||||
location_cache: List[Location], name: str) -> Region:
|
||||
region = Region(name, RegionType.Generic, name, player)
|
||||
region.multiworld = multiworld
|
||||
region = Region(name, player, multiworld)
|
||||
|
||||
if name in locations_per_region:
|
||||
for location_data in locations_per_region[name]:
|
||||
|
||||
@@ -6,12 +6,13 @@ def create_regions(self, world, player: int):
|
||||
|
||||
regions = []
|
||||
for accessPoint in Logic.accessPoints:
|
||||
regions.append(create_region( self,
|
||||
world,
|
||||
player,
|
||||
accessPoint.Name,
|
||||
None,
|
||||
[accessPoint.Name + "->" + key for key in accessPoint.intraTransitions.keys()]))
|
||||
if not accessPoint.Escape:
|
||||
regions.append(create_region(self,
|
||||
world,
|
||||
player,
|
||||
accessPoint.Name,
|
||||
None,
|
||||
[accessPoint.Name + "->" + key for key in accessPoint.intraTransitions.keys()]))
|
||||
|
||||
world.regions += regions
|
||||
|
||||
|
||||
+3
-2
@@ -36,5 +36,6 @@ def set_rules(world, player):
|
||||
add_postAvailable_rule(location, player, value.PostAvailable)
|
||||
|
||||
for accessPoint in Logic.accessPoints:
|
||||
for key, value1 in accessPoint.intraTransitions.items():
|
||||
set_entrance_rule(world.get_entrance(accessPoint.Name + "->" + key, player), player, value1)
|
||||
if not accessPoint.Escape:
|
||||
for key, value1 in accessPoint.intraTransitions.items():
|
||||
set_entrance_rule(world.get_entrance(accessPoint.Name + "->" + key, player), player, value1)
|
||||
|
||||
@@ -18,7 +18,7 @@ from .Client import SMSNIClient
|
||||
from .Rom import get_base_rom_path, SM_ROM_MAX_PLAYERID, SM_ROM_PLAYERDATA_COUNT, SMDeltaPatch, get_sm_symbols
|
||||
import Utils
|
||||
|
||||
from BaseClasses import Region, Entrance, Location, MultiWorld, Item, ItemClassification, RegionType, CollectionState, Tutorial
|
||||
from BaseClasses import Region, Entrance, Location, MultiWorld, Item, ItemClassification, CollectionState, Tutorial
|
||||
from ..AutoWorld import World, AutoLogicRegister, WebWorld
|
||||
|
||||
from logic.smboolmanager import SMBoolManager
|
||||
@@ -107,7 +107,7 @@ class SMWorld(World):
|
||||
super().__init__(world, player)
|
||||
|
||||
@classmethod
|
||||
def stage_assert_generate(cls, world):
|
||||
def stage_assert_generate(cls, multiworld: MultiWorld):
|
||||
rom_file = get_base_rom_path()
|
||||
if not os.path.exists(rom_file):
|
||||
raise FileNotFoundError(rom_file)
|
||||
@@ -721,8 +721,7 @@ def create_locations(self, player: int):
|
||||
|
||||
|
||||
def create_region(self, world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
||||
ret = Region(name, RegionType.LightWorld, name, player)
|
||||
ret.multiworld = world
|
||||
ret = Region(name, player, world)
|
||||
if locations:
|
||||
for loc in locations:
|
||||
location = self.locations[loc]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import typing
|
||||
from BaseClasses import MultiWorld, Region, Entrance, Location, RegionType
|
||||
from BaseClasses import MultiWorld, Region, Entrance, Location
|
||||
from .Locations import SM64Location, location_table, locBoB_table, locWhomp_table, locJRB_table, locCCM_table, \
|
||||
locBBH_table, \
|
||||
locHMC_table, locLLL_table, locSSL_table, locDDD_table, locSL_table, \
|
||||
@@ -27,7 +27,7 @@ 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)
|
||||
regSS = Region("Menu", player, world, "Castle Area")
|
||||
create_default_locs(regSS, locSS_table, player)
|
||||
world.regions.append(regSS)
|
||||
|
||||
@@ -179,7 +179,7 @@ def connect_regions(world: MultiWorld, player: int, source: str, target: str, ru
|
||||
connection.connect(targetRegion)
|
||||
|
||||
def create_region(name: str, player: int, world: MultiWorld) -> Region:
|
||||
return Region(name, RegionType.Generic, name, player, world)
|
||||
return Region(name, player, world)
|
||||
|
||||
def create_default_locs(reg: Region, locs, player):
|
||||
reg_names = [name for name, id in locs.items()]
|
||||
|
||||
@@ -136,6 +136,15 @@ valid_background_colors = {
|
||||
0xFFF45A: [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07], # Castle
|
||||
}
|
||||
|
||||
valid_ow_palettes = {
|
||||
0x2D1E: [0x00, 0x01, 0x03], # Main OW
|
||||
0x2D1F: [0x00, 0x03, 0x04], # Yoshi's Island
|
||||
0x2D20: [0x00, 0x01, 0x03, 0x04], # Vanilla Dome
|
||||
0x2D21: [0x00, 0x02, 0x03, 0x04], # Forest of Illusion
|
||||
0x2D22: [0x00, 0x01, 0x03, 0x04], # Valley of Bowser
|
||||
0x2D24: [0x00, 0x02, 0x03], # Star Road
|
||||
}
|
||||
|
||||
def generate_shuffled_level_music(world, player):
|
||||
shuffled_level_music = level_music_value_data.copy()
|
||||
|
||||
@@ -158,6 +167,12 @@ def generate_shuffled_ow_music(world, player):
|
||||
|
||||
return shuffled_ow_music
|
||||
|
||||
def generate_shuffled_ow_palettes(rom, world, player):
|
||||
if world.overworld_palette_shuffle[player]:
|
||||
for address, valid_palettes in valid_ow_palettes.items():
|
||||
chosen_palette = world.random.choice(valid_palettes)
|
||||
rom.write_byte(address, chosen_palette)
|
||||
|
||||
def generate_shuffled_header_data(rom, world, player):
|
||||
if world.music_shuffle[player] != "full" and not world.foreground_palette_shuffle[player] and not world.background_palette_shuffle[player]:
|
||||
return
|
||||
|
||||
+78
-4
@@ -169,7 +169,74 @@ class SMWSNIClient(SNIClient):
|
||||
|
||||
await snes_flush_writes(ctx)
|
||||
|
||||
return
|
||||
|
||||
def add_trap_to_queue(self, trap_item, trap_msg):
|
||||
self.trap_queue = getattr(self, "trap_queue", [])
|
||||
|
||||
self.trap_queue.append((trap_item, trap_msg))
|
||||
|
||||
|
||||
async def handle_trap_queue(self, ctx):
|
||||
from SNIClient import snes_buffered_write, snes_flush_writes, snes_read
|
||||
|
||||
if not hasattr(self, "trap_queue") or len(self.trap_queue) == 0:
|
||||
return
|
||||
|
||||
game_state = await snes_read(ctx, SMW_GAME_STATE_ADDR, 0x1)
|
||||
if game_state[0] != 0x14:
|
||||
return
|
||||
|
||||
mario_state = await snes_read(ctx, SMW_MARIO_STATE_ADDR, 0x1)
|
||||
if mario_state[0] != 0x00:
|
||||
return
|
||||
|
||||
pause_state = await snes_read(ctx, SMW_PAUSE_ADDR, 0x1)
|
||||
if pause_state[0] != 0x00:
|
||||
return
|
||||
|
||||
next_trap, message = self.trap_queue.pop(0)
|
||||
|
||||
from worlds.smw.Rom import trap_rom_data
|
||||
if next_trap.item in trap_rom_data:
|
||||
trap_active = await snes_read(ctx, WRAM_START + trap_rom_data[next_trap.item][0], 0x3)
|
||||
|
||||
if next_trap.item == 0xBC0016:
|
||||
# Timer Trap
|
||||
if trap_active[0] == 0 or (trap_active[0] == 1 and trap_active[1] == 0 and trap_active[2] == 0):
|
||||
# Trap already active
|
||||
self.add_trap_to_queue(next_trap, message)
|
||||
return
|
||||
else:
|
||||
snes_buffered_write(ctx, WRAM_START + trap_rom_data[next_trap.item][0], bytes([0x01]))
|
||||
snes_buffered_write(ctx, WRAM_START + trap_rom_data[next_trap.item][0] + 1, bytes([0x00]))
|
||||
snes_buffered_write(ctx, WRAM_START + trap_rom_data[next_trap.item][0] + 2, bytes([0x00]))
|
||||
else:
|
||||
if trap_active[0] > 0:
|
||||
# Trap already active
|
||||
self.add_trap_to_queue(next_trap, message)
|
||||
return
|
||||
else:
|
||||
verify_game_state = await snes_read(ctx, SMW_GAME_STATE_ADDR, 0x1)
|
||||
if verify_game_state[0] == 0x14 and len(trap_rom_data[next_trap.item]) > 2:
|
||||
snes_buffered_write(ctx, SMW_SFX_ADDR, bytes([trap_rom_data[next_trap.item][2]]))
|
||||
|
||||
new_item_count = trap_rom_data[next_trap.item][1]
|
||||
snes_buffered_write(ctx, WRAM_START + trap_rom_data[next_trap.item][0], bytes([new_item_count]))
|
||||
|
||||
current_level = await snes_read(ctx, SMW_CURRENT_LEVEL_ADDR, 0x1)
|
||||
if current_level[0] in SMW_BAD_TEXT_BOX_LEVELS:
|
||||
return
|
||||
|
||||
boss_state = await snes_read(ctx, SMW_BOSS_STATE_ADDR, 0x1)
|
||||
if boss_state[0] in SMW_BOSS_STATES:
|
||||
return
|
||||
|
||||
active_boss = await snes_read(ctx, SMW_ACTIVE_BOSS_ADDR, 0x1)
|
||||
if active_boss[0] != 0x00:
|
||||
return
|
||||
|
||||
if ctx.receive_option == 1 or (ctx.receive_option == 2 and ((next_trap.flags & 1) != 0)):
|
||||
self.add_message_to_queue(message)
|
||||
|
||||
|
||||
async def game_watcher(self, ctx):
|
||||
@@ -229,13 +296,14 @@ class SMWSNIClient(SNIClient):
|
||||
await snes_flush_writes(ctx)
|
||||
|
||||
await self.handle_message_queue(ctx)
|
||||
await self.handle_trap_queue(ctx)
|
||||
|
||||
new_checks = []
|
||||
event_data = await snes_read(ctx, SMW_EVENT_ROM_DATA, 0x60)
|
||||
progress_data = bytearray(await snes_read(ctx, SMW_PROGRESS_DATA, 0x0F))
|
||||
dragon_coins_data = bytearray(await snes_read(ctx, SMW_DRAGON_COINS_DATA, 0x0C))
|
||||
dragon_coins_active = await snes_read(ctx, SMW_DRAGON_COINS_ACTIVE_ADDR, 0x1)
|
||||
from worlds.smw.Rom import item_rom_data, ability_rom_data
|
||||
from worlds.smw.Rom import item_rom_data, ability_rom_data, trap_rom_data
|
||||
from worlds.smw.Levels import location_id_to_level_id, level_info_dict
|
||||
from worlds import AutoWorldRegister
|
||||
for loc_name, level_data in location_id_to_level_id.items():
|
||||
@@ -307,7 +375,7 @@ class SMWSNIClient(SNIClient):
|
||||
ctx.location_names[item.location], recv_index, len(ctx.items_received)))
|
||||
|
||||
if ctx.receive_option == 1 or (ctx.receive_option == 2 and ((item.flags & 1) != 0)):
|
||||
if item.item != 0xBC0012:
|
||||
if item.item != 0xBC0012 and item.item not in trap_rom_data:
|
||||
# Don't send messages for Boss Tokens
|
||||
item_name = ctx.item_names[item.item]
|
||||
player_name = ctx.player_names[item.player]
|
||||
@@ -316,7 +384,13 @@ class SMWSNIClient(SNIClient):
|
||||
self.add_message_to_queue(receive_message)
|
||||
|
||||
snes_buffered_write(ctx, SMW_RECV_PROGRESS_ADDR, bytes([recv_index]))
|
||||
if item.item in item_rom_data:
|
||||
if item.item in trap_rom_data:
|
||||
item_name = ctx.item_names[item.item]
|
||||
player_name = ctx.player_names[item.player]
|
||||
|
||||
receive_message = generate_received_text(item_name, player_name)
|
||||
self.add_trap_to_queue(item, receive_message)
|
||||
elif item.item in item_rom_data:
|
||||
item_count = await snes_read(ctx, WRAM_START + item_rom_data[item.item][0], 0x1)
|
||||
increment = item_rom_data[item.item][1]
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ trap_table = {
|
||||
ItemName.ice_trap: ItemData(0xBC0013, False, True),
|
||||
ItemName.stun_trap: ItemData(0xBC0014, False, True),
|
||||
ItemName.literature_trap: ItemData(0xBC0015, False, True),
|
||||
ItemName.timer_trap: ItemData(0xBC0016, False, True),
|
||||
}
|
||||
|
||||
event_table = {
|
||||
|
||||
+91
-8
@@ -1,6 +1,80 @@
|
||||
|
||||
from .Names import LocationName
|
||||
|
||||
|
||||
class BowserRoom():
|
||||
name: str
|
||||
exitAddress: int
|
||||
roomID: int
|
||||
|
||||
def __init__(self, name: str, exitAddress: int, roomID: int):
|
||||
self.name = name
|
||||
self.exitAddress = exitAddress
|
||||
self.roomID = roomID
|
||||
|
||||
full_bowser_rooms = [
|
||||
BowserRoom("Hallway 1 - Door 1", 0x3A680, 0x0D),
|
||||
BowserRoom("Hallway 1 - Door 2", 0x3A684, 0x0D),
|
||||
BowserRoom("Hallway 1 - Door 3", 0x3A688, 0x0D),
|
||||
BowserRoom("Hallway 1 - Door 4", 0x3A68C, 0x0D),
|
||||
BowserRoom("Hallway 2 - Door 1", 0x3A8CB, 0xD0),
|
||||
BowserRoom("Hallway 2 - Door 2", 0x3A8CF, 0xD0),
|
||||
BowserRoom("Hallway 2 - Door 3", 0x3A8D3, 0xD0),
|
||||
BowserRoom("Hallway 2 - Door 4", 0x3A8D7, 0xD0),
|
||||
|
||||
BowserRoom("Room 1", 0x3A705, 0xD4),
|
||||
BowserRoom("Room 2", 0x3A763, 0xD3),
|
||||
BowserRoom("Room 3", 0x3A800, 0xD2),
|
||||
BowserRoom("Room 4", 0x3A83D, 0xD1),
|
||||
BowserRoom("Room 5", 0x3A932, 0xCF),
|
||||
BowserRoom("Room 6", 0x3A9E1, 0xCE),
|
||||
BowserRoom("Room 7", 0x3AA75, 0xCD),
|
||||
BowserRoom("Room 8", 0x3AAC7, 0xCC),
|
||||
]
|
||||
|
||||
standard_bowser_rooms = [
|
||||
BowserRoom("Room 1", 0x3A705, 0xD4),
|
||||
BowserRoom("Room 2", 0x3A763, 0xD3),
|
||||
BowserRoom("Room 3", 0x3A800, 0xD2),
|
||||
BowserRoom("Room 4", 0x3A83D, 0xD1),
|
||||
BowserRoom("Room 5", 0x3A932, 0xCF),
|
||||
BowserRoom("Room 6", 0x3A9E1, 0xCE),
|
||||
BowserRoom("Room 7", 0x3AA75, 0xCD),
|
||||
BowserRoom("Room 8", 0x3AAC7, 0xCC),
|
||||
]
|
||||
|
||||
|
||||
class BossRoom():
|
||||
name: str
|
||||
exitAddress: int
|
||||
exitAddressAlt: int
|
||||
roomID: int
|
||||
|
||||
def __init__(self, name: str, exitAddress: int, roomID: int, exitAddressAlt=None):
|
||||
self.name = name
|
||||
self.exitAddress = exitAddress
|
||||
self.roomID = roomID
|
||||
self.exitAddressAlt = exitAddressAlt
|
||||
|
||||
|
||||
submap_boss_rooms = [
|
||||
BossRoom("#1 Lemmy Koopa", 0x311E3, 0xF6), # Submap 0x1F6
|
||||
BossRoom("#3 Lemmy Koopa", 0x33749, 0xF2), # Submap 0x1F2
|
||||
BossRoom("Valley Reznor", 0x3A132, 0xDE), # Submap 0x1DE
|
||||
BossRoom("#7 Larry Koopa", 0x3A026, 0xEB), # Submap 0x1EB
|
||||
]
|
||||
|
||||
ow_boss_rooms = [
|
||||
BossRoom("#2 Morton Koopa Jr.", 0x3209B, 0xE5), # OW 0x0E5
|
||||
BossRoom("Vanilla Reznor", 0x33EAB, 0xDF), # OW 0x0DF
|
||||
BossRoom("#4 Ludwig von Koopa", 0x346EA, 0xD9), # OW 0x0D9
|
||||
BossRoom("Forest Reznor", 0x3643E, 0xD5, 0x36442), # OW 0x0D5
|
||||
BossRoom("#5 Roy Koopa", 0x35ABC, 0xCC), # OW 0x0CC
|
||||
BossRoom("Chocolate Reznor", 0x3705B, 0xE2), # OW 0x0E2
|
||||
BossRoom("#6 Wendy O. Koopa", 0x38BB5, 0xD3), # OW 0x0D3
|
||||
]
|
||||
|
||||
|
||||
class SMWPath():
|
||||
thisEndDirection: int
|
||||
otherLevelID: int
|
||||
@@ -203,6 +277,9 @@ hard_single_levels = [
|
||||
0x3B,
|
||||
0x3A,
|
||||
0x37,
|
||||
]
|
||||
|
||||
special_zone_levels = [
|
||||
0x4E,
|
||||
0x4F,
|
||||
0x50,
|
||||
@@ -443,6 +520,7 @@ def generate_level_list(world, player):
|
||||
world.random.shuffle(easy_single_levels_copy)
|
||||
hard_single_levels_copy = hard_single_levels.copy()
|
||||
world.random.shuffle(hard_single_levels_copy)
|
||||
special_zone_levels_copy = special_zone_levels.copy()
|
||||
easy_double_levels_copy = easy_double_levels.copy()
|
||||
world.random.shuffle(easy_double_levels_copy)
|
||||
hard_double_levels_copy = hard_double_levels.copy()
|
||||
@@ -474,6 +552,8 @@ def generate_level_list(world, player):
|
||||
shuffled_level_list.append(0x16)
|
||||
|
||||
single_levels_copy = (easy_single_levels_copy.copy() + hard_single_levels_copy.copy())
|
||||
if not world.exclude_special_zone[player]:
|
||||
single_levels_copy.extend(special_zone_levels_copy)
|
||||
world.random.shuffle(single_levels_copy)
|
||||
|
||||
castle_fortress_levels_copy = (easy_castle_fortress_levels_copy.copy() + hard_castle_fortress_levels_copy.copy())
|
||||
@@ -566,14 +646,17 @@ def generate_level_list(world, player):
|
||||
|
||||
# Special Zone
|
||||
shuffled_level_list.append(0x4D)
|
||||
shuffled_level_list.append(single_levels_copy.pop(0))
|
||||
shuffled_level_list.append(single_levels_copy.pop(0))
|
||||
shuffled_level_list.append(single_levels_copy.pop(0))
|
||||
shuffled_level_list.append(single_levels_copy.pop(0))
|
||||
shuffled_level_list.append(single_levels_copy.pop(0))
|
||||
shuffled_level_list.append(single_levels_copy.pop(0))
|
||||
shuffled_level_list.append(single_levels_copy.pop(0))
|
||||
shuffled_level_list.append(single_levels_copy.pop(0))
|
||||
if not world.exclude_special_zone[player]:
|
||||
shuffled_level_list.append(single_levels_copy.pop(0))
|
||||
shuffled_level_list.append(single_levels_copy.pop(0))
|
||||
shuffled_level_list.append(single_levels_copy.pop(0))
|
||||
shuffled_level_list.append(single_levels_copy.pop(0))
|
||||
shuffled_level_list.append(single_levels_copy.pop(0))
|
||||
shuffled_level_list.append(single_levels_copy.pop(0))
|
||||
shuffled_level_list.append(single_levels_copy.pop(0))
|
||||
shuffled_level_list.append(single_levels_copy.pop(0))
|
||||
else:
|
||||
shuffled_level_list.extend(special_zone_levels_copy)
|
||||
shuffled_level_list.append(0x48)
|
||||
|
||||
return shuffled_level_list
|
||||
|
||||
@@ -212,6 +212,28 @@ all_locations = {
|
||||
**yoshi_house_location_table,
|
||||
}
|
||||
|
||||
special_zone_level_names = [
|
||||
LocationName.special_zone_1_exit_1,
|
||||
LocationName.special_zone_2_exit_1,
|
||||
LocationName.special_zone_3_exit_1,
|
||||
LocationName.special_zone_4_exit_1,
|
||||
LocationName.special_zone_5_exit_1,
|
||||
LocationName.special_zone_6_exit_1,
|
||||
LocationName.special_zone_7_exit_1,
|
||||
LocationName.special_zone_8_exit_1,
|
||||
]
|
||||
|
||||
special_zone_dragon_coin_names = [
|
||||
LocationName.special_zone_1_dragon,
|
||||
LocationName.special_zone_2_dragon,
|
||||
LocationName.special_zone_3_dragon,
|
||||
LocationName.special_zone_4_dragon,
|
||||
LocationName.special_zone_5_dragon,
|
||||
LocationName.special_zone_6_dragon,
|
||||
LocationName.special_zone_7_dragon,
|
||||
LocationName.special_zone_8_dragon,
|
||||
]
|
||||
|
||||
location_table = {}
|
||||
|
||||
|
||||
|
||||
@@ -26,6 +26,7 @@ blue_switch_palace = "Blue Switch Palace"
|
||||
ice_trap = "Ice Trap"
|
||||
stun_trap = "Stun Trap"
|
||||
literature_trap = "Literature Trap"
|
||||
timer_trap = "Timer Trap"
|
||||
|
||||
# Other Definitions
|
||||
victory = "The Princess"
|
||||
|
||||
@@ -25,7 +25,7 @@ lit_trap_text_list = [
|
||||
[[0x18, 0x4e, 0x54, 0x1f, 0x47, 0x40, 0x55, 0x44, 0x1f, 0x41, 0x51, 0x40, 0x48, 0x4d, 0x52, 0x1f, 0x48, 0xcd, 0x58, 0x4e, 0x54, 0x51, 0x1f, 0x47, 0x44, 0x40, 0x43, 0x1b, 0x1f, 0x18, 0x4e, 0x54, 0x9f, 0x47, 0x40, 0x55, 0x44, 0x1f, 0x45, 0x44, 0x44, 0x53, 0x1f, 0x48, 0x4d, 0x1f, 0x58, 0x4e, 0x54, 0x51, 0x9f, 0x52, 0x47, 0x4e, 0x44, 0x52, 0x1b, 0x1f, 0x18, 0x4e, 0x54, 0x1f, 0x42, 0x40, 0x4d, 0x9f, 0x52, 0x53, 0x44, 0x44, 0x51, 0x1f, 0x58, 0x4e, 0x54, 0x51, 0x52, 0x44, 0x4b, 0x45, 0x1f, 0x40, 0x4d, 0xd8, 0x43, 0x48, 0x51, 0x44, 0x42, 0x53, 0x48, 0x4e, 0x4d, 0x1f, 0x58, 0x4e, 0x54, 0x9f, 0x42, 0x47, 0x4e, 0x4e, 0x52, 0x44, 0x1b, 0x1f, 0x18, 0x4e, 0x54, 0x5d, 0x51, 0x44, 0x1f, 0x4e, 0x4d, 0x9f, 0x58, 0x4e, 0x54, 0x51, 0x1f, 0x4e, 0x56, 0x4d, 0x1b, 0x1f, 0x0, 0x4d, 0x43, 0x1f, 0x58, 0x4e, 0x54, 0x9f, ], [0x4a, 0x4d, 0x4e, 0x56, 0x1f, 0x56, 0x47, 0x40, 0x53, 0x1f, 0x58, 0x4e, 0x54, 0x9f, 0x4a, 0x4d, 0x4e, 0x56, 0x1b, 0x1f, 0x0, 0x4d, 0x43, 0x1f, 0x18, 0xe, 0x14, 0x1f, 0x40, 0x51, 0x44, 0x9f, 0x53, 0x47, 0x44, 0x1f, 0x4e, 0x4d, 0x44, 0x1f, 0x56, 0x47, 0x4e, 0x5d, 0x4b, 0x4b, 0x9f, 0x43, 0x44, 0x42, 0x48, 0x43, 0x44, 0x1f, 0x56, 0x47, 0x44, 0x51, 0x44, 0x1f, 0x53, 0x4e, 0x9f, 0x46, 0x4e, 0x1b, 0x1b, 0x1b, 0x1f, 0x1c, 0x12, 0x54, 0x44, 0x52, 0x52, 0x1f, 0x9f, 0x9f, 0x9f, 0x9f, ]],
|
||||
[[0x16, 0x47, 0x44, 0x4d, 0x1f, 0x58, 0x4e, 0x54, 0x1f, 0x47, 0x40, 0x55, 0x44, 0x9f, 0x44, 0x4b, 0x48, 0x4c, 0x48, 0x4d, 0x40, 0x53, 0x44, 0x43, 0x1f, 0x40, 0x4b, 0x4b, 0x9f, 0x56, 0x47, 0x48, 0x42, 0x47, 0x1f, 0x48, 0x52, 0x9f, 0x48, 0x4c, 0x4f, 0x4e, 0x52, 0x52, 0x48, 0x41, 0x4b, 0x44, 0x1d, 0x1f, 0x53, 0x47, 0x44, 0x4d, 0x9f, 0x56, 0x47, 0x40, 0x53, 0x44, 0x55, 0x44, 0x51, 0x1f, 0x51, 0x44, 0x4c, 0x40, 0x48, 0x4d, 0x52, 0x1d, 0x9f, 0x47, 0x4e, 0x56, 0x44, 0x55, 0x44, 0x51, 0x9f, 0x48, 0x4c, 0x4f, 0x51, 0x4e, 0x41, 0x40, 0x41, 0x4b, 0x44, 0x1d, 0x1f, 0x4c, 0x54, 0x52, 0x53, 0x9f, 0x41, 0x44, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x53, 0x51, 0x54, 0x53, 0x47, 0x1b, 0x9f, ], [0x1c, 0x3, 0x4e, 0x58, 0x4b, 0x44, 0x1f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, ]],
|
||||
[[0x5d, 0xc, 0x58, 0x1f, 0x4c, 0x48, 0x4d, 0x43, 0x1d, 0x5d, 0x1f, 0x47, 0x44, 0x9f, 0x52, 0x40, 0x48, 0x43, 0x1d, 0x1f, 0x5d, 0x51, 0x44, 0x41, 0x44, 0x4b, 0x52, 0x1f, 0x40, 0x53, 0x9f, 0x52, 0x53, 0x40, 0x46, 0x4d, 0x40, 0x53, 0x48, 0x4e, 0x4d, 0x1b, 0x1f, 0x6, 0x48, 0x55, 0x44, 0x9f, 0x4c, 0x44, 0x1f, 0x4f, 0x51, 0x4e, 0x41, 0x4b, 0x44, 0x4c, 0x52, 0x1d, 0x1f, 0x46, 0x48, 0x55, 0x44, 0x9f, 0x4c, 0x44, 0x1f, 0x56, 0x4e, 0x51, 0x4a, 0x1d, 0x1f, 0x46, 0x48, 0x55, 0x44, 0x1f, 0x4c, 0x44, 0x9f, 0x53, 0x47, 0x44, 0x1f, 0x4c, 0x4e, 0x52, 0x53, 0x1f, 0x40, 0x41, 0x52, 0x53, 0x51, 0x54, 0x52, 0x44, 0x9f, 0x42, 0x51, 0x58, 0x4f, 0x53, 0x4e, 0x46, 0x51, 0x40, 0x4c, 0x1f, 0x4e, 0x51, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x4c, 0x4e, 0x52, 0x53, 0x1f, 0x48, 0x4d, 0x53, 0x51, 0x48, 0x42, 0x40, 0x53, 0x44, 0x9f, ], [0x40, 0x4d, 0x40, 0x4b, 0x58, 0x52, 0x48, 0x52, 0x1d, 0x1f, 0x40, 0x4d, 0x43, 0x1f, 0x8, 0x1f, 0x40, 0xcc, 0x48, 0x4d, 0x1f, 0x4c, 0x58, 0x1f, 0x4e, 0x56, 0x4d, 0x1f, 0x4f, 0x51, 0x4e, 0x4f, 0x44, 0x51, 0x9f, 0x40, 0x53, 0x4c, 0x4e, 0x52, 0x4f, 0x47, 0x44, 0x51, 0x44, 0x1b, 0x1f, 0x8, 0x1f, 0x42, 0x40, 0x4d, 0x9f, 0x43, 0x48, 0x52, 0x4f, 0x44, 0x4d, 0x52, 0x44, 0x1f, 0x53, 0x47, 0x44, 0x4d, 0x1f, 0x56, 0x48, 0x53, 0xc7, 0x40, 0x51, 0x53, 0x48, 0x45, 0x48, 0x42, 0x48, 0x40, 0x4b, 0x9f, 0x52, 0x53, 0x48, 0x4c, 0x54, 0x4b, 0x40, 0x4d, 0x53, 0x52, 0x1b, 0x1f, 0x1, 0x54, 0x53, 0x1f, 0x8, 0x9f, 0x40, 0x41, 0x47, 0x4e, 0x51, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x43, 0x54, 0x4b, 0x4b, 0x9f, 0x51, 0x4e, 0x54, 0x53, 0x48, 0x4d, 0x44, 0x1f, 0x4e, 0x45, 0x9f, ], [0x44, 0x57, 0x48, 0x52, 0x53, 0x44, 0x4d, 0x42, 0x44, 0x1b, 0x1f, 0x8, 0x1f, 0x42, 0x51, 0x40, 0x55, 0xc4, 0x45, 0x4e, 0x51, 0x1f, 0x4c, 0x44, 0x4d, 0x53, 0x40, 0x4b, 0x9f, 0x44, 0x57, 0x40, 0x4b, 0x53, 0x40, 0x53, 0x48, 0x4e, 0x4d, 0x1b, 0x1f, 0x13, 0x47, 0x40, 0x53, 0x9f, 0x48, 0x52, 0x1f, 0x56, 0x47, 0x58, 0x1f, 0x8, 0x1f, 0x47, 0x40, 0x55, 0x44, 0x9f, 0x42, 0x47, 0x4e, 0x52, 0x44, 0x4d, 0x1f, 0x4c, 0x58, 0x1f, 0x4e, 0x56, 0x4d, 0x9f, 0x4f, 0x40, 0x51, 0x53, 0x48, 0x42, 0x54, 0x4b, 0x40, 0x51, 0x9f, 0x4f, 0x51, 0x4e, 0x45, 0x44, 0x52, 0x52, 0x48, 0x4e, 0x4d, 0x1d, 0x1f, 0x4e, 0x51, 0x9f, 0x51, 0x40, 0x53, 0x47, 0x44, 0x51, 0x1f, 0x42, 0x51, 0x44, 0x40, 0x53, 0x44, 0x43, 0x1f, 0x48, 0x53, 0x9d, ], [0x45, 0x4e, 0x51, 0x1f, 0x8, 0x1f, 0x40, 0x4c, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x4e, 0x4d, 0x4b, 0x58, 0x9f, 0x4e, 0x4d, 0x44, 0x1f, 0x48, 0x4d, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x56, 0x4e, 0x51, 0x4b, 0x43, 0x1b, 0xdd, 0x1c, 0x3, 0x4e, 0x58, 0x4b, 0x44, 0x1f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, ]],
|
||||
[[0xb, 0x48, 0x45, 0x44, 0x1f, 0x48, 0x52, 0x1f, 0x48, 0x4d, 0x45, 0x48, 0x4d, 0x48, 0x53, 0x44, 0x4b, 0xd8, 0x52, 0x53, 0x51, 0x40, 0x4d, 0x46, 0x44, 0x51, 0x1f, 0x53, 0x47, 0x40, 0x4d, 0x9f, 0x40, 0x4d, 0x58, 0x53, 0x47, 0x48, 0x4d, 0x46, 0x1f, 0x56, 0x47, 0x48, 0x42, 0x47, 0x1f, 0x53, 0x47, 0xc4, 0x4c, 0x48, 0x4d, 0x43, 0x1f, 0x4e, 0x45, 0x1f, 0x4c, 0x40, 0x4d, 0x1f, 0x42, 0x4e, 0x54, 0x4b, 0x43, 0x9f, 0x48, 0x4d, 0x55, 0x44, 0x4d, 0x53, 0x1b, 0x1f, 0x16, 0x44, 0x1f, 0x56, 0x4e, 0x54, 0x4b, 0x43, 0x9f, 0x4d, 0x4e, 0x53, 0x1f, 0x43, 0x40, 0x51, 0x44, 0x1f, 0x53, 0x4e, 0x9f, 0x42, 0x4e, 0x4d, 0x42, 0x44, 0x48, 0x55, 0x44, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x53, 0x47, 0x48, 0x4d, 0x46, 0x52, 0x1f, 0x56, 0x47, 0x48, 0x42, 0x47, 0x1f, 0x40, 0x51, 0x44, 0x9f, ], [0x51, 0x44, 0x40, 0x4b, 0x4b, 0x58, 0x1f, 0x4c, 0x44, 0x51, 0x44, 0x9f, 0x42, 0x4e, 0x4c, 0x4c, 0x4e, 0x4d, 0x4f, 0x4b, 0x40, 0x42, 0x44, 0x52, 0x1f, 0x4e, 0x45, 0x9f, 0x44, 0x57, 0x48, 0x52, 0x53, 0x44, 0x4d, 0x42, 0x44, 0x1b, 0x1f, 0x8, 0x45, 0x1f, 0x56, 0x44, 0x9f, 0x42, 0x4e, 0x54, 0x4b, 0x43, 0x1f, 0x45, 0x4b, 0x58, 0x1f, 0x4e, 0x54, 0x53, 0x1f, 0x4e, 0x45, 0x9f, 0x53, 0x47, 0x40, 0x53, 0x1f, 0x56, 0x48, 0x4d, 0x43, 0x4e, 0x56, 0x1f, 0x47, 0x40, 0x4d, 0x43, 0x9f, 0x48, 0x4d, 0x1f, 0x47, 0x40, 0x4d, 0x43, 0x1d, 0x1f, 0x47, 0x4e, 0x55, 0x44, 0x51, 0x9f, 0x4e, 0x55, 0x44, 0x51, 0x1f, 0x53, 0x47, 0x48, 0x52, 0x1f, 0x46, 0x51, 0x44, 0x40, 0x53, 0x9f, 0x42, 0x48, 0x53, 0x58, 0x1d, 0x1f, 0x46, 0x44, 0x4d, 0x53, 0x4b, 0x58, 0x9f, ], [0x51, 0x44, 0x4c, 0x4e, 0x55, 0x44, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x51, 0x4e, 0x4e, 0x45, 0x52, 0x1d, 0x9f, 0x40, 0x4d, 0x43, 0x1f, 0x40, 0x4d, 0x43, 0x1f, 0x4f, 0x44, 0x44, 0x4f, 0x1f, 0x48, 0x4d, 0x1f, 0x40, 0xd3, 0x53, 0x47, 0x44, 0x1f, 0x50, 0x54, 0x44, 0x44, 0x51, 0x1f, 0x53, 0x47, 0x48, 0x4d, 0x46, 0x52, 0x9f, 0x56, 0x47, 0x48, 0x42, 0x47, 0x1f, 0x40, 0x51, 0x44, 0x1f, 0x46, 0x4e, 0x48, 0x4d, 0x46, 0x9f, 0x4e, 0x4d, 0x1d, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x52, 0x53, 0x51, 0x40, 0x4d, 0x46, 0x44, 0x9f, 0x42, 0x4e, 0x48, 0x4d, 0x42, 0x48, 0x43, 0x44, 0x4d, 0x42, 0x44, 0x52, 0x1d, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x4f, 0x4b, 0x40, 0x4d, 0x4d, 0x48, 0x4d, 0x46, 0x52, 0x1d, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x42, 0x51, 0x4e, 0x52, 0x52, 0x1c, 0x4f, 0x54, 0x51, 0x4f, 0x4e, 0x52, 0x44, 0x52, 0x1d, 0x9f, ], [0x53, 0x47, 0x44, 0x1f, 0x56, 0x4e, 0x4d, 0x43, 0x44, 0x51, 0x45, 0x54, 0x4b, 0x9f, 0x42, 0x47, 0x40, 0x48, 0x4d, 0x52, 0x1f, 0x4e, 0x45, 0x1f, 0x44, 0x55, 0x44, 0x4d, 0x53, 0x52, 0x1d, 0x9f, 0x56, 0x4e, 0x51, 0x4a, 0x48, 0x4d, 0x46, 0x1f, 0x53, 0x47, 0x51, 0x4e, 0x54, 0x46, 0x47, 0x9f, 0x46, 0x44, 0x4d, 0x44, 0x51, 0x40, 0x53, 0x48, 0x4e, 0x4d, 0x52, 0x1d, 0x1f, 0x40, 0x4d, 0x43, 0x9f, 0x4b, 0x44, 0x40, 0x43, 0x48, 0x4d, 0x46, 0x1f, 0x53, 0x4e, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x4c, 0x4e, 0x52, 0x53, 0x1f, 0x4e, 0x54, 0x53, 0x51, 0x44, 0x9f, 0x51, 0x44, 0x52, 0x54, 0x4b, 0x53, 0x52, 0x1d, 0x1f, 0x48, 0x53, 0x1f, 0x56, 0x4e, 0x54, 0x4b, 0x43, 0x9f, 0x4c, 0x40, 0x4a, 0x44, 0x1f, 0x40, 0x4b, 0x4b, 0x1f, 0x45, 0x48, 0x42, 0x53, 0x48, 0x4e, 0x4d, 0x9f, ], [0x56, 0x48, 0x53, 0x47, 0x1f, 0x48, 0x53, 0x52, 0x9f, 0x42, 0x4e, 0x4d, 0x55, 0x44, 0x4d, 0x53, 0x48, 0x4e, 0x4d, 0x40, 0x4b, 0x48, 0x53, 0x48, 0x44, 0x52, 0x9f, 0x40, 0x4d, 0x43, 0x1f, 0x45, 0x4e, 0x51, 0x44, 0x52, 0x44, 0x44, 0x4d, 0x9f, 0x42, 0x4e, 0x4d, 0x42, 0x4b, 0x54, 0x52, 0x48, 0x4e, 0x4d, 0x52, 0x1f, 0x4c, 0x4e, 0x52, 0x53, 0x9f, 0x52, 0x53, 0x40, 0x4b, 0x44, 0x1f, 0x40, 0x4d, 0x43, 0x9f, 0x54, 0x4d, 0x4f, 0x51, 0x4e, 0x45, 0x48, 0x53, 0x40, 0x41, 0x4b, 0x44, 0x1b, 0x9f, 0x1c, 0x3, 0x4e, 0x58, 0x4b, 0x44, 0x1f, 0x9f, 0x9f, ]],
|
||||
[[0xb, 0x48, 0x45, 0x44, 0x1f, 0x48, 0x52, 0x1f, 0x48, 0x4d, 0x45, 0x48, 0x4d, 0x48, 0x53, 0x44, 0x4b, 0xd8, 0x52, 0x53, 0x51, 0x40, 0x4d, 0x46, 0x44, 0x51, 0x1f, 0x53, 0x47, 0x40, 0x4d, 0x9f, 0x40, 0x4d, 0x58, 0x53, 0x47, 0x48, 0x4d, 0x46, 0x1f, 0x56, 0x47, 0x48, 0x42, 0x47, 0x1f, 0x53, 0x47, 0xc4, 0x4c, 0x48, 0x4d, 0x43, 0x1f, 0x4e, 0x45, 0x1f, 0x4c, 0x40, 0x4d, 0x1f, 0x42, 0x4e, 0x54, 0x4b, 0x43, 0x9f, 0x48, 0x4d, 0x55, 0x44, 0x4d, 0x53, 0x1b, 0x1f, 0x16, 0x44, 0x1f, 0x56, 0x4e, 0x54, 0x4b, 0x43, 0x9f, 0x4d, 0x4e, 0x53, 0x1f, 0x43, 0x40, 0x51, 0x44, 0x1f, 0x53, 0x4e, 0x9f, 0x42, 0x4e, 0x4d, 0x42, 0x44, 0x48, 0x55, 0x44, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x53, 0x47, 0x48, 0x4d, 0x46, 0x52, 0x1f, 0x56, 0x47, 0x48, 0x42, 0x47, 0x1f, 0x40, 0x51, 0x44, 0x9f, ], [0x51, 0x44, 0x40, 0x4b, 0x4b, 0x58, 0x1f, 0x4c, 0x44, 0x51, 0x44, 0x9f, 0x42, 0x4e, 0x4c, 0x4c, 0x4e, 0x4d, 0x4f, 0x4b, 0x40, 0x42, 0x44, 0x52, 0x1f, 0x4e, 0x45, 0x9f, 0x44, 0x57, 0x48, 0x52, 0x53, 0x44, 0x4d, 0x42, 0x44, 0x1b, 0x1f, 0x8, 0x45, 0x1f, 0x56, 0x44, 0x9f, 0x42, 0x4e, 0x54, 0x4b, 0x43, 0x1f, 0x45, 0x4b, 0x58, 0x1f, 0x4e, 0x54, 0x53, 0x1f, 0x4e, 0x45, 0x9f, 0x53, 0x47, 0x40, 0x53, 0x1f, 0x56, 0x48, 0x4d, 0x43, 0x4e, 0x56, 0x1f, 0x47, 0x40, 0x4d, 0x43, 0x9f, 0x48, 0x4d, 0x1f, 0x47, 0x40, 0x4d, 0x43, 0x1d, 0x1f, 0x47, 0x4e, 0x55, 0x44, 0x51, 0x9f, 0x4e, 0x55, 0x44, 0x51, 0x1f, 0x53, 0x47, 0x48, 0x52, 0x1f, 0x46, 0x51, 0x44, 0x40, 0x53, 0x9f, 0x42, 0x48, 0x53, 0x58, 0x1d, 0x1f, 0x46, 0x44, 0x4d, 0x53, 0x4b, 0x58, 0x9f, ], [0x51, 0x44, 0x4c, 0x4e, 0x55, 0x44, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x51, 0x4e, 0x4e, 0x45, 0x52, 0x1d, 0x9f, 0x40, 0x4d, 0x43, 0x1f, 0x4f, 0x44, 0x44, 0x4f, 0x1f, 0x48, 0x4d, 0x1f, 0x40, 0xd3, 0x53, 0x47, 0x44, 0x1f, 0x50, 0x54, 0x44, 0x44, 0x51, 0x1f, 0x53, 0x47, 0x48, 0x4d, 0x46, 0x52, 0x9f, 0x56, 0x47, 0x48, 0x42, 0x47, 0x1f, 0x40, 0x51, 0x44, 0x1f, 0x46, 0x4e, 0x48, 0x4d, 0x46, 0x9f, 0x4e, 0x4d, 0x1d, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x52, 0x53, 0x51, 0x40, 0x4d, 0x46, 0x44, 0x9f, 0x42, 0x4e, 0x48, 0x4d, 0x42, 0x48, 0x43, 0x44, 0x4d, 0x42, 0x44, 0x52, 0x1d, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x4f, 0x4b, 0x40, 0x4d, 0x4d, 0x48, 0x4d, 0x46, 0x52, 0x1d, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x42, 0x51, 0x4e, 0x52, 0x52, 0x1c, 0x4f, 0x54, 0x51, 0x4f, 0x4e, 0x52, 0x44, 0x52, 0x1d, 0x9f, ], [0x53, 0x47, 0x44, 0x1f, 0x56, 0x4e, 0x4d, 0x43, 0x44, 0x51, 0x45, 0x54, 0x4b, 0x9f, 0x42, 0x47, 0x40, 0x48, 0x4d, 0x52, 0x1f, 0x4e, 0x45, 0x1f, 0x44, 0x55, 0x44, 0x4d, 0x53, 0x52, 0x1d, 0x9f, 0x56, 0x4e, 0x51, 0x4a, 0x48, 0x4d, 0x46, 0x1f, 0x53, 0x47, 0x51, 0x4e, 0x54, 0x46, 0x47, 0x9f, 0x46, 0x44, 0x4d, 0x44, 0x51, 0x40, 0x53, 0x48, 0x4e, 0x4d, 0x52, 0x1d, 0x1f, 0x40, 0x4d, 0x43, 0x9f, 0x4b, 0x44, 0x40, 0x43, 0x48, 0x4d, 0x46, 0x1f, 0x53, 0x4e, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x4c, 0x4e, 0x52, 0x53, 0x1f, 0x4e, 0x54, 0x53, 0x51, 0x44, 0x9f, 0x51, 0x44, 0x52, 0x54, 0x4b, 0x53, 0x52, 0x1d, 0x1f, 0x48, 0x53, 0x1f, 0x56, 0x4e, 0x54, 0x4b, 0x43, 0x9f, 0x4c, 0x40, 0x4a, 0x44, 0x1f, 0x40, 0x4b, 0x4b, 0x1f, 0x45, 0x48, 0x42, 0x53, 0x48, 0x4e, 0x4d, 0x9f, ], [0x56, 0x48, 0x53, 0x47, 0x1f, 0x48, 0x53, 0x52, 0x9f, 0x42, 0x4e, 0x4d, 0x55, 0x44, 0x4d, 0x53, 0x48, 0x4e, 0x4d, 0x40, 0x4b, 0x48, 0x53, 0x48, 0x44, 0x52, 0x9f, 0x40, 0x4d, 0x43, 0x1f, 0x45, 0x4e, 0x51, 0x44, 0x52, 0x44, 0x44, 0x4d, 0x9f, 0x42, 0x4e, 0x4d, 0x42, 0x4b, 0x54, 0x52, 0x48, 0x4e, 0x4d, 0x52, 0x1f, 0x4c, 0x4e, 0x52, 0x53, 0x9f, 0x52, 0x53, 0x40, 0x4b, 0x44, 0x1f, 0x40, 0x4d, 0x43, 0x9f, 0x54, 0x4d, 0x4f, 0x51, 0x4e, 0x45, 0x48, 0x53, 0x40, 0x41, 0x4b, 0x44, 0x1b, 0x9f, 0x1c, 0x3, 0x4e, 0x58, 0x4b, 0x44, 0x1f, 0x9f, 0x9f, ]],
|
||||
[[0x13, 0x47, 0x44, 0x1f, 0x52, 0x53, 0x4e, 0x51, 0x58, 0x1f, 0x52, 0x4e, 0x1f, 0x45, 0x40, 0x51, 0x1b, 0x9f, 0x8, 0x4d, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x41, 0x44, 0x46, 0x48, 0x4d, 0x4d, 0x48, 0x4d, 0x46, 0x9f, 0x53, 0x47, 0x44, 0x1f, 0x14, 0x4d, 0x48, 0x55, 0x44, 0x51, 0x52, 0x44, 0x1f, 0x56, 0x40, 0x52, 0x9f, 0x42, 0x51, 0x44, 0x40, 0x53, 0x44, 0x43, 0x1b, 0x1f, 0x13, 0x47, 0x48, 0x52, 0x1f, 0x47, 0x40, 0x52, 0x9f, 0x4c, 0x40, 0x43, 0x44, 0x1f, 0x40, 0x1f, 0x4b, 0x4e, 0x53, 0x1f, 0x4e, 0x45, 0x9f, 0x4f, 0x44, 0x4e, 0x4f, 0x4b, 0x44, 0x1f, 0x55, 0x44, 0x51, 0x58, 0x1f, 0x40, 0x4d, 0x46, 0x51, 0x58, 0x9f, 0x40, 0x4d, 0x43, 0x1f, 0x41, 0x44, 0x44, 0x4d, 0x1f, 0x56, 0x48, 0x43, 0x44, 0x4b, 0x58, 0x9f, 0x51, 0x44, 0x46, 0x40, 0x51, 0x43, 0x44, 0x43, 0x1f, 0x40, 0x52, 0x1f, 0x40, 0x1f, 0x41, 0x40, 0x43, 0x9f, ], [0x4c, 0x4e, 0x55, 0x44, 0x1b, 0x1f, 0x1c, 0x0, 0x43, 0x40, 0x4c, 0x52, 0x1f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, 0x9f, ]],
|
||||
[[0x5, 0x4e, 0x51, 0x1f, 0x48, 0x4d, 0x52, 0x53, 0x40, 0x4d, 0x42, 0x44, 0x1d, 0x1f, 0x4e, 0x4d, 0x9f, 0x53, 0x47, 0x44, 0x1f, 0x4f, 0x4b, 0x40, 0x4d, 0x44, 0x53, 0x1f, 0x4, 0x40, 0x51, 0x53, 0x47, 0x1d, 0x9f, 0x4c, 0x40, 0x4d, 0x1f, 0x47, 0x40, 0x43, 0x1f, 0x40, 0x4b, 0x56, 0x40, 0x58, 0x52, 0x9f, 0x40, 0x52, 0x52, 0x54, 0x4c, 0x44, 0x43, 0x1f, 0x53, 0x47, 0x40, 0x53, 0x1f, 0x47, 0x44, 0x9f, 0x56, 0x40, 0x52, 0x1f, 0x4c, 0x4e, 0x51, 0x44, 0x9f, 0x48, 0x4d, 0x53, 0x44, 0x4b, 0x4b, 0x48, 0x46, 0x44, 0x4d, 0x53, 0x1f, 0x53, 0x47, 0x40, 0x4d, 0x9f, 0x43, 0x4e, 0x4b, 0x4f, 0x47, 0x48, 0x4d, 0x52, 0x1f, 0x41, 0x44, 0x42, 0x40, 0x54, 0x52, 0x44, 0x9f, 0x47, 0x44, 0x1f, 0x47, 0x40, 0x43, 0x1f, 0x40, 0x42, 0x47, 0x48, 0x44, 0x55, 0x44, 0x43, 0x1f, 0x52, 0xce, ], [0x4c, 0x54, 0x42, 0x47, 0x1c, 0x53, 0x47, 0x44, 0x1f, 0x56, 0x47, 0x44, 0x44, 0x4b, 0x1d, 0x9f, 0xd, 0x44, 0x56, 0x1f, 0x18, 0x4e, 0x51, 0x4a, 0x1d, 0x1f, 0x56, 0x40, 0x51, 0x52, 0x1f, 0x40, 0x4d, 0xc3, 0x52, 0x4e, 0x1f, 0x4e, 0x4d, 0x1c, 0x56, 0x47, 0x48, 0x4b, 0x52, 0x53, 0x1f, 0x40, 0x4b, 0x4b, 0x9f, 0x53, 0x47, 0x44, 0x1f, 0x43, 0x4e, 0x4b, 0x4f, 0x47, 0x48, 0x4d, 0x52, 0x1f, 0x47, 0x40, 0x43, 0x9f, 0x44, 0x55, 0x44, 0x51, 0x1f, 0x43, 0x4e, 0x4d, 0x44, 0x1f, 0x56, 0x40, 0x52, 0x1f, 0x4c, 0x54, 0x42, 0xca, 0x40, 0x41, 0x4e, 0x54, 0x53, 0x1f, 0x48, 0x4d, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x56, 0x40, 0x53, 0x44, 0xd1, 0x47, 0x40, 0x55, 0x48, 0x4d, 0x46, 0x1f, 0x40, 0x1f, 0x46, 0x4e, 0x4e, 0x43, 0x9f, 0x53, 0x48, 0x4c, 0x44, 0x1b, 0x1f, 0x1, 0x54, 0x53, 0x9f, ], [0x42, 0x4e, 0x4d, 0x55, 0x44, 0x51, 0x52, 0x44, 0x4b, 0x58, 0x1d, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x43, 0x4e, 0x4b, 0x4f, 0x47, 0x48, 0x4d, 0x52, 0x1f, 0x47, 0x40, 0x43, 0x9f, 0x40, 0x4b, 0x56, 0x40, 0x58, 0x52, 0x1f, 0x41, 0x44, 0x4b, 0x48, 0x44, 0x55, 0x44, 0x43, 0x9f, 0x53, 0x47, 0x40, 0x53, 0x1f, 0x53, 0x47, 0x44, 0x58, 0x1f, 0x56, 0x44, 0x51, 0x44, 0x1f, 0x45, 0x40, 0xd1, 0x4c, 0x4e, 0x51, 0x44, 0x1f, 0x48, 0x4d, 0x53, 0x44, 0x4b, 0x4b, 0x48, 0x46, 0x44, 0x4d, 0x53, 0x9f, 0x53, 0x47, 0x40, 0x4d, 0x1f, 0x4c, 0x40, 0x4d, 0x1b, 0x45, 0x4e, 0x51, 0x9f, 0x4f, 0x51, 0x44, 0x42, 0x48, 0x52, 0x44, 0x4b, 0x58, 0x1f, 0x53, 0x47, 0x44, 0x1f, 0x52, 0x40, 0x4c, 0xc4, 0x51, 0x44, 0x40, 0x52, 0x4e, 0x4d, 0x52, 0x1b, 0x1f, 0x1c, 0x0, 0x43, 0x40, 0x4c, 0x52, 0x1f, 0x9f, ]],
|
||||
[[0x8, 0x53, 0x1f, 0x48, 0x52, 0x1f, 0x4a, 0x4d, 0x4e, 0x56, 0x4d, 0x1f, 0x53, 0x47, 0x40, 0x53, 0x9f, 0x53, 0x47, 0x44, 0x51, 0x44, 0x1f, 0x40, 0x51, 0x44, 0x1f, 0x40, 0x4d, 0x9f, 0x48, 0x4d, 0x45, 0x48, 0x4d, 0x48, 0x53, 0x44, 0x1f, 0x4d, 0x54, 0x4c, 0x41, 0x44, 0x51, 0x1f, 0x4e, 0xc5, 0x56, 0x4e, 0x51, 0x4b, 0x43, 0x52, 0x1d, 0x1f, 0x52, 0x48, 0x4c, 0x4f, 0x4b, 0x58, 0x9f, 0x41, 0x44, 0x42, 0x40, 0x54, 0x52, 0x44, 0x1f, 0x53, 0x47, 0x44, 0x51, 0x44, 0x1f, 0x48, 0x52, 0x9f, 0x40, 0x4d, 0x1f, 0x48, 0x4d, 0x45, 0x48, 0x4d, 0x48, 0x53, 0x44, 0x1f, 0x40, 0x4c, 0x4e, 0x54, 0x4d, 0xd3, 0x4e, 0x45, 0x1f, 0x52, 0x4f, 0x40, 0x42, 0x44, 0x1f, 0x45, 0x4e, 0x51, 0x1f, 0x53, 0x47, 0x44, 0x4c, 0x9f, 0x53, 0x4e, 0x1f, 0x41, 0x44, 0x1f, 0x48, 0x4d, 0x1b, 0x1f, 0x7, 0x4e, 0x56, 0x44, 0x55, 0x44, 0x51, 0x9d, ], [0x4d, 0x4e, 0x53, 0x1f, 0x44, 0x55, 0x44, 0x51, 0x58, 0x1f, 0x4e, 0x4d, 0x44, 0x1f, 0x4e, 0x45, 0x9f, 0x53, 0x47, 0x44, 0x4c, 0x1f, 0x48, 0x52, 0x1f, 0x48, 0x4d, 0x47, 0x40, 0x41, 0x48, 0x53, 0x44, 0x43, 0x9b, 0x13, 0x47, 0x44, 0x51, 0x44, 0x45, 0x4e, 0x51, 0x44, 0x1d, 0x1f, 0x53, 0x47, 0x44, 0x51, 0x44, 0x9f, 0x4c, 0x54, 0x52, 0x53, 0x1f, 0x41, 0x44, 0x1f, 0x40, 0x1f, 0x45, 0x48, 0x4d, 0x48, 0x53, 0x44, 0x9f, 0x4d, 0x54, 0x4c, 0x41, 0x44, 0x51, 0x1f, 0x4e, 0x45, 0x9f, 0x48, 0x4d, 0x47, 0x40, 0x41, 0x48, 0x53, 0x44, 0x43, 0x1f, 0x56, 0x4e, 0x51, 0x4b, 0x43, 0x52, 0x1b, 0x9f, 0x0, 0x4d, 0x58, 0x1f, 0x45, 0x48, 0x4d, 0x48, 0x53, 0x44, 0x1f, 0x4d, 0x54, 0x4c, 0x41, 0x44, 0x51, 0x9f, 0x43, 0x48, 0x55, 0x48, 0x43, 0x44, 0x43, 0x1f, 0x41, 0x58, 0x9f, ], [0x48, 0x4d, 0x45, 0x48, 0x4d, 0x48, 0x53, 0x58, 0x1f, 0x48, 0x52, 0x1f, 0x40, 0x52, 0x9f, 0x4d, 0x44, 0x40, 0x51, 0x1f, 0x53, 0x4e, 0x1f, 0x4d, 0x4e, 0x53, 0x47, 0x48, 0x4d, 0x46, 0x1f, 0x40, 0xd2, 0x4c, 0x40, 0x4a, 0x44, 0x52, 0x1f, 0x4d, 0x4e, 0x1f, 0x4e, 0x43, 0x43, 0x52, 0x1d, 0x1f, 0x52, 0x4e, 0x9f, 0x53, 0x47, 0x44, 0x1f, 0x40, 0x55, 0x44, 0x51, 0x40, 0x46, 0x44, 0x9f, 0x4f, 0x4e, 0x4f, 0x54, 0x4b, 0x40, 0x53, 0x48, 0x4e, 0x4d, 0x1f, 0x4e, 0x45, 0x1f, 0x40, 0x4b, 0x4b, 0x9f, 0x53, 0x47, 0x44, 0x1f, 0x4f, 0x4b, 0x40, 0x4d, 0x44, 0x53, 0x52, 0x1f, 0x48, 0x4d, 0x1f, 0x53, 0x47, 0xc4, 0x14, 0x4d, 0x48, 0x55, 0x44, 0x51, 0x52, 0x44, 0x1f, 0x42, 0x40, 0x4d, 0x1f, 0x41, 0x44, 0x9f, 0x52, 0x40, 0x48, 0x43, 0x1f, 0x53, 0x4e, 0x1f, 0x41, 0x44, 0x1f, 0x59, 0x44, 0x51, 0x4e, 0x1b, 0x9f, ], [0x5, 0x51, 0x4e, 0x4c, 0x1f, 0x53, 0x47, 0x48, 0x52, 0x1f, 0x48, 0x53, 0x9f, 0x45, 0x4e, 0x4b, 0x4b, 0x4e, 0x56, 0x52, 0x1f, 0x53, 0x47, 0x40, 0x53, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x4f, 0x4e, 0x4f, 0x54, 0x4b, 0x40, 0x53, 0x48, 0x4e, 0x4d, 0x1f, 0x4e, 0x45, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x56, 0x47, 0x4e, 0x4b, 0x44, 0x1f, 0x14, 0x4d, 0x48, 0x55, 0x44, 0x51, 0x52, 0x44, 0x1f, 0x48, 0x52, 0x9f, 0x40, 0x4b, 0x52, 0x4e, 0x1f, 0x59, 0x44, 0x51, 0x4e, 0x1d, 0x1f, 0x40, 0x4d, 0x43, 0x9f, 0x53, 0x47, 0x40, 0x53, 0x1f, 0x40, 0x4d, 0x58, 0x1f, 0x4f, 0x44, 0x4e, 0x4f, 0x4b, 0x44, 0x9f, 0x58, 0x4e, 0x54, 0x1f, 0x4c, 0x40, 0x58, 0x1f, 0x4c, 0x44, 0x44, 0x53, 0x1f, 0x45, 0x51, 0x4e, 0x4c, 0x9f, 0x53, 0x48, 0x4c, 0x44, 0x1f, 0x53, 0x4e, 0x1f, 0x53, 0x48, 0x4c, 0x44, 0x1f, 0x40, 0x51, 0x44, 0x9f, ], [0x4c, 0x44, 0x51, 0x44, 0x4b, 0x58, 0x1f, 0x53, 0x47, 0x44, 0x9f, 0x4f, 0x51, 0x4e, 0x43, 0x54, 0x42, 0x53, 0x52, 0x1f, 0x4e, 0x45, 0x1f, 0x40, 0x9f, 0x43, 0x44, 0x51, 0x40, 0x4d, 0x46, 0x44, 0x43, 0x9f, 0x48, 0x4c, 0x40, 0x46, 0x48, 0x4d, 0x40, 0x53, 0x48, 0x4e, 0x4d, 0x1b, 0x9f, 0x1c, 0x0, 0x43, 0x40, 0x4c, 0x52, 0x1f, 0x9f, 0x9f, 0x9f, 0x9f, ]],
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user