mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-28 01:13:24 -07:00
Merge branch 'main' into player-tracker
This commit is contained in:
@@ -27,7 +27,8 @@ class AutoWorldRegister(type):
|
||||
|
||||
# build rest
|
||||
dct["item_names"] = frozenset(dct["item_name_to_id"])
|
||||
dct["item_name_groups"] = dct.get("item_name_groups", {})
|
||||
dct["item_name_groups"] = {group_name: frozenset(group_set) for group_name, group_set
|
||||
in dct.get("item_name_groups", {}).items()}
|
||||
dct["item_name_groups"]["Everything"] = dct["item_names"]
|
||||
dct["location_names"] = frozenset(dct["location_name_to_id"])
|
||||
dct["all_item_and_group_names"] = frozenset(dct["item_names"] | set(dct.get("item_name_groups", {})))
|
||||
@@ -97,22 +98,22 @@ def call_stage(world: "MultiWorld", method_name: str, *args: Any) -> None:
|
||||
|
||||
class WebWorld:
|
||||
"""Webhost integration"""
|
||||
# display a settings page. Can be a link to an out-of-ap settings tool too.
|
||||
|
||||
settings_page: Union[bool, str] = True
|
||||
"""display a settings page. Can be a link to a specific page or external tool."""
|
||||
|
||||
# docs folder will be scanned for game info pages using this list in the format '{language}_{game_name}.md'
|
||||
game_info_languages: List[str] = ['en']
|
||||
"""docs folder will be scanned for game info pages using this list in the format '{language}_{game_name}.md'"""
|
||||
|
||||
# docs folder will also be scanned for tutorial guides given the relevant information in this list. Each Tutorial
|
||||
# class is to be used for one guide.
|
||||
tutorials: List["Tutorial"]
|
||||
"""docs folder will also be scanned for tutorial guides. Each Tutorial class is to be used for one guide."""
|
||||
|
||||
# Choose a theme for your /game/* pages
|
||||
# Available: dirt, grass, grassFlowers, ice, jungle, ocean, partyTime
|
||||
theme: str = "grass"
|
||||
theme = "grass"
|
||||
"""Choose a theme for you /game/* pages.
|
||||
Available: dirt, grass, grassFlowers, ice, jungle, ocean, partyTime, stone"""
|
||||
|
||||
# display a link to a bug report page, most likely a link to a GitHub issue page.
|
||||
bug_report_page: Optional[str]
|
||||
"""display a link to a bug report page, most likely a link to a GitHub issue page."""
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from WebHostLib.tracker import PlayerTracker
|
||||
@@ -230,10 +231,8 @@ class World(metaclass=AutoWorldRegister):
|
||||
@classmethod
|
||||
def fill_hook(cls,
|
||||
progitempool: List["Item"],
|
||||
nonexcludeditempool: List["Item"],
|
||||
localrestitempool: Dict[int, List["Item"]],
|
||||
nonlocalrestitempool: Dict[int, List["Item"]],
|
||||
restitempool: 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."""
|
||||
@@ -251,6 +250,11 @@ class World(metaclass=AutoWorldRegister):
|
||||
"""Fill in the slot_data field in the Connected network package."""
|
||||
return {}
|
||||
|
||||
def extend_hint_information(self, hint_data: Dict[int, Dict[int, str]]):
|
||||
"""Fill in additional entrance information text into locations, which is displayed when hinted.
|
||||
structure is {player_id: {location_id: text}} You will need to insert your own player_id."""
|
||||
pass
|
||||
|
||||
def modify_multidata(self, multidata: Dict[str, Any]) -> None: # TODO: TypedDict for multidata?
|
||||
"""For deeper modification of server multidata."""
|
||||
pass
|
||||
|
||||
@@ -27,7 +27,8 @@ class WorldSource(typing.NamedTuple):
|
||||
world_sources: typing.List[WorldSource] = []
|
||||
file: os.DirEntry # for me (Berserker) at least, PyCharm doesn't seem to infer the type correctly
|
||||
for file in os.scandir(folder):
|
||||
if not file.name.startswith("_"): # prevent explicitly loading __pycache__ and allow _* names for non-world folders
|
||||
# prevent loading of __pycache__ and allow _* for non-world folders, disable files/folders starting with "."
|
||||
if not file.name.startswith(("_", ".")):
|
||||
if file.is_dir():
|
||||
world_sources.append(WorldSource(file.name))
|
||||
elif file.is_file() and file.name.endswith(".apworld"):
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import logging
|
||||
from typing import Optional
|
||||
from typing import Optional, Union, List, Tuple, Callable, Dict
|
||||
|
||||
from BaseClasses import Boss
|
||||
from Fill import FillError
|
||||
from .Options import Bosses
|
||||
|
||||
|
||||
def BossFactory(boss: str, player: int) -> Optional[Boss]:
|
||||
@@ -12,7 +13,7 @@ def BossFactory(boss: str, player: int) -> Optional[Boss]:
|
||||
raise Exception('Unknown Boss: %s', boss)
|
||||
|
||||
|
||||
def ArmosKnightsDefeatRule(state, player: int):
|
||||
def ArmosKnightsDefeatRule(state, player: int) -> bool:
|
||||
# Magic amounts are probably a bit overkill
|
||||
return (
|
||||
state.has_melee_weapon(player) or
|
||||
@@ -25,7 +26,7 @@ def ArmosKnightsDefeatRule(state, player: int):
|
||||
state.has('Red Boomerang', player))
|
||||
|
||||
|
||||
def LanmolasDefeatRule(state, player: int):
|
||||
def LanmolasDefeatRule(state, player: int) -> bool:
|
||||
return (
|
||||
state.has_melee_weapon(player) or
|
||||
state.has('Fire Rod', player) or
|
||||
@@ -35,16 +36,16 @@ def LanmolasDefeatRule(state, player: int):
|
||||
state.can_shoot_arrows(player))
|
||||
|
||||
|
||||
def MoldormDefeatRule(state, player: int):
|
||||
def MoldormDefeatRule(state, player: int) -> bool:
|
||||
return state.has_melee_weapon(player)
|
||||
|
||||
|
||||
def HelmasaurKingDefeatRule(state, player: int):
|
||||
def HelmasaurKingDefeatRule(state, player: int) -> bool:
|
||||
# TODO: technically possible with the hammer
|
||||
return state.has_sword(player) or state.can_shoot_arrows(player)
|
||||
|
||||
|
||||
def ArrghusDefeatRule(state, player: int):
|
||||
def ArrghusDefeatRule(state, player: int) -> bool:
|
||||
if not state.has('Hookshot', player):
|
||||
return False
|
||||
# TODO: ideally we would have a check for bow and silvers, which combined with the
|
||||
@@ -58,7 +59,7 @@ def ArrghusDefeatRule(state, player: int):
|
||||
(state.has('Ice Rod', player) and (state.can_shoot_arrows(player) or state.can_extend_magic(player, 16))))
|
||||
|
||||
|
||||
def MothulaDefeatRule(state, player: int):
|
||||
def MothulaDefeatRule(state, player: int) -> bool:
|
||||
return (
|
||||
state.has_melee_weapon(player) or
|
||||
(state.has('Fire Rod', player) and state.can_extend_magic(player, 10)) or
|
||||
@@ -70,11 +71,11 @@ def MothulaDefeatRule(state, player: int):
|
||||
)
|
||||
|
||||
|
||||
def BlindDefeatRule(state, player: int):
|
||||
def BlindDefeatRule(state, player: int) -> bool:
|
||||
return state.has_melee_weapon(player) or state.has('Cane of Somaria', player) or state.has('Cane of Byrna', player)
|
||||
|
||||
|
||||
def KholdstareDefeatRule(state, player: int):
|
||||
def KholdstareDefeatRule(state, player: int) -> bool:
|
||||
return (
|
||||
(
|
||||
state.has('Fire Rod', player) or
|
||||
@@ -96,11 +97,11 @@ def KholdstareDefeatRule(state, player: int):
|
||||
)
|
||||
|
||||
|
||||
def VitreousDefeatRule(state, player: int):
|
||||
def VitreousDefeatRule(state, player: int) -> bool:
|
||||
return state.can_shoot_arrows(player) or state.has_melee_weapon(player)
|
||||
|
||||
|
||||
def TrinexxDefeatRule(state, player: int):
|
||||
def TrinexxDefeatRule(state, player: int) -> bool:
|
||||
if not (state.has('Fire Rod', player) and state.has('Ice Rod', player)):
|
||||
return False
|
||||
return state.has('Hammer', player) or state.has('Tempered Sword', player) or state.has('Golden Sword', player) or \
|
||||
@@ -108,11 +109,11 @@ def TrinexxDefeatRule(state, player: int):
|
||||
(state.has_sword(player) and state.can_extend_magic(player, 32))
|
||||
|
||||
|
||||
def AgahnimDefeatRule(state, player: int):
|
||||
def AgahnimDefeatRule(state, player: int) -> bool:
|
||||
return state.has_sword(player) or state.has('Hammer', player) or state.has('Bug Catching Net', player)
|
||||
|
||||
|
||||
def GanonDefeatRule(state, player: int):
|
||||
def GanonDefeatRule(state, player: int) -> bool:
|
||||
if state.world.swordless[player]:
|
||||
return state.has('Hammer', player) and \
|
||||
state.has_fire_source(player) and \
|
||||
@@ -132,7 +133,7 @@ def GanonDefeatRule(state, player: int):
|
||||
return common and state.has('Silver Bow', player) and state.can_shoot_arrows(player)
|
||||
|
||||
|
||||
boss_table = {
|
||||
boss_table: Dict[str, Tuple[str, Optional[Callable]]] = {
|
||||
'Armos Knights': ('Armos', ArmosKnightsDefeatRule),
|
||||
'Lanmolas': ('Lanmola', LanmolasDefeatRule),
|
||||
'Moldorm': ('Moldorm', MoldormDefeatRule),
|
||||
@@ -147,7 +148,7 @@ boss_table = {
|
||||
'Agahnim2': ('Agahnim2', AgahnimDefeatRule)
|
||||
}
|
||||
|
||||
boss_location_table = [
|
||||
boss_location_table: List[Tuple[str, str]] = [
|
||||
('Ganons Tower', 'top'),
|
||||
('Tower of Hera', None),
|
||||
('Skull Woods', None),
|
||||
@@ -164,6 +165,34 @@ boss_location_table = [
|
||||
]
|
||||
|
||||
|
||||
def place_plando_bosses(bosses: List[str], world, player: int) -> Tuple[List[str], List[Tuple[str, str]]]:
|
||||
# Most to least restrictive order
|
||||
boss_locations = boss_location_table.copy()
|
||||
world.random.shuffle(boss_locations)
|
||||
boss_locations.sort(key=lambda location: -int(restrictive_boss_locations[location]))
|
||||
already_placed_bosses: List[str] = []
|
||||
|
||||
for boss in bosses:
|
||||
if "-" in boss: # handle plando locations
|
||||
loc, boss = boss.split("-")
|
||||
boss = boss.title()
|
||||
level: str = None
|
||||
if loc.split(" ")[-1] in {"top", "middle", "bottom"}:
|
||||
# split off level
|
||||
loc = loc.split(" ")
|
||||
level = loc[-1]
|
||||
loc = " ".join(loc[:-1])
|
||||
loc = loc.title().replace("Of", "of")
|
||||
place_boss(world, player, boss, loc, level)
|
||||
already_placed_bosses.append(boss)
|
||||
boss_locations.remove((loc, level))
|
||||
else: # boss chosen with no specified locations
|
||||
boss = boss.title()
|
||||
boss_locations, already_placed_bosses = place_where_possible(world, player, boss, boss_locations)
|
||||
|
||||
return already_placed_bosses, boss_locations
|
||||
|
||||
|
||||
def can_place_boss(boss: str, dungeon_name: str, level: Optional[str] = None) -> bool:
|
||||
# blacklist approach
|
||||
if boss in {"Agahnim", "Agahnim2", "Ganon"}:
|
||||
@@ -187,62 +216,50 @@ def can_place_boss(boss: str, dungeon_name: str, level: Optional[str] = None) ->
|
||||
|
||||
return True
|
||||
|
||||
restrictive_boss_locations = {}
|
||||
|
||||
restrictive_boss_locations: Dict[Tuple[str, str], bool] = {}
|
||||
for location in boss_location_table:
|
||||
restrictive_boss_locations[location] = not all(can_place_boss(boss, *location)
|
||||
for boss in boss_table if not boss.startswith("Agahnim"))
|
||||
|
||||
def place_boss(world, player: int, boss: str, location: str, level: Optional[str]):
|
||||
|
||||
def place_boss(world, player: int, boss: str, location: str, level: Optional[str]) -> None:
|
||||
if location == 'Ganons Tower' and world.mode[player] == 'inverted':
|
||||
location = 'Inverted Ganons Tower'
|
||||
logging.debug('Placing boss %s at %s', boss, location + (' (' + level + ')' if level else ''))
|
||||
world.get_dungeon(location, player).bosses[level] = BossFactory(boss, player)
|
||||
|
||||
def format_boss_location(location, level):
|
||||
|
||||
def format_boss_location(location: str, level: str) -> str:
|
||||
return location + (' (' + level + ')' if level else '')
|
||||
|
||||
def place_bosses(world, player: int):
|
||||
if world.boss_shuffle[player] == 'none':
|
||||
|
||||
def place_bosses(world, player: int) -> None:
|
||||
# will either be an int or a lower case string with ';' between options
|
||||
boss_shuffle: Union[str, int] = world.boss_shuffle[player].value
|
||||
already_placed_bosses: List[str] = []
|
||||
remaining_locations: List[Tuple[str, str]] = []
|
||||
# handle plando
|
||||
if isinstance(boss_shuffle, str):
|
||||
# figure out our remaining mode, convert it to an int and remove it from plando_args
|
||||
options = boss_shuffle.split(";")
|
||||
boss_shuffle = Bosses.options[options.pop()]
|
||||
# place our plando bosses
|
||||
already_placed_bosses, remaining_locations = place_plando_bosses(options, world, player)
|
||||
if boss_shuffle == Bosses.option_none: # vanilla boss locations
|
||||
return
|
||||
|
||||
# Most to least restrictive order
|
||||
boss_locations = boss_location_table.copy()
|
||||
world.random.shuffle(boss_locations)
|
||||
boss_locations.sort(key= lambda location: -int(restrictive_boss_locations[location]))
|
||||
if not remaining_locations and not already_placed_bosses:
|
||||
remaining_locations = boss_location_table.copy()
|
||||
world.random.shuffle(remaining_locations)
|
||||
remaining_locations.sort(key=lambda location: -int(restrictive_boss_locations[location]))
|
||||
|
||||
all_bosses = sorted(boss_table.keys()) # sorted to be deterministic on older pythons
|
||||
placeable_bosses = [boss for boss in all_bosses if boss not in ['Agahnim', 'Agahnim2', 'Ganon']]
|
||||
|
||||
shuffle_mode = world.boss_shuffle[player]
|
||||
already_placed_bosses = []
|
||||
if ";" in shuffle_mode:
|
||||
bosses = shuffle_mode.split(";")
|
||||
shuffle_mode = bosses.pop()
|
||||
for boss in bosses:
|
||||
if "-" in boss:
|
||||
loc, boss = boss.split("-")
|
||||
boss = boss.title()
|
||||
level = None
|
||||
if loc.split(" ")[-1] in {"top", "middle", "bottom"}:
|
||||
# split off level
|
||||
loc = loc.split(" ")
|
||||
level = loc[-1]
|
||||
loc = " ".join(loc[:-1])
|
||||
loc = loc.title().replace("Of", "of")
|
||||
if can_place_boss(boss, loc, level) and (loc, level) in boss_locations:
|
||||
place_boss(world, player, boss, loc, level)
|
||||
already_placed_bosses.append(boss)
|
||||
boss_locations.remove((loc, level))
|
||||
else:
|
||||
raise Exception(f"Cannot place {boss} at {format_boss_location(loc, level)} for player {player}.")
|
||||
else:
|
||||
boss = boss.title()
|
||||
boss_locations, already_placed_bosses = place_where_possible(world, player, boss, boss_locations)
|
||||
|
||||
if shuffle_mode == "none":
|
||||
return # vanilla bosses come pre-placed
|
||||
|
||||
if shuffle_mode in ["basic", "full"]:
|
||||
if world.boss_shuffle[player] == "basic": # vanilla bosses shuffled
|
||||
if boss_shuffle == Bosses.option_basic or boss_shuffle == Bosses.option_full:
|
||||
if boss_shuffle == Bosses.option_basic: # vanilla bosses shuffled
|
||||
bosses = placeable_bosses + ['Armos Knights', 'Lanmolas', 'Moldorm']
|
||||
else: # all bosses present, the three duplicates chosen at random
|
||||
bosses = placeable_bosses + world.random.sample(placeable_bosses, 3)
|
||||
@@ -258,7 +275,7 @@ def place_bosses(world, player: int):
|
||||
logging.debug('Bosses chosen %s', bosses)
|
||||
|
||||
world.random.shuffle(bosses)
|
||||
for loc, level in boss_locations:
|
||||
for loc, level in remaining_locations:
|
||||
for _ in range(len(bosses)):
|
||||
boss = bosses.pop()
|
||||
if can_place_boss(boss, loc, level):
|
||||
@@ -272,8 +289,8 @@ def place_bosses(world, player: int):
|
||||
|
||||
place_boss(world, player, boss, loc, level)
|
||||
|
||||
elif shuffle_mode == "chaos": # all bosses chosen at random
|
||||
for loc, level in boss_locations:
|
||||
elif boss_shuffle == Bosses.option_chaos: # all bosses chosen at random
|
||||
for loc, level in remaining_locations:
|
||||
try:
|
||||
boss = world.random.choice(
|
||||
[b for b in placeable_bosses if can_place_boss(b, loc, level)])
|
||||
@@ -282,9 +299,9 @@ def place_bosses(world, player: int):
|
||||
else:
|
||||
place_boss(world, player, boss, loc, level)
|
||||
|
||||
elif shuffle_mode == "singularity":
|
||||
elif boss_shuffle == Bosses.option_singularity:
|
||||
primary_boss = world.random.choice(placeable_bosses)
|
||||
remaining_boss_locations, _ = place_where_possible(world, player, primary_boss, boss_locations)
|
||||
remaining_boss_locations, _ = place_where_possible(world, player, primary_boss, remaining_locations)
|
||||
if remaining_boss_locations:
|
||||
# pick a boss to go into the remaining locations
|
||||
remaining_boss = world.random.choice([boss for boss in placeable_bosses if all(
|
||||
@@ -293,12 +310,12 @@ def place_bosses(world, player: int):
|
||||
if remaining_boss_locations:
|
||||
raise Exception("Unfilled boss locations!")
|
||||
else:
|
||||
raise FillError(f"Could not find boss shuffle mode {shuffle_mode}")
|
||||
raise FillError(f"Could not find boss shuffle mode {boss_shuffle}")
|
||||
|
||||
|
||||
def place_where_possible(world, player: int, boss: str, boss_locations):
|
||||
remainder = []
|
||||
placed_bosses = []
|
||||
def place_where_possible(world, player: int, boss: str, boss_locations) -> Tuple[List[Tuple[str, str]], List[str]]:
|
||||
remainder: List[Tuple[str, str]] = []
|
||||
placed_bosses: List[str] = []
|
||||
for loc, level in boss_locations:
|
||||
# place that boss where it can go
|
||||
if can_place_boss(boss, loc, level):
|
||||
|
||||
@@ -212,9 +212,7 @@ def parse_arguments(argv, no_defaults=False):
|
||||
Alternatively, can be a ALttP Rom patched with a Link
|
||||
sprite that will be extracted.
|
||||
''')
|
||||
parser.add_argument('--gui', help='Launch the GUI', action='store_true')
|
||||
|
||||
parser.add_argument('--enemizercli', default=defval('EnemizerCLI/EnemizerCLI.Core'))
|
||||
parser.add_argument('--shufflebosses', default=defval('none'), choices=['none', 'basic', 'normal', 'chaos',
|
||||
"singularity"])
|
||||
|
||||
|
||||
@@ -480,7 +480,7 @@ def set_up_take_anys(world, player):
|
||||
old_man_take_any.shop = TakeAny(old_man_take_any, 0x0112, 0xE2, True, True, total_shop_slots)
|
||||
world.shops.append(old_man_take_any.shop)
|
||||
|
||||
swords = [item for item in world.itempool if item.type == 'Sword' and item.player == player]
|
||||
swords = [item for item in world.itempool if item.player == player and item.type == 'Sword']
|
||||
if swords:
|
||||
sword = world.random.choice(swords)
|
||||
world.itempool.remove(sword)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import typing
|
||||
|
||||
from BaseClasses import MultiWorld
|
||||
from Options import Choice, Range, Option, Toggle, DefaultOnToggle, DeathLink
|
||||
from Options import Choice, Range, Option, Toggle, DefaultOnToggle, DeathLink, TextChoice
|
||||
|
||||
|
||||
class Logic(Choice):
|
||||
@@ -39,8 +39,6 @@ class OpenPyramid(Choice):
|
||||
option_auto = 3
|
||||
default = option_goal
|
||||
|
||||
alias_true = option_open
|
||||
alias_false = option_closed
|
||||
alias_yes = option_open
|
||||
alias_no = option_closed
|
||||
|
||||
@@ -140,13 +138,143 @@ class WorldState(Choice):
|
||||
option_inverted = 2
|
||||
|
||||
|
||||
class Bosses(Choice):
|
||||
option_vanilla = 0
|
||||
option_simple = 1
|
||||
class Bosses(TextChoice):
|
||||
"""Shuffles bosses around to different locations.
|
||||
Basic will shuffle all bosses except Ganon and Agahnim anywhere they can be placed.
|
||||
Full chooses 3 bosses at random to be placed twice instead of Lanmolas, Moldorm, and Helmasaur.
|
||||
Chaos allows any boss to appear any number of times.
|
||||
Singularity places a single boss in as many places as possible, and a second boss in any remaining locations.
|
||||
Supports plando placement. Formatting here: https://archipelago.gg/tutorial/A%20Link%20to%20the%20Past/plando/en"""
|
||||
display_name = "Boss Shuffle"
|
||||
option_none = 0
|
||||
option_basic = 1
|
||||
option_full = 2
|
||||
option_chaos = 3
|
||||
option_singularity = 4
|
||||
|
||||
bosses: set = {
|
||||
"Armos Knights",
|
||||
"Lanmolas",
|
||||
"Moldorm",
|
||||
"Helmasaur King",
|
||||
"Arrghus",
|
||||
"Mothula",
|
||||
"Blind",
|
||||
"Kholdstare",
|
||||
"Vitreous",
|
||||
"Trinexx",
|
||||
}
|
||||
|
||||
locations: set = {
|
||||
"Ganons Tower Top",
|
||||
"Tower of Hera",
|
||||
"Skull Woods",
|
||||
"Ganons Tower Middle",
|
||||
"Eastern Palace",
|
||||
"Desert Palace",
|
||||
"Palace of Darkness",
|
||||
"Swamp Palace",
|
||||
"Thieves Town",
|
||||
"Ice Palace",
|
||||
"Misery Mire",
|
||||
"Turtle Rock",
|
||||
"Ganons Tower Bottom"
|
||||
}
|
||||
|
||||
def __init__(self, value: typing.Union[str, int]):
|
||||
assert isinstance(value, str) or isinstance(value, int), \
|
||||
f"{value} is not a valid option for {self.__class__.__name__}"
|
||||
self.value = value
|
||||
|
||||
@classmethod
|
||||
def from_text(cls, text: str):
|
||||
import random
|
||||
# set all of our text to lower case for name checking
|
||||
text = text.lower()
|
||||
cls.bosses = {boss_name.lower() for boss_name in cls.bosses}
|
||||
cls.locations = {boss_location.lower() for boss_location in cls.locations}
|
||||
if text == "random":
|
||||
return cls(random.choice(list(cls.options.values())))
|
||||
for option_name, value in cls.options.items():
|
||||
if option_name == text:
|
||||
return cls(value)
|
||||
options = text.split(";")
|
||||
|
||||
# since plando exists in the option verify the plando values given are valid
|
||||
cls.validate_plando_bosses(options)
|
||||
|
||||
# find out what type of boss shuffle we should use for placing bosses after plando
|
||||
# and add as a string to look nice in the spoiler
|
||||
if "random" in options:
|
||||
shuffle = random.choice(list(cls.options))
|
||||
options.remove("random")
|
||||
options = ";".join(options) + ";" + shuffle
|
||||
boss_class = cls(options)
|
||||
else:
|
||||
for option in options:
|
||||
if option in cls.options:
|
||||
boss_class = cls(";".join(options))
|
||||
break
|
||||
else:
|
||||
if len(options) == 1:
|
||||
if cls.valid_boss_name(options[0]):
|
||||
options = options[0] + ";singularity"
|
||||
boss_class = cls(options)
|
||||
else:
|
||||
options = options[0] + ";none"
|
||||
boss_class = cls(options)
|
||||
else:
|
||||
options = ";".join(options) + ";none"
|
||||
boss_class = cls(options)
|
||||
return boss_class
|
||||
|
||||
@classmethod
|
||||
def validate_plando_bosses(cls, options: typing.List[str]) -> None:
|
||||
from .Bosses import can_place_boss, format_boss_location
|
||||
for option in options:
|
||||
if option == "random" or option in cls.options:
|
||||
if option != options[-1]:
|
||||
raise ValueError(f"{option} option must be at the end of the boss_shuffle options!")
|
||||
continue
|
||||
if "-" in option:
|
||||
location, boss = option.split("-")
|
||||
level = ''
|
||||
if not cls.valid_boss_name(boss):
|
||||
raise ValueError(f"{boss} is not a valid boss name for location {location}.")
|
||||
if not cls.valid_location_name(location):
|
||||
raise ValueError(f"{location} is not a valid boss location name.")
|
||||
if location.split(" ")[-1] in ("top", "middle", "bottom"):
|
||||
location = location.split(" ")
|
||||
level = location[-1]
|
||||
location = " ".join(location[:-1])
|
||||
location = location.title().replace("Of", "of")
|
||||
if not can_place_boss(boss.title(), location, level):
|
||||
raise ValueError(f"{format_boss_location(location, level)} "
|
||||
f"is not a valid location for {boss.title()}.")
|
||||
else:
|
||||
if not cls.valid_boss_name(option):
|
||||
raise ValueError(f"{option} is not a valid boss name.")
|
||||
|
||||
@classmethod
|
||||
def valid_boss_name(cls, value: str) -> bool:
|
||||
return value.lower() in cls.bosses
|
||||
|
||||
@classmethod
|
||||
def valid_location_name(cls, value: str) -> bool:
|
||||
return value in cls.locations
|
||||
|
||||
def verify(self, world, player_name: str, plando_options) -> None:
|
||||
if isinstance(self.value, int):
|
||||
return
|
||||
from Generate import PlandoSettings
|
||||
if not(PlandoSettings.bosses & plando_options):
|
||||
import logging
|
||||
# plando is disabled but plando options were given so pull the option and change it to an int
|
||||
option = self.value.split(";")[-1]
|
||||
self.value = self.options[option]
|
||||
logging.warning(f"The plando bosses module is turned off, so {self.name_lookup[self.value].title()} "
|
||||
f"boss shuffle will be used for player {player_name}.")
|
||||
|
||||
|
||||
class Enemies(Choice):
|
||||
option_vanilla = 0
|
||||
@@ -159,8 +287,6 @@ class Progressive(Choice):
|
||||
option_off = 0
|
||||
option_grouped_random = 1
|
||||
option_on = 2
|
||||
alias_false = 0
|
||||
alias_true = 2
|
||||
default = 2
|
||||
|
||||
def want_progressives(self, random):
|
||||
@@ -168,8 +294,8 @@ class Progressive(Choice):
|
||||
|
||||
|
||||
class Swordless(Toggle):
|
||||
"""No swords. Curtains in Skull Woods and Agahnim\'s
|
||||
Tower are removed, Agahnim\'s Tower barrier can be
|
||||
"""No swords. Curtains in Skull Woods and Agahnim's
|
||||
Tower are removed, Agahnim's Tower barrier can be
|
||||
destroyed with hammer. Misery Mire and Turtle Rock
|
||||
can be opened without a sword. Hammer damages Ganon.
|
||||
Ether and Bombos Tablet can be activated with Hammer
|
||||
@@ -202,8 +328,6 @@ class Hints(Choice):
|
||||
option_on = 2
|
||||
option_full = 3
|
||||
default = 2
|
||||
alias_false = 0
|
||||
alias_true = 2
|
||||
|
||||
|
||||
class Scams(Choice):
|
||||
@@ -213,7 +337,6 @@ class Scams(Choice):
|
||||
option_king_zora = 1
|
||||
option_bottle_merchant = 2
|
||||
option_all = 3
|
||||
alias_false = 0
|
||||
|
||||
@property
|
||||
def gives_king_zora_hint(self):
|
||||
@@ -282,8 +405,8 @@ class ShieldPalette(Palette):
|
||||
display_name = "Shield Palette"
|
||||
|
||||
|
||||
class LinkPalette(Palette):
|
||||
display_name = "Link Palette"
|
||||
# class LinkPalette(Palette):
|
||||
# display_name = "Link Palette"
|
||||
|
||||
|
||||
class HeartBeep(Choice):
|
||||
@@ -293,7 +416,6 @@ class HeartBeep(Choice):
|
||||
option_half = 2
|
||||
option_quarter = 3
|
||||
option_off = 4
|
||||
alias_false = 4
|
||||
|
||||
|
||||
class HeartColor(Choice):
|
||||
@@ -375,6 +497,7 @@ alttp_options: typing.Dict[str, type(Option)] = {
|
||||
"hints": Hints,
|
||||
"scams": Scams,
|
||||
"restrict_dungeon_item_on_boss": RestrictBossItem,
|
||||
"boss_shuffle": Bosses,
|
||||
"pot_shuffle": PotShuffle,
|
||||
"enemy_shuffle": EnemyShuffle,
|
||||
"killable_thieves": KillableThieves,
|
||||
@@ -387,7 +510,7 @@ alttp_options: typing.Dict[str, type(Option)] = {
|
||||
"hud_palettes": HUDPalette,
|
||||
"sword_palettes": SwordPalette,
|
||||
"shield_palettes": ShieldPalette,
|
||||
"link_palettes": LinkPalette,
|
||||
# "link_palettes": LinkPalette,
|
||||
"heartbeep": HeartBeep,
|
||||
"heartcolor": HeartColor,
|
||||
"quickswap": QuickSwap,
|
||||
|
||||
@@ -4,6 +4,10 @@ import typing
|
||||
from BaseClasses import Region, Entrance, RegionType
|
||||
|
||||
|
||||
def is_main_entrance(entrance: Entrance) -> bool:
|
||||
return entrance.parent_region.type in {RegionType.DarkWorld, RegionType.LightWorld, RegionType.Generic}
|
||||
|
||||
|
||||
def create_regions(world, player):
|
||||
|
||||
world.regions += [
|
||||
|
||||
@@ -34,7 +34,7 @@ from worlds.alttp.Text import KingsReturn_texts, Sanctuary_texts, Kakariko_texts
|
||||
DeathMountain_texts, \
|
||||
LostWoods_texts, WishingWell_texts, DesertPalace_texts, MountainTower_texts, LinksHouse_texts, Lumberjacks_texts, \
|
||||
SickKid_texts, FluteBoy_texts, Zora_texts, MagicShop_texts, Sahasrahla_names
|
||||
from Utils import local_path, user_path, int16_as_bytes, int32_as_bytes, snes_to_pc, is_frozen
|
||||
from Utils import local_path, user_path, int16_as_bytes, int32_as_bytes, snes_to_pc, is_frozen, parse_yaml
|
||||
from worlds.alttp.Items import ItemFactory, item_table, item_name_groups, progression_items
|
||||
from worlds.alttp.EntranceShuffle import door_addresses
|
||||
from worlds.alttp.Options import smallkey_shuffle
|
||||
@@ -551,18 +551,22 @@ class Sprite():
|
||||
Sprite.base_data = Sprite.sprite + Sprite.palette + Sprite.glove_palette
|
||||
|
||||
def from_ap_sprite(self, filedata):
|
||||
filedata = filedata.decode("utf-8-sig")
|
||||
import yaml
|
||||
obj = yaml.safe_load(filedata)
|
||||
if obj["min_format_version"] > 1:
|
||||
raise Exception("Sprite file requires an updated reader.")
|
||||
self.author_name = obj["author"]
|
||||
self.name = obj["name"]
|
||||
if obj["data"]: # skip patching for vanilla content
|
||||
data = bsdiff4.patch(Sprite.base_data, obj["data"])
|
||||
self.sprite = data[:self.sprite_size]
|
||||
self.palette = data[self.sprite_size:self.palette_size]
|
||||
self.glove_palette = data[self.sprite_size + self.palette_size:]
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
obj = parse_yaml(filedata.decode("utf-8-sig"))
|
||||
if obj["min_format_version"] > 1:
|
||||
raise Exception("Sprite file requires an updated reader.")
|
||||
self.author_name = obj["author"]
|
||||
self.name = obj["name"]
|
||||
if obj["data"]: # skip patching for vanilla content
|
||||
data = bsdiff4.patch(Sprite.base_data, obj["data"])
|
||||
self.sprite = data[:self.sprite_size]
|
||||
self.palette = data[self.sprite_size:self.palette_size]
|
||||
self.glove_palette = data[self.sprite_size + self.palette_size:]
|
||||
except Exception:
|
||||
logger = logging.getLogger("apsprite")
|
||||
logger.exception("Error parsing apsprite file")
|
||||
self.valid = False
|
||||
|
||||
@property
|
||||
def author_game_display(self) -> str:
|
||||
@@ -659,7 +663,7 @@ class Sprite():
|
||||
|
||||
@staticmethod
|
||||
def parse_zspr(filedata, expected_kind):
|
||||
logger = logging.getLogger('ZSPR')
|
||||
logger = logging.getLogger("ZSPR")
|
||||
headerstr = "<4xBHHIHIHH6x"
|
||||
headersize = struct.calcsize(headerstr)
|
||||
if len(filedata) < headersize:
|
||||
@@ -667,7 +671,7 @@ class Sprite():
|
||||
version, csum, icsum, sprite_offset, sprite_size, palette_offset, palette_size, kind = struct.unpack_from(
|
||||
headerstr, filedata)
|
||||
if version not in [1]:
|
||||
logger.error('Error parsing ZSPR file: Version %g not supported', version)
|
||||
logger.error("Error parsing ZSPR file: Version %g not supported", version)
|
||||
return None
|
||||
if kind != expected_kind:
|
||||
return None
|
||||
@@ -676,36 +680,42 @@ class Sprite():
|
||||
stream.seek(headersize)
|
||||
|
||||
def read_utf16le(stream):
|
||||
"Decodes a null-terminated UTF-16_LE string of unknown size from a stream"
|
||||
"""Decodes a null-terminated UTF-16_LE string of unknown size from a stream"""
|
||||
raw = bytearray()
|
||||
while True:
|
||||
char = stream.read(2)
|
||||
if char in [b'', b'\x00\x00']:
|
||||
if char in [b"", b"\x00\x00"]:
|
||||
break
|
||||
raw += char
|
||||
return raw.decode('utf-16_le')
|
||||
return raw.decode("utf-16_le")
|
||||
|
||||
sprite_name = read_utf16le(stream)
|
||||
author_name = read_utf16le(stream)
|
||||
author_credits_name = stream.read().split(b"\x00", 1)[0].decode()
|
||||
# noinspection PyBroadException
|
||||
try:
|
||||
sprite_name = read_utf16le(stream)
|
||||
author_name = read_utf16le(stream)
|
||||
author_credits_name = stream.read().split(b"\x00", 1)[0].decode()
|
||||
|
||||
# Ignoring the Author Rom name for the time being.
|
||||
# Ignoring the Author Rom name for the time being.
|
||||
|
||||
real_csum = sum(filedata) % 0x10000
|
||||
if real_csum != csum or real_csum ^ 0xFFFF != icsum:
|
||||
logger.warning('ZSPR file has incorrect checksum. It may be corrupted.')
|
||||
real_csum = sum(filedata) % 0x10000
|
||||
if real_csum != csum or real_csum ^ 0xFFFF != icsum:
|
||||
logger.warning("ZSPR file has incorrect checksum. It may be corrupted.")
|
||||
|
||||
sprite = filedata[sprite_offset:sprite_offset + sprite_size]
|
||||
palette = filedata[palette_offset:palette_offset + palette_size]
|
||||
sprite = filedata[sprite_offset:sprite_offset + sprite_size]
|
||||
palette = filedata[palette_offset:palette_offset + palette_size]
|
||||
|
||||
if len(sprite) != sprite_size or len(palette) != palette_size:
|
||||
logger.error('Error parsing ZSPR file: Unexpected end of file')
|
||||
if len(sprite) != sprite_size or len(palette) != palette_size:
|
||||
logger.error("Error parsing ZSPR file: Unexpected end of file")
|
||||
return None
|
||||
|
||||
return sprite, palette, sprite_name, author_name, author_credits_name
|
||||
|
||||
except Exception:
|
||||
logger.exception("Error parsing ZSPR file")
|
||||
return None
|
||||
|
||||
return (sprite, palette, sprite_name, author_name, author_credits_name)
|
||||
|
||||
def decode_palette(self):
|
||||
"Returns the palettes as an array of arrays of 15 colors"
|
||||
"""Returns the palettes as an array of arrays of 15 colors"""
|
||||
|
||||
def array_chunk(arr, size):
|
||||
return list(zip(*[iter(arr)] * size))
|
||||
|
||||
@@ -4,6 +4,7 @@ import random
|
||||
import threading
|
||||
import typing
|
||||
|
||||
import Utils
|
||||
from BaseClasses import Item, CollectionState, Tutorial
|
||||
from .Dungeons import create_dungeons
|
||||
from .EntranceShuffle import link_entrances, link_inverted_entrances, plando_connect
|
||||
@@ -11,7 +12,8 @@ from .InvertedRegions import create_inverted_regions, mark_dark_world_regions
|
||||
from .ItemPool import generate_itempool, difficulties
|
||||
from .Items import item_init_table, item_name_groups, item_table, GetBeemizerItem
|
||||
from .Options import alttp_options, smallkey_shuffle
|
||||
from .Regions import lookup_name_to_id, create_regions, mark_light_world_regions
|
||||
from .Regions import lookup_name_to_id, create_regions, mark_light_world_regions, lookup_vanilla_location_to_entrance, \
|
||||
is_main_entrance
|
||||
from .Rom import LocalRom, patch_rom, patch_race_rom, check_enemizer, patch_enemizer, apply_rom_settings, \
|
||||
get_hash_string, get_base_rom_path, LttPDeltaPatch
|
||||
from .Rules import set_rules
|
||||
@@ -23,6 +25,7 @@ lttp_logger = logging.getLogger("A Link to the Past")
|
||||
|
||||
extras_list = sum(difficulties['normal'].extras[0:5], [])
|
||||
|
||||
|
||||
class ALTTPWeb(WebWorld):
|
||||
setup_en = Tutorial(
|
||||
"Multiworld Setup Tutorial",
|
||||
@@ -362,6 +365,10 @@ class ALTTPWorld(World):
|
||||
|
||||
create_items = generate_itempool
|
||||
|
||||
enemizer_path: str = Utils.get_options()["generator"]["enemizer_path"] \
|
||||
if os.path.isabs(Utils.get_options()["generator"]["enemizer_path"]) \
|
||||
else Utils.local_path(Utils.get_options()["generator"]["enemizer_path"])
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.dungeon_local_item_names = set()
|
||||
self.dungeon_specific_item_names = set()
|
||||
@@ -376,12 +383,12 @@ class ALTTPWorld(World):
|
||||
raise FileNotFoundError(rom_file)
|
||||
|
||||
def generate_early(self):
|
||||
if self.use_enemizer():
|
||||
check_enemizer(self.enemizer_path)
|
||||
|
||||
player = self.player
|
||||
world = self.world
|
||||
|
||||
if self.use_enemizer():
|
||||
check_enemizer(world.enemizer)
|
||||
|
||||
# system for sharing ER layouts
|
||||
self.er_seed = str(world.random.randint(0, 2 ** 64))
|
||||
|
||||
@@ -570,7 +577,7 @@ class ALTTPWorld(World):
|
||||
def use_enemizer(self):
|
||||
world = self.world
|
||||
player = self.player
|
||||
return (world.boss_shuffle[player] != 'none' or world.enemy_shuffle[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])
|
||||
@@ -586,7 +593,7 @@ class ALTTPWorld(World):
|
||||
patch_rom(world, rom, player, use_enemizer)
|
||||
|
||||
if use_enemizer:
|
||||
patch_enemizer(world, player, rom, world.enemizer, output_directory)
|
||||
patch_enemizer(world, player, rom, self.enemizer_path, output_directory)
|
||||
|
||||
if world.is_race:
|
||||
patch_race_rom(rom, world, player)
|
||||
@@ -599,7 +606,7 @@ class ALTTPWorld(World):
|
||||
'hud': world.hud_palettes[player],
|
||||
'sword': world.sword_palettes[player],
|
||||
'shield': world.shield_palettes[player],
|
||||
'link': world.link_palettes[player]
|
||||
# 'link': world.link_palettes[player]
|
||||
}
|
||||
palettes_options = {key: option.current_key for key, option in palettes_options.items()}
|
||||
|
||||
@@ -631,6 +638,20 @@ class ALTTPWorld(World):
|
||||
finally:
|
||||
self.rom_name_available_event.set() # make sure threading continues and errors are collected
|
||||
|
||||
@classmethod
|
||||
def stage_extend_hint_information(cls, world, hint_data: typing.Dict[int, typing.Dict[int, str]]):
|
||||
er_hint_data = {player: {} for player in world.get_game_players("A Link to the Past") if
|
||||
world.shuffle[player] != "vanilla" or world.retro_caves[player]}
|
||||
|
||||
for region in world.regions:
|
||||
if region.player in er_hint_data and region.locations:
|
||||
main_entrance = region.get_connecting_entrance(is_main_entrance)
|
||||
for location in region.locations:
|
||||
if type(location.address) == int: # skips events and crystals
|
||||
if lookup_vanilla_location_to_entrance[location.address] != main_entrance.name:
|
||||
er_hint_data[region.player][location.address] = main_entrance.name
|
||||
hint_data.update(er_hint_data)
|
||||
|
||||
def modify_multidata(self, multidata: dict):
|
||||
import base64
|
||||
# wait for self.rom_name to be available.
|
||||
@@ -645,8 +666,7 @@ class ALTTPWorld(World):
|
||||
return ALttPItem(name, self.player, **item_init_table[name])
|
||||
|
||||
@classmethod
|
||||
def stage_fill_hook(cls, world, progitempool, nonexcludeditempool, localrestitempool, nonlocalrestitempool,
|
||||
restitempool, fill_locations):
|
||||
def stage_fill_hook(cls, world, progitempool, usefulitempool, filleritempool, fill_locations):
|
||||
trash_counts = {}
|
||||
standard_keyshuffle_players = set()
|
||||
for player in world.get_game_players("A Link to the Past"):
|
||||
@@ -693,26 +713,15 @@ class ALTTPWorld(World):
|
||||
for player, trash_count in trash_counts.items():
|
||||
gtower_locations = locations_mapping[player]
|
||||
world.random.shuffle(gtower_locations)
|
||||
localrest = localrestitempool[player]
|
||||
if localrest:
|
||||
gt_item_pool = restitempool + localrest
|
||||
world.random.shuffle(gt_item_pool)
|
||||
else:
|
||||
gt_item_pool = restitempool.copy()
|
||||
|
||||
while gtower_locations and gt_item_pool and trash_count > 0:
|
||||
while gtower_locations and filleritempool and trash_count > 0:
|
||||
spot_to_fill = gtower_locations.pop()
|
||||
item_to_place = gt_item_pool.pop()
|
||||
item_to_place = filleritempool.pop()
|
||||
if spot_to_fill.item_rule(item_to_place):
|
||||
if item_to_place in localrest:
|
||||
localrest.remove(item_to_place)
|
||||
else:
|
||||
restitempool.remove(item_to_place)
|
||||
world.push_item(spot_to_fill, item_to_place, False)
|
||||
fill_locations.remove(spot_to_fill) # very slow, unfortunately
|
||||
trash_count -= 1
|
||||
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
if self.world.goal[self.player] == "icerodhunt":
|
||||
item = "Nothing"
|
||||
|
||||
@@ -26,10 +26,14 @@
|
||||
- Example: `Trinexx`
|
||||
- Takes a particular boss and places that boss in any remaining slots in which this boss can function.
|
||||
- In this example, it would fill Desert Palace, but not Tower of Hera.
|
||||
- If no other options are provided this will follow normal singularity rules with that boss.
|
||||
- Boss Shuffle:
|
||||
- Example: `simple`
|
||||
- Example: `basic`
|
||||
- Runs a particular boss shuffle mode to finish construction instead of vanilla placement, typically used as
|
||||
a last instruction.
|
||||
- Supports `random` which will choose a random option from the normal choices.
|
||||
- If one is not supplied any remaining locations will be unshuffled unless a single specific boss is
|
||||
supplied in which case it will use singularity as noted above.
|
||||
- [Available Bosses](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/alttp/Bosses.py#L135)
|
||||
- [Available Arenas](https://github.com/ArchipelagoMW/Archipelago/blob/main/worlds/alttp/Bosses.py#L150)
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ async def dkc3_game_watcher(ctx: Context):
|
||||
return
|
||||
|
||||
new_checks = []
|
||||
from worlds.dkc3.Rom import location_rom_data, item_rom_data
|
||||
from worlds.dkc3.Rom import location_rom_data, item_rom_data, boss_location_ids, level_unlock_map
|
||||
for loc_id, loc_data in location_rom_data.items():
|
||||
if loc_id not in ctx.locations_checked:
|
||||
data = await snes_read(ctx, WRAM_START + loc_data[0], 1)
|
||||
@@ -186,22 +186,40 @@ async def dkc3_game_watcher(ctx: Context):
|
||||
|
||||
# DKC3_TODO: This method of collect should work, however it does not unlock the next level correctly when previous is flagged
|
||||
# Handle Collected Locations
|
||||
#for loc_id in ctx.checked_locations:
|
||||
# if loc_id not in ctx.locations_checked:
|
||||
# loc_data = location_rom_data[loc_id]
|
||||
# data = await snes_read(ctx, WRAM_START + loc_data[0], 1)
|
||||
# invert_bit = ((len(loc_data) >= 3) and loc_data[2])
|
||||
# if not invert_bit:
|
||||
# masked_data = data[0] | (1 << loc_data[1])
|
||||
# print("Collected Location: ", hex(loc_data[0]), " | ", loc_data[1])
|
||||
# snes_buffered_write(ctx, WRAM_START + loc_data[0], bytes([masked_data]))
|
||||
# await snes_flush_writes(ctx)
|
||||
# else:
|
||||
# masked_data = data[0] & ~(1 << loc_data[1])
|
||||
# print("Collected Inverted Location: ", hex(loc_data[0]), " | ", loc_data[1])
|
||||
# snes_buffered_write(ctx, WRAM_START + loc_data[0], bytes([masked_data]))
|
||||
# await snes_flush_writes(ctx)
|
||||
# ctx.locations_checked.add(loc_id)
|
||||
for loc_id in ctx.checked_locations:
|
||||
if loc_id not in ctx.locations_checked and loc_id not in boss_location_ids:
|
||||
loc_data = location_rom_data[loc_id]
|
||||
data = await snes_read(ctx, WRAM_START + loc_data[0], 1)
|
||||
invert_bit = ((len(loc_data) >= 3) and loc_data[2])
|
||||
if not invert_bit:
|
||||
masked_data = data[0] | (1 << loc_data[1])
|
||||
#print("Collected Location: ", hex(loc_data[0]), " | ", loc_data[1])
|
||||
snes_buffered_write(ctx, WRAM_START + loc_data[0], bytes([masked_data]))
|
||||
|
||||
if (loc_data[1] == 1):
|
||||
# Make the next levels accessible
|
||||
level_id = loc_data[0] - 0x632
|
||||
levels_to_tiles = await snes_read(ctx, ROM_START + 0x3FF800, 0x60)
|
||||
tiles_to_levels = await snes_read(ctx, ROM_START + 0x3FF860, 0x60)
|
||||
tile_id = levels_to_tiles[level_id] if levels_to_tiles[level_id] != 0xFF else level_id
|
||||
tile_id = tile_id + 0x632
|
||||
#print("Tile ID: ", hex(tile_id))
|
||||
if tile_id in level_unlock_map:
|
||||
for next_level_address in level_unlock_map[tile_id]:
|
||||
next_level_id = next_level_address - 0x632
|
||||
next_tile_id = tiles_to_levels[next_level_id] if tiles_to_levels[next_level_id] != 0xFF else next_level_id
|
||||
next_tile_id = next_tile_id + 0x632
|
||||
#print("Next Level ID: ", hex(next_tile_id))
|
||||
next_data = await snes_read(ctx, WRAM_START + next_tile_id, 1)
|
||||
snes_buffered_write(ctx, WRAM_START + next_tile_id, bytes([next_data[0] | 0x01]))
|
||||
|
||||
await snes_flush_writes(ctx)
|
||||
else:
|
||||
masked_data = data[0] & ~(1 << loc_data[1])
|
||||
print("Collected Inverted Location: ", hex(loc_data[0]), " | ", loc_data[1])
|
||||
snes_buffered_write(ctx, WRAM_START + loc_data[0], bytes([masked_data]))
|
||||
await snes_flush_writes(ctx)
|
||||
ctx.locations_checked.add(loc_id)
|
||||
|
||||
# Calculate Boomer Cost Text
|
||||
boomer_cost_text = await snes_read(ctx, WRAM_START + 0xAAFD, 2)
|
||||
|
||||
@@ -221,6 +221,55 @@ level_location_table = {
|
||||
LocationName.rocket_rush_dk: 0xDC30A0,
|
||||
}
|
||||
|
||||
kong_location_table = {
|
||||
LocationName.lakeside_limbo_kong: 0xDC3100,
|
||||
LocationName.doorstop_dash_kong: 0xDC3104,
|
||||
LocationName.tidal_trouble_kong: 0xDC3108,
|
||||
LocationName.skiddas_row_kong: 0xDC310C,
|
||||
LocationName.murky_mill_kong: 0xDC3110,
|
||||
|
||||
LocationName.barrel_shield_bust_up_kong: 0xDC3114,
|
||||
LocationName.riverside_race_kong: 0xDC3118,
|
||||
LocationName.squeals_on_wheels_kong: 0xDC311C,
|
||||
LocationName.springin_spiders_kong: 0xDC3120,
|
||||
LocationName.bobbing_barrel_brawl_kong: 0xDC3124,
|
||||
|
||||
LocationName.bazzas_blockade_kong: 0xDC3128,
|
||||
LocationName.rocket_barrel_ride_kong: 0xDC312C,
|
||||
LocationName.kreeping_klasps_kong: 0xDC3130,
|
||||
LocationName.tracker_barrel_trek_kong: 0xDC3134,
|
||||
LocationName.fish_food_frenzy_kong: 0xDC3138,
|
||||
|
||||
LocationName.fire_ball_frenzy_kong: 0xDC313C,
|
||||
LocationName.demolition_drain_pipe_kong: 0xDC3140,
|
||||
LocationName.ripsaw_rage_kong: 0xDC3144,
|
||||
LocationName.blazing_bazookas_kong: 0xDC3148,
|
||||
LocationName.low_g_labyrinth_kong: 0xDC314C,
|
||||
|
||||
LocationName.krevice_kreepers_kong: 0xDC3150,
|
||||
LocationName.tearaway_toboggan_kong: 0xDC3154,
|
||||
LocationName.barrel_drop_bounce_kong: 0xDC3158,
|
||||
LocationName.krack_shot_kroc_kong: 0xDC315C,
|
||||
LocationName.lemguin_lunge_kong: 0xDC3160,
|
||||
|
||||
LocationName.buzzer_barrage_kong: 0xDC3164,
|
||||
LocationName.kong_fused_cliffs_kong: 0xDC3168,
|
||||
LocationName.floodlit_fish_kong: 0xDC316C,
|
||||
LocationName.pothole_panic_kong: 0xDC3170,
|
||||
LocationName.ropey_rumpus_kong: 0xDC3174,
|
||||
|
||||
LocationName.konveyor_rope_clash_kong: 0xDC3178,
|
||||
LocationName.creepy_caverns_kong: 0xDC317C,
|
||||
LocationName.lightning_lookout_kong: 0xDC3180,
|
||||
LocationName.koindozer_klamber_kong: 0xDC3184,
|
||||
LocationName.poisonous_pipeline_kong: 0xDC3188,
|
||||
|
||||
LocationName.stampede_sprint_kong: 0xDC318C,
|
||||
LocationName.criss_cross_cliffs_kong: 0xDC3191,
|
||||
LocationName.tyrant_twin_tussle_kong: 0xDC3195,
|
||||
LocationName.swoopy_salvo_kong: 0xDC319A,
|
||||
}
|
||||
|
||||
|
||||
boss_location_table = {
|
||||
LocationName.belchas_barn: 0xDC30A1,
|
||||
@@ -266,6 +315,7 @@ all_locations = {
|
||||
**boss_location_table,
|
||||
**secret_cave_location_table,
|
||||
**brothers_bear_location_table,
|
||||
**kong_location_table,
|
||||
}
|
||||
|
||||
location_table = {}
|
||||
@@ -277,6 +327,9 @@ def setup_locations(world, player: int):
|
||||
if False:#world.include_trade_sequence[player].value:
|
||||
location_table.update({**brothers_bear_location_table})
|
||||
|
||||
if world.kongsanity[player].value:
|
||||
location_table.update({**kong_location_table})
|
||||
|
||||
return location_table
|
||||
|
||||
|
||||
|
||||
@@ -1,197 +1,236 @@
|
||||
# Level Definitions
|
||||
lakeside_limbo_flag = "Lakeside Limbo - Flag"
|
||||
lakeside_limbo_kong = "Lakeside Limbo - KONG"
|
||||
lakeside_limbo_bonus_1 = "Lakeside Limbo - Bonus 1"
|
||||
lakeside_limbo_bonus_2 = "Lakeside Limbo - Bonus 2"
|
||||
lakeside_limbo_dk = "Lakeside Limbo - DK Coin"
|
||||
|
||||
doorstop_dash_flag = "Doorstop Dash - Flag"
|
||||
doorstop_dash_kong = "Doorstop Dash - KONG"
|
||||
doorstop_dash_bonus_1 = "Doorstop Dash - Bonus 1"
|
||||
doorstop_dash_bonus_2 = "Doorstop Dash - Bonus 2"
|
||||
doorstop_dash_dk = "Doorstop Dash - DK Coin"
|
||||
|
||||
tidal_trouble_flag = "Tidal Trouble - Flag"
|
||||
tidal_trouble_kong = "Tidal Trouble - KONG"
|
||||
tidal_trouble_bonus_1 = "Tidal Trouble - Bonus 1"
|
||||
tidal_trouble_bonus_2 = "Tidal Trouble - Bonus 2"
|
||||
tidal_trouble_dk = "Tidal Trouble - DK Coin"
|
||||
|
||||
skiddas_row_flag = "Skidda's Row - Flag"
|
||||
skiddas_row_kong = "Skidda's Row - KONG"
|
||||
skiddas_row_bonus_1 = "Skidda's Row - Bonus 1"
|
||||
skiddas_row_bonus_2 = "Skidda's Row - Bonus 2"
|
||||
skiddas_row_dk = "Skidda's Row - DK Coin"
|
||||
|
||||
murky_mill_flag = "Murky Mill - Flag"
|
||||
murky_mill_kong = "Murky Mill - KONG"
|
||||
murky_mill_bonus_1 = "Murky Mill - Bonus 1"
|
||||
murky_mill_bonus_2 = "Murky Mill - Bonus 2"
|
||||
murky_mill_dk = "Murky Mill - DK Coin"
|
||||
|
||||
barrel_shield_bust_up_flag = "Barrel Shield Bust-Up - Flag"
|
||||
barrel_shield_bust_up_kong = "Barrel Shield Bust-Up - KONG"
|
||||
barrel_shield_bust_up_bonus_1 = "Barrel Shield Bust-Up - Bonus 1"
|
||||
barrel_shield_bust_up_bonus_2 = "Barrel Shield Bust-Up - Bonus 2"
|
||||
barrel_shield_bust_up_dk = "Barrel Shield Bust-Up - DK Coin"
|
||||
|
||||
riverside_race_flag = "Riverside Race - Flag"
|
||||
riverside_race_kong = "Riverside Race - KONG"
|
||||
riverside_race_bonus_1 = "Riverside Race - Bonus 1"
|
||||
riverside_race_bonus_2 = "Riverside Race - Bonus 2"
|
||||
riverside_race_dk = "Riverside Race - DK Coin"
|
||||
|
||||
squeals_on_wheels_flag = "Squeals On Wheels - Flag"
|
||||
squeals_on_wheels_kong = "Squeals On Wheels - KONG"
|
||||
squeals_on_wheels_bonus_1 = "Squeals On Wheels - Bonus 1"
|
||||
squeals_on_wheels_bonus_2 = "Squeals On Wheels - Bonus 2"
|
||||
squeals_on_wheels_dk = "Squeals On Wheels - DK Coin"
|
||||
|
||||
springin_spiders_flag = "Springin' Spiders - Flag"
|
||||
springin_spiders_kong = "Springin' Spiders - KONG"
|
||||
springin_spiders_bonus_1 = "Springin' Spiders - Bonus 1"
|
||||
springin_spiders_bonus_2 = "Springin' Spiders - Bonus 2"
|
||||
springin_spiders_dk = "Springin' Spiders - DK Coin"
|
||||
|
||||
bobbing_barrel_brawl_flag = "Bobbing Barrel Brawl - Flag"
|
||||
bobbing_barrel_brawl_kong = "Bobbing Barrel Brawl - KONG"
|
||||
bobbing_barrel_brawl_bonus_1 = "Bobbing Barrel Brawl - Bonus 1"
|
||||
bobbing_barrel_brawl_bonus_2 = "Bobbing Barrel Brawl - Bonus 2"
|
||||
bobbing_barrel_brawl_dk = "Bobbing Barrel Brawl - DK Coin"
|
||||
|
||||
bazzas_blockade_flag = "Bazza's Blockade - Flag"
|
||||
bazzas_blockade_kong = "Bazza's Blockade - KONG"
|
||||
bazzas_blockade_bonus_1 = "Bazza's Blockade - Bonus 1"
|
||||
bazzas_blockade_bonus_2 = "Bazza's Blockade - Bonus 2"
|
||||
bazzas_blockade_dk = "Bazza's Blockade - DK Coin"
|
||||
|
||||
rocket_barrel_ride_flag = "Rocket Barrel Ride - Flag"
|
||||
rocket_barrel_ride_kong = "Rocket Barrel Ride - KONG"
|
||||
rocket_barrel_ride_bonus_1 = "Rocket Barrel Ride - Bonus 1"
|
||||
rocket_barrel_ride_bonus_2 = "Rocket Barrel Ride - Bonus 2"
|
||||
rocket_barrel_ride_dk = "Rocket Barrel Ride - DK Coin"
|
||||
|
||||
kreeping_klasps_flag = "Kreeping Klasps - Flag"
|
||||
kreeping_klasps_kong = "Kreeping Klasps - KONG"
|
||||
kreeping_klasps_bonus_1 = "Kreeping Klasps - Bonus 1"
|
||||
kreeping_klasps_bonus_2 = "Kreeping Klasps - Bonus 2"
|
||||
kreeping_klasps_dk = "Kreeping Klasps - DK Coin"
|
||||
|
||||
tracker_barrel_trek_flag = "Tracker Barrel Trek - Flag"
|
||||
tracker_barrel_trek_kong = "Tracker Barrel Trek - KONG"
|
||||
tracker_barrel_trek_bonus_1 = "Tracker Barrel Trek - Bonus 1"
|
||||
tracker_barrel_trek_bonus_2 = "Tracker Barrel Trek - Bonus 2"
|
||||
tracker_barrel_trek_dk = "Tracker Barrel Trek - DK Coin"
|
||||
|
||||
fish_food_frenzy_flag = "Fish Food Frenzy - Flag"
|
||||
fish_food_frenzy_kong = "Fish Food Frenzy - KONG"
|
||||
fish_food_frenzy_bonus_1 = "Fish Food Frenzy - Bonus 1"
|
||||
fish_food_frenzy_bonus_2 = "Fish Food Frenzy - Bonus 2"
|
||||
fish_food_frenzy_dk = "Fish Food Frenzy - DK Coin"
|
||||
|
||||
fire_ball_frenzy_flag = "Fire-Ball Frenzy - Flag"
|
||||
fire_ball_frenzy_kong = "Fire-Ball Frenzy - KONG"
|
||||
fire_ball_frenzy_bonus_1 = "Fire-Ball Frenzy - Bonus 1"
|
||||
fire_ball_frenzy_bonus_2 = "Fire-Ball Frenzy - Bonus 2"
|
||||
fire_ball_frenzy_dk = "Fire-Ball Frenzy - DK Coin"
|
||||
|
||||
demolition_drain_pipe_flag = "Demolition Drain-Pipe - Flag"
|
||||
demolition_drain_pipe_kong = "Demolition Drain-Pipe - KONG"
|
||||
demolition_drain_pipe_bonus_1 = "Demolition Drain-Pipe - Bonus 1"
|
||||
demolition_drain_pipe_bonus_2 = "Demolition Drain-Pipe - Bonus 2"
|
||||
demolition_drain_pipe_dk = "Demolition Drain-Pipe - DK Coin"
|
||||
|
||||
ripsaw_rage_flag = "Ripsaw Rage - Flag"
|
||||
ripsaw_rage_kong = "Ripsaw Rage - KONG"
|
||||
ripsaw_rage_bonus_1 = "Ripsaw Rage - Bonus 1"
|
||||
ripsaw_rage_bonus_2 = "Ripsaw Rage - Bonus 2"
|
||||
ripsaw_rage_dk = "Ripsaw Rage - DK Coin"
|
||||
|
||||
blazing_bazookas_flag = "Blazing Bazookas - Flag"
|
||||
blazing_bazookas_bonus_1 = "Blazing Bazookas - Bonus 1"
|
||||
blazing_bazookas_bonus_2 = "Blazing Bazookas - Bonus 2"
|
||||
blazing_bazookas_dk = "Blazing Bazookas - DK Coin"
|
||||
blazing_bazookas_flag = "Blazing Bazukas - Flag"
|
||||
blazing_bazookas_kong = "Blazing Bazukas - KONG"
|
||||
blazing_bazookas_bonus_1 = "Blazing Bazukas - Bonus 1"
|
||||
blazing_bazookas_bonus_2 = "Blazing Bazukas - Bonus 2"
|
||||
blazing_bazookas_dk = "Blazing Bazukas - DK Coin"
|
||||
|
||||
low_g_labyrinth_flag = "Low-G Labyrinth - Flag"
|
||||
low_g_labyrinth_kong = "Low-G Labyrinth - KONG"
|
||||
low_g_labyrinth_bonus_1 = "Low-G Labyrinth - Bonus 1"
|
||||
low_g_labyrinth_bonus_2 = "Low-G Labyrinth - Bonus 2"
|
||||
low_g_labyrinth_dk = "Low-G Labyrinth - DK Coin"
|
||||
|
||||
krevice_kreepers_flag = "Krevice Kreepers - Flag"
|
||||
krevice_kreepers_kong = "Krevice Kreepers - KONG"
|
||||
krevice_kreepers_bonus_1 = "Krevice Kreepers - Bonus 1"
|
||||
krevice_kreepers_bonus_2 = "Krevice Kreepers - Bonus 2"
|
||||
krevice_kreepers_dk = "Krevice Kreepers - DK Coin"
|
||||
|
||||
tearaway_toboggan_flag = "Tearaway Toboggan - Flag"
|
||||
tearaway_toboggan_kong = "Tearaway Toboggan - KONG"
|
||||
tearaway_toboggan_bonus_1 = "Tearaway Toboggan - Bonus 1"
|
||||
tearaway_toboggan_bonus_2 = "Tearaway Toboggan - Bonus 2"
|
||||
tearaway_toboggan_dk = "Tearaway Toboggan - DK Coin"
|
||||
|
||||
barrel_drop_bounce_flag = "Barrel Drop Bounce - Flag"
|
||||
barrel_drop_bounce_kong = "Barrel Drop Bounce - KONG"
|
||||
barrel_drop_bounce_bonus_1 = "Barrel Drop Bounce - Bonus 1"
|
||||
barrel_drop_bounce_bonus_2 = "Barrel Drop Bounce - Bonus 2"
|
||||
barrel_drop_bounce_dk = "Barrel Drop Bounce - DK Coin"
|
||||
|
||||
krack_shot_kroc_flag = "Krack-Shot Kroc - Flag"
|
||||
krack_shot_kroc_kong = "Krack-Shot Kroc - KONG"
|
||||
krack_shot_kroc_bonus_1 = "Krack-Shot Kroc - Bonus 1"
|
||||
krack_shot_kroc_bonus_2 = "Krack-Shot Kroc - Bonus 2"
|
||||
krack_shot_kroc_dk = "Krack-Shot Kroc - DK Coin"
|
||||
|
||||
lemguin_lunge_flag = "Lemguin Lunge - Flag"
|
||||
lemguin_lunge_kong = "Lemguin Lunge - KONG"
|
||||
lemguin_lunge_bonus_1 = "Lemguin Lunge - Bonus 1"
|
||||
lemguin_lunge_bonus_2 = "Lemguin Lunge - Bonus 2"
|
||||
lemguin_lunge_dk = "Lemguin Lunge - DK Coin"
|
||||
|
||||
buzzer_barrage_flag = "Buzzer Barrage - Flag"
|
||||
buzzer_barrage_kong = "Buzzer Barrage - KONG"
|
||||
buzzer_barrage_bonus_1 = "Buzzer Barrage - Bonus 1"
|
||||
buzzer_barrage_bonus_2 = "Buzzer Barrage - Bonus 2"
|
||||
buzzer_barrage_dk = "Buzzer Barrage - DK Coin"
|
||||
|
||||
kong_fused_cliffs_flag = "Kong-Fused Cliffs - Flag"
|
||||
kong_fused_cliffs_kong = "Kong-Fused Cliffs - KONG"
|
||||
kong_fused_cliffs_bonus_1 = "Kong-Fused Cliffs - Bonus 1"
|
||||
kong_fused_cliffs_bonus_2 = "Kong-Fused Cliffs - Bonus 2"
|
||||
kong_fused_cliffs_dk = "Kong-Fused Cliffs - DK Coin"
|
||||
|
||||
floodlit_fish_flag = "Floodlit Fish - Flag"
|
||||
floodlit_fish_kong = "Floodlit Fish - KONG"
|
||||
floodlit_fish_bonus_1 = "Floodlit Fish - Bonus 1"
|
||||
floodlit_fish_bonus_2 = "Floodlit Fish - Bonus 2"
|
||||
floodlit_fish_dk = "Floodlit Fish - DK Coin"
|
||||
|
||||
pothole_panic_flag = "Pothole Panic - Flag"
|
||||
pothole_panic_kong = "Pothole Panic - KONG"
|
||||
pothole_panic_bonus_1 = "Pothole Panic - Bonus 1"
|
||||
pothole_panic_bonus_2 = "Pothole Panic - Bonus 2"
|
||||
pothole_panic_dk = "Pothole Panic - DK Coin"
|
||||
|
||||
ropey_rumpus_flag = "Ropey Rumpus - Flag"
|
||||
ropey_rumpus_kong = "Ropey Rumpus - KONG"
|
||||
ropey_rumpus_bonus_1 = "Ropey Rumpus - Bonus 1"
|
||||
ropey_rumpus_bonus_2 = "Ropey Rumpus - Bonus 2"
|
||||
ropey_rumpus_dk = "Ropey Rumpus - DK Coin"
|
||||
|
||||
konveyor_rope_clash_flag = "Konveyor Rope Klash - Flag"
|
||||
konveyor_rope_clash_kong = "Konveyor Rope Klash - KONG"
|
||||
konveyor_rope_clash_bonus_1 = "Konveyor Rope Klash - Bonus 1"
|
||||
konveyor_rope_clash_bonus_2 = "Konveyor Rope Klash - Bonus 2"
|
||||
konveyor_rope_clash_dk = "Konveyor Rope Klash - DK Coin"
|
||||
|
||||
creepy_caverns_flag = "Creepy Caverns - Flag"
|
||||
creepy_caverns_kong = "Creepy Caverns - KONG"
|
||||
creepy_caverns_bonus_1 = "Creepy Caverns - Bonus 1"
|
||||
creepy_caverns_bonus_2 = "Creepy Caverns - Bonus 2"
|
||||
creepy_caverns_dk = "Creepy Caverns - DK Coin"
|
||||
|
||||
lightning_lookout_flag = "Lightning Lookout - Flag"
|
||||
lightning_lookout_kong = "Lightning Lookout - KONG"
|
||||
lightning_lookout_bonus_1 = "Lightning Lookout - Bonus 1"
|
||||
lightning_lookout_bonus_2 = "Lightning Lookout - Bonus 2"
|
||||
lightning_lookout_dk = "Lightning Lookout - DK Coin"
|
||||
|
||||
koindozer_klamber_flag = "Koindozer Klamber - Flag"
|
||||
koindozer_klamber_kong = "Koindozer Klamber - KONG"
|
||||
koindozer_klamber_bonus_1 = "Koindozer Klamber - Bonus 1"
|
||||
koindozer_klamber_bonus_2 = "Koindozer Klamber - Bonus 2"
|
||||
koindozer_klamber_dk = "Koindozer Klamber - DK Coin"
|
||||
|
||||
poisonous_pipeline_flag = "Poisonous Pipeline - Flag"
|
||||
poisonous_pipeline_kong = "Poisonous Pipeline - KONG"
|
||||
poisonous_pipeline_bonus_1 = "Poisonous Pipeline - Bonus 1"
|
||||
poisonous_pipeline_bonus_2 = "Poisonous Pipeline - Bonus 2"
|
||||
poisonous_pipeline_dk = "Poisonous Pipeline - DK Coin"
|
||||
|
||||
stampede_sprint_flag = "Stampede Sprint - Flag"
|
||||
stampede_sprint_kong = "Stampede Sprint - KONG"
|
||||
stampede_sprint_bonus_1 = "Stampede Sprint - Bonus 1"
|
||||
stampede_sprint_bonus_2 = "Stampede Sprint - Bonus 2"
|
||||
stampede_sprint_bonus_3 = "Stampede Sprint - Bonus 3"
|
||||
stampede_sprint_dk = "Stampede Sprint - DK Coin"
|
||||
|
||||
criss_cross_cliffs_flag = "Criss Kross Cliffs - Flag"
|
||||
criss_cross_cliffs_kong = "Criss Kross Cliffs - KONG"
|
||||
criss_cross_cliffs_bonus_1 = "Criss Kross Cliffs - Bonus 1"
|
||||
criss_cross_cliffs_bonus_2 = "Criss Kross Cliffs - Bonus 2"
|
||||
criss_cross_cliffs_dk = "Criss Kross Cliffs - DK Coin"
|
||||
|
||||
tyrant_twin_tussle_flag = "Tyrant Twin Tussle - Flag"
|
||||
tyrant_twin_tussle_kong = "Tyrant Twin Tussle - KONG"
|
||||
tyrant_twin_tussle_bonus_1 = "Tyrant Twin Tussle - Bonus 1"
|
||||
tyrant_twin_tussle_bonus_2 = "Tyrant Twin Tussle - Bonus 2"
|
||||
tyrant_twin_tussle_bonus_3 = "Tyrant Twin Tussle - Bonus 3"
|
||||
tyrant_twin_tussle_dk = "Tyrant Twin Tussle - DK Coin"
|
||||
|
||||
swoopy_salvo_flag = "Swoopy Salvo - Flag"
|
||||
swoopy_salvo_kong = "Swoopy Salvo - KONG"
|
||||
swoopy_salvo_bonus_1 = "Swoopy Salvo - Bonus 1"
|
||||
swoopy_salvo_bonus_2 = "Swoopy Salvo - Bonus 2"
|
||||
swoopy_salvo_bonus_3 = "Swoopy Salvo - Bonus 3"
|
||||
|
||||
@@ -6,7 +6,7 @@ from Options import Choice, Range, Option, Toggle, DeathLink, DefaultOnToggle, O
|
||||
class Goal(Choice):
|
||||
"""
|
||||
Determines the goal of the seed
|
||||
Knautilus: Reach the Knautilus and defeat Baron K. Roolenstein
|
||||
Knautilus: Scuttle the Knautilus in Krematoa and defeat Baron K. Roolenstein
|
||||
Banana Bird Hunt: Find a certain number of Banana Birds and rescue their mother
|
||||
"""
|
||||
display_name = "Goal"
|
||||
@@ -75,6 +75,13 @@ class PercentageOfBananaBirds(Range):
|
||||
default = 100
|
||||
|
||||
|
||||
class KONGsanity(Toggle):
|
||||
"""
|
||||
Whether collecting all four KONG letters in each level grants a check
|
||||
"""
|
||||
display_name = "KONGsanity"
|
||||
|
||||
|
||||
class LevelShuffle(Toggle):
|
||||
"""
|
||||
Whether levels are shuffled
|
||||
@@ -82,6 +89,41 @@ class LevelShuffle(Toggle):
|
||||
display_name = "Level Shuffle"
|
||||
|
||||
|
||||
class Difficulty(Choice):
|
||||
"""
|
||||
Which Difficulty Level to use
|
||||
NORML: The Normal Difficulty
|
||||
HARDR: Many DK Barrels are removed
|
||||
TUFST: Most DK Barrels and all Midway Barrels are removed
|
||||
"""
|
||||
display_name = "Difficulty"
|
||||
option_norml = 0
|
||||
option_hardr = 1
|
||||
option_tufst = 2
|
||||
default = 0
|
||||
|
||||
@classmethod
|
||||
def get_option_name(cls, value) -> str:
|
||||
if cls.auto_display_name:
|
||||
return cls.name_lookup[value].upper()
|
||||
else:
|
||||
return cls.name_lookup[value]
|
||||
|
||||
|
||||
class Autosave(DefaultOnToggle):
|
||||
"""
|
||||
Whether the game should autosave after each level
|
||||
"""
|
||||
display_name = "Autosave"
|
||||
|
||||
|
||||
class MERRY(Toggle):
|
||||
"""
|
||||
Whether the Bonus Barrels will be Christmas-themed
|
||||
"""
|
||||
display_name = "MERRY"
|
||||
|
||||
|
||||
class MusicShuffle(Toggle):
|
||||
"""
|
||||
Whether music is shuffled
|
||||
@@ -125,7 +167,11 @@ dkc3_options: typing.Dict[str, type(Option)] = {
|
||||
"percentage_of_extra_bonus_coins": PercentageOfExtraBonusCoins,
|
||||
"number_of_banana_birds": NumberOfBananaBirds,
|
||||
"percentage_of_banana_birds": PercentageOfBananaBirds,
|
||||
"kongsanity": KONGsanity,
|
||||
"level_shuffle": LevelShuffle,
|
||||
"difficulty": Difficulty,
|
||||
"autosave": Autosave,
|
||||
"merry": MERRY,
|
||||
"music_shuffle": MusicShuffle,
|
||||
"kong_palette_swap": KongPaletteSwap,
|
||||
"starting_life_count": StartingLifeCount,
|
||||
|
||||
@@ -44,6 +44,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.lakeside_limbo_bonus_2 : [0x657, 3],
|
||||
LocationName.lakeside_limbo_dk : [0x657, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -53,6 +55,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.doorstop_dash_bonus_2 : [0x65A, 3],
|
||||
LocationName.doorstop_dash_dk : [0x65A, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -62,6 +66,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.tidal_trouble_bonus_2 : [0x659, 3],
|
||||
LocationName.tidal_trouble_dk : [0x659, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -71,6 +77,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.skiddas_row_bonus_2 : [0x65D, 3],
|
||||
LocationName.skiddas_row_dk : [0x65D, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -80,6 +88,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.murky_mill_bonus_2 : [0x65C, 3],
|
||||
LocationName.murky_mill_dk : [0x65C, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -89,6 +99,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.barrel_shield_bust_up_bonus_2 : [0x662, 3],
|
||||
LocationName.barrel_shield_bust_up_dk : [0x662, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -98,6 +110,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.riverside_race_bonus_2 : [0x664, 3],
|
||||
LocationName.riverside_race_dk : [0x664, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -107,6 +121,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.squeals_on_wheels_bonus_2 : [0x65B, 3],
|
||||
LocationName.squeals_on_wheels_dk : [0x65B, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -116,6 +132,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.springin_spiders_bonus_2 : [0x661, 3],
|
||||
LocationName.springin_spiders_dk : [0x661, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -125,6 +143,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.bobbing_barrel_brawl_bonus_2 : [0x666, 3],
|
||||
LocationName.bobbing_barrel_brawl_dk : [0x666, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -134,6 +154,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.bazzas_blockade_bonus_2 : [0x667, 3],
|
||||
LocationName.bazzas_blockade_dk : [0x667, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -143,6 +165,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.rocket_barrel_ride_bonus_2 : [0x66A, 3],
|
||||
LocationName.rocket_barrel_ride_dk : [0x66A, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -152,6 +176,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.kreeping_klasps_bonus_2 : [0x658, 3],
|
||||
LocationName.kreeping_klasps_dk : [0x658, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -161,6 +187,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.tracker_barrel_trek_bonus_2 : [0x66B, 3],
|
||||
LocationName.tracker_barrel_trek_dk : [0x66B, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -170,6 +198,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.fish_food_frenzy_bonus_2 : [0x668, 3],
|
||||
LocationName.fish_food_frenzy_dk : [0x668, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -179,6 +209,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.fire_ball_frenzy_bonus_2 : [0x66D, 3],
|
||||
LocationName.fire_ball_frenzy_dk : [0x66D, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -188,6 +220,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.demolition_drain_pipe_bonus_2 : [0x672, 3],
|
||||
LocationName.demolition_drain_pipe_dk : [0x672, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -197,6 +231,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.ripsaw_rage_bonus_2 : [0x660, 3],
|
||||
LocationName.ripsaw_rage_dk : [0x660, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -206,6 +242,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.blazing_bazookas_bonus_2 : [0x66E, 3],
|
||||
LocationName.blazing_bazookas_dk : [0x66E, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -215,6 +253,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.low_g_labyrinth_bonus_2 : [0x670, 3],
|
||||
LocationName.low_g_labyrinth_dk : [0x670, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -224,6 +264,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.krevice_kreepers_bonus_2 : [0x673, 3],
|
||||
LocationName.krevice_kreepers_dk : [0x673, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -233,6 +275,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.tearaway_toboggan_bonus_2 : [0x65F, 3],
|
||||
LocationName.tearaway_toboggan_dk : [0x65F, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -242,6 +286,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.barrel_drop_bounce_bonus_2 : [0x66C, 3],
|
||||
LocationName.barrel_drop_bounce_dk : [0x66C, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -251,6 +297,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.krack_shot_kroc_bonus_2 : [0x66F, 3],
|
||||
LocationName.krack_shot_kroc_dk : [0x66F, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -260,6 +308,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.lemguin_lunge_bonus_2 : [0x65E, 3],
|
||||
LocationName.lemguin_lunge_dk : [0x65E, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -269,6 +319,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.buzzer_barrage_bonus_2 : [0x676, 3],
|
||||
LocationName.buzzer_barrage_dk : [0x676, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -278,6 +330,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.kong_fused_cliffs_bonus_2 : [0x674, 3],
|
||||
LocationName.kong_fused_cliffs_dk : [0x674, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -287,6 +341,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.floodlit_fish_bonus_2 : [0x669, 3],
|
||||
LocationName.floodlit_fish_dk : [0x669, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -296,6 +352,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.pothole_panic_bonus_2 : [0x677, 3],
|
||||
LocationName.pothole_panic_dk : [0x677, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -305,6 +363,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.ropey_rumpus_bonus_2 : [0x675, 3],
|
||||
LocationName.ropey_rumpus_dk : [0x675, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -314,6 +374,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.konveyor_rope_clash_bonus_2 : [0x657, 3],
|
||||
LocationName.konveyor_rope_clash_dk : [0x657, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -323,6 +385,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.creepy_caverns_bonus_2 : [0x678, 3],
|
||||
LocationName.creepy_caverns_dk : [0x678, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -332,6 +396,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.lightning_lookout_bonus_2 : [0x665, 3],
|
||||
LocationName.lightning_lookout_dk : [0x665, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -341,6 +407,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.koindozer_klamber_bonus_2 : [0x679, 3],
|
||||
LocationName.koindozer_klamber_dk : [0x679, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -350,6 +418,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.poisonous_pipeline_bonus_2 : [0x671, 3],
|
||||
LocationName.poisonous_pipeline_dk : [0x671, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -360,6 +430,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.stampede_sprint_bonus_3 : [0x67B, 4],
|
||||
LocationName.stampede_sprint_dk : [0x67B, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -369,6 +441,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.criss_cross_cliffs_bonus_2 : [0x67C, 3],
|
||||
LocationName.criss_cross_cliffs_dk : [0x67C, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -379,6 +453,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.tyrant_twin_tussle_bonus_3 : [0x67D, 4],
|
||||
LocationName.tyrant_twin_tussle_dk : [0x67D, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -389,6 +465,8 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.swoopy_salvo_bonus_3 : [0x663, 4],
|
||||
LocationName.swoopy_salvo_dk : [0x663, 5],
|
||||
}
|
||||
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)
|
||||
|
||||
@@ -503,9 +581,7 @@ def create_regions(world, player: int, active_locations):
|
||||
|
||||
sky_high_secret_region_locations = {}
|
||||
if False:#world.include_trade_sequence[player]:
|
||||
sky_high_secret_region_locations.update({
|
||||
LocationName.sky_high_secret: [0x64B, 1],
|
||||
})
|
||||
sky_high_secret_region_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)
|
||||
|
||||
@@ -517,9 +593,7 @@ def create_regions(world, player: int, active_locations):
|
||||
|
||||
cifftop_cache_region_locations = {}
|
||||
if False:#world.include_trade_sequence[player]:
|
||||
cifftop_cache_region_locations.update({
|
||||
LocationName.cifftop_cache: [0x64D, 1],
|
||||
})
|
||||
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)
|
||||
|
||||
@@ -622,29 +696,19 @@ def create_regions(world, player: int, active_locations):
|
||||
LocationName.bazaars_general_store_2: [0x615, 3, True],
|
||||
})
|
||||
|
||||
bramble_region_locations.update({
|
||||
LocationName.brambles_bungalow: [0x619, 2],
|
||||
})
|
||||
bramble_region_locations[LocationName.brambles_bungalow] = [0x619, 2]
|
||||
|
||||
#flower_spot_region_locations.update({
|
||||
# LocationName.flower_spot: [0x615, 3, True],
|
||||
#})
|
||||
|
||||
barter_region_locations.update({
|
||||
LocationName.barters_swap_shop: [0x61B, 3],
|
||||
})
|
||||
barter_region_locations[LocationName.barters_swap_shop] = [0x61B, 3]
|
||||
|
||||
barnacle_region_locations.update({
|
||||
LocationName.barnacles_island: [0x61D, 2],
|
||||
})
|
||||
barnacle_region_locations[LocationName.barnacles_island] = [0x61D, 2]
|
||||
|
||||
blue_region_locations.update({
|
||||
LocationName.blues_beach_hut: [0x621, 4],
|
||||
})
|
||||
blue_region_locations[LocationName.blues_beach_hut] = [0x621, 4]
|
||||
|
||||
blizzard_region_locations.update({
|
||||
LocationName.blizzards_basecamp: [0x625, 4, True],
|
||||
})
|
||||
blizzard_region_locations[LocationName.blizzards_basecamp] = [0x625, 4, True]
|
||||
|
||||
bazaar_region = create_region(world, player, active_locations, LocationName.bazaar_region,
|
||||
bazaar_region_locations, None)
|
||||
@@ -817,7 +881,6 @@ def connect_regions(world, player, level_list):
|
||||
level_list[32],
|
||||
level_list[33],
|
||||
level_list[34],
|
||||
LocationName.kastle_kaos_region,
|
||||
LocationName.sewer_stockpile_region,
|
||||
]
|
||||
|
||||
@@ -835,10 +898,16 @@ def connect_regions(world, player, level_list):
|
||||
|
||||
for i in range(0, len(krematoa_levels)):
|
||||
connect(world, player, names, LocationName.krematoa_region, krematoa_levels[i],
|
||||
lambda state: (state.has(ItemName.bonus_coin, player, world.krematoa_bonus_coin_cost[player].value * (i+1))))
|
||||
|
||||
connect(world, player, names, LocationName.krematoa_region, LocationName.knautilus_region,
|
||||
lambda state: (state.has(ItemName.krematoa_cog, player, 5)))
|
||||
lambda state, i=i: (state.has(ItemName.bonus_coin, player, world.krematoa_bonus_coin_cost[player].value * (i+1))))
|
||||
|
||||
if world.goal[player] == "knautilus":
|
||||
connect(world, player, names, LocationName.kaos_kore_region, LocationName.knautilus_region)
|
||||
connect(world, player, names, LocationName.krematoa_region, LocationName.kastle_kaos_region,
|
||||
lambda state: (state.has(ItemName.krematoa_cog, player, 5)))
|
||||
else:
|
||||
connect(world, player, names, LocationName.kaos_kore_region, LocationName.kastle_kaos_region)
|
||||
connect(world, player, names, LocationName.krematoa_region, LocationName.knautilus_region,
|
||||
lambda state: (state.has(ItemName.krematoa_cog, player, 5)))
|
||||
|
||||
|
||||
def create_region(world: MultiWorld, player: int, active_locations, name: str, locations=None, exits=None):
|
||||
|
||||
@@ -11,187 +11,270 @@ import os
|
||||
import math
|
||||
|
||||
|
||||
level_unlock_map = {
|
||||
0x657: [0x65A],
|
||||
0x65A: [0x680, 0x639, 0x659],
|
||||
0x659: [0x65D],
|
||||
0x65D: [0x65C],
|
||||
0x65C: [0x688, 0x64F],
|
||||
|
||||
0x662: [0x681, 0x664],
|
||||
0x664: [0x65B],
|
||||
0x65B: [0x689, 0x661],
|
||||
0x661: [0x63A, 0x666],
|
||||
0x666: [0x650, 0x649],
|
||||
|
||||
0x667: [0x66A],
|
||||
0x66A: [0x682, 0x658],
|
||||
0x658: [0x68A, 0x66B],
|
||||
0x66B: [0x668],
|
||||
0x668: [0x651],
|
||||
|
||||
0x66D: [0x63C, 0x672],
|
||||
0x672: [0x68B, 0x660],
|
||||
0x660: [0x683, 0x66E],
|
||||
0x66E: [0x670],
|
||||
0x670: [0x652],
|
||||
|
||||
0x673: [0x684, 0x65F],
|
||||
0x65F: [0x66C],
|
||||
0x66C: [0x66F],
|
||||
0x66F: [0x65E],
|
||||
0x65E: [0x63D, 0x653, 0x68C, 0x64C],
|
||||
|
||||
0x676: [0x63E, 0x674, 0x685],
|
||||
0x674: [0x63F, 0x669],
|
||||
0x669: [0x677],
|
||||
0x677: [0x68D, 0x675],
|
||||
0x675: [0x654],
|
||||
|
||||
0x67A: [0x640, 0x678],
|
||||
0x678: [0x665],
|
||||
0x665: [0x686, 0x679],
|
||||
0x679: [0x68E, 0x671],
|
||||
|
||||
0x67B: [0x67C],
|
||||
0x67C: [0x67D],
|
||||
0x67D: [0x663],
|
||||
0x663: [0x67E],
|
||||
}
|
||||
|
||||
location_rom_data = {
|
||||
0xDC3000: [0x657, 1], # Lakeside Limbo
|
||||
0xDC3001: [0x657, 2],
|
||||
0xDC3002: [0x657, 3],
|
||||
0xDC3003: [0x657, 5],
|
||||
0xDC3100: [0x657, 7],
|
||||
|
||||
0xDC3004: [0x65A, 1], # Doorstop Dash
|
||||
0xDC3005: [0x65A, 2],
|
||||
0xDC3006: [0x65A, 3],
|
||||
0xDC3007: [0x65A, 5],
|
||||
0xDC3104: [0x65A, 7],
|
||||
|
||||
0xDC3008: [0x659, 1], # Tidal Trouble
|
||||
0xDC3009: [0x659, 2],
|
||||
0xDC300A: [0x659, 3],
|
||||
0xDC300B: [0x659, 5],
|
||||
0xDC3108: [0x659, 7],
|
||||
|
||||
0xDC300C: [0x65D, 1], # Skidda's Row
|
||||
0xDC300D: [0x65D, 2],
|
||||
0xDC300E: [0x65D, 3],
|
||||
0xDC300F: [0x65D, 5],
|
||||
0xDC310C: [0x65D, 7],
|
||||
|
||||
0xDC3010: [0x65C, 1], # Murky Mill
|
||||
0xDC3011: [0x65C, 2],
|
||||
0xDC3012: [0x65C, 3],
|
||||
0xDC3013: [0x65C, 5],
|
||||
0xDC3110: [0x65C, 7],
|
||||
|
||||
|
||||
0xDC3014: [0x662, 1], # Barrel Shield Bust-Up
|
||||
0xDC3015: [0x662, 2],
|
||||
0xDC3016: [0x662, 3],
|
||||
0xDC3017: [0x662, 5],
|
||||
0xDC3114: [0x662, 7],
|
||||
|
||||
0xDC3018: [0x664, 1], # Riverside Race
|
||||
0xDC3019: [0x664, 2],
|
||||
0xDC301A: [0x664, 3],
|
||||
0xDC301B: [0x664, 5],
|
||||
0xDC3118: [0x664, 7],
|
||||
|
||||
0xDC301C: [0x65B, 1], # Squeals on Wheels
|
||||
0xDC301D: [0x65B, 2],
|
||||
0xDC301E: [0x65B, 3],
|
||||
0xDC301F: [0x65B, 5],
|
||||
0xDC311C: [0x65B, 7],
|
||||
|
||||
0xDC3020: [0x661, 1], # Springin' Spiders
|
||||
0xDC3021: [0x661, 2],
|
||||
0xDC3022: [0x661, 3],
|
||||
0xDC3023: [0x661, 5],
|
||||
0xDC3120: [0x661, 7],
|
||||
|
||||
0xDC3024: [0x666, 1], # Bobbing Barrel Brawl
|
||||
0xDC3025: [0x666, 2],
|
||||
0xDC3026: [0x666, 3],
|
||||
0xDC3027: [0x666, 5],
|
||||
0xDC3124: [0x666, 7],
|
||||
|
||||
|
||||
0xDC3028: [0x667, 1], # Bazza's Blockade
|
||||
0xDC3029: [0x667, 2],
|
||||
0xDC302A: [0x667, 3],
|
||||
0xDC302B: [0x667, 5],
|
||||
0xDC3128: [0x667, 7],
|
||||
|
||||
0xDC302C: [0x66A, 1], # Rocket Barrel Ride
|
||||
0xDC302D: [0x66A, 2],
|
||||
0xDC302E: [0x66A, 3],
|
||||
0xDC302F: [0x66A, 5],
|
||||
0xDC312C: [0x66A, 7],
|
||||
|
||||
0xDC3030: [0x658, 1], # Kreeping Klasps
|
||||
0xDC3031: [0x658, 2],
|
||||
0xDC3032: [0x658, 3],
|
||||
0xDC3033: [0x658, 5],
|
||||
0xDC3130: [0x658, 7],
|
||||
|
||||
0xDC3034: [0x66B, 1], # Tracker Barrel Trek
|
||||
0xDC3035: [0x66B, 2],
|
||||
0xDC3036: [0x66B, 3],
|
||||
0xDC3037: [0x66B, 5],
|
||||
0xDC3134: [0x66B, 7],
|
||||
|
||||
0xDC3038: [0x668, 1], # Fish Food Frenzy
|
||||
0xDC3039: [0x668, 2],
|
||||
0xDC303A: [0x668, 3],
|
||||
0xDC303B: [0x668, 5],
|
||||
0xDC3138: [0x668, 7],
|
||||
|
||||
|
||||
0xDC303C: [0x66D, 1], # Fire-ball Frenzy
|
||||
0xDC303D: [0x66D, 2],
|
||||
0xDC303E: [0x66D, 3],
|
||||
0xDC303F: [0x66D, 5],
|
||||
0xDC313C: [0x66D, 7],
|
||||
|
||||
0xDC3040: [0x672, 1], # Demolition Drainpipe
|
||||
0xDC3041: [0x672, 2],
|
||||
0xDC3042: [0x672, 3],
|
||||
0xDC3043: [0x672, 5],
|
||||
0xDC3140: [0x672, 7],
|
||||
|
||||
0xDC3044: [0x660, 1], # Ripsaw Rage
|
||||
0xDC3045: [0x660, 2],
|
||||
0xDC3046: [0x660, 3],
|
||||
0xDC3047: [0x660, 5],
|
||||
0xDC3144: [0x660, 7],
|
||||
|
||||
0xDC3048: [0x66E, 1], # Blazing Bazukas
|
||||
0xDC3049: [0x66E, 2],
|
||||
0xDC304A: [0x66E, 3],
|
||||
0xDC304B: [0x66E, 5],
|
||||
0xDC3148: [0x66E, 7],
|
||||
|
||||
0xDC304C: [0x670, 1], # Low-G Labyrinth
|
||||
0xDC304D: [0x670, 2],
|
||||
0xDC304E: [0x670, 3],
|
||||
0xDC304F: [0x670, 5],
|
||||
0xDC314C: [0x670, 7],
|
||||
|
||||
|
||||
0xDC3050: [0x673, 1], # Krevice Kreepers
|
||||
0xDC3051: [0x673, 2],
|
||||
0xDC3052: [0x673, 3],
|
||||
0xDC3053: [0x673, 5],
|
||||
0xDC3150: [0x673, 7],
|
||||
|
||||
0xDC3054: [0x65F, 1], # Tearaway Toboggan
|
||||
0xDC3055: [0x65F, 2],
|
||||
0xDC3056: [0x65F, 3],
|
||||
0xDC3057: [0x65F, 5],
|
||||
0xDC3154: [0x65F, 7],
|
||||
|
||||
0xDC3058: [0x66C, 1], # Barrel Drop Bounce
|
||||
0xDC3059: [0x66C, 2],
|
||||
0xDC305A: [0x66C, 3],
|
||||
0xDC305B: [0x66C, 5],
|
||||
0xDC3158: [0x66C, 7],
|
||||
|
||||
0xDC305C: [0x66F, 1], # Krack-Shot Kroc
|
||||
0xDC305D: [0x66F, 2],
|
||||
0xDC305E: [0x66F, 3],
|
||||
0xDC305F: [0x66F, 5],
|
||||
0xDC315C: [0x66F, 7],
|
||||
|
||||
0xDC3060: [0x65E, 1], # Lemguin Lunge
|
||||
0xDC3061: [0x65E, 2],
|
||||
0xDC3062: [0x65E, 3],
|
||||
0xDC3063: [0x65E, 5],
|
||||
0xDC3160: [0x65E, 7],
|
||||
|
||||
|
||||
0xDC3064: [0x676, 1], # Buzzer Barrage
|
||||
0xDC3065: [0x676, 2],
|
||||
0xDC3066: [0x676, 3],
|
||||
0xDC3067: [0x676, 5],
|
||||
0xDC3164: [0x676, 7],
|
||||
|
||||
0xDC3068: [0x674, 1], # Kong-Fused Cliffs
|
||||
0xDC3069: [0x674, 2],
|
||||
0xDC306A: [0x674, 3],
|
||||
0xDC306B: [0x674, 5],
|
||||
0xDC3168: [0x674, 7],
|
||||
|
||||
0xDC306C: [0x669, 1], # Floodlit Fish
|
||||
0xDC306D: [0x669, 2],
|
||||
0xDC306E: [0x669, 3],
|
||||
0xDC306F: [0x669, 5],
|
||||
0xDC316C: [0x669, 7],
|
||||
|
||||
0xDC3070: [0x677, 1], # Pothole Panic
|
||||
0xDC3071: [0x677, 2],
|
||||
0xDC3072: [0x677, 3],
|
||||
0xDC3073: [0x677, 5],
|
||||
0xDC3170: [0x677, 7],
|
||||
|
||||
0xDC3074: [0x675, 1], # Ropey Rumpus
|
||||
0xDC3075: [0x675, 2],
|
||||
0xDC3076: [0x675, 3],
|
||||
0xDC3077: [0x675, 5],
|
||||
0xDC3174: [0x675, 7],
|
||||
|
||||
|
||||
0xDC3078: [0x67A, 1], # Konveyor Rope Klash
|
||||
0xDC3079: [0x67A, 2],
|
||||
0xDC307A: [0x67A, 3],
|
||||
0xDC307B: [0x67A, 5],
|
||||
0xDC3178: [0x67A, 7],
|
||||
|
||||
0xDC307C: [0x678, 1], # Creepy Caverns
|
||||
0xDC307D: [0x678, 2],
|
||||
0xDC307E: [0x678, 3],
|
||||
0xDC307F: [0x678, 5],
|
||||
0xDC317C: [0x678, 7],
|
||||
|
||||
0xDC3080: [0x665, 1], # Lightning Lookout
|
||||
0xDC3081: [0x665, 2],
|
||||
0xDC3082: [0x665, 3],
|
||||
0xDC3083: [0x665, 5],
|
||||
0xDC3180: [0x665, 7],
|
||||
|
||||
0xDC3084: [0x679, 1], # Koindozer Klamber
|
||||
0xDC3085: [0x679, 2],
|
||||
0xDC3086: [0x679, 3],
|
||||
0xDC3087: [0x679, 5],
|
||||
0xDC3184: [0x679, 7],
|
||||
|
||||
0xDC3088: [0x671, 1], # Poisonous Pipeline
|
||||
0xDC3089: [0x671, 2],
|
||||
0xDC308A: [0x671, 3],
|
||||
0xDC308B: [0x671, 5],
|
||||
0xDC3188: [0x671, 7],
|
||||
|
||||
|
||||
0xDC308C: [0x67B, 1], # Stampede Sprint
|
||||
@@ -199,23 +282,27 @@ location_rom_data = {
|
||||
0xDC308E: [0x67B, 3],
|
||||
0xDC308F: [0x67B, 4],
|
||||
0xDC3090: [0x67B, 5],
|
||||
0xDC318C: [0x67B, 7],
|
||||
|
||||
0xDC3091: [0x67C, 1], # Criss Kross Cliffs
|
||||
0xDC3092: [0x67C, 2],
|
||||
0xDC3093: [0x67C, 3],
|
||||
0xDC3094: [0x67C, 5],
|
||||
0xDC3191: [0x67C, 7],
|
||||
|
||||
0xDC3095: [0x67D, 1], # Tyrant Twin Tussle
|
||||
0xDC3096: [0x67D, 2],
|
||||
0xDC3097: [0x67D, 3],
|
||||
0xDC3098: [0x67D, 4],
|
||||
0xDC3099: [0x67D, 5],
|
||||
0xDC3195: [0x67D, 7],
|
||||
|
||||
0xDC309A: [0x663, 1], # Swoopy Salvo
|
||||
0xDC309B: [0x663, 2],
|
||||
0xDC309C: [0x663, 3],
|
||||
0xDC309D: [0x663, 4],
|
||||
0xDC309E: [0x663, 5],
|
||||
0xDC319A: [0x663, 7],
|
||||
|
||||
0xDC309F: [0x67E, 1], # Rocket Rush
|
||||
0xDC30A0: [0x67E, 5],
|
||||
@@ -243,7 +330,7 @@ location_rom_data = {
|
||||
#0xDC30B4: [0x64D, 1], # Disabled until Trade Sequence
|
||||
0xDC30B5: [0x64E, 1],
|
||||
|
||||
0xDC30B6: [0x5FD, 4], # Banana Bird Mother
|
||||
0xDC30B6: [0x5FE, 4], # Banana Bird Mother
|
||||
|
||||
# DKC3_TODO: Disabled until Trade Sequence
|
||||
#0xDC30B7: [0x615, 2, True],
|
||||
@@ -256,6 +343,18 @@ location_rom_data = {
|
||||
#0xDC30BE: [0x625, 4, True],
|
||||
}
|
||||
|
||||
boss_location_ids = [
|
||||
0xDC30A1,
|
||||
0xDC30A2,
|
||||
0xDC30A3,
|
||||
0xDC30A4,
|
||||
0xDC30A5,
|
||||
0xDC30A6,
|
||||
0xDC30A7,
|
||||
0xDC30A8,
|
||||
0xDC30B6,
|
||||
]
|
||||
|
||||
|
||||
item_rom_data = {
|
||||
0xDC3001: [0x5D5], # 1-Up Balloon
|
||||
@@ -400,10 +499,13 @@ def patch_rom(world, rom, player, active_level_list):
|
||||
rom.write_byte(0x3484DE, 0xEA)
|
||||
rom.write_byte(0x348528, 0x80) # Prevent Single-Ski Lock
|
||||
|
||||
|
||||
# Make Swanky free
|
||||
rom.write_byte(0x348C48, 0x00)
|
||||
|
||||
rom.write_bytes(0x34AB70, bytearray([0xEA, 0xEA]))
|
||||
rom.write_bytes(0x34ABF7, bytearray([0xEA, 0xEA]))
|
||||
rom.write_bytes(0x34ACD0, bytearray([0xEA, 0xEA]))
|
||||
|
||||
# Banana Bird Costs
|
||||
if world.goal[player] == "banana_bird_hunt":
|
||||
banana_bird_cost = math.floor(world.number_of_banana_birds[player] * world.percentage_of_banana_birds[player] / 100.0)
|
||||
@@ -462,6 +564,25 @@ def patch_rom(world, rom, player, active_level_list):
|
||||
rom.write_byte(0x9130, world.starting_life_count[player].value)
|
||||
rom.write_byte(0x913B, world.starting_life_count[player].value)
|
||||
|
||||
# Cheat options
|
||||
cheat_bytes = [0x00, 0x00]
|
||||
|
||||
if world.merry[player]:
|
||||
cheat_bytes[0] |= 0x01
|
||||
|
||||
if world.autosave[player]:
|
||||
cheat_bytes[0] |= 0x02
|
||||
|
||||
if world.difficulty[player] == "tufst":
|
||||
cheat_bytes[0] |= 0x80
|
||||
cheat_bytes[1] |= 0x80
|
||||
elif world.difficulty[player] == "hardr":
|
||||
cheat_bytes[0] |= 0x00
|
||||
cheat_bytes[1] |= 0x00
|
||||
elif world.difficulty[player] == "norml":
|
||||
cheat_bytes[1] |= 0x40
|
||||
|
||||
rom.write_bytes(0x8303, bytearray(cheat_bytes))
|
||||
|
||||
# Handle Level Shuffle Here
|
||||
if world.level_shuffle[player]:
|
||||
@@ -469,6 +590,9 @@ def patch_rom(world, rom, player, active_level_list):
|
||||
rom.write_byte(level_dict[level_list[i]].nameIDAddress, level_dict[active_level_list[i]].nameID)
|
||||
rom.write_byte(level_dict[level_list[i]].levelIDAddress, level_dict[active_level_list[i]].levelID)
|
||||
|
||||
rom.write_byte(0x3FF800 + level_dict[active_level_list[i]].levelID, level_dict[level_list[i]].levelID)
|
||||
rom.write_byte(0x3FF860 + level_dict[level_list[i]].levelID, level_dict[active_level_list[i]].levelID)
|
||||
|
||||
# First levels of each world
|
||||
rom.write_byte(0x34BC3E, (0x32 + level_dict[active_level_list[0]].levelID))
|
||||
rom.write_byte(0x34BC47, (0x32 + level_dict[active_level_list[5]].levelID))
|
||||
@@ -495,6 +619,52 @@ def patch_rom(world, rom, player, active_level_list):
|
||||
|
||||
rom.write_byte(0x32F339, 0x55)
|
||||
|
||||
# Handle KONGsanity Here
|
||||
if world.kongsanity[player]:
|
||||
# Arich's Hoard KONGsanity fix
|
||||
rom.write_bytes(0x34BA8C, bytearray([0xEA, 0xEA]))
|
||||
|
||||
# Don't hide the level flag if the 0x80 bit is set
|
||||
rom.write_bytes(0x34CE92, bytearray([0x80]))
|
||||
|
||||
# Use the `!` next to level name for indicating KONG letters
|
||||
rom.write_bytes(0x34B8F0, bytearray([0x80]))
|
||||
rom.write_bytes(0x34B8F3, bytearray([0x80]))
|
||||
|
||||
# Hijack to code to set the 0x80 flag for the level when you complete KONG
|
||||
rom.write_bytes(0x3BCD4B, bytearray([0x22, 0x80, 0xFA, 0XB8])) # JSL $B8FA80
|
||||
|
||||
rom.write_bytes(0x38FA80, bytearray([0xDA])) # PHX
|
||||
rom.write_bytes(0x38FA81, bytearray([0x48])) # PHA
|
||||
rom.write_bytes(0x38FA82, bytearray([0x08])) # PHP
|
||||
rom.write_bytes(0x38FA83, bytearray([0xE2, 0x20])) # SEP #20
|
||||
rom.write_bytes(0x38FA85, bytearray([0x48])) # PHA
|
||||
rom.write_bytes(0x38FA86, bytearray([0x18])) # CLC
|
||||
rom.write_bytes(0x38FA87, bytearray([0x6D, 0xD3, 0x18])) # ADC $18D3
|
||||
rom.write_bytes(0x38FA8A, bytearray([0x8D, 0xD3, 0x18])) # STA $18D3
|
||||
rom.write_bytes(0x38FA8D, bytearray([0x68])) # PLA
|
||||
rom.write_bytes(0x38FA8E, bytearray([0xC2, 0x20])) # REP 20
|
||||
rom.write_bytes(0x38FA90, bytearray([0X18])) # CLC
|
||||
rom.write_bytes(0x38FA91, bytearray([0x6D, 0xD5, 0x05])) # ADC $05D5
|
||||
rom.write_bytes(0x38FA94, bytearray([0x8D, 0xD5, 0x05])) # STA $05D5
|
||||
rom.write_bytes(0x38FA97, bytearray([0xAE, 0xB9, 0x05])) # LDX $05B9
|
||||
rom.write_bytes(0x38FA9A, bytearray([0xBD, 0x32, 0x06])) # LDA $0632, X
|
||||
rom.write_bytes(0x38FA9D, bytearray([0x09, 0x80, 0x00])) # ORA #8000
|
||||
rom.write_bytes(0x38FAA0, bytearray([0x9D, 0x32, 0x06])) # STA $0632, X
|
||||
rom.write_bytes(0x38FAA3, bytearray([0xAD, 0xD5, 0x18])) # LDA $18D5
|
||||
rom.write_bytes(0x38FAA6, bytearray([0xD0, 0x03])) # BNE $80EA
|
||||
rom.write_bytes(0x38FAA8, bytearray([0x9C, 0xD9, 0x18])) # STZ $18D9
|
||||
rom.write_bytes(0x38FAAB, bytearray([0xA9, 0x78, 0x00])) # LDA #0078
|
||||
rom.write_bytes(0x38FAAE, bytearray([0x8D, 0xD5, 0x18])) # STA $18D5
|
||||
rom.write_bytes(0x38FAB1, bytearray([0x28])) # PLP
|
||||
rom.write_bytes(0x38FAB2, bytearray([0x68])) # PLA
|
||||
rom.write_bytes(0x38FAB3, bytearray([0xFA])) # PLX
|
||||
rom.write_bytes(0x38FAB4, bytearray([0x6B])) # RTL
|
||||
# End Handle KONGsanity
|
||||
|
||||
# Handle Credits
|
||||
rom.write_bytes(0x32A5DF, bytearray([0x41, 0x52, 0x43, 0x48, 0x49, 0x50, 0x45, 0x4C, 0x41, 0x47, 0x4F, 0x20, 0x4D, 0x4F, 0xC4])) # "ARCHIPELAGO MOD"
|
||||
rom.write_bytes(0x32A5EE, bytearray([0x00, 0x03, 0x50, 0x4F, 0x52, 0x59, 0x47, 0x4F, 0x4E, 0xC5])) # "PORYGONE"
|
||||
|
||||
from Main import __version__
|
||||
rom.name = bytearray(f'D3{__version__.replace(".", "")[0:3]}_{player}_{world.seed:11}\0', 'utf8')[:21]
|
||||
@@ -516,6 +686,17 @@ def patch_rom(world, rom, player, active_level_list):
|
||||
rom.write_byte(0x32DD63, 0xEA)
|
||||
rom.write_byte(0x32DD64, 0xEA)
|
||||
|
||||
# Don't grant Banana Birds at Bears
|
||||
rom.write_byte(0x3492DB, 0xEA)
|
||||
rom.write_byte(0x3492DC, 0xEA)
|
||||
rom.write_byte(0x3492DD, 0xEA)
|
||||
rom.write_byte(0x3493F4, 0xEA)
|
||||
rom.write_byte(0x3493F5, 0xEA)
|
||||
rom.write_byte(0x3493F6, 0xEA)
|
||||
|
||||
# Don't grant present at Blizzard
|
||||
rom.write_byte(0x8454, 0x00)
|
||||
|
||||
# Don't grant Patch and Skis from their bosses
|
||||
rom.write_byte(0x3F3762, 0x00)
|
||||
rom.write_byte(0x3F377B, 0x00)
|
||||
|
||||
@@ -4,7 +4,7 @@ import math
|
||||
import threading
|
||||
|
||||
from BaseClasses import Item, MultiWorld, Tutorial, ItemClassification
|
||||
from .Items import DKC3Item, ItemData, item_table, inventory_table
|
||||
from .Items import DKC3Item, ItemData, item_table, inventory_table, junk_table
|
||||
from .Locations import DKC3Location, all_locations, setup_locations
|
||||
from .Options import dkc3_options
|
||||
from .Regions import create_regions, connect_regions
|
||||
@@ -40,7 +40,7 @@ class DKC3World(World):
|
||||
game: str = "Donkey Kong Country 3"
|
||||
option_definitions = dkc3_options
|
||||
topology_present = False
|
||||
data_version = 1
|
||||
data_version = 2
|
||||
#hint_blacklist = {LocationName.rocket_rush_flag}
|
||||
|
||||
item_name_to_id = {name: data.code for name, data in item_table.items()}
|
||||
@@ -99,10 +99,13 @@ class DKC3World(World):
|
||||
|
||||
# Bosses
|
||||
total_required_locations += number_of_bosses
|
||||
|
||||
|
||||
# Secret Caves
|
||||
total_required_locations += 13
|
||||
|
||||
if self.world.kongsanity[self.player]:
|
||||
total_required_locations += 39
|
||||
|
||||
## Brothers Bear
|
||||
if False:#self.world.include_trade_sequence[self.player]:
|
||||
total_required_locations += 10
|
||||
@@ -118,7 +121,11 @@ class DKC3World(World):
|
||||
|
||||
total_junk_count = total_required_locations - len(itempool)
|
||||
|
||||
itempool += [self.create_item(ItemName.bear_coin)] * total_junk_count
|
||||
junk_pool = []
|
||||
for item_name in self.world.random.choices(list(junk_table.keys()), k=total_junk_count):
|
||||
junk_pool += [self.create_item(item_name)]
|
||||
|
||||
itempool += junk_pool
|
||||
|
||||
self.active_level_list = level_list.copy()
|
||||
|
||||
|
||||
@@ -34,7 +34,8 @@ base_info = {
|
||||
"factorio_version": "1.1",
|
||||
"dependencies": [
|
||||
"base >= 1.1.0",
|
||||
"? science-not-invited"
|
||||
"? science-not-invited",
|
||||
"? factory-levels"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -107,7 +108,7 @@ def generate_mod(world, output_directory: str):
|
||||
random = multiworld.slot_seeds[player]
|
||||
|
||||
def flop_random(low, high, base=None):
|
||||
"""Guarentees 50% below base and 50% above base, uniform distribution in each direction."""
|
||||
"""Guarantees 50% below base and 50% above base, uniform distribution in each direction."""
|
||||
if base:
|
||||
distance = random.random()
|
||||
if random.randint(0, 1):
|
||||
|
||||
@@ -137,8 +137,6 @@ class Progressive(Choice):
|
||||
option_off = 0
|
||||
option_grouped_random = 1
|
||||
option_on = 2
|
||||
alias_false = 0
|
||||
alias_true = 2
|
||||
default = 2
|
||||
|
||||
def want_progressives(self, random):
|
||||
|
||||
@@ -7,7 +7,8 @@
|
||||
"description": "Integration client for the Archipelago Randomizer",
|
||||
"factorio_version": "1.1",
|
||||
"dependencies": [
|
||||
"base >= 1.1.0",
|
||||
"? science-not-invited"
|
||||
]
|
||||
"base >= 1.1.0",
|
||||
"? science-not-invited",
|
||||
"? factory-levels"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -249,6 +249,10 @@ script.on_event(defines.events.on_player_main_inventory_changed, update_player_e
|
||||
|
||||
function add_samples(force, name, count)
|
||||
local function add_to_table(t)
|
||||
if count <= 0 then
|
||||
-- Fixes a bug with single craft, if a recipe gives 0 of a given item.
|
||||
return
|
||||
end
|
||||
t[name] = (t[name] or 0) + count
|
||||
end
|
||||
-- Add to global table of earned samples for future new players
|
||||
|
||||
@@ -183,6 +183,18 @@ end
|
||||
data.raw["assembling-machine"]["assembling-machine-1"].crafting_categories = table.deepcopy(data.raw["assembling-machine"]["assembling-machine-3"].crafting_categories)
|
||||
data.raw["assembling-machine"]["assembling-machine-2"].crafting_categories = table.deepcopy(data.raw["assembling-machine"]["assembling-machine-3"].crafting_categories)
|
||||
data.raw["assembling-machine"]["assembling-machine-1"].fluid_boxes = table.deepcopy(data.raw["assembling-machine"]["assembling-machine-2"].fluid_boxes)
|
||||
if mods["factory-levels"] then
|
||||
-- Factory-Levels allows the assembling machines to get faster (and depending on settings), more productive at crafting products, the more the
|
||||
-- assembling machine crafts the product. If the machine crafts enough, it may auto-upgrade to the next tier.
|
||||
for i = 1, 25, 1 do
|
||||
data.raw["assembling-machine"]["assembling-machine-1-level-" .. i].crafting_categories = table.deepcopy(data.raw["assembling-machine"]["assembling-machine-3"].crafting_categories)
|
||||
data.raw["assembling-machine"]["assembling-machine-1-level-" .. i].fluid_boxes = table.deepcopy(data.raw["assembling-machine"]["assembling-machine-2"].fluid_boxes)
|
||||
end
|
||||
for i = 1, 50, 1 do
|
||||
data.raw["assembling-machine"]["assembling-machine-2-level-" .. i].crafting_categories = table.deepcopy(data.raw["assembling-machine"]["assembling-machine-3"].crafting_categories)
|
||||
end
|
||||
end
|
||||
|
||||
data.raw["ammo"]["artillery-shell"].stack_size = 10
|
||||
|
||||
{# each randomized tech gets set to be invisible, with new nodes added that trigger those #}
|
||||
|
||||
@@ -1,4 +1,15 @@
|
||||
{% from "macros.lua" import dict_to_lua %}
|
||||
-- TODO: Replace the tinting code with an actual rendered picture of the energy bridge icon.
|
||||
-- This tint is so that one is less likely to accidentally mass-produce energy-bridges, then wonder why their rocket is not building.
|
||||
function energy_bridge_tint()
|
||||
return { r = 0, g = 1, b = 0.667, a = 1}
|
||||
end
|
||||
function tint_icon(obj, tint)
|
||||
obj.icons = { {icon = obj.icon, icon_size = obj.icon_size, icon_mipmaps = obj.icon_mipmaps, tint = tint} }
|
||||
obj.icon = nil
|
||||
obj.icon_size = nil
|
||||
obj.icon_mipmaps = nil
|
||||
end
|
||||
local energy_bridge = table.deepcopy(data.raw["accumulator"]["accumulator"])
|
||||
energy_bridge.name = "ap-energy-bridge"
|
||||
energy_bridge.minable.result = "ap-energy-bridge"
|
||||
@@ -6,12 +17,20 @@ energy_bridge.localised_name = "Archipelago EnergyLink Bridge"
|
||||
energy_bridge.energy_source.buffer_capacity = "5MJ"
|
||||
energy_bridge.energy_source.input_flow_limit = "1MW"
|
||||
energy_bridge.energy_source.output_flow_limit = "1MW"
|
||||
tint_icon(energy_bridge, energy_bridge_tint())
|
||||
energy_bridge.picture.layers[1].tint = energy_bridge_tint()
|
||||
energy_bridge.picture.layers[1].hr_version.tint = energy_bridge_tint()
|
||||
energy_bridge.charge_animation.layers[1].layers[1].tint = energy_bridge_tint()
|
||||
energy_bridge.charge_animation.layers[1].layers[1].hr_version.tint = energy_bridge_tint()
|
||||
energy_bridge.discharge_animation.layers[1].layers[1].tint = energy_bridge_tint()
|
||||
energy_bridge.discharge_animation.layers[1].layers[1].hr_version.tint = energy_bridge_tint()
|
||||
data.raw["accumulator"]["ap-energy-bridge"] = energy_bridge
|
||||
|
||||
local energy_bridge_item = table.deepcopy(data.raw["item"]["accumulator"])
|
||||
energy_bridge_item.name = "ap-energy-bridge"
|
||||
energy_bridge_item.localised_name = "Archipelago EnergyLink Bridge"
|
||||
energy_bridge_item.place_result = energy_bridge.name
|
||||
tint_icon(energy_bridge_item, energy_bridge_tint())
|
||||
data.raw["item"]["ap-energy-bridge"] = energy_bridge_item
|
||||
|
||||
local energy_bridge_recipe = table.deepcopy(data.raw["recipe"]["accumulator"])
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
-- Find out if more than one AP mod is loaded, and if so, error out.
|
||||
function mod_is_AP(str)
|
||||
-- lua string.match is way more restrictive than regex. Regex would be "^AP-W?\d{20}-P[1-9]\d*-.+$"
|
||||
local result = string.match(str, "^AP%-W?%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%-P[1-9]%d-%-.+$")
|
||||
if result ~= nil then
|
||||
log("Archipelago Mod: " .. result .. " is loaded.")
|
||||
end
|
||||
return result ~= nil
|
||||
end
|
||||
local ap_mod_count = 0
|
||||
for name, _ in pairs(mods) do
|
||||
if mod_is_AP(name) then
|
||||
ap_mod_count = ap_mod_count + 1
|
||||
if ap_mod_count > 1 then
|
||||
error("More than one Archipelago Factorio mod is loaded.")
|
||||
end
|
||||
end
|
||||
end
|
||||
data:extend({
|
||||
{
|
||||
type = "bool-setting",
|
||||
|
||||
@@ -20,7 +20,7 @@ FF1_STARTER_ITEMS = [
|
||||
|
||||
FF1_PROGRESSION_LIST = [
|
||||
"Rod", "Cube", "Lute", "Key", "Chime", "Oxyale",
|
||||
"Ship", "Canoe", "Floater", "Canal",
|
||||
"Ship", "Canoe", "Floater", "Mark", "Sigil", "Canal",
|
||||
"Crown", "Crystal", "Herb", "Tnt", "Adamant", "Slab", "Ruby", "Bottle",
|
||||
"Shard",
|
||||
"EarthOrb", "FireOrb", "WaterOrb", "AirOrb"
|
||||
|
||||
@@ -31,7 +31,7 @@ class FF1World(World):
|
||||
game = "Final Fantasy"
|
||||
topology_present = False
|
||||
remote_items = True
|
||||
data_version = 1
|
||||
data_version = 2
|
||||
remote_start_inventory = True
|
||||
|
||||
ff1_items = FF1Items()
|
||||
@@ -66,7 +66,10 @@ class FF1World(World):
|
||||
def goal_rule_and_shards(state):
|
||||
return goal_rule(state) and state.has("Shard", self.player, 32)
|
||||
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")
|
||||
menu_region.locations.append(terminated_event)
|
||||
self.world.regions += [menu_region]
|
||||
|
||||
|
||||
@@ -190,5 +190,7 @@
|
||||
"Ship": 480,
|
||||
"Bridge": 488,
|
||||
"Canal": 492,
|
||||
"Canoe": 498
|
||||
"Canoe": 498,
|
||||
"Sigil": 499,
|
||||
"Mark": 500
|
||||
}
|
||||
|
||||
@@ -49,11 +49,11 @@ def exclusion_rules(world, player: int, exclude_locations: typing.Set[str]):
|
||||
location.progress_type = LocationProgressType.EXCLUDED
|
||||
|
||||
|
||||
def set_rule(spot, rule: CollectionRule):
|
||||
def set_rule(spot: typing.Union["BaseClasses.Location", "BaseClasses.Entrance"], rule: CollectionRule):
|
||||
spot.access_rule = rule
|
||||
|
||||
|
||||
def add_rule(spot, rule: CollectionRule, combine='and'):
|
||||
def add_rule(spot: typing.Union["BaseClasses.Location", "BaseClasses.Entrance"], rule: CollectionRule, combine='and'):
|
||||
old_rule = spot.access_rule
|
||||
if combine == 'or':
|
||||
spot.access_rule = lambda state: rule(state) or old_rule(state)
|
||||
@@ -61,35 +61,37 @@ def add_rule(spot, rule: CollectionRule, combine='and'):
|
||||
spot.access_rule = lambda state: rule(state) and old_rule(state)
|
||||
|
||||
|
||||
def forbid_item(location, item: str, player: int):
|
||||
def forbid_item(location: "BaseClasses.Location", item: str, player: int):
|
||||
old_rule = location.item_rule
|
||||
location.item_rule = lambda i: (i.name != item or i.player != player) and old_rule(i)
|
||||
|
||||
|
||||
def forbid_items_for_player(location, items: typing.Set[str], player: int):
|
||||
def forbid_items_for_player(location: "BaseClasses.Location", items: typing.Set[str], player: int):
|
||||
old_rule = location.item_rule
|
||||
location.item_rule = lambda i: (i.player != player or i.name not in items) and old_rule(i)
|
||||
|
||||
|
||||
def forbid_items(location, items: typing.Set[str]):
|
||||
def forbid_items(location: "BaseClasses.Location", items: typing.Set[str]):
|
||||
"""unused, but kept as a debugging tool."""
|
||||
old_rule = location.item_rule
|
||||
location.item_rule = lambda i: i.name not in items and old_rule(i)
|
||||
|
||||
|
||||
def add_item_rule(location, rule: ItemRule):
|
||||
def add_item_rule(location: "BaseClasses.Location", rule: ItemRule):
|
||||
old_rule = location.item_rule
|
||||
location.item_rule = lambda item: rule(item) and old_rule(item)
|
||||
|
||||
|
||||
def item_in_locations(state, item: str, player: int, locations: typing.Sequence):
|
||||
def item_in_locations(state: "BaseClasses.CollectionState", item: str, player: int,
|
||||
locations: typing.Sequence["BaseClasses.Location"]) -> bool:
|
||||
for location in locations:
|
||||
if item_name(state, location[0], location[1]) == (item, player):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def item_name(state, location: str, player: int) -> typing.Optional[typing.Tuple[str, int]]:
|
||||
def item_name(state: "BaseClasses.CollectionState", location: str, player: int) -> \
|
||||
typing.Optional[typing.Tuple[str, int]]:
|
||||
location = state.world.get_location(location, player)
|
||||
if location.item is None:
|
||||
return None
|
||||
|
||||
@@ -409,7 +409,6 @@ class DeathLink(Choice):
|
||||
shade: DeathLink functions like a normal death if you do not already have a shade, shadeless otherwise.
|
||||
"""
|
||||
option_off = 0
|
||||
alias_false = 0
|
||||
alias_no = 0
|
||||
alias_true = 1
|
||||
alias_on = 1
|
||||
@@ -435,10 +434,8 @@ class CostSanity(Choice):
|
||||
These costs can be in Geo (except Grubfather, Seer and Eggshop), Grubs, Charms, Essence and/or Rancid Eggs
|
||||
"""
|
||||
option_off = 0
|
||||
alias_false = 0
|
||||
alias_no = 0
|
||||
option_on = 1
|
||||
alias_true = 1
|
||||
alias_yes = 1
|
||||
option_shopsonly = 2
|
||||
option_notshops = 3
|
||||
|
||||
@@ -276,10 +276,10 @@ def set_advancement_rules(world: MultiWorld, player: int):
|
||||
|
||||
# 1.19 advancements
|
||||
|
||||
# can make a cake, and can reach a pillager outposts for allays
|
||||
set_rule(world.get_location("Birthday Song", player), lambda state: state.can_reach("The Lie", "Location", player))
|
||||
# find allay and craft a noteblock
|
||||
set_rule(world.get_location("You've Got a Friend in Me", player), lambda state: state.has("Progressive Tools", player, 2) and state._mc_has_iron_ingots(player))
|
||||
# can make a cake, and a noteblock, and can reach a pillager outposts for allays
|
||||
set_rule(world.get_location("Birthday Song", player), lambda state: state.can_reach("The Lie", "Location", player) and state.has("Progressive Tools", player, 2) and state._mc_has_iron_ingots(player))
|
||||
# can get to outposts.
|
||||
# set_rule(world.get_location("You've Got a Friend in Me", player), lambda state: True)
|
||||
# craft bucket and adventure to find frog spawning biome
|
||||
set_rule(world.get_location("Bukkit Bukkit", player), lambda state: state.has("Bucket", player) and state._mc_has_iron_ingots(player) and state._mc_can_adventure(player))
|
||||
# I don't like this one its way to easy to get. just a pain to find.
|
||||
|
||||
@@ -7,9 +7,9 @@ config file.
|
||||
|
||||
## What does randomization do to this game?
|
||||
|
||||
Recipes are removed from the crafting book and shuffled into the item pool. It can also optionally change which
|
||||
Some recipes are locked from being able to be crafted and shuffled into the item pool. It can also optionally change which
|
||||
structures appear in each dimension. Crafting recipes are re-learned when they are received from other players as item
|
||||
checks, and occasionally when completing your own achievements.
|
||||
checks, and occasionally when completing your own achievements. See below for which recipes are shuffled.
|
||||
|
||||
## What is considered a location check in minecraft?
|
||||
|
||||
@@ -25,3 +25,86 @@ inventory directly.
|
||||
|
||||
Victory is achieved when the player kills the Ender Dragon, enters the portal in The End, and completes the credits
|
||||
sequence either by skipping it or watching hit play out.
|
||||
|
||||
## Which recipes are locked?
|
||||
|
||||
* Archery
|
||||
* Bow
|
||||
* Arrow
|
||||
* Crossbow
|
||||
* Brewing
|
||||
* Blaze Powder
|
||||
* Brewing Stand
|
||||
* Enchanting
|
||||
* Enchanting Table
|
||||
* Bookshelf
|
||||
* Bucket
|
||||
* Flint & Steel
|
||||
* All Beds
|
||||
* Bottles
|
||||
* Shield
|
||||
* Fishing Rod
|
||||
* Fishing Rod
|
||||
* Carrot on a Stick
|
||||
* Warped Fungus on a Stick
|
||||
* Campfire
|
||||
* Campfire
|
||||
* Soul Campfire
|
||||
* Spyglass
|
||||
* Lead
|
||||
* Progressive Weapons
|
||||
* Tier I
|
||||
* Stone Sword
|
||||
* Stone Axe
|
||||
* Tier II
|
||||
* Iron Sword
|
||||
* Iron Axe
|
||||
* Tier III
|
||||
* Diamond Sword
|
||||
* Diamond Axe
|
||||
* Progessive Tools
|
||||
* Tier I
|
||||
* Stone Shovel
|
||||
* Stone Hoe
|
||||
* Tier II
|
||||
* Iron Shovel
|
||||
* Iron Hoe
|
||||
* Tier III
|
||||
* Diamond Shovel
|
||||
* Diamond Hoe
|
||||
* Netherite Ingot
|
||||
* Progressive Armor
|
||||
* Tier I
|
||||
* Iron Helmet
|
||||
* Iron Chestplate
|
||||
* Iron Leggings
|
||||
* Iron Boots
|
||||
* Tier II
|
||||
* Diamond Helmet
|
||||
* Diamond Chestplate
|
||||
* Diamond Leggings
|
||||
* Diamond Boots
|
||||
* Progressive Resource Crafting
|
||||
* Tier I
|
||||
* Iron Ingot from Nuggets
|
||||
* Iron Nugget
|
||||
* Gold Ingot from Nuggets
|
||||
* Gold Nugget
|
||||
* Furnace
|
||||
* Blast Furnace
|
||||
* Tier II
|
||||
* Redstone
|
||||
* Redstone Block
|
||||
* Glowstone
|
||||
* Iron Ingot from Iron Block
|
||||
* Iron Block
|
||||
* Gold Ingot from Gold Block
|
||||
* Gold Block
|
||||
* Diamond
|
||||
* Diamond Block
|
||||
* Netherite Block
|
||||
* Netherite Ingot from Netherite Block
|
||||
* Anvil
|
||||
* Emerald
|
||||
* Emerald Block
|
||||
* Copper Block
|
||||
|
||||
@@ -129,6 +129,8 @@ def getItemGenericName(item):
|
||||
|
||||
|
||||
def isRestrictedDungeonItem(dungeon, item):
|
||||
if not isinstance(item, OOTItem):
|
||||
return False
|
||||
if (item.map or item.compass) and dungeon.world.shuffle_mapcompass == 'dungeon':
|
||||
return item in dungeon.dungeon_items
|
||||
if item.type == 'SmallKey' and dungeon.world.shuffle_smallkeys == 'dungeon':
|
||||
|
||||
@@ -1388,6 +1388,10 @@ def get_pool_core(world):
|
||||
remove_junk_pool = list(remove_junk_pool) + ['Recovery Heart', 'Bombs (20)', 'Arrows (30)', 'Ice Trap']
|
||||
|
||||
junk_candidates = [item for item in pool if item in remove_junk_pool]
|
||||
if len(pending_junk_pool) > len(junk_candidates):
|
||||
excess = len(pending_junk_pool) - len(junk_candidates)
|
||||
if world.triforce_hunt:
|
||||
raise RuntimeError(f"Items in the pool for player {world.player} exceed locations. Add {excess} location(s) or remove {excess} triforce piece(s).")
|
||||
while pending_junk_pool:
|
||||
pending_item = pending_junk_pool.pop()
|
||||
if not junk_candidates:
|
||||
|
||||
@@ -22,6 +22,12 @@ def ap_id_to_oot_data(ap_id):
|
||||
raise Exception(f'Could not find desired item ID: {ap_id}')
|
||||
|
||||
|
||||
def oot_is_item_of_type(item, item_type):
|
||||
if not isinstance(item, OOTItem):
|
||||
return False
|
||||
return item.type == item_type
|
||||
|
||||
|
||||
class OOTItem(Item):
|
||||
game: str = "Ocarina of Time"
|
||||
type: str
|
||||
@@ -43,7 +49,6 @@ class OOTItem(Item):
|
||||
self.type = type
|
||||
self.index = index
|
||||
self.special = special or {}
|
||||
self.looks_like_item = None
|
||||
self.price = special.get('price', None) if special else None
|
||||
self.internal = False
|
||||
|
||||
|
||||
@@ -101,7 +101,6 @@ class InteriorEntrances(Choice):
|
||||
option_off = 0
|
||||
option_simple = 1
|
||||
option_all = 2
|
||||
alias_false = 0
|
||||
alias_true = 2
|
||||
|
||||
|
||||
@@ -141,7 +140,6 @@ class MixEntrancePools(Choice):
|
||||
option_off = 0
|
||||
option_indoor = 1
|
||||
option_all = 2
|
||||
alias_false = 0
|
||||
|
||||
|
||||
class DecoupleEntrances(Toggle):
|
||||
@@ -158,12 +156,12 @@ class TriforceGoal(Range):
|
||||
"""Number of Triforce pieces required to complete the game."""
|
||||
display_name = "Required Triforce Pieces"
|
||||
range_start = 1
|
||||
range_end = 100
|
||||
range_end = 80
|
||||
default = 20
|
||||
|
||||
|
||||
class ExtraTriforces(Range):
|
||||
"""Percentage of additional Triforce pieces in the pool, separate from the item pool setting."""
|
||||
"""Percentage of additional Triforce pieces in the pool. With high numbers, you may need to randomize additional locations to have enough items."""
|
||||
display_name = "Percentage of Extra Triforce Pieces"
|
||||
range_start = 0
|
||||
range_end = 100
|
||||
@@ -308,7 +306,6 @@ class ShopShuffle(Choice):
|
||||
option_off = 0
|
||||
option_fixed_number = 1
|
||||
option_random_number = 2
|
||||
alias_false = 0
|
||||
|
||||
|
||||
class ShopSlots(Range):
|
||||
@@ -326,7 +323,6 @@ class TokenShuffle(Choice):
|
||||
option_dungeons = 1
|
||||
option_overworld = 2
|
||||
option_all = 3
|
||||
alias_false = 0
|
||||
|
||||
|
||||
class ScrubShuffle(Choice):
|
||||
@@ -336,7 +332,6 @@ class ScrubShuffle(Choice):
|
||||
option_low = 1
|
||||
option_regular = 2
|
||||
option_random_prices = 3
|
||||
alias_false = 0
|
||||
alias_affordable = 1
|
||||
alias_expensive = 2
|
||||
|
||||
@@ -569,7 +564,6 @@ class Hints(Choice):
|
||||
option_agony = 2
|
||||
option_always = 3
|
||||
default = 3
|
||||
alias_false = 0
|
||||
|
||||
|
||||
class MiscHints(DefaultOnToggle):
|
||||
@@ -673,8 +667,6 @@ class IceTraps(Choice):
|
||||
option_mayhem = 3
|
||||
option_onslaught = 4
|
||||
default = 1
|
||||
alias_false = 0
|
||||
alias_true = 2
|
||||
alias_extra = 2
|
||||
|
||||
|
||||
@@ -742,7 +734,6 @@ class Music(Choice):
|
||||
option_normal = 0
|
||||
option_off = 1
|
||||
option_randomized = 2
|
||||
alias_false = 1
|
||||
|
||||
|
||||
class BackgroundMusic(Music):
|
||||
|
||||
@@ -1844,7 +1844,7 @@ def write_rom_item(rom, item_id, item):
|
||||
|
||||
|
||||
def get_override_table(world):
|
||||
return list(filter(lambda val: val != None, map(partial(get_override_entry, world.player), world.world.get_filled_locations(world.player))))
|
||||
return list(filter(lambda val: val != None, map(partial(get_override_entry, world), world.world.get_filled_locations(world.player))))
|
||||
|
||||
|
||||
override_struct = struct.Struct('>xBBBHBB') # match override_t in get_items.c
|
||||
@@ -1852,10 +1852,10 @@ def get_override_table_bytes(override_table):
|
||||
return b''.join(sorted(itertools.starmap(override_struct.pack, override_table)))
|
||||
|
||||
|
||||
def get_override_entry(player_id, location):
|
||||
def get_override_entry(ootworld, location):
|
||||
scene = location.scene
|
||||
default = location.default
|
||||
player_id = 0 if player_id == location.item.player else min(location.item.player, 255)
|
||||
player_id = 0 if ootworld.player == location.item.player else min(location.item.player, 255)
|
||||
if location.item.game != 'Ocarina of Time':
|
||||
# This is an AP sendable. It's guaranteed to not be None.
|
||||
if location.item.advancement:
|
||||
@@ -1869,7 +1869,7 @@ def get_override_entry(player_id, location):
|
||||
|
||||
if location.item.trap:
|
||||
item_id = 0x7C # Ice Trap ID, to get "X is a fool" message
|
||||
looks_like_item_id = location.item.looks_like_item.index
|
||||
looks_like_item_id = ootworld.trap_appearances[location.address].index
|
||||
else:
|
||||
looks_like_item_id = 0
|
||||
|
||||
@@ -2091,7 +2091,8 @@ def get_locked_doors(rom, world):
|
||||
return [0x00D4 + scene * 0x1C + 0x04 + flag_byte, flag_bits]
|
||||
|
||||
# If boss door, set the door's unlock flag
|
||||
if (world.shuffle_bosskeys == 'remove' and scene != 0x0A) or (world.shuffle_ganon_bosskey == 'remove' and scene == 0x0A):
|
||||
if (world.shuffle_bosskeys == 'remove' and scene != 0x0A) or (
|
||||
world.shuffle_ganon_bosskey == 'remove' and scene == 0x0A and not world.triforce_hunt):
|
||||
if actor_id == 0x002E and actor_type == 0x05:
|
||||
return [0x00D4 + scene * 0x1C + 0x04 + flag_byte, flag_bits]
|
||||
|
||||
@@ -2109,23 +2110,20 @@ def place_shop_items(rom, world, shop_items, messages, locations, init_shop_id=F
|
||||
rom.write_int16(location.address1, location.item.index)
|
||||
else:
|
||||
if location.item.trap:
|
||||
item_display = location.item.looks_like_item
|
||||
elif location.item.game != "Ocarina of Time":
|
||||
item_display = location.item
|
||||
if location.item.advancement:
|
||||
item_display.index = 0xCB
|
||||
else:
|
||||
item_display.index = 0xCC
|
||||
item_display.special = {}
|
||||
item_display = world.trap_appearances[location.address]
|
||||
else:
|
||||
item_display = location.item
|
||||
|
||||
# bottles in shops should look like empty bottles
|
||||
# so that that are different than normal shop refils
|
||||
if 'shop_object' in item_display.special:
|
||||
rom_item = read_rom_item(rom, item_display.special['shop_object'])
|
||||
if location.item.trap or location.item.game == "Ocarina of Time":
|
||||
if 'shop_object' in item_display.special:
|
||||
rom_item = read_rom_item(rom, item_display.special['shop_object'])
|
||||
else:
|
||||
rom_item = read_rom_item(rom, item_display.index)
|
||||
else:
|
||||
rom_item = read_rom_item(rom, item_display.index)
|
||||
display_index = 0xCB if location.item.advancement else 0xCC
|
||||
rom_item = read_rom_item(rom, display_index)
|
||||
|
||||
shop_objs.add(rom_item['object_id'])
|
||||
shop_id = world.current_shop_id
|
||||
|
||||
@@ -3,6 +3,7 @@ import logging
|
||||
|
||||
from .SaveContext import SaveContext
|
||||
from .Regions import TimeOfDay
|
||||
from .Items import oot_is_item_of_type
|
||||
|
||||
from BaseClasses import CollectionState
|
||||
from worlds.generic.Rules import set_rule, add_rule, add_item_rule, forbid_item
|
||||
@@ -138,7 +139,7 @@ def set_rules(ootworld):
|
||||
# Sheik in Ice Cavern is the only song location in a dungeon; need to ensure that it cannot be anything else.
|
||||
# This is required if map/compass included, or any_dungeon shuffle.
|
||||
location = world.get_location('Sheik in Ice Cavern', player)
|
||||
add_item_rule(location, lambda item: item.player == player and item.type == 'Song')
|
||||
add_item_rule(location, lambda item: item.player == player and oot_is_item_of_type(item, 'Song'))
|
||||
|
||||
if ootworld.skip_child_zelda:
|
||||
# If skip child zelda is on, the item at Song from Impa must be giveable by the save context.
|
||||
@@ -181,7 +182,7 @@ def set_shop_rules(ootworld):
|
||||
wallet = ootworld.parser.parse_rule('Progressive_Wallet')
|
||||
wallet2 = ootworld.parser.parse_rule('(Progressive_Wallet, 2)')
|
||||
|
||||
for location in filter(lambda location: location.item and location.item.type == 'Shop', ootworld.get_locations()):
|
||||
for location in filter(lambda location: location.item and oot_is_item_of_type(location.item, 'Shop'), ootworld.get_locations()):
|
||||
# Add wallet requirements
|
||||
if location.item.name in ['Buy Arrows (50)', 'Buy Fish', 'Buy Goron Tunic', 'Buy Bombchu (20)', 'Buy Bombs (30)']:
|
||||
add_rule(location, wallet)
|
||||
|
||||
@@ -8,7 +8,7 @@ logger = logging.getLogger("Ocarina of Time")
|
||||
from .Location import OOTLocation, LocationFactory, location_name_to_id
|
||||
from .Entrance import OOTEntrance
|
||||
from .EntranceShuffle import shuffle_random_entrances, entrance_shuffle_table, EntranceShuffleError
|
||||
from .Items import OOTItem, item_table, oot_data_to_ap_id
|
||||
from .Items import OOTItem, item_table, oot_data_to_ap_id, oot_is_item_of_type
|
||||
from .ItemPool import generate_itempool, add_dungeon_items, get_junk_item, get_junk_pool, normal_bottles
|
||||
from .Regions import OOTRegion, TimeOfDay
|
||||
from .Rules import set_rules, set_shop_rules, set_entrances_based_rules
|
||||
@@ -305,6 +305,10 @@ class OOTWorld(World):
|
||||
if self.skip_child_zelda:
|
||||
self.shuffle_weird_egg = False
|
||||
|
||||
# Ganon boss key should not be in itempool in triforce hunt
|
||||
if self.triforce_hunt:
|
||||
self.shuffle_ganon_bosskey = 'remove'
|
||||
|
||||
# Determine skipped trials in GT
|
||||
# This needs to be done before the logic rules in GT are parsed
|
||||
trial_list = ['Forest', 'Fire', 'Water', 'Spirit', 'Shadow', 'Light']
|
||||
@@ -313,7 +317,11 @@ class OOTWorld(World):
|
||||
|
||||
# Determine which dungeons are MQ
|
||||
# Possible future plan: allow user to pick which dungeons are MQ
|
||||
mq_dungeons = self.world.random.sample(dungeon_table, self.mq_dungeons)
|
||||
if self.logic_rules == 'glitchless':
|
||||
mq_dungeons = self.world.random.sample(dungeon_table, self.mq_dungeons)
|
||||
else:
|
||||
self.mq_dungeons = 0
|
||||
mq_dungeons = []
|
||||
self.dungeon_mq = {item['name']: (item in mq_dungeons) for item in dungeon_table}
|
||||
|
||||
# Determine tricks in logic
|
||||
@@ -920,7 +928,7 @@ class OOTWorld(World):
|
||||
# This includes all locations for which show_in_spoiler is false, and shuffled shop items.
|
||||
for loc in self.get_locations():
|
||||
if loc.address is not None and (
|
||||
not loc.show_in_spoiler or (loc.item is not None and loc.item.type == 'Shop')
|
||||
not loc.show_in_spoiler or oot_is_item_of_type(loc.item, 'Shop')
|
||||
or (self.skip_child_zelda and loc.name in ['HC Zeldas Letter', 'Song from Impa'])):
|
||||
loc.address = None
|
||||
|
||||
@@ -930,9 +938,10 @@ class OOTWorld(World):
|
||||
|
||||
with i_o_limiter:
|
||||
# Make traps appear as other random items
|
||||
ice_traps = [loc.item for loc in self.get_locations() if loc.item.trap]
|
||||
for trap in ice_traps:
|
||||
trap.looks_like_item = self.create_item(self.world.slot_seeds[self.player].choice(self.fake_items).name)
|
||||
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.world.slot_seeds[self.player].choice(self.fake_items).name)
|
||||
|
||||
# Seed hint RNG, used for ganon text lines also
|
||||
self.hint_rng = self.world.slot_seeds[self.player]
|
||||
@@ -996,11 +1005,11 @@ class OOTWorld(World):
|
||||
autoworld.major_item_locations.append(loc)
|
||||
|
||||
if loc.game == "Ocarina of Time" and loc.item.code and (not loc.locked or
|
||||
(loc.item.type == 'Song' or
|
||||
(loc.item.type == 'SmallKey' and world.worlds[loc.player].shuffle_smallkeys == 'any_dungeon') or
|
||||
(loc.item.type == 'HideoutSmallKey' and world.worlds[loc.player].shuffle_fortresskeys == 'any_dungeon') or
|
||||
(loc.item.type == 'BossKey' and world.worlds[loc.player].shuffle_bosskeys == 'any_dungeon') or
|
||||
(loc.item.type == 'GanonBossKey' and world.worlds[loc.player].shuffle_ganon_bosskey == 'any_dungeon'))):
|
||||
(oot_is_item_of_type(loc.item, 'Song') or
|
||||
(oot_is_item_of_type(loc.item, 'SmallKey') and world.worlds[loc.player].shuffle_smallkeys == 'any_dungeon') or
|
||||
(oot_is_item_of_type(loc.item, 'HideoutSmallKey') and world.worlds[loc.player].shuffle_fortresskeys == 'any_dungeon') or
|
||||
(oot_is_item_of_type(loc.item, 'BossKey') and world.worlds[loc.player].shuffle_bosskeys == 'any_dungeon') or
|
||||
(oot_is_item_of_type(loc.item, 'GanonBossKey') and world.worlds[loc.player].shuffle_ganon_bosskey == 'any_dungeon'))):
|
||||
if loc.player in barren_hint_players:
|
||||
hint_area = get_hint_area(loc)
|
||||
items_by_region[loc.player][hint_area]['weight'] += 1
|
||||
@@ -1015,7 +1024,7 @@ class OOTWorld(World):
|
||||
elif barren_hint_players or woth_hint_players: # Check only relevant oot locations for barren/woth
|
||||
for player in (barren_hint_players | woth_hint_players):
|
||||
for loc in world.worlds[player].get_locations():
|
||||
if loc.item.code and (not loc.locked or loc.item.type == 'Song'):
|
||||
if loc.item.code and (not loc.locked or oot_is_item_of_type(loc.item, 'Song')):
|
||||
if player in barren_hint_players:
|
||||
hint_area = get_hint_area(loc)
|
||||
items_by_region[player][hint_area]['weight'] += 1
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
from Options import Range, Toggle, DefaultOnToggle, Choice
|
||||
|
||||
class UseResourcePacks(DefaultOnToggle):
|
||||
"""Uses Resource Packs to fill out the item pool from Raft. Resource Packs have basic earlygame items such as planks, plastic, or food."""
|
||||
display_name = "Use resource packs"
|
||||
from Options import Range, Toggle, DefaultOnToggle, Choice, DeathLink
|
||||
|
||||
class MinimumResourcePackAmount(Range):
|
||||
"""The minimum amount of resources available in a resource pack"""
|
||||
@@ -19,23 +15,30 @@ class MaximumResourcePackAmount(Range):
|
||||
default = 5
|
||||
|
||||
class DuplicateItems(Choice):
|
||||
"""Adds duplicates of items to the item pool. These will be selected alongside
|
||||
Resource Packs (if configured). Note that there are not many progression items,
|
||||
and selecting Progression may produce many of the same duplicate item."""
|
||||
"""Adds duplicates of items to the item pool (if configured in Filler items). These will be selected alongside Resource Packs (if configured). Note that there are not many progression items, and selecting Progression may produce many of the same duplicate item."""
|
||||
display_name = "Duplicate items"
|
||||
option_disabled = 0
|
||||
option_progression = 1
|
||||
option_non_progression = 2
|
||||
option_any = 3
|
||||
option_progression = 0
|
||||
option_non_progression = 1
|
||||
option_any = 2
|
||||
default = 2
|
||||
|
||||
class FillerItemTypes(Choice):
|
||||
"""Determines whether to use Resource Packs, Duplicate Items (as configured), or both."""
|
||||
display_name = "Filler items"
|
||||
option_resource_packs = 0
|
||||
option_duplicates = 1
|
||||
option_both = 2
|
||||
|
||||
class IslandFrequencyLocations(Choice):
|
||||
"""Sets where frequencies for story islands are located."""
|
||||
display_name = "Frequency locations"
|
||||
option_vanilla = 0
|
||||
option_random_on_island = 1
|
||||
option_progressive = 2
|
||||
option_anywhere = 3
|
||||
default = 1
|
||||
option_random_island_order = 2
|
||||
option_random_on_island_random_order = 3
|
||||
option_progressive = 4
|
||||
option_anywhere = 5
|
||||
default = 2
|
||||
|
||||
class IslandGenerationDistance(Choice):
|
||||
"""Sets how far away islands spawn from you when you input their coordinates into the Receiver."""
|
||||
@@ -56,7 +59,7 @@ class ProgressiveItems(DefaultOnToggle):
|
||||
display_name = "Progressive items"
|
||||
|
||||
class BigIslandEarlyCrafting(Toggle):
|
||||
"""Allows recipes that require items from big islands (eg leather) to lock earlygame items like the Receiver, Bolt, or Smelter."""
|
||||
"""Allows recipes that require items from big islands (eg leather) to lock earlygame items like the Receiver, Bolt, or Smelter. Big islands are available from the start of the game, however it can take a long time to find them."""
|
||||
display_name = "Early recipes behind big islands"
|
||||
|
||||
class PaddleboardMode(Toggle):
|
||||
@@ -64,14 +67,15 @@ class PaddleboardMode(Toggle):
|
||||
display_name = "Paddleboard Mode"
|
||||
|
||||
raft_options = {
|
||||
"use_resource_packs": UseResourcePacks,
|
||||
"minimum_resource_pack_amount": MinimumResourcePackAmount,
|
||||
"maximum_resource_pack_amount": MaximumResourcePackAmount,
|
||||
"duplicate_items": DuplicateItems,
|
||||
"filler_item_types": FillerItemTypes,
|
||||
"island_frequency_locations": IslandFrequencyLocations,
|
||||
"island_generation_distance": IslandGenerationDistance,
|
||||
"expensive_research": ExpensiveResearch,
|
||||
"progressive_items": ProgressiveItems,
|
||||
"big_island_early_crafting": BigIslandEarlyCrafting,
|
||||
"paddleboard_mode": PaddleboardMode
|
||||
"paddleboard_mode": PaddleboardMode,
|
||||
"death_link": DeathLink
|
||||
}
|
||||
|
||||
@@ -12,9 +12,6 @@ class RaftLogic(LogicMixin):
|
||||
|
||||
def raft_can_smelt_items(self, player):
|
||||
return self.has("Smelter", player)
|
||||
|
||||
def raft_can_find_titanium(self, player):
|
||||
return self.has("Metal detector", player)
|
||||
|
||||
def raft_can_craft_bolt(self, player):
|
||||
return self.raft_can_smelt_items(player) and self.has("Bolt", player)
|
||||
@@ -27,12 +24,19 @@ class RaftLogic(LogicMixin):
|
||||
|
||||
def raft_can_craft_circuitBoard(self, player):
|
||||
return self.raft_can_smelt_items(player) and self.has("Circuit board", player)
|
||||
|
||||
def raft_can_craft_shovel(self, player):
|
||||
return self.raft_can_smelt_items(player) and self.has("Shovel", player) and self.raft_can_craft_bolt(player)
|
||||
|
||||
def raft_can_craft_reciever(self, player):
|
||||
return self.raft_can_craft_circuitBoard(player) and self.raft_can_craft_hinge(player) and self.has("Receiver", player)
|
||||
|
||||
def raft_can_craft_antenna(self, player):
|
||||
return self.raft_can_craft_circuitBoard(player) and self.raft_can_craft_bolt(player) and self.has("Antenna", player)
|
||||
|
||||
def raft_can_find_titanium(self, player):
|
||||
return (self.has("Metal detector", player) and self.raft_can_craft_battery(player)
|
||||
and self.raft_can_craft_shovel(player))
|
||||
|
||||
def raft_can_craft_plasticBottle(self, player):
|
||||
return self.raft_can_smelt_items(player) and self.has("Empty bottle", player)
|
||||
@@ -60,7 +64,7 @@ class RaftLogic(LogicMixin):
|
||||
return self.raft_can_craft_hinge(player) and self.raft_can_craft_bolt(player) and self.has("Zipline tool", player)
|
||||
|
||||
def raft_can_get_dirt(self, player):
|
||||
return self.raft_can_smelt_items(player) and self.raft_can_craft_bolt(player) and self.has("Shovel", player)
|
||||
return self.raft_can_craft_shovel(player) and self.raft_big_islands_available(player)
|
||||
|
||||
def raft_can_craft_grassPlot(self, player):
|
||||
return self.raft_can_get_dirt(player) and self.has("Grass plot", player)
|
||||
@@ -88,60 +92,69 @@ class RaftLogic(LogicMixin):
|
||||
return self.raft_can_access_radio_tower(player)
|
||||
|
||||
def raft_can_access_vasagatan(self, player):
|
||||
return self.raft_can_complete_radio_tower(player) and self.raft_can_navigate(player) and self.has("Vasagatan Frequency", player)
|
||||
return self.raft_can_navigate(player) and self.has("Vasagatan Frequency", player)
|
||||
|
||||
def raft_can_complete_vasagatan(self, player):
|
||||
return self.raft_can_access_vasagatan(player)
|
||||
|
||||
def raft_can_access_balboa_island(self, player):
|
||||
return (self.raft_can_complete_vasagatan(player)
|
||||
and self.raft_can_drive(player)
|
||||
and self.has("Balboa Island Frequency", player))
|
||||
return self.raft_can_drive(player) and self.has("Balboa Island Frequency", player)
|
||||
|
||||
def raft_can_complete_balboa_island(self, player):
|
||||
return self.raft_can_access_balboa_island(player) and self.raft_can_craft_machete(player)
|
||||
|
||||
def raft_can_access_caravan_island(self, player):
|
||||
return self.raft_can_complete_balboa_island(player) and self.raft_can_drive(player) and self.has("Caravan Island Frequency", player)
|
||||
return self.raft_can_drive(player) and self.has("Caravan Island Frequency", player)
|
||||
|
||||
def raft_can_complete_caravan_island(self, player):
|
||||
return self.raft_can_access_caravan_island(player) and self.raft_can_craft_ziplineTool(player)
|
||||
|
||||
def raft_can_access_tangaroa(self, player):
|
||||
return self.raft_can_complete_caravan_island(player) and self.raft_can_drive(player) and self.has("Tangaroa Frequency", player)
|
||||
return self.raft_can_drive(player) and self.has("Tangaroa Frequency", player)
|
||||
|
||||
def raft_can_complete_tangaroa(self, player):
|
||||
return self.raft_can_access_tangaroa(player)
|
||||
return self.raft_can_access_tangaroa(player) and self.raft_can_craft_ziplineTool(player)
|
||||
|
||||
def raft_can_access_varuna_point(self, player):
|
||||
return self.raft_can_complete_tangaroa(player) and self.raft_can_drive(player) and self.has("Varuna Point Frequency", player)
|
||||
return self.raft_can_drive(player) and self.has("Varuna Point Frequency", player)
|
||||
|
||||
def raft_can_complete_varuna_point(self, player):
|
||||
return self.raft_can_access_varuna_point(player)
|
||||
return self.raft_can_access_varuna_point(player) and self.raft_can_craft_ziplineTool(player)
|
||||
|
||||
def raft_can_access_temperance(self, player):
|
||||
return self.raft_can_complete_varuna_point(player) and self.raft_can_drive(player) and self.has("Temperance Frequency", player)
|
||||
return self.raft_can_drive(player) and self.has("Temperance Frequency", player)
|
||||
|
||||
def raft_can_complete_temperance(self, player):
|
||||
return self.raft_can_access_temperance(player)
|
||||
return self.raft_can_access_temperance(player) # No zipline required on Temperance
|
||||
|
||||
def raft_can_access_utopia(self, player):
|
||||
return self.raft_can_complete_temperance(player) and self.raft_can_drive(player) and self.has("Utopia Frequency", player)
|
||||
return (self.raft_can_drive(player)
|
||||
# Access checks are to prevent frequencies for other
|
||||
# islands from appearing in Utopia
|
||||
and self.raft_can_access_radio_tower(player)
|
||||
and self.raft_can_access_vasagatan(player)
|
||||
and self.raft_can_access_balboa_island(player)
|
||||
and self.raft_can_access_caravan_island(player)
|
||||
and self.raft_can_access_tangaroa(player)
|
||||
and self.raft_can_access_varuna_point(player)
|
||||
and self.raft_can_access_temperance(player)
|
||||
and self.has("Utopia Frequency", player)
|
||||
and self.raft_can_craft_shovel(player)) # Shovels are available but we don't want to softlock players
|
||||
|
||||
def raft_can_complete_utopia(self, player):
|
||||
return self.raft_can_access_utopia(player)
|
||||
return self.raft_can_access_utopia(player) and self.raft_can_craft_ziplineTool(player)
|
||||
|
||||
def set_rules(world, player):
|
||||
regionChecks = {
|
||||
"Raft": lambda state: True,
|
||||
"ResearchTable": lambda state: True,
|
||||
"RadioTower": lambda state: state.raft_can_access_radio_tower(player), # All can_access functions have state as implicit parameter for function
|
||||
"Vasagatan": lambda state: state.raft_can_complete_radio_tower(player) and state.raft_can_access_vasagatan(player),
|
||||
"BalboaIsland": lambda state: state.raft_can_complete_vasagatan(player) and state.raft_can_access_balboa_island(player),
|
||||
"CaravanIsland": lambda state: state.raft_can_complete_balboa_island(player) and state.raft_can_access_caravan_island(player),
|
||||
"Tangaroa": lambda state: state.raft_can_complete_caravan_island(player) and state.raft_can_access_tangaroa(player),
|
||||
"Varuna Point": lambda state: state.raft_can_complete_tangaroa(player) and state.raft_can_access_varuna_point(player),
|
||||
"Temperance": lambda state: state.raft_can_complete_varuna_point(player) and state.raft_can_access_temperance(player),
|
||||
"Vasagatan": lambda state: state.raft_can_access_vasagatan(player),
|
||||
"BalboaIsland": lambda state: state.raft_can_access_balboa_island(player),
|
||||
"CaravanIsland": lambda state: state.raft_can_access_caravan_island(player),
|
||||
"Tangaroa": lambda state: state.raft_can_access_tangaroa(player),
|
||||
"Varuna Point": lambda state: state.raft_can_access_varuna_point(player),
|
||||
"Temperance": lambda state: state.raft_can_access_temperance(player),
|
||||
"Utopia": lambda state: state.raft_can_complete_temperance(player) and state.raft_can_access_utopia(player)
|
||||
}
|
||||
itemChecks = {
|
||||
@@ -183,7 +196,7 @@ def set_rules(world, player):
|
||||
if region != "Menu":
|
||||
for exitRegion in world.get_region(region, player).exits:
|
||||
set_rule(world.get_entrance(exitRegion.name, player), regionChecks[region])
|
||||
|
||||
|
||||
# Location access rules
|
||||
for location in location_table:
|
||||
locFromWorld = world.get_location(location["name"], player)
|
||||
|
||||
@@ -56,21 +56,21 @@ class RaftWorld(World):
|
||||
extraItemNamePool = []
|
||||
extras = len(location_table) - len(item_table) - 1 # Victory takes up 1 unaccounted-for slot
|
||||
if extras > 0:
|
||||
if (self.world.use_resource_packs[self.player].value):
|
||||
if (self.world.filler_item_types[self.player].value != 1): # Use resource packs
|
||||
for packItem in resourcePackItems:
|
||||
for i in range(minimumResourcePackAmount, maximumResourcePackAmount + 1):
|
||||
extraItemNamePool.append(createResourcePackName(i, packItem))
|
||||
|
||||
if self.world.duplicate_items[self.player].value != 0:
|
||||
if self.world.filler_item_types[self.player].value != 0: # Use duplicate items
|
||||
dupeItemPool = item_table.copy()
|
||||
# Remove frequencies if necessary
|
||||
if self.world.island_frequency_locations[self.player].value != 3: # Not completely random locations
|
||||
if self.world.island_frequency_locations[self.player].value != 5: # Not completely random locations
|
||||
dupeItemPool = (itm for itm in dupeItemPool if "Frequency" not in itm["name"])
|
||||
|
||||
# Remove progression or non-progression items if necessary
|
||||
if (self.world.duplicate_items[self.player].value == 1): # Progression only
|
||||
if (self.world.duplicate_items[self.player].value == 0): # Progression only
|
||||
dupeItemPool = (itm for itm in dupeItemPool if itm["progression"] == True)
|
||||
elif (self.world.duplicate_items[self.player].value == 2): # Non-progression only
|
||||
elif (self.world.duplicate_items[self.player].value == 1): # Non-progression only
|
||||
dupeItemPool = (itm for itm in dupeItemPool if itm["progression"] == False)
|
||||
|
||||
dupeItemPool = list(dupeItemPool)
|
||||
@@ -91,19 +91,15 @@ class RaftWorld(World):
|
||||
|
||||
def create_regions(self):
|
||||
create_regions(self.world, self.player)
|
||||
|
||||
def fill_slot_data(self):
|
||||
slot_data = {}
|
||||
return slot_data
|
||||
|
||||
def get_pre_fill_items(self):
|
||||
if self.world.island_frequency_locations[self.player] in [0, 1]:
|
||||
if self.world.island_frequency_locations[self.player] in [0, 1, 2, 3]:
|
||||
return [loc.item for loc in self.world.get_filled_locations()]
|
||||
return []
|
||||
|
||||
def create_item_replaceAsNecessary(self, name: str) -> Item:
|
||||
isFrequency = "Frequency" in name
|
||||
shouldUseProgressive = ((isFrequency and self.world.island_frequency_locations[self.player].value == 2)
|
||||
shouldUseProgressive = ((isFrequency and self.world.island_frequency_locations[self.player].value == 4)
|
||||
or (not isFrequency and self.world.progressive_items[self.player].value))
|
||||
if shouldUseProgressive and name in progressive_table:
|
||||
name = progressive_table[name]
|
||||
@@ -148,6 +144,40 @@ class RaftWorld(World):
|
||||
self.setLocationItemFromRegion("Tangaroa", "Varuna Point Frequency")
|
||||
self.setLocationItemFromRegion("Varuna Point", "Temperance Frequency")
|
||||
self.setLocationItemFromRegion("Temperance", "Utopia Frequency")
|
||||
elif self.world.island_frequency_locations[self.player] in [2, 3]:
|
||||
locationToFrequencyItemMap = {
|
||||
"Vasagatan": "Vasagatan Frequency",
|
||||
"BalboaIsland": "Balboa Island Frequency",
|
||||
"CaravanIsland": "Caravan Island Frequency",
|
||||
"Tangaroa": "Tangaroa Frequency",
|
||||
"Varuna Point": "Varuna Point Frequency",
|
||||
"Temperance": "Temperance Frequency",
|
||||
"Utopia": "Utopia Frequency"
|
||||
}
|
||||
locationToVanillaFrequencyLocationMap = {
|
||||
"RadioTower": "Radio Tower Frequency to Vasagatan",
|
||||
"Vasagatan": "Vasagatan Frequency to Balboa",
|
||||
"BalboaIsland": "Relay Station quest",
|
||||
"CaravanIsland": "Caravan Island Frequency to Tangaroa",
|
||||
"Tangaroa": "Tangaroa Frequency to Varuna Point",
|
||||
"Varuna Point": "Varuna Point Frequency to Temperance",
|
||||
"Temperance": "Temperance Frequency to Utopia"
|
||||
}
|
||||
# Utopia is never chosen until the end, otherwise these are chosen randomly
|
||||
availableLocationList = ["Vasagatan", "BalboaIsland", "CaravanIsland", "Tangaroa", "Varuna Point", "Temperance", "Utopia"]
|
||||
previousLocation = "RadioTower"
|
||||
while (len(availableLocationList) > 0):
|
||||
if (len(availableLocationList) > 1):
|
||||
currentLocation = availableLocationList[random.randint(0, len(availableLocationList) - 2)]
|
||||
else:
|
||||
currentLocation = availableLocationList[0] # Utopia (only one left in list)
|
||||
availableLocationList.remove(currentLocation)
|
||||
if self.world.island_frequency_locations[self.player] == 2:
|
||||
self.setLocationItem(locationToVanillaFrequencyLocationMap[previousLocation], locationToFrequencyItemMap[currentLocation])
|
||||
elif self.world.island_frequency_locations[self.player] == 3:
|
||||
self.setLocationItemFromRegion(previousLocation, locationToFrequencyItemMap[currentLocation])
|
||||
previousLocation = currentLocation
|
||||
|
||||
# Victory item
|
||||
self.world.get_location("Utopia Complete", self.player).place_locked_item(
|
||||
RaftItem("Victory", ItemClassification.progression, None, player=self.player))
|
||||
@@ -166,7 +196,8 @@ class RaftWorld(World):
|
||||
def fill_slot_data(self):
|
||||
return {
|
||||
"IslandGenerationDistance": self.world.island_generation_distance[self.player].value,
|
||||
"ExpensiveResearch": self.world.expensive_research[self.player].value
|
||||
"ExpensiveResearch": bool(self.world.expensive_research[self.player].value),
|
||||
"DeathLink": bool(self.world.death_link[self.player].value)
|
||||
}
|
||||
|
||||
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
||||
|
||||
@@ -22,7 +22,7 @@ Decoration Packages are unchanged.
|
||||
Researches and pickups remain visually unchanged, regardless of what the unlock is.
|
||||
|
||||
## When the player receives an item, what happens?
|
||||
A Raft notification will appear with the item information. The unlock will also appear in the chat. Unlocks that would normally give you the item (eg Machete) will NOT give it to you, but must instead be crafted.
|
||||
A Raft notification will appear with the item information. The unlock will also appear in the chat. Unlocks that would normally give you the item (eg Zipline) will NOT give it to you, but must instead be crafted.
|
||||
|
||||
## Are there any limitations compared to vanilla Raft?
|
||||
- Mods that add new researchable technologies, modify story islands, or give items like blueprints are likely incompatible with Raftipelago.
|
||||
|
||||
@@ -4,23 +4,50 @@
|
||||
|
||||
- [Raft](https://store.steampowered.com/app/648800/Raft/)
|
||||
- [Raft Mod Loader](https://www.raftmodding.com/loader) ("*RML*")
|
||||
- [ModUtils mod](https://www.raftmodding.com/mods/modutils)
|
||||
- [Raftipelago mod](https://www.raftmodding.com/mods/raftipelago)
|
||||
|
||||
## Installation Procedures
|
||||
|
||||
1. Install Raft. The currently-supported Raft version is Version 1.0: The Final Chapter. If you plan on playing Raft mainly with Archipelago, it's recommended to disable Raft auto-updating through Steam, as there is no beta channel to get old builds.
|
||||
1. Install Raft. The currently-supported Raft version is Version 1.0: The Final Chapter. Any minor version (such as 1.08) should be compatible.
|
||||
|
||||
2. Install RML.
|
||||
|
||||
3. Install the Raftipelago mod from the Raft Modding website. You should open the auto-installation link on the webpage through RML. Alternatively, you can download the .rmod file and place it in the Mods folder manually.
|
||||
3. Install the Raftipelago and ModUtils mods from the Raft Modding website. You should open the auto-installation link on the webpage through RML. Alternatively, you can download the .rmod file and place it in the Mods folder manually.
|
||||
|
||||
4. Open RML and click Play. If you've already installed it, the shortcut in the Start Menu is called "RMLLauncher.exe". Raft should start.
|
||||
4. Open RML and click Play. If you've already installed it, the executable that was used to install RML ("RMLLauncher.exe" unless renamed) should be used to run RML. Raft should start after clicking Play.
|
||||
|
||||
5. Open the RML menu. This should open automatically when Raft first loads. If it does not, and you see RML information in the top center of the Raft main menu, press F9 to open it.
|
||||
|
||||
6. Navigate to the "Mod manager" tab in the left-hand menu.
|
||||
|
||||
7. Click on the plug icon for Raftipelago to load the mod.
|
||||
7. Click on the plug icon for ModUtils to load the mod. You can also click on the (i) next to the plug icon, then check the "Load this mod at startup" button. This will make the mod always load at startup.
|
||||
|
||||
8. Click on the plug icon for Raftipelago to load the mod. While it's possible to also make this mod load at startup, it's recommended *not* to do so; if this mod loads before ModUtils, the mod will fail to load properly.
|
||||
|
||||
## Joining a MultiWorld Game
|
||||
|
||||
1. Ensure you're on the Main Menu with Raftipelago loaded.
|
||||
|
||||
2. Open the Debug Console by pressing F10.
|
||||
|
||||
3. Type */connect {serverAddress} {username} {password}* into the console and hit Enter.
|
||||
- Example: */connect archipelago.gg:12345 SunnyBat*
|
||||
- If there is no password, the password argument may be omitted (as is the case in the above example).
|
||||
- serverAddress must not contain spaces.
|
||||
- If your username or password contains spaces, surround that value with quotation marks ("). Adding quotation marks even when not necessary (eg "SunnyBat") is fine.
|
||||
- If your username or password starts with a quotation mark, surround the value with an additional set of quotation marks (eg the value *"myP@s$w0rD* would be entered as *""myP@s$w0rD"*).
|
||||
|
||||
4. Start a new game or load an existing one. It's recommended to avoid using an existing game that was not created with your current run of Raftipelago (either vanilla or a different Raftipelago run). It will work, but if anything is unlocked, it will be automatically registered with Archipelago once the world is loaded. This is irreversible.
|
||||
|
||||
5. You can disconnect from an Archipelago server by typing */disconnect confirmDisconnect* into the console and hitting Enter.
|
||||
|
||||
## Multiplayer Raft
|
||||
|
||||
You're able to have multiple Raft players on a single Raftipelago world. This will work, with a few notes:
|
||||
- Only the player that creates/loads the world can connect to Archipelago (this is the "host" of the Raft world). Other players do not need to connect; everything will be routed through the the host.
|
||||
- Players other than the host will be labeled as a "Raft Player (Steam name)" when using ingame chat, which will be routed through Archipelago chat.
|
||||
- Ingame chat will only work when the host is connected to the Archipelago server.
|
||||
|
||||
## Installation Troubleshooting
|
||||
|
||||
@@ -45,38 +72,12 @@ If this happens, then RML is configured to only start a new instance of Raft, th
|
||||
You can either:
|
||||
- Close the existing instance of Raft then click Play
|
||||
- Check the box next to the "Disable Automatic Game Start" setting in the Settings menu then click Play.
|
||||
|
||||
## Joining a MultiWorld Game
|
||||
|
||||
1. Ensure you're on the Main Menu with Raftipelago loaded.
|
||||
|
||||
2. Open the Debug Console by pressing F10.
|
||||
|
||||
3. Type */connect {serverAddress} {username} {password}* into the console and hit Enter.
|
||||
- Example: */connect archipelago.gg:12345 SunnyBat*
|
||||
- serverAddress must not contain spaces.
|
||||
- If your username or password contains spaces, surround that value with quotation marks ("). Adding quotation marks even when not necessary (eg "SunnyBat") is fine.
|
||||
- If your username or password starts with a quotation mark, surround the value with an additional set of quotation marks (eg the value *"myP@s$w0rD* would be entered as *""myP@s$w0rD"*).
|
||||
|
||||
4. Start a new game or load an existing one.
|
||||
- Raftipelago save games are marked as *incompatible* with vanilla Raft. This means when Raftipelago is not loaded, saves made with Raftipelago will show as corrupt/unselectable.
|
||||
- Avoid using an existing game that was not created with your current run of Raftipelago (either vanilla or a different Raftipelago run). It will work, but if anything is unlocked, it will be automatically registered with Archipelago once the world is loaded. This is irreversible.
|
||||
|
||||
5. You can disconnect from an Archipelago server by typing */disconnect confirmDisconnect* into the console and hitting Enter.
|
||||
|
||||
## Multiplayer Raft
|
||||
|
||||
You're able to have multiple Raft players on a single Raftipelago world. This will work, with a few notes:
|
||||
- Only the player that creates/loads the world can connect to Archipelago (this is the "host" of the Raft world). Other players do not need to connect; everything will be routed through the the host.
|
||||
- Resource Packs are only received by the host and any other players connected to the Raft world when the resource pack is received.
|
||||
- Players other than the host will be labeled as a "Raft Player (Steam name)" when using ingame chat, which will be routed through Archipelago chat.
|
||||
- Ingame chat will only work when the host is connected to the Archipelago server.
|
||||
|
||||
## Game Troubleshooting
|
||||
|
||||
### The "Load game" button is disabled for my world / my world is corrupt
|
||||
|
||||
Be sure that you click the "Load game" button **after** you load Raftipelago. You can click the Load Game button again to reload all of the saves in your folder (there is no need to restart Raft if the mod loaded successfully).
|
||||
Be sure that you click the "Load game" button **after** you load Raftipelago. You can click the Load Game button again to refresh all of the saves in your folder (there is no need to restart Raft if the mod loaded successfully).
|
||||
|
||||
### I'm certain I'm doing things correctly, but the world is still not loadable
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"Raft": ["RadioTower", "ResearchTable"],
|
||||
"Raft": ["ResearchTable", "RadioTower", "Vasagatan", "BalboaIsland", "CaravanIsland", "Tangaroa", "Varuna Point", "Temperance", "Utopia"],
|
||||
"ResearchTable": [],
|
||||
"RadioTower": ["Vasagatan"],
|
||||
"Vasagatan": ["BalboaIsland"],
|
||||
"BalboaIsland": ["CaravanIsland"],
|
||||
"CaravanIsland": ["Tangaroa"],
|
||||
"Tangaroa": ["Varuna Point"],
|
||||
"Varuna Point": ["Temperance"],
|
||||
"Temperance": ["Utopia"],
|
||||
"RadioTower": [],
|
||||
"Vasagatan": [],
|
||||
"BalboaIsland": [],
|
||||
"CaravanIsland": [],
|
||||
"Tangaroa": [],
|
||||
"Varuna Point": [],
|
||||
"Temperance": [],
|
||||
"Utopia": []
|
||||
}
|
||||
@@ -1,143 +1,154 @@
|
||||
from typing import Dict
|
||||
from BaseClasses import Item
|
||||
import typing
|
||||
from .Options import ItemWeights
|
||||
|
||||
|
||||
class RiskOfRainItem(Item):
|
||||
game: str = "Risk of Rain 2"
|
||||
|
||||
|
||||
# 37000 - 38000
|
||||
item_table = {
|
||||
"Dio's Best Friend": 37001,
|
||||
"Common Item": 37002,
|
||||
"Uncommon Item": 37003,
|
||||
"Legendary Item": 37004,
|
||||
"Boss Item": 37005,
|
||||
"Lunar Item": 37006,
|
||||
"Equipment": 37007,
|
||||
"Item Scrap, White": 37008,
|
||||
"Item Scrap, Green": 37009,
|
||||
"Item Scrap, Red": 37010,
|
||||
"Item Scrap, Yellow": 37011,
|
||||
"Victory": None,
|
||||
"Beat Level One": None,
|
||||
"Beat Level Two": None,
|
||||
"Beat Level Three": None,
|
||||
"Beat Level Four": None,
|
||||
"Beat Level Five": None,
|
||||
item_table: Dict[str, int] = {
|
||||
"Dio's Best Friend": 37001,
|
||||
"Common Item": 37002,
|
||||
"Uncommon Item": 37003,
|
||||
"Legendary Item": 37004,
|
||||
"Boss Item": 37005,
|
||||
"Lunar Item": 37006,
|
||||
"Equipment": 37007,
|
||||
"Item Scrap, White": 37008,
|
||||
"Item Scrap, Green": 37009,
|
||||
"Item Scrap, Red": 37010,
|
||||
"Item Scrap, Yellow": 37011
|
||||
}
|
||||
|
||||
default_weights = {
|
||||
"Item Scrap, Green": 16,
|
||||
"Item Scrap, Red": 4,
|
||||
"Item Scrap, Yellow": 1,
|
||||
"Item Scrap, White": 32,
|
||||
"Common Item": 64,
|
||||
"Uncommon Item": 32,
|
||||
"Legendary Item": 8,
|
||||
"Boss Item": 4,
|
||||
"Lunar Item": 16,
|
||||
"Equipment": 32
|
||||
default_weights: Dict[str, int] = {
|
||||
"Item Scrap, Green": 16,
|
||||
"Item Scrap, Red": 4,
|
||||
"Item Scrap, Yellow": 1,
|
||||
"Item Scrap, White": 32,
|
||||
"Common Item": 64,
|
||||
"Uncommon Item": 32,
|
||||
"Legendary Item": 8,
|
||||
"Boss Item": 4,
|
||||
"Lunar Item": 16,
|
||||
"Equipment": 32
|
||||
}
|
||||
|
||||
new_weights = {
|
||||
"Item Scrap, Green": 15,
|
||||
"Item Scrap, Red": 5,
|
||||
"Item Scrap, Yellow": 1,
|
||||
"Item Scrap, White": 30,
|
||||
"Common Item": 75,
|
||||
"Uncommon Item": 40,
|
||||
"Legendary Item": 10,
|
||||
"Boss Item": 5,
|
||||
"Lunar Item": 10,
|
||||
"Equipment": 20
|
||||
new_weights: Dict[str, int] = {
|
||||
"Item Scrap, Green": 15,
|
||||
"Item Scrap, Red": 5,
|
||||
"Item Scrap, Yellow": 1,
|
||||
"Item Scrap, White": 30,
|
||||
"Common Item": 75,
|
||||
"Uncommon Item": 40,
|
||||
"Legendary Item": 10,
|
||||
"Boss Item": 5,
|
||||
"Lunar Item": 10,
|
||||
"Equipment": 20
|
||||
}
|
||||
|
||||
uncommon_weights = {
|
||||
"Item Scrap, Green": 45,
|
||||
"Item Scrap, Red": 5,
|
||||
"Item Scrap, Yellow": 1,
|
||||
"Item Scrap, White": 30,
|
||||
"Common Item": 45,
|
||||
"Uncommon Item": 100,
|
||||
"Legendary Item": 10,
|
||||
"Boss Item": 5,
|
||||
"Lunar Item": 15,
|
||||
"Equipment": 20
|
||||
uncommon_weights: Dict[str, int] = {
|
||||
"Item Scrap, Green": 45,
|
||||
"Item Scrap, Red": 5,
|
||||
"Item Scrap, Yellow": 1,
|
||||
"Item Scrap, White": 30,
|
||||
"Common Item": 45,
|
||||
"Uncommon Item": 100,
|
||||
"Legendary Item": 10,
|
||||
"Boss Item": 5,
|
||||
"Lunar Item": 15,
|
||||
"Equipment": 20
|
||||
}
|
||||
|
||||
legendary_weights = {
|
||||
"Item Scrap, Green": 15,
|
||||
"Item Scrap, Red": 5,
|
||||
"Item Scrap, Yellow": 1,
|
||||
"Item Scrap, White": 30,
|
||||
"Common Item": 50,
|
||||
"Uncommon Item": 25,
|
||||
"Legendary Item": 100,
|
||||
"Boss Item": 5,
|
||||
"Lunar Item": 15,
|
||||
"Equipment": 20
|
||||
legendary_weights: Dict[str, int] = {
|
||||
"Item Scrap, Green": 15,
|
||||
"Item Scrap, Red": 5,
|
||||
"Item Scrap, Yellow": 1,
|
||||
"Item Scrap, White": 30,
|
||||
"Common Item": 50,
|
||||
"Uncommon Item": 25,
|
||||
"Legendary Item": 100,
|
||||
"Boss Item": 5,
|
||||
"Lunar Item": 15,
|
||||
"Equipment": 20
|
||||
}
|
||||
|
||||
lunartic_weights = {
|
||||
"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": 100,
|
||||
"Equipment": 0
|
||||
lunartic_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": 100,
|
||||
"Equipment": 0
|
||||
}
|
||||
|
||||
no_scraps_weights = {
|
||||
"Item Scrap, Green": 0,
|
||||
"Item Scrap, Red": 0,
|
||||
"Item Scrap, Yellow": 0,
|
||||
"Item Scrap, White": 0,
|
||||
"Common Item": 100,
|
||||
"Uncommon Item": 40,
|
||||
"Legendary Item": 15,
|
||||
"Boss Item": 5,
|
||||
"Lunar Item": 10,
|
||||
"Equipment": 25
|
||||
chaos_weights: Dict[str, int] = {
|
||||
"Item Scrap, Green": 80,
|
||||
"Item Scrap, Red": 45,
|
||||
"Item Scrap, Yellow": 30,
|
||||
"Item Scrap, White": 100,
|
||||
"Common Item": 100,
|
||||
"Uncommon Item": 70,
|
||||
"Legendary Item": 30,
|
||||
"Boss Item": 20,
|
||||
"Lunar Item": 60,
|
||||
"Equipment": 40
|
||||
}
|
||||
|
||||
even_weights = {
|
||||
"Item Scrap, Green": 1,
|
||||
"Item Scrap, Red": 1,
|
||||
"Item Scrap, Yellow": 1,
|
||||
"Item Scrap, White": 1,
|
||||
"Common Item": 1,
|
||||
"Uncommon Item": 1,
|
||||
"Legendary Item": 1,
|
||||
"Boss Item": 1,
|
||||
"Lunar Item": 1,
|
||||
"Equipment": 1
|
||||
no_scraps_weights: Dict[str, int] = {
|
||||
"Item Scrap, Green": 0,
|
||||
"Item Scrap, Red": 0,
|
||||
"Item Scrap, Yellow": 0,
|
||||
"Item Scrap, White": 0,
|
||||
"Common Item": 100,
|
||||
"Uncommon Item": 40,
|
||||
"Legendary Item": 15,
|
||||
"Boss Item": 5,
|
||||
"Lunar Item": 10,
|
||||
"Equipment": 25
|
||||
}
|
||||
|
||||
scraps_only = {
|
||||
"Item Scrap, Green": 70,
|
||||
"Item Scrap, White": 100,
|
||||
"Item Scrap, Red": 30,
|
||||
"Item Scrap, Yellow": 5,
|
||||
"Common Item": 0,
|
||||
"Uncommon Item": 0,
|
||||
"Legendary Item": 0,
|
||||
"Boss Item": 0,
|
||||
"Lunar Item": 0,
|
||||
"Equipment": 0
|
||||
even_weights: Dict[str, int] = {
|
||||
"Item Scrap, Green": 1,
|
||||
"Item Scrap, Red": 1,
|
||||
"Item Scrap, Yellow": 1,
|
||||
"Item Scrap, White": 1,
|
||||
"Common Item": 1,
|
||||
"Uncommon Item": 1,
|
||||
"Legendary Item": 1,
|
||||
"Boss Item": 1,
|
||||
"Lunar Item": 1,
|
||||
"Equipment": 1
|
||||
}
|
||||
|
||||
item_pool_weights: typing.Dict[int, typing.Dict[str, int]] = {
|
||||
0: default_weights,
|
||||
1: new_weights,
|
||||
2: uncommon_weights,
|
||||
3: legendary_weights,
|
||||
4: lunartic_weights,
|
||||
6: no_scraps_weights,
|
||||
7: even_weights,
|
||||
8: scraps_only
|
||||
scraps_only: Dict[str, int] = {
|
||||
"Item Scrap, Green": 70,
|
||||
"Item Scrap, White": 100,
|
||||
"Item Scrap, Red": 30,
|
||||
"Item Scrap, Yellow": 5,
|
||||
"Common Item": 0,
|
||||
"Uncommon Item": 0,
|
||||
"Legendary Item": 0,
|
||||
"Boss Item": 0,
|
||||
"Lunar Item": 0,
|
||||
"Equipment": 0
|
||||
}
|
||||
|
||||
lookup_id_to_name: typing.Dict[int, str] = {id: name for name, id in item_table.items() if id}
|
||||
item_pool_weights: Dict[int, Dict[str, int]] = {
|
||||
ItemWeights.option_default: default_weights,
|
||||
ItemWeights.option_new: new_weights,
|
||||
ItemWeights.option_uncommon: uncommon_weights,
|
||||
ItemWeights.option_legendary: legendary_weights,
|
||||
ItemWeights.option_lunartic: lunartic_weights,
|
||||
ItemWeights.option_chaos: chaos_weights,
|
||||
ItemWeights.option_no_scraps: no_scraps_weights,
|
||||
ItemWeights.option_even: even_weights,
|
||||
ItemWeights.option_scraps_only: scraps_only
|
||||
}
|
||||
|
||||
lookup_id_to_name: Dict[int, str] = {id: name for name, id in item_table.items()}
|
||||
|
||||
@@ -1,19 +1,13 @@
|
||||
from typing import Dict
|
||||
from BaseClasses import Location
|
||||
import typing
|
||||
from .Options import TotalLocations
|
||||
|
||||
|
||||
class RiskOfRainLocation(Location):
|
||||
game: str = "Risk of Rain 2"
|
||||
|
||||
# 37000 - 38000
|
||||
base_location_table = {
|
||||
"Victory": None,
|
||||
|
||||
}
|
||||
# 37006 - 37506
|
||||
item_pickups = {
|
||||
f"ItemPickup{i}": 37005+i for i in range(1, 501)
|
||||
item_pickups: Dict[str, int] = {
|
||||
f"ItemPickup{i+1}": 37000+i for i in range(TotalLocations.range_end)
|
||||
}
|
||||
|
||||
location_table = {**base_location_table, **item_pickups}
|
||||
|
||||
lookup_id_to_name: typing.Dict[int, str] = {id: name for name, id in location_table.items()}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import typing
|
||||
from typing import Dict
|
||||
from Options import Option, DefaultOnToggle, Range, Choice
|
||||
|
||||
|
||||
@@ -36,7 +36,8 @@ class AllowLunarItems(DefaultOnToggle):
|
||||
class StartWithRevive(DefaultOnToggle):
|
||||
"""Start the game with a `Dio's Best Friend` item."""
|
||||
display_name = "Start with a Revive"
|
||||
|
||||
|
||||
|
||||
class FinalStageDeath(DefaultOnToggle):
|
||||
"""Death on the final boss stage counts as a win."""
|
||||
display_name = "Final Stage Death is Win"
|
||||
@@ -124,7 +125,7 @@ class Equipment(Range):
|
||||
|
||||
class ItemPoolPresetToggle(DefaultOnToggle):
|
||||
"""Will use the item weight presets when set to true, otherwise will use the custom set item pool weights."""
|
||||
display_name = "Item Weight Presets"
|
||||
display_name = "Use Item Weight Presets"
|
||||
|
||||
|
||||
class ItemWeights(Choice):
|
||||
@@ -150,7 +151,7 @@ class ItemWeights(Choice):
|
||||
|
||||
|
||||
# define a dictionary for the weights of the generated item pool.
|
||||
ror2_weights: typing.Dict[str, type(Option)] = {
|
||||
ror2_weights: Dict[str, type(Option)] = {
|
||||
"green_scrap": GreenScrap,
|
||||
"red_scrap": RedScrap,
|
||||
"yellow_scrap": YellowScrap,
|
||||
@@ -163,7 +164,7 @@ ror2_weights: typing.Dict[str, type(Option)] = {
|
||||
"equipment": Equipment
|
||||
}
|
||||
|
||||
ror2_options: typing.Dict[str, type(Option)] = {
|
||||
ror2_options: Dict[str, type(Option)] = {
|
||||
"total_locations": TotalLocations,
|
||||
"total_revivals": TotalRevivals,
|
||||
"start_with_revive": StartWithRevive,
|
||||
|
||||
@@ -2,29 +2,32 @@ from BaseClasses import MultiWorld
|
||||
from worlds.generic.Rules import set_rule, add_rule
|
||||
|
||||
|
||||
def set_rules(world: MultiWorld, player: int):
|
||||
total_locations = world.total_locations[player] # total locations for current player
|
||||
def set_rules(world: MultiWorld, player: int) -> None:
|
||||
total_locations = world.total_locations[player].value # total locations for current player
|
||||
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
|
||||
|
||||
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))
|
||||
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))
|
||||
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))
|
||||
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(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 world.total_revivals[player] or world.start_with_revive[player]:
|
||||
total_revivals = world.total_revivals[player] * world.total_locations[player] // 100
|
||||
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].value))
|
||||
lambda state: state.has("Dio's Best Friend", player,
|
||||
total_revivals + world.start_with_revive[player]))
|
||||
|
||||
world.completion_condition[player] = lambda state: state.has("Victory", player)
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import string
|
||||
from typing import Dict, List
|
||||
from .Items import RiskOfRainItem, item_table, item_pool_weights
|
||||
from .Locations import location_table, RiskOfRainLocation, base_location_table
|
||||
from .Locations import RiskOfRainLocation, item_pickups
|
||||
from .Rules import set_rules
|
||||
|
||||
from BaseClasses import Region, RegionType, Entrance, Item, ItemClassification, MultiWorld, Tutorial
|
||||
from .Options import ror2_options
|
||||
from .Options import ror2_options, ItemWeights
|
||||
from worlds.AutoWorld import World, WebWorld
|
||||
|
||||
client_version = 1
|
||||
@@ -32,34 +33,31 @@ class RiskOfRainWorld(World):
|
||||
topology_present = False
|
||||
|
||||
item_name_to_id = item_table
|
||||
location_name_to_id = location_table
|
||||
location_name_to_id = item_pickups
|
||||
|
||||
data_version = 3
|
||||
data_version = 4
|
||||
forced_auto_forfeit = True
|
||||
web = RiskOfWeb()
|
||||
total_revivals: int
|
||||
|
||||
def generate_basic(self):
|
||||
def generate_early(self) -> None:
|
||||
# figure out how many revivals should exist in the pool
|
||||
self.total_revivals = int(self.world.total_revivals[self.player].value / 100 *
|
||||
self.world.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
|
||||
if self.world.start_with_revive[self.player].value:
|
||||
self.world.push_precollected(self.world.create_item("Dio's Best Friend", self.player))
|
||||
|
||||
# if presets are enabled generate junk_pool from the selected preset
|
||||
pool_option = self.world.item_weights[self.player].value
|
||||
if self.world.item_pool_presets[self.player].value:
|
||||
junk_pool: Dict[str, int] = {}
|
||||
if self.world.item_pool_presets[self.player]:
|
||||
# generate chaos weights if the preset is chosen
|
||||
if pool_option == 5:
|
||||
junk_pool = {
|
||||
"Item Scrap, Green": self.world.random.randint(0, 80),
|
||||
"Item Scrap, Red": self.world.random.randint(0, 45),
|
||||
"Item Scrap, Yellow": self.world.random.randint(0, 30),
|
||||
"Item Scrap, White": self.world.random.randint(0, 100),
|
||||
"Common Item": self.world.random.randint(0, 100),
|
||||
"Uncommon Item": self.world.random.randint(0, 70),
|
||||
"Legendary Item": self.world.random.randint(0, 30),
|
||||
"Boss Item": self.world.random.randint(0, 20),
|
||||
"Lunar Item": self.world.random.randint(0, 60),
|
||||
"Equipment": self.world.random.randint(0, 40)
|
||||
}
|
||||
if pool_option == ItemWeights.option_chaos:
|
||||
for name, max_value in item_pool_weights[pool_option].items():
|
||||
junk_pool[name] = self.world.random.randint(0, max_value)
|
||||
else:
|
||||
junk_pool = item_pool_weights[pool_option].copy()
|
||||
else: # generate junk pool from user created presets
|
||||
@@ -77,37 +75,43 @@ class RiskOfRainWorld(World):
|
||||
}
|
||||
|
||||
# remove lunar items from the pool if they're disabled in the yaml unless lunartic is rolled
|
||||
if not self.world.enable_lunar[self.player]:
|
||||
if not pool_option == 4:
|
||||
junk_pool.pop("Lunar Item")
|
||||
if not (self.world.enable_lunar[self.player] or pool_option == ItemWeights.option_lunartic):
|
||||
junk_pool.pop("Lunar Item")
|
||||
|
||||
# Generate item pool
|
||||
itempool = []
|
||||
|
||||
itempool: List = []
|
||||
# Add revive items for the player
|
||||
itempool += ["Dio's Best Friend"] * int(self.world.total_revivals[self.player] / 100 * self.world.total_locations[self.player])
|
||||
itempool += ["Dio's Best Friend"] * self.total_revivals
|
||||
|
||||
# Fill remaining items with randomly generated junk
|
||||
itempool += self.world.random.choices(list(junk_pool.keys()), weights=list(junk_pool.values()),
|
||||
k=self.world.total_locations[self.player] -
|
||||
int(self.world.total_revivals[self.player] / 100 * self.world.total_locations[self.player]))
|
||||
k=self.world.total_locations[self.player].value - self.total_revivals)
|
||||
|
||||
# Convert itempool into real items
|
||||
itempool = list(map(lambda name: self.create_item(name), itempool))
|
||||
|
||||
self.world.itempool += itempool
|
||||
|
||||
def set_rules(self):
|
||||
def set_rules(self) -> None:
|
||||
set_rules(self.world, self.player)
|
||||
|
||||
def create_regions(self):
|
||||
create_regions(self.world, self.player)
|
||||
create_events(self.world, self.player, int(self.world.total_locations[self.player]))
|
||||
def create_regions(self) -> None:
|
||||
menu = create_region(self.world, self.player, "Menu")
|
||||
petrichor = create_region(self.world, self.player, "Petrichor V",
|
||||
[f"ItemPickup{i + 1}" for i in range(self.world.total_locations[self.player].value)])
|
||||
|
||||
connection = Entrance(self.player, "Lobby", menu)
|
||||
menu.exits.append(connection)
|
||||
connection.connect(petrichor)
|
||||
|
||||
self.world.regions += [menu, petrichor]
|
||||
|
||||
create_events(self.world, self.player)
|
||||
|
||||
def fill_slot_data(self):
|
||||
return {
|
||||
"itemPickupStep": self.world.item_pickup_step[self.player].value,
|
||||
"seed": "".join(self.world.slot_seeds[self.player].choice(string.digits) for i in range(16)),
|
||||
"seed": "".join(self.world.slot_seeds[self.player].choice(string.digits) for _ in range(16)),
|
||||
"totalLocations": self.world.total_locations[self.player].value,
|
||||
"totalRevivals": self.world.total_revivals[self.player].value,
|
||||
"startWithDio": self.world.start_with_revive[self.player].value,
|
||||
@@ -116,49 +120,39 @@ class RiskOfRainWorld(World):
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
item_id = item_table[name]
|
||||
item = RiskOfRainItem(name, ItemClassification.filler, item_id, self.player)
|
||||
if name == "Dio's Best Friend":
|
||||
item.classification = ItemClassification.progression
|
||||
classification = ItemClassification.progression
|
||||
elif name in {"Equipment", "Legendary Item"}:
|
||||
item.classification = ItemClassification.useful
|
||||
classification = ItemClassification.useful
|
||||
else:
|
||||
classification = ItemClassification.filler
|
||||
item = RiskOfRainItem(name, classification, item_id, self.player)
|
||||
return item
|
||||
|
||||
|
||||
def create_events(world: MultiWorld, player: int, total_locations: int):
|
||||
def create_events(world: MultiWorld, player: int) -> None:
|
||||
total_locations = world.total_locations[player].value
|
||||
num_of_events = total_locations // 25
|
||||
if total_locations / 25 == num_of_events:
|
||||
num_of_events -= 1
|
||||
world_region = world.get_region("Petrichor V", player)
|
||||
|
||||
for i in range(num_of_events):
|
||||
event_loc = RiskOfRainLocation(player, f"Pickup{(i + 1) * 25}", None, world.get_region('Petrichor V', player))
|
||||
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.get_region('Petrichor V', player).locations.append(event_loc)
|
||||
world_region.locations.append(event_loc)
|
||||
|
||||
victory_event = RiskOfRainLocation(player, "Victory", None, world_region)
|
||||
victory_event.place_locked_item(RiskOfRainItem("Victory", ItemClassification.progression, None, player))
|
||||
world_region.locations.append(victory_event)
|
||||
|
||||
|
||||
# generate locations based on player setting
|
||||
def create_regions(world, player: int):
|
||||
world.regions += [
|
||||
create_region(world, player, 'Menu', None, ['Lobby']),
|
||||
create_region(world, player, 'Petrichor V',
|
||||
[location for location in base_location_table] +
|
||||
[f"ItemPickup{i}" for i in range(1, 1 + world.total_locations[player])])
|
||||
]
|
||||
|
||||
world.get_entrance("Lobby", player).connect(world.get_region("Petrichor V", player))
|
||||
world.get_location("Victory", player).place_locked_item(RiskOfRainItem("Victory", ItemClassification.progression,
|
||||
None, player))
|
||||
|
||||
|
||||
def create_region(world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
||||
ret = Region(name, RegionType.Generic, name, player)
|
||||
ret.world = world
|
||||
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 = location_table[location]
|
||||
loc_id = item_pickups[location]
|
||||
location = RiskOfRainLocation(player, location, loc_id, ret)
|
||||
ret.locations.append(location)
|
||||
if exits:
|
||||
for exit in exits:
|
||||
ret.exits.append(Entrance(player, exit, ret))
|
||||
|
||||
return ret
|
||||
|
||||
@@ -29,7 +29,7 @@ You can see the [basic multiworld setup guide](/tutorial/Archipelago/setup/en) h
|
||||
about why Archipelago uses YAML files and what they're for.
|
||||
|
||||
### Where do I get a YAML?
|
||||
You can use the [game settings page for Hollow Knight](/games/Hollow%20Knight/player-settings) here on the Archipelago
|
||||
You can use the [game settings page](/games/Risk%20of%20Rain%202/player-settings) here on the Archipelago
|
||||
website to generate a YAML using a graphical interface.
|
||||
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ def check_for_impossible_shuffle(shuffled_levels: typing.List[int], gate_0_range
|
||||
|
||||
class SA2BWorld(World):
|
||||
"""
|
||||
Sonic Adventure 2 Battle is an action platforming game. Play as Sonic, Tails, Knuckles, Shadow, Rogue, and Eggman across 31 stages and prevent the destruction of the earth.
|
||||
Sonic Adventure 2 Battle is an action platforming game. Play as Sonic, Tails, Knuckles, Shadow, Rouge, and Eggman across 31 stages and prevent the destruction of the earth.
|
||||
"""
|
||||
game: str = "Sonic Adventure 2 Battle"
|
||||
option_definitions = sa2b_options
|
||||
@@ -282,8 +282,7 @@ class SA2BWorld(World):
|
||||
spoiler_handle.writelines(text)
|
||||
|
||||
@classmethod
|
||||
def stage_fill_hook(cls, world, progitempool, nonexcludeditempool, localrestitempool, nonlocalrestitempool,
|
||||
restitempool, fill_locations):
|
||||
def stage_fill_hook(cls, world, progitempool, usefulitempool, filleritempool, fill_locations):
|
||||
if world.get_game_players("Sonic Adventure 2 Battle"):
|
||||
progitempool.sort(
|
||||
key=lambda item: 0 if (item.name != 'Emblem') else 1)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from BaseClasses import Item, ItemClassification
|
||||
import typing
|
||||
from .MissionTables import vanilla_mission_req_table
|
||||
|
||||
|
||||
class ItemData(typing.NamedTuple):
|
||||
@@ -49,27 +50,27 @@ item_table = {
|
||||
|
||||
"Projectile Accelerator (Bunker)": ItemData(200 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 0),
|
||||
"Neosteel Bunker (Bunker)": ItemData(201 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 1),
|
||||
"Titanium Housing (Missile Turret)": ItemData(202 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 2),
|
||||
"Titanium Housing (Missile Turret)": ItemData(202 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 2, classification=ItemClassification.filler),
|
||||
"Hellstorm Batteries (Missile Turret)": ItemData(203 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 3),
|
||||
"Advanced Construction (SCV)": ItemData(204 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 4),
|
||||
"Dual-Fusion Welders (SCV)": ItemData(205 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 5),
|
||||
"Fire-Suppression System (Building)": ItemData(206 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 6),
|
||||
"Fire-Suppression System (Building)": ItemData(206 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 6, classification=ItemClassification.filler),
|
||||
"Orbital Command (Building)": ItemData(207 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 7),
|
||||
"Stimpack (Marine)": ItemData(208 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 8),
|
||||
"Combat Shield (Marine)": ItemData(209 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 9),
|
||||
"Advanced Medic Facilities (Medic)": ItemData(210 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 10),
|
||||
"Stabilizer Medpacks (Medic)": ItemData(211 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 11),
|
||||
"Combat Shield (Marine)": ItemData(209 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 9, classification=ItemClassification.progression),
|
||||
"Advanced Medic Facilities (Medic)": ItemData(210 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 10, classification=ItemClassification.progression),
|
||||
"Stabilizer Medpacks (Medic)": ItemData(211 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 11, classification=ItemClassification.progression),
|
||||
"Incinerator Gauntlets (Firebat)": ItemData(212 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 12, classification=ItemClassification.filler),
|
||||
"Juggernaut Plating (Firebat)": ItemData(213 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 13),
|
||||
"Concussive Shells (Marauder)": ItemData(214 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 14),
|
||||
"Kinetic Foam (Marauder)": ItemData(215 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 15),
|
||||
"U-238 Rounds (Reaper)": ItemData(216 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 16),
|
||||
"G-4 Clusterbomb (Reaper)": ItemData(217 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 17, classification=ItemClassification.filler),
|
||||
"G-4 Clusterbomb (Reaper)": ItemData(217 + SC2WOL_ITEM_ID_OFFSET, "Armory 1", 17, classification=ItemClassification.progression),
|
||||
|
||||
"Twin-Linked Flamethrower (Hellion)": ItemData(300 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 0, classification=ItemClassification.filler),
|
||||
"Thermite Filaments (Hellion)": ItemData(301 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 1),
|
||||
"Cerberus Mine (Vulture)": ItemData(302 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 2),
|
||||
"Replenishable Magazine (Vulture)": ItemData(303 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 3),
|
||||
"Cerberus Mine (Vulture)": ItemData(302 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 2, classification=ItemClassification.filler),
|
||||
"Replenishable Magazine (Vulture)": ItemData(303 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 3, classification=ItemClassification.filler),
|
||||
"Multi-Lock Weapons System (Goliath)": ItemData(304 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 4),
|
||||
"Ares-Class Targeting System (Goliath)": ItemData(305 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 5),
|
||||
"Tri-Lithium Power Cell (Diamondback)": ItemData(306 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 6, classification=ItemClassification.filler),
|
||||
@@ -77,9 +78,9 @@ item_table = {
|
||||
"Maelstrom Rounds (Siege Tank)": ItemData(308 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 8),
|
||||
"Shaped Blast (Siege Tank)": ItemData(309 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 9),
|
||||
"Rapid Deployment Tube (Medivac)": ItemData(310 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 10, classification=ItemClassification.filler),
|
||||
"Advanced Healing AI (Medivac)": ItemData(311 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 11),
|
||||
"Advanced Healing AI (Medivac)": ItemData(311 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 11, classification=ItemClassification.filler),
|
||||
"Tomahawk Power Cells (Wraith)": ItemData(312 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 12, classification=ItemClassification.filler),
|
||||
"Displacement Field (Wraith)": ItemData(313 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 13),
|
||||
"Displacement Field (Wraith)": ItemData(313 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 13, classification=ItemClassification.filler),
|
||||
"Ripwave Missiles (Viking)": ItemData(314 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 14),
|
||||
"Phobos-Class Weapons System (Viking)": ItemData(315 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 15),
|
||||
"Cross-Spectrum Dampeners (Banshee)": ItemData(316 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 16, classification=ItemClassification.filler),
|
||||
@@ -88,7 +89,7 @@ item_table = {
|
||||
"Defensive Matrix (Battlecruiser)": ItemData(319 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 19, classification=ItemClassification.filler),
|
||||
"Ocular Implants (Ghost)": ItemData(320 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 20),
|
||||
"Crius Suit (Ghost)": ItemData(321 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 21),
|
||||
"Psionic Lash (Spectre)": ItemData(322 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 22),
|
||||
"Psionic Lash (Spectre)": ItemData(322 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 22, classification=ItemClassification.progression),
|
||||
"Nyx-Class Cloaking Module (Spectre)": ItemData(323 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 23),
|
||||
"330mm Barrage Cannon (Thor)": ItemData(324 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 24, classification=ItemClassification.filler),
|
||||
"Immortality Protocol (Thor)": ItemData(325 + SC2WOL_ITEM_ID_OFFSET, "Armory 2", 25, classification=ItemClassification.filler),
|
||||
@@ -97,12 +98,12 @@ item_table = {
|
||||
"Missile Turret": ItemData(401 + SC2WOL_ITEM_ID_OFFSET, "Building", 1, classification=ItemClassification.progression),
|
||||
"Sensor Tower": ItemData(402 + SC2WOL_ITEM_ID_OFFSET, "Building", 2),
|
||||
|
||||
"War Pigs": ItemData(500 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 0),
|
||||
"War Pigs": ItemData(500 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 0, classification=ItemClassification.progression),
|
||||
"Devil Dogs": ItemData(501 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 1, classification=ItemClassification.filler),
|
||||
"Hammer Securities": ItemData(502 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 2),
|
||||
"Spartan Company": ItemData(503 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 3),
|
||||
"Spartan Company": ItemData(503 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 3, classification=ItemClassification.progression),
|
||||
"Siege Breakers": ItemData(504 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 4),
|
||||
"Hel's Angel": ItemData(505 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 5),
|
||||
"Hel's Angel": ItemData(505 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 5, classification=ItemClassification.progression),
|
||||
"Dusk Wings": ItemData(506 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 6),
|
||||
"Jackson's Revenge": ItemData(507 + SC2WOL_ITEM_ID_OFFSET, "Mercenary", 7),
|
||||
|
||||
@@ -153,12 +154,7 @@ basic_unit: typing.Tuple[str, ...] = (
|
||||
item_name_groups = {}
|
||||
for item, data in item_table.items():
|
||||
item_name_groups.setdefault(data.type, []).append(item)
|
||||
item_name_groups["Missions"] = ["Beat Liberation Day", "Beat The Outlaws", "Beat Zero Hour", "Beat Evacuation",
|
||||
"None Outbreak", "Beat Safe Haven", "Beat Haven's Fall", "Beat Smash and Grab", "Beat The Dig",
|
||||
"Beat The Moebius Factor", "Beat Supernova", "Beat Maw of the Void", "Beat Devil's Playground",
|
||||
"Beat Welcome to the Jungle", "Beat Breakout", "Beat Ghost of a Chance",
|
||||
"Beat The Great Train Robbery", "Beat Cutthroat", "Beat Engine of Destruction",
|
||||
"Beat Media Blitz", "Beat Piercing the Shroud"]
|
||||
item_name_groups["Missions"] = ["Beat " + mission_name for mission_name in vanilla_mission_req_table]
|
||||
|
||||
filler_items: typing.Tuple[str, ...] = (
|
||||
'+15 Starting Minerals',
|
||||
@@ -167,3 +163,17 @@ filler_items: typing.Tuple[str, ...] = (
|
||||
|
||||
lookup_id_to_name: typing.Dict[int, str] = {data.code: item_name for item_name, data in get_full_item_list().items() if
|
||||
data.code}
|
||||
# Map type to expected int
|
||||
type_flaggroups: typing.Dict[str, int] = {
|
||||
"Unit": 0,
|
||||
"Upgrade": 1,
|
||||
"Armory 1": 2,
|
||||
"Armory 2": 3,
|
||||
"Building": 4,
|
||||
"Mercenary": 5,
|
||||
"Laboratory": 6,
|
||||
"Protoss": 7,
|
||||
"Minerals": 8,
|
||||
"Vespene": 9,
|
||||
"Supply": 10,
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
|
||||
LocationData("Zero Hour", "Beat Zero Hour", None,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player)),
|
||||
LocationData("Evacuation", "Evacuation: Victory", SC2WOL_LOC_ID_OFFSET + 400,
|
||||
lambda state: state._sc2wol_has_mobile_anti_air(world, player)),
|
||||
lambda state: state._sc2wol_has_anti_air(world, player)),
|
||||
LocationData("Evacuation", "Evacuation: First Chysalis", SC2WOL_LOC_ID_OFFSET + 401),
|
||||
LocationData("Evacuation", "Evacuation: Second Chysalis", SC2WOL_LOC_ID_OFFSET + 402,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player)),
|
||||
@@ -52,7 +52,7 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
|
||||
lambda state: state._sc2wol_has_common_unit(world, player)),
|
||||
LocationData("Evacuation", "Beat Evacuation", None,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) and
|
||||
state._sc2wol_has_mobile_anti_air(world, player)),
|
||||
state._sc2wol_has_competent_anti_air(world, player)),
|
||||
LocationData("Outbreak", "Outbreak: Victory", SC2WOL_LOC_ID_OFFSET + 500,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) or state.has("Reaper", player)),
|
||||
LocationData("Outbreak", "Outbreak: Left Infestor", SC2WOL_LOC_ID_OFFSET + 501,
|
||||
@@ -63,19 +63,37 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) or state.has("Reaper", player)),
|
||||
LocationData("Safe Haven", "Safe Haven: Victory", SC2WOL_LOC_ID_OFFSET + 600,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) and
|
||||
state._sc2wol_has_mobile_anti_air(world, player)),
|
||||
state._sc2wol_has_competent_anti_air(world, player)),
|
||||
LocationData("Safe Haven", "Safe Haven: North Nexus", SC2WOL_LOC_ID_OFFSET + 601,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) and
|
||||
state._sc2wol_has_competent_anti_air(world, player)),
|
||||
LocationData("Safe Haven", "Safe Haven: East Nexus", SC2WOL_LOC_ID_OFFSET + 602,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) and
|
||||
state._sc2wol_has_competent_anti_air(world, player)),
|
||||
LocationData("Safe Haven", "Safe Haven: South Nexus", SC2WOL_LOC_ID_OFFSET + 603,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) and
|
||||
state._sc2wol_has_competent_anti_air(world, player)),
|
||||
LocationData("Safe Haven", "Beat Safe Haven", None,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) and
|
||||
state._sc2wol_has_mobile_anti_air(world, player)),
|
||||
state._sc2wol_has_competent_anti_air(world, player)),
|
||||
LocationData("Haven's Fall", "Haven's Fall: Victory", SC2WOL_LOC_ID_OFFSET + 700,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) and
|
||||
state._sc2wol_has_mobile_anti_air(world, player)),
|
||||
state._sc2wol_has_competent_anti_air(world, player)),
|
||||
LocationData("Haven's Fall", "Haven's Fall: North Hive", SC2WOL_LOC_ID_OFFSET + 701,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) and
|
||||
state._sc2wol_has_competent_anti_air(world, player)),
|
||||
LocationData("Haven's Fall", "Haven's Fall: East Hive", SC2WOL_LOC_ID_OFFSET + 702,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) and
|
||||
state._sc2wol_has_competent_anti_air(world, player)),
|
||||
LocationData("Haven's Fall", "Haven's Fall: South Hive", SC2WOL_LOC_ID_OFFSET + 703,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) and
|
||||
state._sc2wol_has_competent_anti_air(world, player)),
|
||||
LocationData("Haven's Fall", "Beat Haven's Fall", None,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) and
|
||||
state._sc2wol_has_mobile_anti_air(world, player)),
|
||||
state._sc2wol_has_competent_anti_air(world, player)),
|
||||
LocationData("Smash and Grab", "Smash and Grab: Victory", SC2WOL_LOC_ID_OFFSET + 800,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) and
|
||||
state._sc2wol_has_mobile_anti_air(world, player)),
|
||||
state._sc2wol_has_competent_anti_air(world, player)),
|
||||
LocationData("Smash and Grab", "Smash and Grab: First Relic", SC2WOL_LOC_ID_OFFSET + 801),
|
||||
LocationData("Smash and Grab", "Smash and Grab: Second Relic", SC2WOL_LOC_ID_OFFSET + 802),
|
||||
LocationData("Smash and Grab", "Smash and Grab: Third Relic", SC2WOL_LOC_ID_OFFSET + 803,
|
||||
@@ -101,11 +119,7 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
|
||||
state._sc2wol_has_anti_air(world, player) and
|
||||
state._sc2wol_has_heavy_defense(world, player)),
|
||||
LocationData("The Moebius Factor", "The Moebius Factor: Victory", SC2WOL_LOC_ID_OFFSET + 1000,
|
||||
lambda state: state._sc2wol_has_air(world, player)),
|
||||
LocationData("The Moebius Factor", "The Moebius Factor: 1st Data Core ", SC2WOL_LOC_ID_OFFSET + 1001,
|
||||
lambda state: True),
|
||||
LocationData("The Moebius Factor", "The Moebius Factor: 2nd Data Core", SC2WOL_LOC_ID_OFFSET + 1002,
|
||||
lambda state: state._sc2wol_has_air(world, player)),
|
||||
lambda state: state._sc2wol_has_air(world, player) and state._sc2wol_has_anti_air(world, player)),
|
||||
LocationData("The Moebius Factor", "The Moebius Factor: South Rescue", SC2WOL_LOC_ID_OFFSET + 1003,
|
||||
lambda state: state._sc2wol_able_to_rescue(world, player)),
|
||||
LocationData("The Moebius Factor", "The Moebius Factor: Wall Rescue", SC2WOL_LOC_ID_OFFSET + 1004,
|
||||
@@ -121,35 +135,49 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
|
||||
LocationData("The Moebius Factor", "Beat The Moebius Factor", None,
|
||||
lambda state: state._sc2wol_has_air(world, player)),
|
||||
LocationData("Supernova", "Supernova: Victory", SC2WOL_LOC_ID_OFFSET + 1100,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player)),
|
||||
lambda state: state._sc2wol_beats_protoss_deathball(world, player)),
|
||||
LocationData("Supernova", "Supernova: West Relic", SC2WOL_LOC_ID_OFFSET + 1101),
|
||||
LocationData("Supernova", "Supernova: North Relic", SC2WOL_LOC_ID_OFFSET + 1102,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player)),
|
||||
LocationData("Supernova", "Supernova: North Relic", SC2WOL_LOC_ID_OFFSET + 1102),
|
||||
LocationData("Supernova", "Supernova: South Relic", SC2WOL_LOC_ID_OFFSET + 1103,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player)),
|
||||
lambda state: state._sc2wol_beats_protoss_deathball(world, player)),
|
||||
LocationData("Supernova", "Supernova: East Relic", SC2WOL_LOC_ID_OFFSET + 1104,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player)),
|
||||
lambda state: state._sc2wol_beats_protoss_deathball(world, player)),
|
||||
LocationData("Supernova", "Beat Supernova", None,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player)),
|
||||
lambda state: state._sc2wol_beats_protoss_deathball(world, player)),
|
||||
LocationData("Maw of the Void", "Maw of the Void: Victory", SC2WOL_LOC_ID_OFFSET + 1200,
|
||||
lambda state: state.has('Battlecruiser', player) or state.has('Science Vessel', player) and
|
||||
state._sc2wol_has_air(world, player)),
|
||||
lambda state: state.has('Battlecruiser', player) or
|
||||
state._sc2wol_has_air(world, player) and
|
||||
state._sc2wol_has_competent_anti_air(world, player) and
|
||||
state.has('Science Vessel', player)),
|
||||
LocationData("Maw of the Void", "Maw of the Void: Landing Zone Cleared", SC2WOL_LOC_ID_OFFSET + 1201),
|
||||
LocationData("Maw of the Void", "Maw of the Void: Expansion Prisoners", SC2WOL_LOC_ID_OFFSET + 1202),
|
||||
LocationData("Maw of the Void", "Maw of the Void: Expansion Prisoners", SC2WOL_LOC_ID_OFFSET + 1202,
|
||||
lambda state: state.has('Battlecruiser', player) or
|
||||
state._sc2wol_has_air(world, player) and
|
||||
state._sc2wol_has_competent_anti_air(world, player) and
|
||||
state.has('Science Vessel', player)),
|
||||
LocationData("Maw of the Void", "Maw of the Void: South Close Prisoners", SC2WOL_LOC_ID_OFFSET + 1203,
|
||||
lambda state: state.has('Battlecruiser', player) or state.has('Science Vessel', player) and
|
||||
state._sc2wol_has_air(world, player)),
|
||||
lambda state: state.has('Battlecruiser', player) or
|
||||
state._sc2wol_has_air(world, player) and
|
||||
state._sc2wol_has_competent_anti_air(world, player) and
|
||||
state.has('Science Vessel', player)),
|
||||
LocationData("Maw of the Void", "Maw of the Void: South Far Prisoners", SC2WOL_LOC_ID_OFFSET + 1204,
|
||||
lambda state: state.has('Battlecruiser', player) or state.has('Science Vessel', player) and
|
||||
state._sc2wol_has_air(world, player)),
|
||||
lambda state: state.has('Battlecruiser', player) or
|
||||
state._sc2wol_has_air(world, player) and
|
||||
state._sc2wol_has_competent_anti_air(world, player) and
|
||||
state.has('Science Vessel', player)),
|
||||
LocationData("Maw of the Void", "Maw of the Void: North Prisoners", SC2WOL_LOC_ID_OFFSET + 1205,
|
||||
lambda state: state.has('Battlecruiser', player) or state.has('Science Vessel', player) and
|
||||
state._sc2wol_has_air(world, player)),
|
||||
lambda state: state.has('Battlecruiser', player) or
|
||||
state._sc2wol_has_air(world, player) and
|
||||
state._sc2wol_has_competent_anti_air(world, player) and
|
||||
state.has('Science Vessel', player)),
|
||||
LocationData("Maw of the Void", "Beat Maw of the Void", None,
|
||||
lambda state: state.has('Battlecruiser', player) or state.has('Science Vessel', player) and
|
||||
state._sc2wol_has_air(world, player)),
|
||||
lambda state: state.has('Battlecruiser', player) or
|
||||
state._sc2wol_has_air(world, player) and
|
||||
state._sc2wol_has_competent_anti_air(world, player) and
|
||||
state.has('Science Vessel', player)),
|
||||
LocationData("Devil's Playground", "Devil's Playground: Victory", SC2WOL_LOC_ID_OFFSET + 1300,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) or state.has("Reaper", player)),
|
||||
lambda state: state._sc2wol_has_anti_air(world, player) and (
|
||||
state._sc2wol_has_common_unit(world, player) or state.has("Reaper", player))),
|
||||
LocationData("Devil's Playground", "Devil's Playground: Tosh's Miners", SC2WOL_LOC_ID_OFFSET + 1301),
|
||||
LocationData("Devil's Playground", "Devil's Playground: Brutalisk", SC2WOL_LOC_ID_OFFSET + 1302,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) or state.has("Reaper", player)),
|
||||
@@ -157,17 +185,17 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) or state.has("Reaper", player)),
|
||||
LocationData("Welcome to the Jungle", "Welcome to the Jungle: Victory", SC2WOL_LOC_ID_OFFSET + 1400,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) and
|
||||
state._sc2wol_has_mobile_anti_air(world, player)),
|
||||
state._sc2wol_has_competent_anti_air(world, player)),
|
||||
LocationData("Welcome to the Jungle", "Welcome to the Jungle: Close Relic", SC2WOL_LOC_ID_OFFSET + 1401),
|
||||
LocationData("Welcome to the Jungle", "Welcome to the Jungle: West Relic", SC2WOL_LOC_ID_OFFSET + 1402,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) and
|
||||
state._sc2wol_has_mobile_anti_air(world, player)),
|
||||
state._sc2wol_has_competent_anti_air(world, player)),
|
||||
LocationData("Welcome to the Jungle", "Welcome to the Jungle: North-East Relic", SC2WOL_LOC_ID_OFFSET + 1403,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) and
|
||||
state._sc2wol_has_mobile_anti_air(world, player)),
|
||||
state._sc2wol_has_competent_anti_air(world, player)),
|
||||
LocationData("Welcome to the Jungle", "Beat Welcome to the Jungle", None,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player) and
|
||||
state._sc2wol_has_mobile_anti_air(world, player)),
|
||||
state._sc2wol_has_competent_anti_air(world, player)),
|
||||
LocationData("Breakout", "Breakout: Victory", SC2WOL_LOC_ID_OFFSET + 1500),
|
||||
LocationData("Breakout", "Breakout: Diamondback Prison", SC2WOL_LOC_ID_OFFSET + 1501),
|
||||
LocationData("Breakout", "Breakout: Siegetank Prison", SC2WOL_LOC_ID_OFFSET + 1502),
|
||||
@@ -180,7 +208,8 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
|
||||
LocationData("Ghost of a Chance", "Ghost of a Chance: Third Island Spectres", SC2WOL_LOC_ID_OFFSET + 1605),
|
||||
LocationData("Ghost of a Chance", "Beat Ghost of a Chance", None),
|
||||
LocationData("The Great Train Robbery", "The Great Train Robbery: Victory", SC2WOL_LOC_ID_OFFSET + 1700,
|
||||
lambda state: state._sc2wol_has_train_killers(world, player)),
|
||||
lambda state: state._sc2wol_has_train_killers(world, player) and
|
||||
state._sc2wol_has_anti_air(world, player)),
|
||||
LocationData("The Great Train Robbery", "The Great Train Robbery: North Defiler", SC2WOL_LOC_ID_OFFSET + 1701),
|
||||
LocationData("The Great Train Robbery", "The Great Train Robbery: Mid Defiler", SC2WOL_LOC_ID_OFFSET + 1702),
|
||||
LocationData("The Great Train Robbery", "The Great Train Robbery: South Defiler", SC2WOL_LOC_ID_OFFSET + 1703),
|
||||
@@ -198,20 +227,20 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
|
||||
LocationData("Cutthroat", "Beat Cutthroat", None,
|
||||
lambda state: state._sc2wol_has_common_unit(world, player)),
|
||||
LocationData("Engine of Destruction", "Engine of Destruction: Victory", SC2WOL_LOC_ID_OFFSET + 1900,
|
||||
lambda state: state._sc2wol_has_mobile_anti_air(world, player)),
|
||||
lambda state: state._sc2wol_has_competent_anti_air(world, player)),
|
||||
LocationData("Engine of Destruction", "Engine of Destruction: Odin", SC2WOL_LOC_ID_OFFSET + 1901),
|
||||
LocationData("Engine of Destruction", "Engine of Destruction: Loki", SC2WOL_LOC_ID_OFFSET + 1902,
|
||||
lambda state: state._sc2wol_has_mobile_anti_air(world, player) and
|
||||
lambda state: state._sc2wol_has_competent_anti_air(world, player) and
|
||||
state._sc2wol_has_common_unit(world, player) or state.has('Wraith', player)),
|
||||
LocationData("Engine of Destruction", "Engine of Destruction: Lab Devourer", SC2WOL_LOC_ID_OFFSET + 1903),
|
||||
LocationData("Engine of Destruction", "Engine of Destruction: North Devourer", SC2WOL_LOC_ID_OFFSET + 1904,
|
||||
lambda state: state._sc2wol_has_mobile_anti_air(world, player) and
|
||||
lambda state: state._sc2wol_has_competent_anti_air(world, player) and
|
||||
state._sc2wol_has_common_unit(world, player) or state.has('Wraith', player)),
|
||||
LocationData("Engine of Destruction", "Engine of Destruction: Southeast Devourer", SC2WOL_LOC_ID_OFFSET + 1905,
|
||||
lambda state: state._sc2wol_has_mobile_anti_air(world, player) and
|
||||
lambda state: state._sc2wol_has_competent_anti_air(world, player) and
|
||||
state._sc2wol_has_common_unit(world, player) or state.has('Wraith', player)),
|
||||
LocationData("Engine of Destruction", "Beat Engine of Destruction", None,
|
||||
lambda state: state._sc2wol_has_mobile_anti_air(world, player) and
|
||||
lambda state: state._sc2wol_has_competent_anti_air(world, player) and
|
||||
state._sc2wol_has_common_unit(world, player) or state.has('Wraith', player)),
|
||||
LocationData("Media Blitz", "Media Blitz: Victory", SC2WOL_LOC_ID_OFFSET + 2000,
|
||||
lambda state: state._sc2wol_has_competent_comp(world, player)),
|
||||
@@ -224,13 +253,19 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
|
||||
LocationData("Media Blitz", "Media Blitz: Science Facility", SC2WOL_LOC_ID_OFFSET + 2004),
|
||||
LocationData("Media Blitz", "Beat Media Blitz", None,
|
||||
lambda state: state._sc2wol_has_competent_comp(world, player)),
|
||||
LocationData("Piercing the Shroud", "Piercing the Shroud: Victory", SC2WOL_LOC_ID_OFFSET + 2100),
|
||||
LocationData("Piercing the Shroud", "Piercing the Shroud: Victory", SC2WOL_LOC_ID_OFFSET + 2100,
|
||||
lambda state: state.has_any({'Combat Shield (Marine)', 'Stabilizer Medpacks (Medic)'}, player)),
|
||||
LocationData("Piercing the Shroud", "Piercing the Shroud: Holding Cell Relic", SC2WOL_LOC_ID_OFFSET + 2101),
|
||||
LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk Relic", SC2WOL_LOC_ID_OFFSET + 2102),
|
||||
LocationData("Piercing the Shroud", "Piercing the Shroud: First Escape Relic", SC2WOL_LOC_ID_OFFSET + 2103),
|
||||
LocationData("Piercing the Shroud", "Piercing the Shroud: Second Escape Relic", SC2WOL_LOC_ID_OFFSET + 2104),
|
||||
LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk ", SC2WOL_LOC_ID_OFFSET + 2105),
|
||||
LocationData("Piercing the Shroud", "Beat Piercing the Shroud", None),
|
||||
LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk Relic", SC2WOL_LOC_ID_OFFSET + 2102,
|
||||
lambda state: state.has_any({'Combat Shield (Marine)', 'Stabilizer Medpacks (Medic)'}, player)),
|
||||
LocationData("Piercing the Shroud", "Piercing the Shroud: First Escape Relic", SC2WOL_LOC_ID_OFFSET + 2103,
|
||||
lambda state: state.has_any({'Combat Shield (Marine)', 'Stabilizer Medpacks (Medic)'}, player)),
|
||||
LocationData("Piercing the Shroud", "Piercing the Shroud: Second Escape Relic", SC2WOL_LOC_ID_OFFSET + 2104,
|
||||
lambda state: state.has_any({'Combat Shield (Marine)', 'Stabilizer Medpacks (Medic)'}, player)),
|
||||
LocationData("Piercing the Shroud", "Piercing the Shroud: Brutalisk ", SC2WOL_LOC_ID_OFFSET + 2105,
|
||||
lambda state: state.has_any({'Combat Shield (Marine)', 'Stabilizer Medpacks (Medic)'}, player)),
|
||||
LocationData("Piercing the Shroud", "Beat Piercing the Shroud", None,
|
||||
lambda state: state.has_any({'Combat Shield (Marine)', 'Stabilizer Medpacks (Medic)'}, player)),
|
||||
LocationData("Whispers of Doom", "Whispers of Doom: Victory", SC2WOL_LOC_ID_OFFSET + 2200),
|
||||
LocationData("Whispers of Doom", "Whispers of Doom: First Hatchery", SC2WOL_LOC_ID_OFFSET + 2201),
|
||||
LocationData("Whispers of Doom", "Whispers of Doom: Second Hatchery", SC2WOL_LOC_ID_OFFSET + 2202),
|
||||
@@ -251,13 +286,12 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]) -> Tuple[L
|
||||
lambda state: state._sc2wol_has_protoss_common_units(world, player)),
|
||||
LocationData("Echoes of the Future", "Beat Echoes of the Future", None,
|
||||
lambda state: state._sc2wol_has_protoss_medium_units(world, player)),
|
||||
LocationData("In Utter Darkness", "In Utter Darkness: Defeat", SC2WOL_LOC_ID_OFFSET + 2500,
|
||||
lambda state: state._sc2wol_has_protoss_medium_units(world, player)),
|
||||
LocationData("In Utter Darkness", "In Utter Darkness: Defeat", SC2WOL_LOC_ID_OFFSET + 2500),
|
||||
LocationData("In Utter Darkness", "In Utter Darkness: Protoss Archive", SC2WOL_LOC_ID_OFFSET + 2501,
|
||||
lambda state: state._sc2wol_has_protoss_medium_units(world, player)),
|
||||
LocationData("In Utter Darkness", "In Utter Darkness: Kills", SC2WOL_LOC_ID_OFFSET + 2502),
|
||||
LocationData("In Utter Darkness", "Beat In Utter Darkness", None,
|
||||
lambda state: state._sc2wol_has_protoss_medium_units(world, player)),
|
||||
LocationData("In Utter Darkness", "In Utter Darkness: Kills", SC2WOL_LOC_ID_OFFSET + 2502,
|
||||
lambda state: state._sc2wol_has_protoss_common_units(world, player)),
|
||||
LocationData("In Utter Darkness", "Beat In Utter Darkness", None),
|
||||
LocationData("Gates of Hell", "Gates of Hell: Victory", SC2WOL_LOC_ID_OFFSET + 2600,
|
||||
lambda state: state._sc2wol_has_competent_comp(world, player)),
|
||||
LocationData("Gates of Hell", "Gates of Hell: Large Army", SC2WOL_LOC_ID_OFFSET + 2601,
|
||||
|
||||
@@ -10,16 +10,17 @@ class SC2WoLLogic(LogicMixin):
|
||||
return self.has_any({'Marine', 'Marauder'}, player)
|
||||
|
||||
def _sc2wol_has_air(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Viking', 'Wraith', 'Medivac', 'Banshee', 'Hercules'}, player)
|
||||
return self.has_any({'Viking', 'Wraith', 'Banshee'}, player) or \
|
||||
self.has_any({'Hercules', 'Medivac'}, player) and self._sc2wol_has_common_unit(world, player)
|
||||
|
||||
def _sc2wol_has_air_anti_air(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Viking', 'Wraith'}, player)
|
||||
|
||||
def _sc2wol_has_mobile_anti_air(self, world: MultiWorld, player: int) -> bool:
|
||||
def _sc2wol_has_competent_anti_air(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Marine', 'Goliath'}, player) or self._sc2wol_has_air_anti_air(world, player)
|
||||
|
||||
def _sc2wol_has_anti_air(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has('Missile Turret', player) or self._sc2wol_has_mobile_anti_air(world, player)
|
||||
return self.has_any({'Missile Turret', 'Thor', 'War Pigs', 'Spartan Company', "Hel's Angel", 'Battlecruiser'}, player) or self._sc2wol_has_competent_anti_air(world, player)
|
||||
|
||||
def _sc2wol_has_heavy_defense(self, world: MultiWorld, player: int) -> bool:
|
||||
return (self.has_any({'Siege Tank', 'Vulture'}, player) or
|
||||
@@ -28,13 +29,15 @@ class SC2WoLLogic(LogicMixin):
|
||||
|
||||
def _sc2wol_has_competent_comp(self, world: MultiWorld, player: int) -> bool:
|
||||
return (self.has('Marine', player) or self.has('Marauder', player) and
|
||||
self._sc2wol_has_mobile_anti_air(world, player)) and self.has_any({'Medivac', 'Medic'}, player) or \
|
||||
self.has('Thor', player) or self.has("Banshee", player) and self._sc2wol_has_mobile_anti_air(world, player) or \
|
||||
self.has('Battlecruiser', player) and self._sc2wol_has_common_unit(world, player)
|
||||
self._sc2wol_has_competent_anti_air(world, player)) and self.has_any({'Medivac', 'Medic'}, player) or \
|
||||
self.has('Thor', player) or self.has("Banshee", player) and self._sc2wol_has_competent_anti_air(world, player) or \
|
||||
self.has('Battlecruiser', player) and self._sc2wol_has_common_unit(world, player) or \
|
||||
self.has('Siege Tank', player) and self._sc2wol_has_competent_anti_air(world, player)
|
||||
|
||||
def _sc2wol_has_train_killers(self, world: MultiWorld, player: int) -> bool:
|
||||
return (self.has_any({'Siege Tank', 'Diamondback'}, player) or
|
||||
self.has_all({'Reaper', "G-4 Clusterbomb"}, player) or self.has_all({'Spectre', 'Psionic Lash'}, player))
|
||||
self.has_all({'Reaper', "G-4 Clusterbomb"}, player) or self.has_all({'Spectre', 'Psionic Lash'}, player)
|
||||
or self.has('Marauders', player))
|
||||
|
||||
def _sc2wol_able_to_rescue(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Medivac', 'Hercules', 'Raven', 'Viking'}, player)
|
||||
@@ -46,6 +49,10 @@ class SC2WoLLogic(LogicMixin):
|
||||
return self._sc2wol_has_protoss_common_units(world, player) and \
|
||||
self.has_any({'Stalker', 'Void Ray', 'Phoenix', 'Carrier'}, player)
|
||||
|
||||
def _sc2wol_beats_protoss_deathball(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any({'Banshee', 'Battlecruiser'}, player) and self._sc2wol_has_competent_anti_air or \
|
||||
self._sc2wol_has_competent_comp(world, player) and self._sc2wol_has_air_anti_air(world, player)
|
||||
|
||||
def _sc2wol_cleared_missions(self, world: MultiWorld, player: int, mission_count: int) -> bool:
|
||||
return self.has_group("Missions", player, mission_count)
|
||||
|
||||
|
||||
@@ -69,8 +69,8 @@ vanilla_mission_req_table = {
|
||||
"Zero Hour": MissionInfo(3, 4, [2], "Mar Sara", completion_critical=True),
|
||||
"Evacuation": MissionInfo(4, 4, [3], "Colonist"),
|
||||
"Outbreak": MissionInfo(5, 3, [4], "Colonist"),
|
||||
"Safe Haven": MissionInfo(6, 1, [5], "Colonist", number=7),
|
||||
"Haven's Fall": MissionInfo(7, 1, [5], "Colonist", number=7),
|
||||
"Safe Haven": MissionInfo(6, 4, [5], "Colonist", number=7),
|
||||
"Haven's Fall": MissionInfo(7, 4, [5], "Colonist", number=7),
|
||||
"Smash and Grab": MissionInfo(8, 5, [3], "Artifact", completion_critical=True),
|
||||
"The Dig": MissionInfo(9, 4, [8], "Artifact", number=8, completion_critical=True),
|
||||
"The Moebius Factor": MissionInfo(10, 9, [9], "Artifact", number=11, completion_critical=True),
|
||||
|
||||
@@ -200,7 +200,10 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData
|
||||
connect(world, player, names, "Menu", missions[i])
|
||||
else:
|
||||
connect(world, player, names, missions[connection], missions[i],
|
||||
(lambda name: (lambda state: state.has(f"Beat {name}", player)))(missions[connection]))
|
||||
(lambda name, missions_req: (lambda state: state.has(f"Beat {name}", player) and
|
||||
state._sc2wol_cleared_missions(world, player,
|
||||
missions_req)))
|
||||
(missions[connection], vanilla_shuffle_order[i].number))
|
||||
connections.append(connection + 1)
|
||||
|
||||
mission_req_table.update({missions[i]: MissionInfo(
|
||||
|
||||
@@ -33,7 +33,7 @@ class SC2WoLWorld(World):
|
||||
|
||||
game = "Starcraft 2 Wings of Liberty"
|
||||
web = Starcraft2WoLWebWorld()
|
||||
data_version = 2
|
||||
data_version = 3
|
||||
|
||||
item_name_to_id = {name: data.code for name, data in item_table.items()}
|
||||
location_name_to_id = {location.name: location.code for location in get_locations(None, None)}
|
||||
@@ -43,6 +43,7 @@ class SC2WoLWorld(World):
|
||||
locked_locations: typing.List[str]
|
||||
location_cache: typing.List[Location]
|
||||
mission_req_table = {}
|
||||
required_client_version = 0, 3, 5
|
||||
|
||||
def __init__(self, world: MultiWorld, player: int):
|
||||
super(SC2WoLWorld, self).__init__(world, player)
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
from worlds.sm.variaRandomizer.rando.Items import ItemManager
|
||||
|
||||
items_start_id = 83000
|
||||
|
||||
def gen_special_id():
|
||||
special_id_value_start = 32
|
||||
while True:
|
||||
yield special_id_value_start
|
||||
special_id_value_start += 1
|
||||
|
||||
gen_run = gen_special_id()
|
||||
|
||||
lookup_id_to_name = dict((items_start_id + (value.Id if value.Id != None else next(gen_run)), value.Name) for key, value in ItemManager.Items.items())
|
||||
lookup_name_to_id = {item_name: item_id for item_id, item_name in lookup_id_to_name.items()}
|
||||
@@ -1,14 +0,0 @@
|
||||
from worlds.sm.variaRandomizer.graph.location import locationsDict
|
||||
|
||||
locations_start_id = 82000
|
||||
|
||||
def gen_boss_id():
|
||||
boss_id_value_start = 256
|
||||
while True:
|
||||
yield boss_id_value_start
|
||||
boss_id_value_start += 1
|
||||
|
||||
gen_run = gen_boss_id()
|
||||
|
||||
lookup_id_to_name = dict((locations_start_id + (value.Id if value.Id != None else next(gen_run)), key) for key, value in locationsDict.items())
|
||||
lookup_name_to_id = {location_name: location_id for location_id, location_name in lookup_id_to_name.items()}
|
||||
@@ -122,8 +122,6 @@ class AreaRandomization(Choice):
|
||||
option_off = 0
|
||||
option_light = 1
|
||||
option_on = 2
|
||||
alias_false = 0
|
||||
alias_true = 2
|
||||
default = 0
|
||||
|
||||
class AreaLayout(Toggle):
|
||||
|
||||
@@ -11,8 +11,6 @@ from worlds.sm.variaRandomizer.graph.graph_utils import GraphUtils
|
||||
|
||||
logger = logging.getLogger("Super Metroid")
|
||||
|
||||
from .Locations import lookup_name_to_id as locations_lookup_name_to_id
|
||||
from .Items import lookup_name_to_id as items_lookup_name_to_id
|
||||
from .Regions import create_regions
|
||||
from .Rules import set_rules, add_entrance_rule
|
||||
from .Options import sm_options
|
||||
@@ -68,6 +66,8 @@ class SMWeb(WebWorld):
|
||||
["Farrak Kilhn"]
|
||||
)]
|
||||
|
||||
locations_start_id = 82000
|
||||
items_start_id = 83000
|
||||
|
||||
class SMWorld(World):
|
||||
"""
|
||||
@@ -78,12 +78,11 @@ class SMWorld(World):
|
||||
|
||||
game: str = "Super Metroid"
|
||||
topology_present = True
|
||||
data_version = 1
|
||||
data_version = 2
|
||||
option_definitions = sm_options
|
||||
item_names: Set[str] = frozenset(items_lookup_name_to_id)
|
||||
location_names: Set[str] = frozenset(locations_lookup_name_to_id)
|
||||
item_name_to_id = items_lookup_name_to_id
|
||||
location_name_to_id = locations_lookup_name_to_id
|
||||
|
||||
item_name_to_id = {value.Name: items_start_id + value.Id for key, value in ItemManager.Items.items() if value.Id != None}
|
||||
location_name_to_id = {key: locations_start_id + value.Id for key, value in locationsDict.items() if value.Id != None}
|
||||
web = SMWeb()
|
||||
|
||||
remote_items: bool = False
|
||||
@@ -661,8 +660,7 @@ class SMWorld(World):
|
||||
loc.address = loc.item.code = None
|
||||
|
||||
@classmethod
|
||||
def stage_fill_hook(cls, world, progitempool, nonexcludeditempool, localrestitempool, nonlocalrestitempool,
|
||||
restitempool, fill_locations):
|
||||
def stage_fill_hook(cls, world, progitempool, usefulitempool, filleritempool, fill_locations):
|
||||
if world.get_game_players("Super Metroid"):
|
||||
progitempool.sort(
|
||||
key=lambda item: 1 if (item.name == 'Morph Ball') else 0)
|
||||
@@ -701,8 +699,8 @@ class SMWorld(World):
|
||||
dest.Name) for src, dest in self.variaRando.randoExec.areaGraph.InterAreaTransitions if src.Boss]))
|
||||
|
||||
def create_locations(self, player: int):
|
||||
for name, id in locations_lookup_name_to_id.items():
|
||||
self.locations[name] = SMLocation(player, name, id)
|
||||
for name in locationsDict:
|
||||
self.locations[name] = SMLocation(player, name, self.location_name_to_id.get(name, None))
|
||||
|
||||
|
||||
def create_region(self, world: MultiWorld, player: int, name: str, locations=None, exits=None):
|
||||
|
||||
Binary file not shown.
@@ -2,14 +2,14 @@
|
||||
; generated by asar
|
||||
|
||||
[labels]
|
||||
B8:8026 :neg_1_1
|
||||
B8:83C1 :neg_1_1
|
||||
85:B9B4 :neg_1_2
|
||||
85:B9E6 :neg_1_3
|
||||
B8:C81F :neg_1_4
|
||||
B8:C831 :neg_1_5
|
||||
B8:C843 :neg_1_6
|
||||
B8:800C :pos_1_0
|
||||
B8:81DE :pos_1_1
|
||||
B8:830C :pos_1_0
|
||||
B8:85D7 :pos_1_1
|
||||
84:FA6B :pos_1_2
|
||||
84:FA75 :pos_1_3
|
||||
B8:C862 :pos_1_4
|
||||
@@ -20,7 +20,7 @@ B8:C87C :pos_1_6
|
||||
85:990F CLIPLEN_end
|
||||
85:990C CLIPLEN_no_multi
|
||||
85:FF1D CLIPSET
|
||||
B8:80EF COLLECTTANK
|
||||
B8:84E8 COLLECTTANK
|
||||
85:FF45 MISCFX
|
||||
84:8BF2 NORMAL
|
||||
85:FF4E SETFX
|
||||
@@ -38,6 +38,11 @@ CE:FF00 config_multiworld
|
||||
CE:FF08 config_player_id
|
||||
CE:FF06 config_remote_items
|
||||
CE:FF02 config_sprite
|
||||
B8:8419 copy_config_to_sram
|
||||
B8:83FD copy_memory
|
||||
B8:8417 copy_memory_done
|
||||
B8:8409 copy_memory_even
|
||||
B8:840F copy_memory_loop
|
||||
84:F894 h_item
|
||||
84:F8AD i_chozo_item
|
||||
84:F8B4 i_hidden_item
|
||||
@@ -46,11 +51,11 @@ B8:885C i_item_setup_shared
|
||||
B8:8878 i_item_setup_shared_all_items
|
||||
B8:8883 i_item_setup_shared_alwaysloaded
|
||||
84:FA79 i_live_pickup
|
||||
B8:817F i_live_pickup_multiworld
|
||||
B8:81C4 i_live_pickup_multiworld_end
|
||||
B8:819B i_live_pickup_multiworld_local_item_or_offworld
|
||||
B8:81B0 i_live_pickup_multiworld_own_item
|
||||
B8:81BC i_live_pickup_multiworld_own_item1
|
||||
B8:8578 i_live_pickup_multiworld
|
||||
B8:85BD i_live_pickup_multiworld_end
|
||||
B8:8594 i_live_pickup_multiworld_local_item_or_offworld
|
||||
B8:85A9 i_live_pickup_multiworld_own_item
|
||||
B8:85B5 i_live_pickup_multiworld_own_item1
|
||||
84:FA1E i_load_custom_graphics
|
||||
84:FA39 i_load_custom_graphics_all_items
|
||||
84:FA49 i_load_custom_graphics_alwaysloaded
|
||||
@@ -85,22 +90,27 @@ B8:81BC i_live_pickup_multiworld_own_item1
|
||||
85:B9CA message_write_placeholders_loop
|
||||
85:B9DC message_write_placeholders_notfound
|
||||
85:B9DF message_write_placeholders_value_ok
|
||||
B8:8092 mw_display_item_sent
|
||||
B8:80FF mw_handle_queue
|
||||
B8:8178 mw_handle_queue_end
|
||||
B8:8101 mw_handle_queue_loop
|
||||
B8:8151 mw_handle_queue_new_remote_item
|
||||
B8:816D mw_handle_queue_next
|
||||
B8:8163 mw_handle_queue_perform_receive
|
||||
B8:81C8 mw_hook_main_game
|
||||
B8:8011 mw_init
|
||||
B8:8044 mw_init_end
|
||||
B8:8000 mw_init_memory
|
||||
B8:8083 mw_load_sram
|
||||
B8:80B0 mw_receive_item
|
||||
B8:80E8 mw_receive_item_end
|
||||
B8:8070 mw_save_sram
|
||||
B8:8049 mw_write_message
|
||||
B8:848B mw_display_item_sent
|
||||
B8:84F8 mw_handle_queue
|
||||
B8:8571 mw_handle_queue_end
|
||||
B8:84FA mw_handle_queue_loop
|
||||
B8:854A mw_handle_queue_new_remote_item
|
||||
B8:8566 mw_handle_queue_next
|
||||
B8:855C mw_handle_queue_perform_receive
|
||||
B8:85C1 mw_hook_main_game
|
||||
B8:8311 mw_init
|
||||
B8:8366 mw_init_continuereset
|
||||
B8:83EA mw_init_end
|
||||
B8:8300 mw_init_memory
|
||||
B8:833B mw_init_reset_sram
|
||||
B8:8351 mw_init_smstringdata
|
||||
B8:8474 mw_load_sram
|
||||
B8:8482 mw_load_sram_done
|
||||
B8:8485 mw_load_sram_setnewgame
|
||||
B8:84A9 mw_receive_item
|
||||
B8:84E1 mw_receive_item_end
|
||||
B8:8469 mw_save_sram
|
||||
B8:8442 mw_write_message
|
||||
84:F888 nonprog_item_eight_palette_indices
|
||||
89:9200 offworld_graphics_data_item
|
||||
89:9100 offworld_graphics_data_progression_item
|
||||
@@ -125,7 +135,7 @@ B8:8049 mw_write_message
|
||||
84:F96E p_visible_item_end
|
||||
84:F95B p_visible_item_loop
|
||||
84:F967 p_visible_item_trigger
|
||||
B8:81DF patch_load_multiworld
|
||||
B8:85D8 patch_load_multiworld
|
||||
84:FA7E perform_item_pickup
|
||||
84:F886 plm_graphics_entry_offworld_item
|
||||
84:F87C plm_graphics_entry_offworld_progression_item
|
||||
@@ -144,17 +154,19 @@ B8:C808 start_item_data_minor
|
||||
B8:C818 start_item_data_reserve
|
||||
B8:C856 update_graphic
|
||||
84:F890 v_item
|
||||
B8:83EF write_repeated_memory
|
||||
B8:83F4 write_repeated_memory_loop
|
||||
|
||||
[source files]
|
||||
0000 e25029c5 main.asm
|
||||
0000 f30c3fdc main.asm
|
||||
0001 06780555 ../common/nofanfare.asm
|
||||
0002 e76d1f83 ../common/multiworld.asm
|
||||
0003 613d24e1 ../common/itemextras.asm
|
||||
0002 4f9a780e ../common/multiworld.asm
|
||||
0003 f7e9db95 ../common/itemextras.asm
|
||||
0004 d6616c0c ../common/items.asm
|
||||
0005 440b54fe ../common/startitem.asm
|
||||
0005 dbfcb38d ../common/startitem.asm
|
||||
|
||||
[rom checksum]
|
||||
09b134c5
|
||||
b526d5c7
|
||||
|
||||
[addr-to-line mapping]
|
||||
ff:ffff 0000:00000001
|
||||
@@ -204,330 +216,423 @@ ff:ffff 0000:00000001
|
||||
84:8bf2 0001:00000152
|
||||
84:8bf6 0001:00000153
|
||||
84:8bf7 0001:00000153
|
||||
b8:8000 0002:00000019
|
||||
b8:8002 0002:0000001a
|
||||
b8:8006 0002:0000001b
|
||||
b8:8008 0002:0000001c
|
||||
b8:800c 0002:00000020
|
||||
b8:800e 0002:00000021
|
||||
b8:8010 0002:00000022
|
||||
b8:8011 0002:00000025
|
||||
b8:8012 0002:00000025
|
||||
b8:8013 0002:00000025
|
||||
b8:8014 0002:00000025
|
||||
b8:8015 0000:00000013
|
||||
b8:8017 0002:00000029
|
||||
b8:801b 0002:0000002a
|
||||
b8:801e 0002:0000002b
|
||||
b8:8020 0002:0000002d
|
||||
b8:8023 0002:0000002e
|
||||
b8:8026 0002:00000031
|
||||
b8:802a 0002:00000032
|
||||
b8:802e 0002:00000033
|
||||
b8:8032 0002:00000034
|
||||
b8:8036 0002:00000035
|
||||
b8:8037 0002:00000035
|
||||
b8:8038 0002:00000036
|
||||
b8:803b 0002:00000037
|
||||
b8:803d 0002:00000039
|
||||
b8:8040 0002:0000003a
|
||||
b8:8044 0002:0000003d
|
||||
b8:8045 0002:0000003d
|
||||
b8:8046 0002:0000003d
|
||||
b8:8047 0002:0000003d
|
||||
b8:8048 0002:0000003e
|
||||
b8:8049 0002:00000043
|
||||
b8:804a 0002:00000043
|
||||
b8:804b 0002:00000044
|
||||
b8:804c 0002:00000044
|
||||
b8:804d 0002:00000045
|
||||
b8:8051 0002:00000046
|
||||
b8:8054 0002:00000046
|
||||
b8:8055 0002:00000047
|
||||
b8:8056 0002:00000048
|
||||
b8:805a 0002:00000049
|
||||
b8:805b 0002:0000004a
|
||||
b8:805f 0002:0000004b
|
||||
b8:8060 0002:0000004c
|
||||
b8:8064 0002:0000004e
|
||||
b8:8068 0002:0000004f
|
||||
b8:8069 0002:00000050
|
||||
b8:806d 0002:00000051
|
||||
b8:806e 0002:00000051
|
||||
b8:806f 0002:00000052
|
||||
b8:8070 0002:00000055
|
||||
b8:8071 0002:00000055
|
||||
b8:8072 0000:00000013
|
||||
b8:8074 0002:00000057
|
||||
b8:8078 0002:00000058
|
||||
b8:807c 0002:00000059
|
||||
b8:807d 0002:00000059
|
||||
b8:807e 0002:0000005b
|
||||
b8:807f 0002:0000005c
|
||||
b8:8082 0002:0000005d
|
||||
b8:8083 0002:00000060
|
||||
b8:8084 0002:00000060
|
||||
b8:8085 0000:00000013
|
||||
b8:8087 0002:00000062
|
||||
b8:808b 0002:00000063
|
||||
b8:808f 0002:00000064
|
||||
b8:8090 0002:00000064
|
||||
b8:8091 0002:00000065
|
||||
b8:8092 0002:0000006a
|
||||
b8:8094 0002:0000006b
|
||||
b8:8096 0002:0000006e
|
||||
b8:8099 0002:0000006f
|
||||
b8:809b 0002:00000070
|
||||
b8:809e 0002:00000071
|
||||
b8:80a0 0002:00000072
|
||||
b8:80a3 0002:00000073
|
||||
b8:80a7 0002:00000074
|
||||
b8:80a9 0002:00000075
|
||||
b8:80ab 0002:00000076
|
||||
b8:80ad 0002:00000077
|
||||
b8:80af 0002:00000078
|
||||
b8:80b0 0002:0000007c
|
||||
b8:80b1 0002:0000007c
|
||||
b8:80b2 0002:0000007d
|
||||
b8:80b5 0002:0000007e
|
||||
b8:80b7 0002:0000007f
|
||||
b8:80ba 0002:00000080
|
||||
b8:80bc 0002:00000081
|
||||
b8:80bd 0002:00000082
|
||||
b8:80be 0002:00000084
|
||||
b8:80c1 0002:00000085
|
||||
b8:80c3 0002:00000086
|
||||
b8:80c6 0002:00000087
|
||||
b8:80c7 0002:00000088
|
||||
b8:80ca 0002:00000089
|
||||
b8:80cb 0002:00000089
|
||||
b8:80cc 0002:0000008a
|
||||
b8:80d0 0002:0000008b
|
||||
b8:80d1 0002:0000008c
|
||||
b8:80d4 0002:0000008d
|
||||
b8:80d8 0002:0000008e
|
||||
b8:80da 0002:00000090
|
||||
b8:80dd 0002:00000091
|
||||
b8:80df 0002:00000092
|
||||
b8:80e2 0002:00000093
|
||||
b8:80e4 0002:00000095
|
||||
b8:80e8 0002:00000097
|
||||
b8:80ea 0002:00000098
|
||||
b8:80ec 0002:00000099
|
||||
b8:80ed 0002:00000099
|
||||
b8:80ee 0002:0000009a
|
||||
b8:80ef 0002:000000a5
|
||||
b8:80f0 0002:000000a6
|
||||
b8:80f4 0002:000000a7
|
||||
b8:80f5 0002:000000a8
|
||||
b8:80f9 0002:000000a9
|
||||
b8:80fa 0002:000000ab
|
||||
b8:80fe 0002:000000ac
|
||||
b8:80ff 0002:000000de
|
||||
b8:8100 0002:000000de
|
||||
b8:8101 0002:000000e1
|
||||
b8:8105 0002:000000e2
|
||||
b8:8109 0002:000000e3
|
||||
b8:810b 0002:000000e5
|
||||
b8:810d 0002:000000e5
|
||||
b8:810e 0002:000000e8
|
||||
b8:8112 0002:000000e9
|
||||
b8:8114 0002:000000ea
|
||||
b8:8118 0002:000000eb
|
||||
b8:811a 0002:000000ec
|
||||
b8:811e 0002:000000ed
|
||||
b8:8121 0002:000000ee
|
||||
b8:8123 0002:000000ef
|
||||
b8:8125 0002:000000f0
|
||||
b8:8129 0002:000000f1
|
||||
b8:812b 0002:000000f2
|
||||
b8:812d 0002:000000f3
|
||||
b8:8130 0002:000000f4
|
||||
b8:8133 0002:000000f5
|
||||
b8:8135 0002:000000f6
|
||||
b8:813d 0002:000000fa
|
||||
b8:813e 0002:000000fb
|
||||
b8:813f 0002:000000fc
|
||||
b8:8143 0002:000000ff
|
||||
b8:8147 0002:00000100
|
||||
b8:814b 0002:00000101
|
||||
b8:814d 0002:00000103
|
||||
b8:814e 0002:00000104
|
||||
b8:814f 0002:00000105
|
||||
b8:8151 0002:0000010a
|
||||
b8:8152 0002:0000010b
|
||||
b8:8156 0002:0000010e
|
||||
b8:815a 0002:0000010f
|
||||
b8:815e 0002:00000110
|
||||
b8:8162 0002:00000111
|
||||
b8:8163 0002:00000115
|
||||
b8:8165 0002:00000116
|
||||
b8:8168 0002:00000117
|
||||
b8:816a 0002:00000118
|
||||
b8:816d 0002:0000011b
|
||||
b8:8171 0002:0000011c
|
||||
b8:8172 0002:0000011d
|
||||
b8:8176 0002:0000011f
|
||||
b8:8178 0002:00000122
|
||||
b8:817a 0002:00000123
|
||||
b8:817c 0002:00000124
|
||||
b8:817d 0002:00000124
|
||||
b8:817e 0002:00000125
|
||||
b8:817f 0002:00000129
|
||||
b8:8180 0002:00000129
|
||||
b8:8181 0002:00000129
|
||||
b8:8182 0002:0000012a
|
||||
b8:8186 0002:0000012b
|
||||
b8:8189 0002:0000012b
|
||||
b8:818a 0002:0000012d
|
||||
b8:818e 0002:0000012e
|
||||
b8:818f 0002:0000012f
|
||||
b8:8193 0002:00000130
|
||||
b8:8196 0002:00000131
|
||||
b8:8198 0002:00000133
|
||||
b8:819b 0002:00000136
|
||||
b8:819f 0002:00000137
|
||||
b8:81a3 0002:00000138
|
||||
b8:81a5 0002:0000013a
|
||||
b8:81a9 0002:0000013b
|
||||
b8:81aa 0002:0000013d
|
||||
b8:81ae 0002:0000013e
|
||||
b8:81b0 0002:00000141
|
||||
b8:81b4 0002:00000142
|
||||
b8:81b7 0002:00000143
|
||||
b8:81b9 0002:00000144
|
||||
b8:81bc 0002:00000147
|
||||
b8:81bd 0002:00000148
|
||||
b8:81be 0002:00000149
|
||||
b8:81c2 0002:0000014a
|
||||
b8:81c4 0002:0000014d
|
||||
b8:81c5 0002:0000014d
|
||||
b8:81c6 0002:0000014d
|
||||
b8:81c7 0002:0000014e
|
||||
b8:81c8 0002:00000152
|
||||
b8:81cc 0002:00000153
|
||||
b8:81d0 0002:00000154
|
||||
b8:81d2 0002:00000155
|
||||
b8:81d6 0002:00000156
|
||||
b8:81d9 0002:00000157
|
||||
b8:81db 0002:00000158
|
||||
b8:81de 0002:0000015a
|
||||
b8:81df 0002:0000015d
|
||||
b8:81e3 0002:0000015e
|
||||
b8:81e4 0002:0000015f
|
||||
b8:81e7 0002:00000160
|
||||
b8:81eb 0002:00000162
|
||||
b8:81ec 0002:00000163
|
||||
b8:81ed 0002:00000164
|
||||
b8:81ee 0002:00000165
|
||||
b8:81ef 0002:00000166
|
||||
8b:914a 0002:0000016b
|
||||
81:80f7 0002:0000016e
|
||||
81:8027 0002:00000171
|
||||
82:8bb3 0002:00000174
|
||||
85:b9a3 0002:0000020e
|
||||
85:b9a4 0002:0000020e
|
||||
85:b9a5 0002:00000211
|
||||
85:b9a7 0002:00000212
|
||||
85:b9ad 0002:00000212
|
||||
85:b9ae 0002:00000213
|
||||
85:b9b1 0002:00000214
|
||||
85:b9b2 0002:00000215
|
||||
85:b9b3 0002:00000215
|
||||
85:b9b4 0002:00000219
|
||||
85:b9b7 0002:0000021a
|
||||
85:b9bb 0002:0000021b
|
||||
85:b9bd 0002:0000021b
|
||||
85:b9bf 0002:0000021c
|
||||
85:b9c2 0002:0000021d
|
||||
85:b9c4 0002:0000021f
|
||||
85:b9c5 0002:00000220
|
||||
85:b9c7 0002:00000224
|
||||
85:b9ca 0002:00000226
|
||||
85:b9cd 0002:00000227
|
||||
85:b9cf 0002:00000228
|
||||
85:b9d1 0002:00000229
|
||||
85:b9d5 0002:0000022a
|
||||
85:b9d7 0002:0000022b
|
||||
85:b9d9 0002:0000022c
|
||||
85:b9da 0002:0000022d
|
||||
85:b9dc 0002:0000022f
|
||||
85:b9df 0002:00000231
|
||||
85:b9e2 0002:00000231
|
||||
85:b9e3 0002:00000232
|
||||
85:b9e6 0002:00000234
|
||||
85:b9ea 0002:00000235
|
||||
85:b9ed 0002:00000236
|
||||
85:b9ee 0002:00000237
|
||||
85:b9ef 0002:00000237
|
||||
85:b9f0 0002:00000238
|
||||
85:b9f4 0002:00000239
|
||||
85:b9f5 0002:0000023a
|
||||
85:b9f9 0002:0000023b
|
||||
85:b9fb 0002:0000023c
|
||||
85:b9fc 0002:0000023d
|
||||
85:b9fd 0002:0000023e
|
||||
85:ba00 0002:0000023f
|
||||
85:ba02 0002:00000240
|
||||
85:ba04 0002:00000243
|
||||
85:ba05 0002:00000243
|
||||
85:ba06 0002:00000244
|
||||
85:ba09 0002:00000245
|
||||
85:ba8a 0002:00000253
|
||||
85:ba8c 0002:00000254
|
||||
85:ba8f 0002:00000255
|
||||
85:ba92 0002:00000256
|
||||
85:ba95 0002:0000025e
|
||||
85:ba96 0002:0000025f
|
||||
85:ba98 0002:00000260
|
||||
85:ba9b 0002:00000261
|
||||
85:ba9d 0002:00000262
|
||||
85:ba9f 0002:00000263
|
||||
85:baa2 0002:00000264
|
||||
85:baa4 0002:00000265
|
||||
85:baa7 0002:00000266
|
||||
85:baa9 0002:00000269
|
||||
85:baaa 0002:0000026a
|
||||
85:baab 0002:0000026b
|
||||
85:baac 0002:0000026c
|
||||
85:baae 0002:0000026d
|
||||
85:baaf 0002:0000026e
|
||||
85:bab0 0002:0000026f
|
||||
85:bab1 0002:00000274
|
||||
85:bab4 0002:00000275
|
||||
85:bab5 0002:00000276
|
||||
85:bab8 0002:00000277
|
||||
85:bab9 0002:00000278
|
||||
85:baba 0002:00000279
|
||||
85:babb 0002:0000027a
|
||||
85:babc 0002:00000285
|
||||
85:babd 0002:00000286
|
||||
85:babf 0002:00000287
|
||||
85:bac2 0002:00000288
|
||||
85:bac4 0002:00000289
|
||||
85:bac7 0002:0000028a
|
||||
85:bac9 0002:0000028d
|
||||
85:baca 0002:0000028e
|
||||
85:bacb 0002:0000028f
|
||||
85:bacd 0002:00000290
|
||||
85:bace 0002:00000292
|
||||
85:bacf 0002:00000293
|
||||
85:bad1 0002:00000294
|
||||
85:bad4 0002:00000295
|
||||
85:bad6 0002:00000296
|
||||
85:bad9 0002:00000297
|
||||
85:badb 0002:00000298
|
||||
85:badc 0002:0000029a
|
||||
85:badd 0002:0000029b
|
||||
85:badf 0002:0000029c
|
||||
85:bae2 0002:0000029d
|
||||
85:bae4 0002:0000029e
|
||||
85:bae7 0002:0000029f
|
||||
85:bae9 0002:000002a0
|
||||
85:8246 0002:000002a5
|
||||
85:8249 0002:000002a6
|
||||
85:824b 0002:000002a7
|
||||
85:82f9 0002:000002ab
|
||||
b8:8300 0002:0000005a
|
||||
b8:8302 0002:0000005b
|
||||
b8:8306 0002:0000005c
|
||||
b8:8308 0002:0000005d
|
||||
b8:830c 0002:00000061
|
||||
b8:830e 0002:00000062
|
||||
b8:8310 0002:00000063
|
||||
b8:8311 0002:00000066
|
||||
b8:8312 0002:00000066
|
||||
b8:8313 0002:00000066
|
||||
b8:8314 0002:00000066
|
||||
b8:8315 0000:00000013
|
||||
b8:8317 0002:0000006a
|
||||
b8:831b 0002:0000006b
|
||||
b8:831e 0002:0000006c
|
||||
b8:8320 0002:0000006d
|
||||
b8:8324 0002:0000006e
|
||||
b8:8328 0002:0000006f
|
||||
b8:832a 0002:00000070
|
||||
b8:832e 0002:00000071
|
||||
b8:8332 0002:00000072
|
||||
b8:8334 0002:00000074
|
||||
b8:8338 0002:00000075
|
||||
b8:833b 0002:00000078
|
||||
b8:833c 0002:00000079
|
||||
b8:833f 0002:0000007a
|
||||
b8:8342 0002:0000007b
|
||||
b8:8345 0002:0000007c
|
||||
b8:8348 0002:0000007d
|
||||
b8:8349 0002:0000007e
|
||||
b8:834a 0002:0000007f
|
||||
b8:834e 0002:00000080
|
||||
b8:834f 0002:00000082
|
||||
b8:8366 0002:00000086
|
||||
b8:8368 0002:00000087
|
||||
b8:8369 0002:00000088
|
||||
b8:836a 0002:00000089
|
||||
b8:836c 0002:0000008a
|
||||
b8:836e 0002:0000008b
|
||||
b8:8370 0002:0000008c
|
||||
b8:8372 0002:0000008d
|
||||
b8:8375 0002:0000008e
|
||||
b8:8377 0002:0000008f
|
||||
b8:837a 0002:00000090
|
||||
b8:837d 0002:00000091
|
||||
b8:837f 0002:00000092
|
||||
b8:8383 0002:00000094
|
||||
b8:8385 0002:00000095
|
||||
b8:8387 0002:00000096
|
||||
b8:8389 0002:00000097
|
||||
b8:838b 0002:00000098
|
||||
b8:838d 0002:00000099
|
||||
b8:838f 0002:0000009a
|
||||
b8:8392 0002:0000009b
|
||||
b8:8394 0002:0000009c
|
||||
b8:8397 0002:0000009d
|
||||
b8:839a 0002:0000009e
|
||||
b8:839c 0002:0000009f
|
||||
b8:83a0 0002:000000a1
|
||||
b8:83a3 0002:000000a2
|
||||
b8:83a7 0002:000000a3
|
||||
b8:83ab 0002:000000a4
|
||||
b8:83af 0002:000000a5
|
||||
b8:83b3 0002:000000a6
|
||||
b8:83b7 0002:000000a8
|
||||
b8:83bb 0002:000000b0
|
||||
b8:83be 0002:000000b1
|
||||
b8:83c1 0002:000000b3
|
||||
b8:83c2 0002:000000b4
|
||||
b8:83c3 0002:000000b5
|
||||
b8:83c7 0002:000000b6
|
||||
b8:83cb 0002:000000b7
|
||||
b8:83cd 0002:000000c4
|
||||
b8:83d1 0002:000000c5
|
||||
b8:83d4 0002:000000c6
|
||||
b8:83d6 0002:000000c7
|
||||
b8:83da 0002:000000c8
|
||||
b8:83dd 0002:000000c9
|
||||
b8:83df 0002:000000ce
|
||||
b8:83e2 0002:000000cf
|
||||
b8:83e6 0002:000000d0
|
||||
b8:83ea 0002:000000d3
|
||||
b8:83eb 0002:000000d3
|
||||
b8:83ec 0002:000000d3
|
||||
b8:83ed 0002:000000d3
|
||||
b8:83ee 0002:000000d4
|
||||
b8:83ef 0002:000000db
|
||||
b8:83f0 0002:000000dc
|
||||
b8:83f1 0002:000000dd
|
||||
b8:83f2 0002:000000de
|
||||
b8:83f3 0002:000000df
|
||||
b8:83f4 0002:000000e1
|
||||
b8:83f7 0002:000000e2
|
||||
b8:83f8 0002:000000e3
|
||||
b8:83f9 0002:000000e4
|
||||
b8:83fa 0002:000000e5
|
||||
b8:83fc 0002:000000e7
|
||||
b8:83fd 0002:000000ee
|
||||
b8:83fe 0002:000000ef
|
||||
b8:83ff 0002:000000f0
|
||||
b8:8400 0002:000000f1
|
||||
b8:8402 0002:000000f3
|
||||
b8:8404 0002:000000f4
|
||||
b8:8405 0002:000000f5
|
||||
b8:8407 0002:000000f6
|
||||
b8:8409 0002:000000f8
|
||||
b8:840b 0002:000000f9
|
||||
b8:840c 0002:000000fa
|
||||
b8:840d 0002:000000fb
|
||||
b8:840f 0002:000000fd
|
||||
b8:8411 0002:000000fe
|
||||
b8:8413 0002:000000ff
|
||||
b8:8414 0002:00000100
|
||||
b8:8415 0002:00000101
|
||||
b8:8417 0002:00000103
|
||||
b8:8418 0002:00000104
|
||||
b8:8419 0002:00000108
|
||||
b8:841d 0002:00000109
|
||||
b8:8421 0002:0000010a
|
||||
b8:8425 0002:0000010b
|
||||
b8:8429 0002:0000010c
|
||||
b8:842d 0002:0000010d
|
||||
b8:8431 0002:0000010e
|
||||
b8:8435 0002:0000010f
|
||||
b8:8439 0002:00000110
|
||||
b8:843d 0002:00000111
|
||||
b8:8441 0002:00000112
|
||||
b8:8442 0002:00000118
|
||||
b8:8443 0002:00000118
|
||||
b8:8444 0002:00000119
|
||||
b8:8445 0002:00000119
|
||||
b8:8446 0002:0000011a
|
||||
b8:844a 0002:0000011b
|
||||
b8:844d 0002:0000011b
|
||||
b8:844e 0002:0000011c
|
||||
b8:844f 0002:0000011d
|
||||
b8:8453 0002:0000011e
|
||||
b8:8454 0002:0000011f
|
||||
b8:8458 0002:00000120
|
||||
b8:8459 0002:00000121
|
||||
b8:845d 0002:00000123
|
||||
b8:8461 0002:00000124
|
||||
b8:8462 0002:00000125
|
||||
b8:8466 0002:00000126
|
||||
b8:8467 0002:00000126
|
||||
b8:8468 0002:00000127
|
||||
b8:8469 0002:0000012c
|
||||
b8:846a 0002:0000012c
|
||||
b8:846b 0000:00000013
|
||||
b8:846d 0002:0000012f
|
||||
b8:846e 0002:0000012f
|
||||
b8:846f 0002:00000131
|
||||
b8:8470 0002:00000132
|
||||
b8:8473 0002:00000133
|
||||
b8:8474 0002:00000138
|
||||
b8:8475 0002:00000138
|
||||
b8:8476 0000:00000013
|
||||
b8:8478 0002:0000013a
|
||||
b8:847c 0002:0000013b
|
||||
b8:8480 0002:0000013c
|
||||
b8:8482 0002:0000013e
|
||||
b8:8483 0002:0000013e
|
||||
b8:8484 0002:0000013f
|
||||
b8:8485 0002:00000147
|
||||
b8:8489 0002:00000148
|
||||
b8:848b 0002:0000014e
|
||||
b8:848d 0002:0000014f
|
||||
b8:848f 0002:00000152
|
||||
b8:8492 0002:00000153
|
||||
b8:8494 0002:00000154
|
||||
b8:8497 0002:00000155
|
||||
b8:8499 0002:00000156
|
||||
b8:849c 0002:00000157
|
||||
b8:84a0 0002:00000158
|
||||
b8:84a2 0002:00000159
|
||||
b8:84a4 0002:0000015a
|
||||
b8:84a6 0002:0000015b
|
||||
b8:84a8 0002:0000015c
|
||||
b8:84a9 0002:00000160
|
||||
b8:84aa 0002:00000160
|
||||
b8:84ab 0002:00000161
|
||||
b8:84ae 0002:00000162
|
||||
b8:84b0 0002:00000163
|
||||
b8:84b3 0002:00000164
|
||||
b8:84b5 0002:00000165
|
||||
b8:84b6 0002:00000166
|
||||
b8:84b7 0002:00000168
|
||||
b8:84ba 0002:00000169
|
||||
b8:84bc 0002:0000016a
|
||||
b8:84bf 0002:0000016b
|
||||
b8:84c0 0002:0000016c
|
||||
b8:84c3 0002:0000016d
|
||||
b8:84c4 0002:0000016d
|
||||
b8:84c5 0002:0000016e
|
||||
b8:84c9 0002:0000016f
|
||||
b8:84ca 0002:00000170
|
||||
b8:84cd 0002:00000171
|
||||
b8:84d1 0002:00000172
|
||||
b8:84d3 0002:00000174
|
||||
b8:84d6 0002:00000175
|
||||
b8:84d8 0002:00000176
|
||||
b8:84db 0002:00000177
|
||||
b8:84dd 0002:00000179
|
||||
b8:84e1 0002:0000017b
|
||||
b8:84e3 0002:0000017c
|
||||
b8:84e5 0002:0000017d
|
||||
b8:84e6 0002:0000017d
|
||||
b8:84e7 0002:0000017e
|
||||
b8:84e8 0002:00000189
|
||||
b8:84e9 0002:0000018a
|
||||
b8:84ed 0002:0000018b
|
||||
b8:84ee 0002:0000018c
|
||||
b8:84f2 0002:0000018d
|
||||
b8:84f3 0002:0000018f
|
||||
b8:84f7 0002:00000190
|
||||
b8:84f8 0002:000001c2
|
||||
b8:84f9 0002:000001c2
|
||||
b8:84fa 0002:000001c5
|
||||
b8:84fe 0002:000001c6
|
||||
b8:8502 0002:000001c7
|
||||
b8:8504 0002:000001c9
|
||||
b8:8506 0002:000001c9
|
||||
b8:8507 0002:000001cc
|
||||
b8:850b 0002:000001cd
|
||||
b8:850d 0002:000001ce
|
||||
b8:8511 0002:000001cf
|
||||
b8:8513 0002:000001d0
|
||||
b8:8517 0002:000001d1
|
||||
b8:851a 0002:000001d2
|
||||
b8:851c 0002:000001d3
|
||||
b8:851e 0002:000001d4
|
||||
b8:8522 0002:000001d5
|
||||
b8:8524 0002:000001d6
|
||||
b8:8526 0002:000001d7
|
||||
b8:8529 0002:000001d8
|
||||
b8:852c 0002:000001d9
|
||||
b8:852e 0002:000001da
|
||||
b8:8536 0002:000001de
|
||||
b8:8537 0002:000001df
|
||||
b8:8538 0002:000001e0
|
||||
b8:853c 0002:000001e3
|
||||
b8:8540 0002:000001e4
|
||||
b8:8544 0002:000001e5
|
||||
b8:8546 0002:000001e7
|
||||
b8:8547 0002:000001e8
|
||||
b8:8548 0002:000001e9
|
||||
b8:854a 0002:000001ee
|
||||
b8:854b 0002:000001ef
|
||||
b8:854f 0002:000001f2
|
||||
b8:8553 0002:000001f3
|
||||
b8:8557 0002:000001f4
|
||||
b8:855b 0002:000001f5
|
||||
b8:855c 0002:000001f9
|
||||
b8:855e 0002:000001fa
|
||||
b8:8561 0002:000001fb
|
||||
b8:8563 0002:000001fc
|
||||
b8:8566 0002:000001ff
|
||||
b8:856a 0002:00000200
|
||||
b8:856b 0002:00000201
|
||||
b8:856f 0002:00000203
|
||||
b8:8571 0002:00000206
|
||||
b8:8573 0002:00000207
|
||||
b8:8575 0002:00000208
|
||||
b8:8576 0002:00000208
|
||||
b8:8577 0002:00000209
|
||||
b8:8578 0002:0000020d
|
||||
b8:8579 0002:0000020d
|
||||
b8:857a 0002:0000020d
|
||||
b8:857b 0002:0000020e
|
||||
b8:857f 0002:0000020f
|
||||
b8:8582 0002:0000020f
|
||||
b8:8583 0002:00000211
|
||||
b8:8587 0002:00000212
|
||||
b8:8588 0002:00000213
|
||||
b8:858c 0002:00000214
|
||||
b8:858f 0002:00000215
|
||||
b8:8591 0002:00000217
|
||||
b8:8594 0002:0000021a
|
||||
b8:8598 0002:0000021b
|
||||
b8:859c 0002:0000021c
|
||||
b8:859e 0002:0000021e
|
||||
b8:85a2 0002:0000021f
|
||||
b8:85a3 0002:00000221
|
||||
b8:85a7 0002:00000222
|
||||
b8:85a9 0002:00000225
|
||||
b8:85ad 0002:00000226
|
||||
b8:85b0 0002:00000227
|
||||
b8:85b2 0002:00000228
|
||||
b8:85b5 0002:0000022b
|
||||
b8:85b6 0002:0000022c
|
||||
b8:85b7 0002:0000022d
|
||||
b8:85bb 0002:0000022e
|
||||
b8:85bd 0002:00000231
|
||||
b8:85be 0002:00000231
|
||||
b8:85bf 0002:00000231
|
||||
b8:85c0 0002:00000232
|
||||
b8:85c1 0002:00000236
|
||||
b8:85c5 0002:00000237
|
||||
b8:85c9 0002:00000238
|
||||
b8:85cb 0002:00000239
|
||||
b8:85cf 0002:0000023a
|
||||
b8:85d2 0002:0000023b
|
||||
b8:85d4 0002:0000023c
|
||||
b8:85d7 0002:0000023e
|
||||
b8:85d8 0002:00000241
|
||||
b8:85dc 0002:00000243
|
||||
b8:85dd 0002:00000244
|
||||
b8:85de 0002:00000245
|
||||
b8:85df 0002:00000246
|
||||
b8:85e0 0002:00000247
|
||||
8b:914a 0002:0000024c
|
||||
81:80f7 0002:0000024f
|
||||
81:8027 0002:00000252
|
||||
82:8bb3 0002:00000255
|
||||
85:b9a3 0002:000002ef
|
||||
85:b9a4 0002:000002ef
|
||||
85:b9a5 0002:000002f2
|
||||
85:b9a7 0002:000002f3
|
||||
85:b9ad 0002:000002f3
|
||||
85:b9ae 0002:000002f4
|
||||
85:b9b1 0002:000002f5
|
||||
85:b9b2 0002:000002f6
|
||||
85:b9b3 0002:000002f6
|
||||
85:b9b4 0002:000002fa
|
||||
85:b9b7 0002:000002fb
|
||||
85:b9bb 0002:000002fc
|
||||
85:b9bd 0002:000002fc
|
||||
85:b9bf 0002:000002fd
|
||||
85:b9c2 0002:000002fe
|
||||
85:b9c4 0002:00000300
|
||||
85:b9c5 0002:00000301
|
||||
85:b9c7 0002:00000305
|
||||
85:b9ca 0002:00000307
|
||||
85:b9cd 0002:00000308
|
||||
85:b9cf 0002:00000309
|
||||
85:b9d1 0002:0000030a
|
||||
85:b9d5 0002:0000030b
|
||||
85:b9d7 0002:0000030c
|
||||
85:b9d9 0002:0000030d
|
||||
85:b9da 0002:0000030e
|
||||
85:b9dc 0002:00000310
|
||||
85:b9df 0002:00000312
|
||||
85:b9e2 0002:00000312
|
||||
85:b9e3 0002:00000313
|
||||
85:b9e6 0002:00000315
|
||||
85:b9ea 0002:00000316
|
||||
85:b9ed 0002:00000317
|
||||
85:b9ee 0002:00000318
|
||||
85:b9ef 0002:00000318
|
||||
85:b9f0 0002:00000319
|
||||
85:b9f4 0002:0000031a
|
||||
85:b9f5 0002:0000031b
|
||||
85:b9f9 0002:0000031c
|
||||
85:b9fb 0002:0000031d
|
||||
85:b9fc 0002:0000031e
|
||||
85:b9fd 0002:0000031f
|
||||
85:ba00 0002:00000320
|
||||
85:ba02 0002:00000321
|
||||
85:ba04 0002:00000324
|
||||
85:ba05 0002:00000324
|
||||
85:ba06 0002:00000325
|
||||
85:ba09 0002:00000326
|
||||
85:ba8a 0002:00000334
|
||||
85:ba8c 0002:00000335
|
||||
85:ba8f 0002:00000336
|
||||
85:ba92 0002:00000337
|
||||
85:ba95 0002:0000033f
|
||||
85:ba96 0002:00000340
|
||||
85:ba98 0002:00000341
|
||||
85:ba9b 0002:00000342
|
||||
85:ba9d 0002:00000343
|
||||
85:ba9f 0002:00000344
|
||||
85:baa2 0002:00000345
|
||||
85:baa4 0002:00000346
|
||||
85:baa7 0002:00000347
|
||||
85:baa9 0002:0000034a
|
||||
85:baaa 0002:0000034b
|
||||
85:baab 0002:0000034c
|
||||
85:baac 0002:0000034d
|
||||
85:baae 0002:0000034e
|
||||
85:baaf 0002:0000034f
|
||||
85:bab0 0002:00000350
|
||||
85:bab1 0002:00000355
|
||||
85:bab4 0002:00000356
|
||||
85:bab5 0002:00000357
|
||||
85:bab8 0002:00000358
|
||||
85:bab9 0002:00000359
|
||||
85:baba 0002:0000035a
|
||||
85:babb 0002:0000035b
|
||||
85:babc 0002:00000366
|
||||
85:babd 0002:00000367
|
||||
85:babf 0002:00000368
|
||||
85:bac2 0002:00000369
|
||||
85:bac4 0002:0000036a
|
||||
85:bac7 0002:0000036b
|
||||
85:bac9 0002:0000036e
|
||||
85:baca 0002:0000036f
|
||||
85:bacb 0002:00000370
|
||||
85:bacd 0002:00000371
|
||||
85:bace 0002:00000373
|
||||
85:bacf 0002:00000374
|
||||
85:bad1 0002:00000375
|
||||
85:bad4 0002:00000376
|
||||
85:bad6 0002:00000377
|
||||
85:bad9 0002:00000378
|
||||
85:badb 0002:00000379
|
||||
85:badc 0002:0000037b
|
||||
85:badd 0002:0000037c
|
||||
85:badf 0002:0000037d
|
||||
85:bae2 0002:0000037e
|
||||
85:bae4 0002:0000037f
|
||||
85:bae7 0002:00000380
|
||||
85:bae9 0002:00000381
|
||||
85:8246 0002:00000386
|
||||
85:8249 0002:00000387
|
||||
85:824b 0002:00000388
|
||||
85:82f9 0002:0000038c
|
||||
b8:885c 0003:00000045
|
||||
b8:885d 0003:00000045
|
||||
b8:885e 0003:00000046
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"CLIPLEN_end": "85:990F",
|
||||
"CLIPLEN_no_multi": "85:990C",
|
||||
"CLIPSET": "85:FF1D",
|
||||
"COLLECTTANK": "B8:80EF",
|
||||
"COLLECTTANK": "B8:84E8",
|
||||
"MISCFX": "85:FF45",
|
||||
"NORMAL": "84:8BF2",
|
||||
"SETFX": "85:FF4E",
|
||||
@@ -22,6 +22,11 @@
|
||||
"config_player_id": "CE:FF08",
|
||||
"config_remote_items": "CE:FF06",
|
||||
"config_sprite": "CE:FF02",
|
||||
"copy_config_to_sram": "B8:8419",
|
||||
"copy_memory": "B8:83FD",
|
||||
"copy_memory_done": "B8:8417",
|
||||
"copy_memory_even": "B8:8409",
|
||||
"copy_memory_loop": "B8:840F",
|
||||
"h_item": "84:F894",
|
||||
"i_chozo_item": "84:F8AD",
|
||||
"i_hidden_item": "84:F8B4",
|
||||
@@ -30,11 +35,11 @@
|
||||
"i_item_setup_shared_all_items": "B8:8878",
|
||||
"i_item_setup_shared_alwaysloaded": "B8:8883",
|
||||
"i_live_pickup": "84:FA79",
|
||||
"i_live_pickup_multiworld": "B8:817F",
|
||||
"i_live_pickup_multiworld_end": "B8:81C4",
|
||||
"i_live_pickup_multiworld_local_item_or_offworld": "B8:819B",
|
||||
"i_live_pickup_multiworld_own_item": "B8:81B0",
|
||||
"i_live_pickup_multiworld_own_item1": "B8:81BC",
|
||||
"i_live_pickup_multiworld": "B8:8578",
|
||||
"i_live_pickup_multiworld_end": "B8:85BD",
|
||||
"i_live_pickup_multiworld_local_item_or_offworld": "B8:8594",
|
||||
"i_live_pickup_multiworld_own_item": "B8:85A9",
|
||||
"i_live_pickup_multiworld_own_item1": "B8:85B5",
|
||||
"i_load_custom_graphics": "84:FA1E",
|
||||
"i_load_custom_graphics_all_items": "84:FA39",
|
||||
"i_load_custom_graphics_alwaysloaded": "84:FA49",
|
||||
@@ -69,22 +74,27 @@
|
||||
"message_write_placeholders_loop": "85:B9CA",
|
||||
"message_write_placeholders_notfound": "85:B9DC",
|
||||
"message_write_placeholders_value_ok": "85:B9DF",
|
||||
"mw_display_item_sent": "B8:8092",
|
||||
"mw_handle_queue": "B8:80FF",
|
||||
"mw_handle_queue_end": "B8:8178",
|
||||
"mw_handle_queue_loop": "B8:8101",
|
||||
"mw_handle_queue_new_remote_item": "B8:8151",
|
||||
"mw_handle_queue_next": "B8:816D",
|
||||
"mw_handle_queue_perform_receive": "B8:8163",
|
||||
"mw_hook_main_game": "B8:81C8",
|
||||
"mw_init": "B8:8011",
|
||||
"mw_init_end": "B8:8044",
|
||||
"mw_init_memory": "B8:8000",
|
||||
"mw_load_sram": "B8:8083",
|
||||
"mw_receive_item": "B8:80B0",
|
||||
"mw_receive_item_end": "B8:80E8",
|
||||
"mw_save_sram": "B8:8070",
|
||||
"mw_write_message": "B8:8049",
|
||||
"mw_display_item_sent": "B8:848B",
|
||||
"mw_handle_queue": "B8:84F8",
|
||||
"mw_handle_queue_end": "B8:8571",
|
||||
"mw_handle_queue_loop": "B8:84FA",
|
||||
"mw_handle_queue_new_remote_item": "B8:854A",
|
||||
"mw_handle_queue_next": "B8:8566",
|
||||
"mw_handle_queue_perform_receive": "B8:855C",
|
||||
"mw_hook_main_game": "B8:85C1",
|
||||
"mw_init": "B8:8311",
|
||||
"mw_init_continuereset": "B8:8366",
|
||||
"mw_init_end": "B8:83EA",
|
||||
"mw_init_memory": "B8:8300",
|
||||
"mw_init_reset_sram": "B8:833B",
|
||||
"mw_init_smstringdata": "B8:8351",
|
||||
"mw_load_sram": "B8:8474",
|
||||
"mw_load_sram_done": "B8:8482",
|
||||
"mw_load_sram_setnewgame": "B8:8485",
|
||||
"mw_receive_item": "B8:84A9",
|
||||
"mw_receive_item_end": "B8:84E1",
|
||||
"mw_save_sram": "B8:8469",
|
||||
"mw_write_message": "B8:8442",
|
||||
"nonprog_item_eight_palette_indices": "84:F888",
|
||||
"offworld_graphics_data_item": "89:9200",
|
||||
"offworld_graphics_data_progression_item": "89:9100",
|
||||
@@ -109,7 +119,7 @@
|
||||
"p_visible_item_end": "84:F96E",
|
||||
"p_visible_item_loop": "84:F95B",
|
||||
"p_visible_item_trigger": "84:F967",
|
||||
"patch_load_multiworld": "B8:81DF",
|
||||
"patch_load_multiworld": "B8:85D8",
|
||||
"perform_item_pickup": "84:FA7E",
|
||||
"plm_graphics_entry_offworld_item": "84:F886",
|
||||
"plm_graphics_entry_offworld_progression_item": "84:F87C",
|
||||
@@ -128,14 +138,24 @@
|
||||
"start_item_data_reserve": "B8:C818",
|
||||
"update_graphic": "B8:C856",
|
||||
"v_item": "84:F890",
|
||||
"write_repeated_memory": "B8:83EF",
|
||||
"write_repeated_memory_loop": "B8:83F4",
|
||||
"ITEM_RAM": "7E:09A2",
|
||||
"SRAM_MW_ITEMS_RECV": "70:2000",
|
||||
"SRAM_MW_ITEMS_RECV_RPTR": "70:2600",
|
||||
"SRAM_MW_ITEMS_RECV_WPTR": "70:2602",
|
||||
"SRAM_MW_ITEMS_RECV_SPTR": "70:2604",
|
||||
"SRAM_MW_ITEMS_SENT_RPTR": "70:2680",
|
||||
"SRAM_MW_ITEMS_SENT_WPTR": "70:2682",
|
||||
"SRAM_MW_ITEMS_RECV_WCOUNT": "70:2602",
|
||||
"ReceiveQueueCompletedCount_InRamThatGetsSavedToSaveSlot": "7e:d8ae",
|
||||
"SRAM_MW_ITEMS_SENT_RCOUNT": "70:2680",
|
||||
"SRAM_MW_ITEMS_SENT_WCOUNT": "70:2682",
|
||||
"SRAM_MW_ITEMS_SENT": "70:2700",
|
||||
"SRAM_MW_INITIALIZED": "70:26fe",
|
||||
"SRAM_MW_SM": "70:3000",
|
||||
"SRAM_MW_ROMTITLE": "70:3015",
|
||||
"SRAM_MW_SEEDINT": "70:3060",
|
||||
"SRAM_MW_INITIALIZED": "70:3064",
|
||||
"SRAM_MW_CONFIG_ENABLED": "70:3070",
|
||||
"SRAM_MW_CONFIG_CUSTOM_SPRITE": "70:3072",
|
||||
"SRAM_MW_CONFIG_DEATHLINK": "70:3074",
|
||||
"SRAM_MW_CONFIG_REMOTE_ITEMS": "70:3076",
|
||||
"SRAM_MW_CONFIG_PLAYER_ID": "70:3078",
|
||||
"varia_seedint_location": "df:ff00",
|
||||
"CollectedItems": "7E:D86E"
|
||||
}
|
||||
@@ -294,7 +294,18 @@ accessPoints = [
|
||||
sm.canGetBackFromRidleyZone(),
|
||||
sm.canPassWastelandDessgeegas(),
|
||||
sm.canPassRedKiHunters())),
|
||||
'RidleyRoomOut': Cache.ldeco(lambda sm: sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']))
|
||||
'RidleyRoomOut': Cache.ldeco(lambda sm: sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main'])),
|
||||
'Wasteland': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']),
|
||||
sm.canGetBackFromRidleyZone(),
|
||||
sm.canPassWastelandDessgeegas()))
|
||||
}, internal=True),
|
||||
AccessPoint('Wasteland', 'LowerNorfair', {
|
||||
# no transition to firefleas to exlude pb of shame location when starting at firefleas top
|
||||
'Ridley Zone': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']),
|
||||
sm.traverse('WastelandLeft'),
|
||||
sm.canGetBackFromRidleyZone(),
|
||||
sm.canPassWastelandDessgeegas(),
|
||||
sm.canPassNinjaPirates()))
|
||||
}, internal=True),
|
||||
AccessPoint('Three Muskateers Room Left', 'LowerNorfair', {
|
||||
'Firefleas': Cache.ldeco(lambda sm: sm.wand(sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main']),
|
||||
|
||||
@@ -797,10 +797,10 @@ locationsDict["Power Bomb (lower Norfair above fire flea room)"].Available = (
|
||||
lambda sm: SMBool(True)
|
||||
)
|
||||
locationsDict["Power Bomb (Power Bombs of shame)"].AccessFrom = {
|
||||
'Ridley Zone': lambda sm: sm.canUsePowerBombs()
|
||||
'Wasteland': lambda sm: sm.canUsePowerBombs()
|
||||
}
|
||||
locationsDict["Power Bomb (Power Bombs of shame)"].Available = (
|
||||
lambda sm: sm.canHellRun(**Settings.hellRunsTable['LowerNorfair']['Main'])
|
||||
lambda sm: SMBool(True)
|
||||
)
|
||||
locationsDict["Missile (lower Norfair near Wave Beam)"].AccessFrom = {
|
||||
'Firefleas': lambda sm: SMBool(True)
|
||||
|
||||
@@ -46,6 +46,9 @@ locCCM_table = {
|
||||
"CCM: Snowman's Lost His Head": 3626025,
|
||||
"CCM: Wall Kicks Will Work": 3626026,
|
||||
"CCM: Bob-omb Buddy": 3626203,
|
||||
"CCM: 1Up Block Near Snowman": 3626215,
|
||||
"CCM: 1Up Block Ice Pillar": 3626216,
|
||||
"CCM: 1Up Block Secret Slide": 3626217
|
||||
}
|
||||
|
||||
#Big Boo's Haunt
|
||||
@@ -55,7 +58,8 @@ locBBH_table = {
|
||||
"BBH: Secret of the Haunted Books": 3626030,
|
||||
"BBH: Seek the 8 Red Coins": 3626031,
|
||||
"BBH: Big Boo's Balcony": 3626032,
|
||||
"BBH: Eye to Eye in the Secret Room": 3626033
|
||||
"BBH: Eye to Eye in the Secret Room": 3626033,
|
||||
"BBH: 1Up Block Top of Mansion": 3626218
|
||||
}
|
||||
|
||||
#Hazy Maze Cave
|
||||
@@ -65,7 +69,9 @@ locHMC_table = {
|
||||
"HMC: Metal-Head Mario Can Move!": 3626037,
|
||||
"HMC: Navigating the Toxic Maze": 3626038,
|
||||
"HMC: A-Maze-Ing Emergency Exit": 3626039,
|
||||
"HMC: Watch for Rolling Rocks": 3626040
|
||||
"HMC: Watch for Rolling Rocks": 3626040,
|
||||
"HMC: 1Up Block above Pit": 3626219,
|
||||
"HMC: 1Up Block Past Rolling Rocks": 3626220,
|
||||
}
|
||||
|
||||
#Lethal Lava Land
|
||||
@@ -87,6 +93,9 @@ locSSL_table = {
|
||||
"SSL: Free Flying for 8 Red Coins": 3626053,
|
||||
"SSL: Pyramid Puzzle": 3626054,
|
||||
"SSL: Bob-omb Buddy": 3626207,
|
||||
"SSL: 1Up Block Outside Pyramid": 3626221,
|
||||
"SSL: 1Up Block Pyramid Left Path": 3626222,
|
||||
"SSL: 1Up Block Pyramid Back": 3626223
|
||||
}
|
||||
|
||||
#Dire, Dire Docks
|
||||
@@ -108,6 +117,8 @@ locSL_table = {
|
||||
"SL: Shell Shreddin' for Red Coins": 3626067,
|
||||
"SL: Into the Igloo": 3626068,
|
||||
"SL: Bob-omb Buddy": 3626209,
|
||||
"SL: 1Up Block Near Moneybags": 3626224,
|
||||
"SL: 1Up Block inside Igloo": 3626225
|
||||
}
|
||||
|
||||
#Wet-Dry World
|
||||
@@ -119,6 +130,7 @@ locWDW_table = {
|
||||
"WDW: Go to Town for Red Coins": 3626074,
|
||||
"WDW: Quick Race Through Downtown!": 3626075,
|
||||
"WDW: Bob-omb Buddy": 3626210,
|
||||
"WDW: 1Up Block in Downtown": 3626226
|
||||
}
|
||||
|
||||
#Tall, Tall Mountain
|
||||
@@ -130,6 +142,7 @@ locTTM_table = {
|
||||
"TTM: Breathtaking View from Bridge": 3626081,
|
||||
"TTM: Blast to the Lonely Mushroom": 3626082,
|
||||
"TTM: Bob-omb Buddy": 3626211,
|
||||
"TTM: 1Up Block on Red Mushroom": 3626227
|
||||
}
|
||||
|
||||
#Tiny-Huge Island
|
||||
@@ -141,6 +154,9 @@ locTHI_table = {
|
||||
"THI: Wiggler's Red Coins": 3626088,
|
||||
"THI: Make Wiggler Squirm": 3626089,
|
||||
"THI: Bob-omb Buddy": 3626212,
|
||||
"THI: 1Up Block THI Small near Start": 3626228,
|
||||
"THI: 1Up Block THI Large near Start": 3626229,
|
||||
"THI: 1Up Block Windy Area": 3626230
|
||||
}
|
||||
|
||||
#Tick Tock Clock
|
||||
@@ -150,7 +166,9 @@ locTTC_table = {
|
||||
"TTC: Get a Hand": 3626093,
|
||||
"TTC: Stomp on the Thwomp": 3626094,
|
||||
"TTC: Timed Jumps on Moving Bars": 3626095,
|
||||
"TTC: Stop Time for Red Coins": 3626096
|
||||
"TTC: Stop Time for Red Coins": 3626096,
|
||||
"TTC: 1Up Block Midway Up": 3626231,
|
||||
"TTC: 1Up Block at the Top": 3626232
|
||||
}
|
||||
|
||||
#Rainbow Ride
|
||||
@@ -162,6 +180,9 @@ locRR_table = {
|
||||
"RR: Tricky Triangles!": 3626102,
|
||||
"RR: Somewhere Over the Rainbow": 3626103,
|
||||
"RR: Bob-omb Buddy": 3626214,
|
||||
"RR: 1Up Block Top of Red Coin Maze": 3626233,
|
||||
"RR: 1Up Block Under Fly Guy": 3626234,
|
||||
"RR: 1Up Block On House in the Sky": 3626235
|
||||
}
|
||||
|
||||
loc100Coin_table = {
|
||||
@@ -193,7 +214,9 @@ locSA_table = {
|
||||
|
||||
locBitDW_table = {
|
||||
"Bowser in the Dark World Red Coins": 3626105,
|
||||
"Bowser in the Dark World Key": 3626178
|
||||
"Bowser in the Dark World Key": 3626178,
|
||||
"Bowser in the Dark World 1Up Block on Tower": 3626236,
|
||||
"Bowser in the Dark World 1Up Block near Goombas": 3626237
|
||||
}
|
||||
|
||||
locTotWC_table = {
|
||||
@@ -203,25 +226,31 @@ locTotWC_table = {
|
||||
|
||||
locCotMC_table = {
|
||||
"Cavern of the Metal Cap Switch": 3626182,
|
||||
"Cavern of the Metal Cap Red Coins": 3626133
|
||||
"Cavern of the Metal Cap Red Coins": 3626133,
|
||||
"Cavern of the Metal Cap 1Up Block": 3626241
|
||||
}
|
||||
|
||||
locVCutM_table = {
|
||||
"Vanish Cap Under the Moat Switch": 3626183,
|
||||
"Vanish Cap Under the Moat Red Coins": 3626147
|
||||
"Vanish Cap Under the Moat Red Coins": 3626147,
|
||||
"Vanish Cap Under the Moat 1Up Block": 3626242
|
||||
}
|
||||
|
||||
locBitFS_table = {
|
||||
"Bowser in the Fire Sea Red Coins": 3626112,
|
||||
"Bowser in the Fire Sea Key": 3626179
|
||||
"Bowser in the Fire Sea Key": 3626179,
|
||||
"Bowser in the Fire Sea 1Up Block Swaying Stairs": 3626238,
|
||||
"Bowser in the Fire Sea 1Up Block Near Poles": 3626239
|
||||
}
|
||||
|
||||
locWMotR_table = {
|
||||
"Wing Mario Over the Rainbow": 3626154
|
||||
"Wing Mario Over the Rainbow Red Coins": 3626154,
|
||||
"Wing Mario Over the Rainbow 1Up Block": 3626243
|
||||
}
|
||||
|
||||
locBitS_table = {
|
||||
"Bowser in the Sky Red Coins": 3626119
|
||||
"Bowser in the Sky Red Coins": 3626119,
|
||||
"Bowser in the Sky 1Up Block": 3626240
|
||||
}
|
||||
|
||||
#Secret Stars found inside the Castle
|
||||
@@ -239,4 +268,4 @@ location_table = {**locBoB_table,**locWhomp_table,**locJRB_table,**locCCM_table,
|
||||
**locWDW_table,**locTTM_table,**locTHI_table,**locTTC_table,**locRR_table, \
|
||||
**loc100Coin_table,**locPSS_table,**locSA_table,**locBitDW_table,**locTotWC_table, \
|
||||
**locCotMC_table, **locVCutM_table, **locBitFS_table, **locWMotR_table, **locBitS_table, \
|
||||
**locSS_table}
|
||||
**locSS_table}
|
||||
|
||||
@@ -1,48 +1,57 @@
|
||||
import typing
|
||||
from Options import Option, DefaultOnToggle, Range, Toggle, DeathLink, Choice
|
||||
|
||||
|
||||
class EnableCoinStars(DefaultOnToggle):
|
||||
"""Disable to Ignore 100 Coin Stars. You can still collect them, but they don't do anything"""
|
||||
display_name = "Enable 100 Coin Stars"
|
||||
|
||||
|
||||
class StrictCapRequirements(DefaultOnToggle):
|
||||
"""If disabled, Stars that expect special caps may have to be acquired without the caps"""
|
||||
display_name = "Strict Cap Requirements"
|
||||
|
||||
|
||||
class StrictCannonRequirements(DefaultOnToggle):
|
||||
"""If disabled, Stars that expect cannons may have to be acquired without them. Only makes a difference if Buddy Checks are enabled"""
|
||||
display_name = "Strict Cannon Requirements"
|
||||
|
||||
|
||||
class FirstBowserStarDoorCost(Range):
|
||||
"""How many stars are required at the Star Door to Bowser in the Dark World"""
|
||||
range_start = 0
|
||||
range_end = 50
|
||||
default = 8
|
||||
|
||||
|
||||
class BasementStarDoorCost(Range):
|
||||
"""How many stars are required at the Star Door in the Basement"""
|
||||
range_start = 0
|
||||
range_end = 70
|
||||
default = 30
|
||||
|
||||
|
||||
class SecondFloorStarDoorCost(Range):
|
||||
"""How many stars are required to access the third floor"""
|
||||
range_start = 0
|
||||
range_end = 90
|
||||
default = 50
|
||||
|
||||
|
||||
class MIPS1Cost(Range):
|
||||
"""How many stars are required to spawn MIPS the first time"""
|
||||
range_start = 0
|
||||
range_end = 40
|
||||
default = 15
|
||||
|
||||
|
||||
class MIPS2Cost(Range):
|
||||
"""How many stars are required to spawn MIPS the secound time. Must be bigger or equal MIPS1Cost"""
|
||||
range_start = 0
|
||||
range_end = 80
|
||||
default = 50
|
||||
|
||||
|
||||
class StarsToFinish(Range):
|
||||
"""How many stars are required at the infinite stairs"""
|
||||
display_name = "Endless Stairs Stars"
|
||||
@@ -50,29 +59,40 @@ class StarsToFinish(Range):
|
||||
range_end = 100
|
||||
default = 70
|
||||
|
||||
|
||||
class AmountOfStars(Range):
|
||||
"""How many stars exist. Disabling 100 Coin Stars removes 15 from the Pool. At least max of any Cost set"""
|
||||
range_start = 35
|
||||
range_end = 120
|
||||
default = 120
|
||||
|
||||
|
||||
class AreaRandomizer(Choice):
|
||||
"""Randomize Entrances"""
|
||||
display_name = "Entrance Randomizer"
|
||||
alias_false = 0
|
||||
option_Off = 0
|
||||
option_Courses_Only = 1
|
||||
option_Courses_and_Secrets = 2
|
||||
|
||||
|
||||
class BuddyChecks(Toggle):
|
||||
"""Bob-omb Buddies are checks, Cannon Unlocks are items"""
|
||||
display_name = "Bob-omb Buddy Checks"
|
||||
|
||||
|
||||
class ExclamationBoxes(Choice):
|
||||
"""Include 1Up Exclamation Boxes during randomization"""
|
||||
display_name = "Randomize 1Up !-Blocks"
|
||||
option_Off = 0
|
||||
option_1Ups_Only = 1
|
||||
|
||||
|
||||
class ProgressiveKeys(DefaultOnToggle):
|
||||
"""Keys will first grant you access to the Basement, then to the Secound Floor"""
|
||||
display_name = "Progressive Keys"
|
||||
|
||||
sm64_options: typing.Dict[str,type(Option)] = {
|
||||
|
||||
sm64_options: typing.Dict[str, type(Option)] = {
|
||||
"AreaRandomizer": AreaRandomizer,
|
||||
"ProgressiveKeys": ProgressiveKeys,
|
||||
"EnableCoinStars": EnableCoinStars,
|
||||
@@ -87,4 +107,5 @@ sm64_options: typing.Dict[str,type(Option)] = {
|
||||
"StarsToFinish": StarsToFinish,
|
||||
"death_link": DeathLink,
|
||||
"BuddyChecks": BuddyChecks,
|
||||
}
|
||||
"ExclamationBoxes": ExclamationBoxes,
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ def set_rules(world, player: int, area_connections):
|
||||
# which would make it impossible to reach downtown area without the cannon.
|
||||
add_rule(world.get_location("WDW: Quick Race Through Downtown!", player), lambda state: state.has("Cannon Unlock WDW", player))
|
||||
add_rule(world.get_location("WDW: Go to Town for Red Coins", player), lambda state: state.has("Cannon Unlock WDW", player))
|
||||
add_rule(world.get_location("WDW: 1Up Block in Downtown", player), lambda state: state.has("Cannon Unlock WDW", player))
|
||||
|
||||
if world.StrictCapRequirements[player]:
|
||||
add_rule(world.get_location("BoB: Mario Wings to the Sky", player), lambda state: state.has("Wing Cap", player))
|
||||
@@ -109,7 +110,8 @@ def set_rules(world, player: int, area_connections):
|
||||
add_rule(world.get_location("BoB: 100 Coins", player), lambda state: state.has("Cannon Unlock BoB", player) or state.has("Wing Cap", player))
|
||||
|
||||
#Rules for Secret Stars
|
||||
add_rule(world.get_location("Wing Mario Over the Rainbow", player), lambda state: state.has("Wing Cap", player))
|
||||
add_rule(world.get_location("Wing Mario Over the Rainbow Red Coins", player), lambda state: state.has("Wing Cap", player))
|
||||
add_rule(world.get_location("Wing Mario Over the Rainbow 1Up Block", player), lambda state: state.has("Wing Cap", player))
|
||||
add_rule(world.get_location("Toad (Basement)", player), lambda state: state.can_reach("Basement", 'Region', player) and state.has("Power Star", player, 12))
|
||||
add_rule(world.get_location("Toad (Second Floor)", player), lambda state: state.can_reach("Second Floor", 'Region', player) and state.has("Power Star", player, 25))
|
||||
add_rule(world.get_location("Toad (Third Floor)", player), lambda state: state.can_reach("Third Floor", 'Region', player) and state.has("Power Star", player, 35))
|
||||
|
||||
@@ -9,6 +9,7 @@ from .Regions import create_regions, sm64courses, sm64entrances_s, sm64_internal
|
||||
from BaseClasses import Item, Tutorial, ItemClassification
|
||||
from ..AutoWorld import World, WebWorld
|
||||
|
||||
|
||||
class SM64Web(WebWorld):
|
||||
tutorials = [Tutorial(
|
||||
"Multiworld Setup Guide",
|
||||
@@ -34,7 +35,7 @@ class SM64World(World):
|
||||
item_name_to_id = item_table
|
||||
location_name_to_id = location_table
|
||||
|
||||
data_version = 6
|
||||
data_version = 8
|
||||
required_client_version = (0, 3, 0)
|
||||
|
||||
area_connections: typing.Dict[int, int]
|
||||
@@ -71,7 +72,6 @@ class SM64World(World):
|
||||
return item
|
||||
|
||||
def generate_basic(self):
|
||||
staritem = self.create_item("Power Star")
|
||||
starcount = self.world.AmountOfStars[self.player].value
|
||||
if (not self.world.EnableCoinStars[self.player].value):
|
||||
starcount = max(35,self.world.AmountOfStars[self.player].value-15)
|
||||
@@ -79,17 +79,15 @@ class SM64World(World):
|
||||
self.world.BasementStarDoorCost[self.player].value, self.world.SecondFloorStarDoorCost[self.player].value,
|
||||
self.world.MIPS1Cost[self.player].value, self.world.MIPS2Cost[self.player].value,
|
||||
self.world.StarsToFinish[self.player].value)
|
||||
self.world.itempool += [staritem for i in range(0,starcount)]
|
||||
mushroomitem = self.create_item("1Up Mushroom")
|
||||
self.world.itempool += [mushroomitem for i in range(starcount,120 - (15 if not self.world.EnableCoinStars[self.player].value else 0))]
|
||||
self.world.itempool += [self.create_item("Power Star") for i in range(0,starcount)]
|
||||
self.world.itempool += [self.create_item("1Up Mushroom") for i in range(starcount,120 - (15 if not self.world.EnableCoinStars[self.player].value else 0))]
|
||||
|
||||
if (not self.world.ProgressiveKeys[self.player].value):
|
||||
key1 = self.create_item("Basement Key")
|
||||
key2 = self.create_item("Second Floor Key")
|
||||
self.world.itempool += [key1,key2]
|
||||
else:
|
||||
key = self.create_item("Progressive Key")
|
||||
self.world.itempool += [key,key]
|
||||
self.world.itempool += [self.create_item("Progressive Key") for i in range(0,2)]
|
||||
|
||||
wingcap = self.create_item("Wing Cap")
|
||||
metalcap = self.create_item("Metal Cap")
|
||||
@@ -110,6 +108,39 @@ class SM64World(World):
|
||||
self.world.get_location("THI: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock THI"))
|
||||
self.world.get_location("RR: Bob-omb Buddy", self.player).place_locked_item(self.create_item("Cannon Unlock RR"))
|
||||
|
||||
if (self.world.ExclamationBoxes[self.player].value > 0):
|
||||
self.world.itempool += [self.create_item("1Up Mushroom") for i in range(0,29)]
|
||||
else:
|
||||
self.world.get_location("CCM: 1Up Block Near Snowman", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("CCM: 1Up Block Ice Pillar", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("CCM: 1Up Block Secret Slide", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("BBH: 1Up Block Top of Mansion", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("HMC: 1Up Block above Pit", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("HMC: 1Up Block Past Rolling Rocks", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("SSL: 1Up Block Outside Pyramid", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("SSL: 1Up Block Pyramid Left Path", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("SSL: 1Up Block Pyramid Back", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("SL: 1Up Block Near Moneybags", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("SL: 1Up Block inside Igloo", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("WDW: 1Up Block in Downtown", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("TTM: 1Up Block on Red Mushroom", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("THI: 1Up Block THI Small near Start", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("THI: 1Up Block THI Large near Start", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("THI: 1Up Block Windy Area", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("TTC: 1Up Block Midway Up", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("TTC: 1Up Block at the Top", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("RR: 1Up Block Top of Red Coin Maze", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("RR: 1Up Block Under Fly Guy", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("RR: 1Up Block On House in the Sky", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("Bowser in the Dark World 1Up Block on Tower", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("Bowser in the Dark World 1Up Block near Goombas", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("Cavern of the Metal Cap 1Up Block", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("Vanish Cap Under the Moat 1Up Block", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("Bowser in the Fire Sea 1Up Block Swaying Stairs", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("Bowser in the Fire Sea 1Up Block Near Poles", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("Wing Mario Over the Rainbow 1Up Block", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
self.world.get_location("Bowser in the Sky 1Up Block", self.player).place_locked_item(self.create_item("1Up Mushroom"))
|
||||
|
||||
def get_filler_item_name(self) -> str:
|
||||
return "1Up Mushroom"
|
||||
|
||||
|
||||
@@ -107,7 +107,6 @@ class HeartBeepSpeed(Choice):
|
||||
option_Half = 2
|
||||
option_Normal = 3
|
||||
option_Double = 4
|
||||
alias_false = 0
|
||||
default = 3
|
||||
|
||||
class HeartColor(Choice):
|
||||
|
||||
@@ -25,6 +25,10 @@ from Options import Accessibility
|
||||
world_folder = os.path.dirname(__file__)
|
||||
logger = logging.getLogger("SMZ3")
|
||||
|
||||
# Location IDs in the range 256+196 to 256+202 shifted +34 between 11.2 and 11.3
|
||||
# this is required to keep backward compatibility
|
||||
def convertLocSMZ3IDToAPID(value):
|
||||
return (value - 34) if value >= 256+230 and value <= 256+236 else value
|
||||
|
||||
class SMZ3CollectionState(metaclass=AutoLogicRegister):
|
||||
def init_mixin(self, parent: MultiWorld):
|
||||
@@ -61,12 +65,13 @@ class SMZ3World(World):
|
||||
"""
|
||||
game: str = "SMZ3"
|
||||
topology_present = False
|
||||
data_version = 2
|
||||
data_version = 3
|
||||
option_definitions = smz3_options
|
||||
item_names: Set[str] = frozenset(TotalSMZ3Item.lookup_name_to_id)
|
||||
location_names: Set[str]
|
||||
item_name_to_id = TotalSMZ3Item.lookup_name_to_id
|
||||
location_name_to_id: Dict[str, int] = {key : locations_start_id + value.Id for key, value in TotalSMZ3World(Config(), "", 0, "").locationLookup.items()}
|
||||
location_name_to_id: Dict[str, int] = {key : locations_start_id + convertLocSMZ3IDToAPID(value.Id)
|
||||
for key, value in TotalSMZ3World(Config(), "", 0, "").locationLookup.items()}
|
||||
web = SMZ3Web()
|
||||
|
||||
remote_items: bool = False
|
||||
@@ -526,9 +531,11 @@ class SMZ3World(World):
|
||||
|
||||
def JunkFillGT(self, factor):
|
||||
poolLength = len(self.world.itempool)
|
||||
playerGroups = self.world.get_player_groups(self.player)
|
||||
playerGroups.add(self.player)
|
||||
junkPoolIdx = [i for i in range(0, poolLength)
|
||||
if self.world.itempool[i].classification in (ItemClassification.filler, ItemClassification.trap) and
|
||||
self.world.itempool[i].player == self.player]
|
||||
self.world.itempool[i].player in playerGroups]
|
||||
toRemove = []
|
||||
for loc in self.locations.values():
|
||||
# commenting this for now since doing a partial GT pre fill would allow for non SMZ3 progression in GT
|
||||
@@ -590,8 +597,6 @@ class SMZ3World(World):
|
||||
# /* Check Swords option and place as needed */
|
||||
if self.smz3World.Config.SwordLocation == SwordLocation.Uncle:
|
||||
self.FillItemAtLocation(self.progression, TotalSMZ3Item.ItemType.ProgressiveSword, self.smz3World.GetLocation("Link's Uncle"))
|
||||
elif self.smz3World.Config.SwordLocation == SwordLocation.Early:
|
||||
self.FrontFillItemInOwnWorld(self.progression, TotalSMZ3Item.ItemType.ProgressiveSword)
|
||||
|
||||
# /* Check Morph option and place as needed */
|
||||
if self.smz3World.Config.MorphLocation == MorphLocation.Original:
|
||||
@@ -599,6 +604,10 @@ class SMZ3World(World):
|
||||
elif self.smz3World.Config.MorphLocation == MorphLocation.Early:
|
||||
self.FrontFillItemInOwnWorld(self.progression, TotalSMZ3Item.ItemType.Morph)
|
||||
|
||||
# We do early Sword placement after Morph in case its Original location
|
||||
if self.smz3World.Config.SwordLocation == SwordLocation.Early:
|
||||
self.FrontFillItemInOwnWorld(self.progression, TotalSMZ3Item.ItemType.ProgressiveSword)
|
||||
|
||||
# /* We place a PB and Super in Sphere 1 to make sure the filler
|
||||
# * doesn't start locking items behind this when there are a
|
||||
# * high chance of the trash fill actually making them available */
|
||||
|
||||
@@ -21,8 +21,6 @@ class OffOnFullChoice(Choice):
|
||||
option_on = 1
|
||||
option_full = 2
|
||||
alias_chaos = 2
|
||||
alias_false = 0
|
||||
alias_true = 1
|
||||
|
||||
|
||||
class Difficulty(EvermizerFlags, Choice):
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import functools
|
||||
from typing import Dict, Set, List
|
||||
|
||||
# EN Locale Creature Name to rough depth in meters found at
|
||||
@@ -50,33 +51,75 @@ all_creatures: Dict[str, int] = {
|
||||
"Lava Larva": 1300,
|
||||
"Lava Lizard": 1300,
|
||||
"Sea Dragon Leviathan": 1300,
|
||||
"Sea Emperor Leviathan": 1700,
|
||||
"Cuddlefish": 300,
|
||||
"Sea Emperor Juvenile": 1700,
|
||||
|
||||
# "Cuddlefish": 300, # maybe at some point, needs hatching in containment chamber (20 real-life minutes)
|
||||
}
|
||||
|
||||
# be nice and make these require Stasis Rifle
|
||||
aggressive: Set[str] = {
|
||||
"Cave Crawler", # is very easy without Stasis Rifle, but included for consistency
|
||||
"Crashfish",
|
||||
"Biter",
|
||||
"Bleeder",
|
||||
"Blighter",
|
||||
"Blood Crawler",
|
||||
"Mesmer",
|
||||
"Reaper Leviathan",
|
||||
"Crabsquid",
|
||||
"Warper",
|
||||
"Crabsnake",
|
||||
"Ampeel",
|
||||
"Stalker",
|
||||
"Sand Shark",
|
||||
"Boneshark",
|
||||
"Lava Lizard",
|
||||
"Sea Dragon Leviathan",
|
||||
"River Prowler",
|
||||
"Ghost Leviathan Juvenile",
|
||||
"Ghost Leviathan"
|
||||
}
|
||||
|
||||
containment: Set[str] = { # creatures that have to be raised from eggs
|
||||
"Cuddlefish",
|
||||
}
|
||||
|
||||
hatchable: Set[str] = { # aggressive creatures that can be grown from eggs as alternative to stasis
|
||||
"Ampeel", # warning: electric shocks
|
||||
"Crabsquid", # warning: electric shocks
|
||||
"Crabsnake",
|
||||
"Boneshark",
|
||||
"Crashfish",
|
||||
"Gasopod",
|
||||
"Lava Lizard",
|
||||
"Mesmer",
|
||||
"Sand Shark",
|
||||
"Stalker",
|
||||
}
|
||||
|
||||
suffix: str = " Scan"
|
||||
|
||||
creature_locations: Dict[str, int] = {
|
||||
creature+suffix: creature_id for creature_id, creature in enumerate(all_creatures, start=34000)
|
||||
creature + suffix: creature_id for creature_id, creature in enumerate(all_creatures, start=34000)
|
||||
}
|
||||
|
||||
all_creatures_presorted: List[str] = sorted(all_creatures)
|
||||
|
||||
class Definitions:
|
||||
"""Only compute lists if needed and then cache them."""
|
||||
|
||||
@functools.cached_property
|
||||
def all_creatures_presorted(self) -> List[str]:
|
||||
return sorted(all_creatures)
|
||||
|
||||
@functools.cached_property
|
||||
def all_creatures_presorted_without_containment(self) -> List[str]:
|
||||
return [name for name in self.all_creatures_presorted if name not in containment]
|
||||
|
||||
@functools.cached_property
|
||||
def all_creatures_presorted_without_stasis(self) -> List[str]:
|
||||
return [name for name in self.all_creatures_presorted if name not in aggressive or name in hatchable]
|
||||
|
||||
@functools.cached_property
|
||||
def all_creatures_presorted_without_aggressive(self) -> List[str]:
|
||||
return [name for name in self.all_creatures_presorted if name not in aggressive]
|
||||
|
||||
# only singleton needed
|
||||
Definitions: Definitions = Definitions()
|
||||
|
||||
@@ -42,7 +42,7 @@ item_table: Dict[int, ItemDict] = {
|
||||
'count': 1,
|
||||
'name': 'Stillsuit',
|
||||
'tech_type': 'Stillsuit'},
|
||||
35008: {'classification': ItemClassification.filler,
|
||||
35008: {'classification': ItemClassification.progression,
|
||||
'count': 2,
|
||||
'name': 'Alien Containment Fragment',
|
||||
'tech_type': 'BaseWaterParkFragment'},
|
||||
@@ -139,7 +139,7 @@ item_table: Dict[int, ItemDict] = {
|
||||
'name': 'Power Transmitter Fragment',
|
||||
'tech_type': 'PowerTransmitterFragment'},
|
||||
35032: {'classification': ItemClassification.progression,
|
||||
'count': 4,
|
||||
'count': 5,
|
||||
'name': 'Prawn Suit Fragment',
|
||||
'tech_type': 'ExosuitFragment'},
|
||||
35033: {'classification': ItemClassification.useful,
|
||||
@@ -163,7 +163,7 @@ item_table: Dict[int, ItemDict] = {
|
||||
'name': 'Scanner Room Fragment',
|
||||
'tech_type': 'BaseMapRoomFragment'},
|
||||
35038: {'classification': ItemClassification.progression,
|
||||
'count': 5,
|
||||
'count': 4,
|
||||
'name': 'Seamoth Fragment',
|
||||
'tech_type': 'SeamothFragment'},
|
||||
35039: {'classification': ItemClassification.progression,
|
||||
@@ -203,9 +203,9 @@ item_table: Dict[int, ItemDict] = {
|
||||
'name': 'Picture Frame',
|
||||
'tech_type': 'PictureFrameFragment'},
|
||||
35048: {'classification': ItemClassification.filler,
|
||||
'count': 2,
|
||||
'name': 'Bench Fragment',
|
||||
'tech_type': 'BenchFragment'},
|
||||
'count': 1,
|
||||
'name': 'Bench',
|
||||
'tech_type': 'Bench'},
|
||||
35049: {'classification': ItemClassification.filler,
|
||||
'count': 1,
|
||||
'name': 'Basic Plant Pot',
|
||||
@@ -222,7 +222,7 @@ item_table: Dict[int, ItemDict] = {
|
||||
'count': 2,
|
||||
'name': 'Observatory Fragment',
|
||||
'tech_type': 'BaseObservatoryFragment'},
|
||||
35053: {'classification': ItemClassification.useful,
|
||||
35053: {'classification': ItemClassification.progression,
|
||||
'count': 2,
|
||||
'name': 'Multipurpose Room',
|
||||
'tech_type': 'BaseRoom'},
|
||||
@@ -333,7 +333,12 @@ item_table: Dict[int, ItemDict] = {
|
||||
35080: {'classification': ItemClassification.filler,
|
||||
'count': 1,
|
||||
'name': 'Water Filtration Machine',
|
||||
'tech_type': 'BaseFiltrationMachine'}}
|
||||
'tech_type': 'BaseFiltrationMachine'},
|
||||
35081: {'classification': ItemClassification.progression,
|
||||
'count': 1,
|
||||
'name': 'Ultra High Capacity Tank',
|
||||
'tech_type': 'HighCapacityTank'},
|
||||
}
|
||||
|
||||
advancement_item_names: Set[str] = set()
|
||||
non_advancement_item_names: Set[str] = set()
|
||||
|
||||
@@ -15,7 +15,12 @@ class LocationDict(TypedDict, total=False):
|
||||
need_propulsion_cannon: bool
|
||||
|
||||
|
||||
events: List[str] = ["Neptune Launch", "Disable Quarantine", "Full Infection", "Repair Aurora Drive"]
|
||||
events: List[str] = [
|
||||
"Neptune Launch",
|
||||
"Disable Quarantine",
|
||||
"Full Infection",
|
||||
"Repair Aurora Drive",
|
||||
]
|
||||
|
||||
location_table: Dict[int, LocationDict] = {
|
||||
33000: {'can_slip_through': False,
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import typing
|
||||
|
||||
from Options import Choice, Range, DeathLink
|
||||
from .Creatures import all_creatures
|
||||
from .Creatures import all_creatures, Definitions
|
||||
|
||||
|
||||
class ItemPool(Choice):
|
||||
@@ -33,12 +35,40 @@ class Goal(Choice):
|
||||
|
||||
|
||||
class CreatureScans(Range):
|
||||
"""Place items on specific creature scans.
|
||||
"""Place items on specific, randomly chosen, creature scans.
|
||||
Warning: Includes aggressive Leviathans."""
|
||||
display_name = "Creature Scans"
|
||||
range_end = len(all_creatures)
|
||||
|
||||
|
||||
class AggressiveScanLogic(Choice):
|
||||
"""By default (Stasis), aggressive Creature Scans are logically expected only with a Stasis Rifle.
|
||||
Containment: Removes Stasis Rifle as expected solution and expects Alien Containment instead.
|
||||
Either: Creatures may be expected to be scanned via Stasis Rifle or Containment, whichever is found first.
|
||||
None: Aggressive Creatures are assumed to not need any tools to scan.
|
||||
Removed: No Creatures needing Stasis or Containment will be in the pool at all.
|
||||
|
||||
Note: Containment, Either and None adds Cuddlefish as an option for scans.
|
||||
Note: Stasis, Either and None adds unhatchable aggressive species, such as Warper.
|
||||
Note: This is purely a logic expectation, and does not affect gameplay, only placement."""
|
||||
display_name = "Aggressive Creature Scan Logic"
|
||||
option_stasis = 0
|
||||
option_containment = 1
|
||||
option_either = 2
|
||||
option_none = 3
|
||||
option_removed = 4
|
||||
|
||||
def get_pool(self) -> typing.List[str]:
|
||||
if self == self.option_removed:
|
||||
return Definitions.all_creatures_presorted_without_aggressive
|
||||
elif self == self.option_stasis:
|
||||
return Definitions.all_creatures_presorted_without_containment
|
||||
elif self == self.option_containment:
|
||||
return Definitions.all_creatures_presorted_without_stasis
|
||||
else:
|
||||
return Definitions.all_creatures_presorted
|
||||
|
||||
|
||||
class SubnauticaDeathLink(DeathLink):
|
||||
"""When you die, everyone dies. Of course the reverse is true too.
|
||||
Note: can be toggled via in-game console command "deathlink"."""
|
||||
@@ -48,5 +78,6 @@ options = {
|
||||
"item_pool": ItemPool,
|
||||
"goal": Goal,
|
||||
"creature_scans": CreatureScans,
|
||||
"creature_scan_logic": AggressiveScanLogic,
|
||||
"death_link": SubnauticaDeathLink,
|
||||
}
|
||||
|
||||
@@ -1,122 +1,128 @@
|
||||
from typing import TYPE_CHECKING
|
||||
from typing import TYPE_CHECKING, Dict, Callable, Optional
|
||||
|
||||
from worlds.generic.Rules import set_rule
|
||||
from worlds.generic.Rules import set_rule, add_rule
|
||||
from .Locations import location_table, LocationDict
|
||||
from .Creatures import all_creatures, aggressive, suffix
|
||||
from .Creatures import all_creatures, aggressive, suffix, hatchable, containment
|
||||
from .Options import AggressiveScanLogic
|
||||
import math
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from . import SubnauticaWorld
|
||||
from BaseClasses import CollectionState, Location
|
||||
|
||||
|
||||
def has_seaglide(state, player: int):
|
||||
def has_seaglide(state: "CollectionState", player: int):
|
||||
return state.has("Seaglide Fragment", player, 2)
|
||||
|
||||
|
||||
def has_modification_station(state, player: int):
|
||||
def has_modification_station(state: "CollectionState", player: int):
|
||||
return state.has("Modification Station Fragment", player, 3)
|
||||
|
||||
|
||||
def has_mobile_vehicle_bay(state, player: int):
|
||||
def has_mobile_vehicle_bay(state: "CollectionState", player: int):
|
||||
return state.has("Mobile Vehicle Bay Fragment", player, 3)
|
||||
|
||||
|
||||
def has_moonpool(state, player: int):
|
||||
def has_moonpool(state: "CollectionState", player: int):
|
||||
return state.has("Moonpool Fragment", player, 2)
|
||||
|
||||
|
||||
def has_vehicle_upgrade_console(state, player: int):
|
||||
def has_vehicle_upgrade_console(state: "CollectionState", player: int):
|
||||
return state.has("Vehicle Upgrade Console", player) and \
|
||||
has_moonpool(state, player)
|
||||
|
||||
|
||||
def has_seamoth(state, player: int):
|
||||
def has_seamoth(state: "CollectionState", player: int):
|
||||
return state.has("Seamoth Fragment", player, 3) and \
|
||||
has_mobile_vehicle_bay(state, player)
|
||||
|
||||
|
||||
def has_seamoth_depth_module_mk1(state, player: int):
|
||||
def has_seamoth_depth_module_mk1(state: "CollectionState", player: int):
|
||||
return has_vehicle_upgrade_console(state, player)
|
||||
|
||||
|
||||
def has_seamoth_depth_module_mk2(state, player: int):
|
||||
def has_seamoth_depth_module_mk2(state: "CollectionState", player: int):
|
||||
return has_seamoth_depth_module_mk1(state, player) and \
|
||||
has_modification_station(state, player)
|
||||
|
||||
|
||||
def has_seamoth_depth_module_mk3(state, player: int):
|
||||
def has_seamoth_depth_module_mk3(state: "CollectionState", player: int):
|
||||
return has_seamoth_depth_module_mk2(state, player) and \
|
||||
has_modification_station(state, player)
|
||||
|
||||
|
||||
def has_cyclops_bridge(state, player: int):
|
||||
def has_cyclops_bridge(state: "CollectionState", player: int):
|
||||
return state.has("Cyclops Bridge Fragment", player, 3)
|
||||
|
||||
|
||||
def has_cyclops_engine(state, player: int):
|
||||
def has_cyclops_engine(state: "CollectionState", player: int):
|
||||
return state.has("Cyclops Engine Fragment", player, 3)
|
||||
|
||||
|
||||
def has_cyclops_hull(state, player: int):
|
||||
def has_cyclops_hull(state: "CollectionState", player: int):
|
||||
return state.has("Cyclops Hull Fragment", player, 3)
|
||||
|
||||
|
||||
def has_cyclops(state, player: int):
|
||||
def has_cyclops(state: "CollectionState", player: int):
|
||||
return has_cyclops_bridge(state, player) and \
|
||||
has_cyclops_engine(state, player) and \
|
||||
has_cyclops_hull(state, player) and \
|
||||
has_mobile_vehicle_bay(state, player)
|
||||
|
||||
|
||||
def has_cyclops_depth_module_mk1(state, player: int):
|
||||
def has_cyclops_depth_module_mk1(state: "CollectionState", player: int):
|
||||
return state.has("Cyclops Depth Module MK1", player) and \
|
||||
has_modification_station(state, player)
|
||||
|
||||
|
||||
def has_cyclops_depth_module_mk2(state, player: int):
|
||||
def has_cyclops_depth_module_mk2(state: "CollectionState", player: int):
|
||||
return has_cyclops_depth_module_mk1(state, player) and \
|
||||
has_modification_station(state, player)
|
||||
|
||||
|
||||
def has_cyclops_depth_module_mk3(state, player: int):
|
||||
def has_cyclops_depth_module_mk3(state: "CollectionState", player: int):
|
||||
return has_cyclops_depth_module_mk2(state, player) and \
|
||||
has_modification_station(state, player)
|
||||
|
||||
|
||||
def has_prawn(state, player: int):
|
||||
def has_prawn(state: "CollectionState", player: int):
|
||||
return state.has("Prawn Suit Fragment", player, 4) and \
|
||||
has_mobile_vehicle_bay(state, player)
|
||||
|
||||
|
||||
def has_praw_propulsion_arm(state, player: int):
|
||||
def has_prawn_propulsion_arm(state: "CollectionState", player: int):
|
||||
return state.has("Prawn Suit Propulsion Cannon Fragment", player, 2) and \
|
||||
has_vehicle_upgrade_console(state, player)
|
||||
|
||||
|
||||
def has_prawn_depth_module_mk1(state, player: int):
|
||||
def has_prawn_depth_module_mk1(state: "CollectionState", player: int):
|
||||
return has_vehicle_upgrade_console(state, player)
|
||||
|
||||
|
||||
def has_prawn_depth_module_mk2(state, player: int):
|
||||
def has_prawn_depth_module_mk2(state: "CollectionState", player: int):
|
||||
return has_prawn_depth_module_mk1(state, player) and \
|
||||
has_modification_station(state, player)
|
||||
|
||||
|
||||
def has_laser_cutter(state, player: int):
|
||||
def has_laser_cutter(state: "CollectionState", player: int):
|
||||
return state.has("Laser Cutter Fragment", player, 3)
|
||||
|
||||
|
||||
def has_stasis_rile(state, player: int):
|
||||
def has_stasis_rifle(state: "CollectionState", player: int):
|
||||
return state.has("Stasis Rifle Fragment", player, 2)
|
||||
|
||||
|
||||
def has_containment(state: "CollectionState", player: int):
|
||||
return state.has("Alien Containment Fragment", player, 2) and state.has("Multipurpose Room", player)
|
||||
|
||||
|
||||
# Either we have propulsion cannon, or prawn + propulsion cannon arm
|
||||
def has_propulsion_cannon(state, player: int):
|
||||
def has_propulsion_cannon(state: "CollectionState", player: int):
|
||||
return state.has("Propulsion Cannon Fragment", player, 2) or \
|
||||
(has_prawn(state, player) and has_praw_propulsion_arm(state, player))
|
||||
(has_prawn(state, player) and has_prawn_propulsion_arm(state, player))
|
||||
|
||||
|
||||
def has_cyclops_shield(state, player: int):
|
||||
def has_cyclops_shield(state: "CollectionState", player: int):
|
||||
return has_cyclops(state, player) and \
|
||||
state.has("Cyclops Shield Generator", player)
|
||||
|
||||
@@ -129,7 +135,7 @@ def has_cyclops_shield(state, player: int):
|
||||
# negligeable with from high capacity tank. 430m -> 460m
|
||||
# Fins are not used when using seaglide
|
||||
#
|
||||
def get_max_swim_depth(state, player: int):
|
||||
def get_max_swim_depth(state: "CollectionState", player: int):
|
||||
# TODO, Make this a difficulty setting.
|
||||
# Only go up to 200m without any submarines for now.
|
||||
return 200
|
||||
@@ -140,7 +146,7 @@ def get_max_swim_depth(state, player: int):
|
||||
# has_ultra_glide_fins = state.has("Ultra Glide Fins", player)
|
||||
|
||||
# max_depth = 400 # More like 430m. Give some room
|
||||
# if has_seaglide(state, player: int):
|
||||
# if has_seaglide(state: "CollectionState", player: int):
|
||||
# if has_ultra_high_capacity_tank:
|
||||
# max_depth = 750 # It's about 50m more. Give some room
|
||||
# else:
|
||||
@@ -156,7 +162,7 @@ def get_max_swim_depth(state, player: int):
|
||||
# return max_depth
|
||||
|
||||
|
||||
def get_seamoth_max_depth(state, player: int):
|
||||
def get_seamoth_max_depth(state: "CollectionState", player: int):
|
||||
if has_seamoth(state, player):
|
||||
if has_seamoth_depth_module_mk3(state, player):
|
||||
return 900
|
||||
@@ -170,7 +176,7 @@ def get_seamoth_max_depth(state, player: int):
|
||||
return 0
|
||||
|
||||
|
||||
def get_cyclops_max_depth(state, player):
|
||||
def get_cyclops_max_depth(state: "CollectionState", player):
|
||||
if has_cyclops(state, player):
|
||||
if has_cyclops_depth_module_mk3(state, player):
|
||||
return 1700
|
||||
@@ -184,7 +190,7 @@ def get_cyclops_max_depth(state, player):
|
||||
return 0
|
||||
|
||||
|
||||
def get_prawn_max_depth(state, player):
|
||||
def get_prawn_max_depth(state: "CollectionState", player):
|
||||
if has_prawn(state, player):
|
||||
if has_prawn_depth_module_mk2(state, player):
|
||||
return 1700
|
||||
@@ -196,7 +202,7 @@ def get_prawn_max_depth(state, player):
|
||||
return 0
|
||||
|
||||
|
||||
def get_max_depth(state, player: int):
|
||||
def get_max_depth(state: "CollectionState", player: int):
|
||||
# TODO, Difficulty option, we can add vehicle depth + swim depth
|
||||
# But at this point, we have to consider traver distance in caves, not
|
||||
# just depth
|
||||
@@ -206,7 +212,7 @@ def get_max_depth(state, player: int):
|
||||
get_prawn_max_depth(state, player))
|
||||
|
||||
|
||||
def can_access_location(state, player: int, loc: LocationDict) -> bool:
|
||||
def can_access_location(state: "CollectionState", player: int, loc: LocationDict) -> bool:
|
||||
need_laser_cutter = loc.get("need_laser_cutter", False)
|
||||
if need_laser_cutter and not has_laser_cutter(state, player):
|
||||
return False
|
||||
@@ -239,17 +245,34 @@ def set_location_rule(world, player: int, loc: LocationDict):
|
||||
set_rule(world.get_location(loc["name"], player), lambda state: can_access_location(state, player, loc))
|
||||
|
||||
|
||||
def can_scan_creature(state, player: int, creature: str) -> bool:
|
||||
def can_scan_creature(state: "CollectionState", player: int, creature: str) -> bool:
|
||||
if not has_seaglide(state, player):
|
||||
return False
|
||||
if creature in aggressive and not has_stasis_rile(state, player):
|
||||
return False
|
||||
return get_max_depth(state, player) >= all_creatures[creature]
|
||||
|
||||
|
||||
def set_creature_rule(world, player, creature_name: str):
|
||||
set_rule(world.get_location(creature_name + suffix, player),
|
||||
def set_creature_rule(world, player: int, creature_name: str) -> "Location":
|
||||
location = world.get_location(creature_name + suffix, player)
|
||||
set_rule(location,
|
||||
lambda state: can_scan_creature(state, player, creature_name))
|
||||
return location
|
||||
|
||||
|
||||
def get_aggression_rule(option: AggressiveScanLogic, creature_name: str) -> \
|
||||
Optional[Callable[["CollectionState", int], bool]]:
|
||||
"""Get logic rule for a creature scan location."""
|
||||
if creature_name not in hatchable and option != option.option_none: # can only be done via stasis
|
||||
return has_stasis_rifle
|
||||
# otherwise allow option preference
|
||||
return aggression_rules.get(option.value, None)
|
||||
|
||||
|
||||
aggression_rules: Dict[int, Callable[["CollectionState", int], bool]] = {
|
||||
AggressiveScanLogic.option_stasis: has_stasis_rifle,
|
||||
AggressiveScanLogic.option_containment: has_containment,
|
||||
AggressiveScanLogic.option_either: lambda state, player:
|
||||
has_stasis_rifle(state, player) or has_containment(state, player)
|
||||
}
|
||||
|
||||
|
||||
def set_rules(subnautica_world: "SubnauticaWorld"):
|
||||
@@ -259,11 +282,22 @@ def set_rules(subnautica_world: "SubnauticaWorld"):
|
||||
for loc in location_table.values():
|
||||
set_location_rule(world, player, loc)
|
||||
|
||||
for creature_name in subnautica_world.creatures_to_scan:
|
||||
set_creature_rule(world, player, creature_name)
|
||||
if subnautica_world.creatures_to_scan:
|
||||
option = world.creature_scan_logic[player]
|
||||
|
||||
for creature_name in subnautica_world.creatures_to_scan:
|
||||
location = set_creature_rule(world, player, creature_name)
|
||||
if creature_name in containment: # there is no other way, hard-required containment
|
||||
add_rule(location, lambda state: has_containment(state, player))
|
||||
elif creature_name in aggressive:
|
||||
rule = get_aggression_rule(option, creature_name)
|
||||
if rule:
|
||||
add_rule(location,
|
||||
lambda state, loc_rule=get_aggression_rule(option, creature_name): loc_rule(state, player))
|
||||
|
||||
# Victory locations
|
||||
set_rule(world.get_location("Neptune Launch", player), lambda state:
|
||||
set_rule(world.get_location("Neptune Launch", player),
|
||||
lambda state:
|
||||
get_max_depth(state, player) >= 1444 and
|
||||
has_mobile_vehicle_bay(state, player) and
|
||||
state.has("Neptune Launch Platform", player) and
|
||||
|
||||
@@ -41,8 +41,8 @@ class SubnauticaWorld(World):
|
||||
location_name_to_id = all_locations
|
||||
option_definitions = Options.options
|
||||
|
||||
data_version = 5
|
||||
required_client_version = (0, 3, 4)
|
||||
data_version = 7
|
||||
required_client_version = (0, 3, 5)
|
||||
|
||||
prefill_items: List[Item]
|
||||
creatures_to_scan: List[str]
|
||||
@@ -52,7 +52,15 @@ class SubnauticaWorld(World):
|
||||
self.create_item("Seaglide Fragment"),
|
||||
self.create_item("Seaglide Fragment")
|
||||
]
|
||||
self.creatures_to_scan = self.world.random.sample(Creatures.all_creatures_presorted,
|
||||
scan_option: Options.AggressiveScanLogic = self.world.creature_scan_logic[self.player]
|
||||
creature_pool = scan_option.get_pool()
|
||||
|
||||
self.world.creature_scans[self.player].value = min(
|
||||
len(creature_pool),
|
||||
self.world.creature_scans[self.player].value
|
||||
)
|
||||
|
||||
self.creatures_to_scan = self.world.random.sample(creature_pool,
|
||||
self.world.creature_scans[self.player].value)
|
||||
|
||||
def create_regions(self):
|
||||
|
||||
@@ -3,66 +3,82 @@ from BaseClasses import MultiWorld
|
||||
from Options import Toggle, DefaultOnToggle, DeathLink, Choice, Range, Option, OptionDict
|
||||
from schema import Schema, And, Optional
|
||||
|
||||
|
||||
class StartWithJewelryBox(Toggle):
|
||||
"Start with Jewelry Box unlocked"
|
||||
display_name = "Start with Jewelry Box"
|
||||
|
||||
|
||||
#class ProgressiveVerticalMovement(Toggle):
|
||||
# "Always find vertical movement in the following order Succubus Hairpin -> Light Wall -> Celestial Sash"
|
||||
# display_name = "Progressive vertical movement"
|
||||
|
||||
|
||||
#class ProgressiveKeycards(Toggle):
|
||||
# "Always find Security Keycard's in the following order D -> C -> B -> A"
|
||||
# display_name = "Progressive keycards"
|
||||
|
||||
|
||||
class DownloadableItems(DefaultOnToggle):
|
||||
"With the tablet you will be able to download items at terminals"
|
||||
display_name = "Downloadable items"
|
||||
|
||||
|
||||
class EyeSpy(Toggle):
|
||||
"Requires Oculus Ring in inventory to be able to break hidden walls."
|
||||
display_name = "Eye Spy"
|
||||
|
||||
|
||||
class StartWithMeyef(Toggle):
|
||||
"Start with Meyef, ideal for when you want to play multiplayer."
|
||||
display_name = "Start with Meyef"
|
||||
|
||||
|
||||
class QuickSeed(Toggle):
|
||||
"Start with Talaria Attachment, Nyoom!"
|
||||
display_name = "Quick seed"
|
||||
|
||||
|
||||
class SpecificKeycards(Toggle):
|
||||
"Keycards can only open corresponding doors"
|
||||
display_name = "Specific Keycards"
|
||||
|
||||
|
||||
class Inverted(Toggle):
|
||||
"Start in the past"
|
||||
display_name = "Inverted"
|
||||
|
||||
|
||||
#class StinkyMaw(Toggle):
|
||||
# "Require gasmask for Maw"
|
||||
# display_name = "Stinky Maw"
|
||||
|
||||
|
||||
class GyreArchives(Toggle):
|
||||
"Gyre locations are in logic. New warps are gated by Merchant Crow and Kobo"
|
||||
display_name = "Gyre Archives"
|
||||
|
||||
|
||||
class Cantoran(Toggle):
|
||||
"Cantoran's fight and check are available upon revisiting his room"
|
||||
display_name = "Cantoran"
|
||||
|
||||
|
||||
class LoreChecks(Toggle):
|
||||
"Memories and journal entries contain items."
|
||||
display_name = "Lore Checks"
|
||||
|
||||
|
||||
class BossRando(Toggle):
|
||||
"Shuffles the positions of all bosses."
|
||||
display_name = "Boss Randomization"
|
||||
|
||||
|
||||
class BossScaling(DefaultOnToggle):
|
||||
"When Boss Rando is enabled, scales the bosses' HP, XP, and ATK to the stats of the location they replace (Reccomended)"
|
||||
display_name = "Scale Random Boss Stats"
|
||||
|
||||
|
||||
class DamageRando(Choice):
|
||||
"Randomly nerfs and buffs some orbs and their associated spells as well as some associated rings."
|
||||
display_name = "Damage Rando"
|
||||
@@ -73,9 +89,9 @@ class DamageRando(Choice):
|
||||
option_mostlybuffs = 4
|
||||
option_allbuffs = 5
|
||||
option_manual = 6
|
||||
alias_false = 0
|
||||
alias_true = 2
|
||||
|
||||
|
||||
class DamageRandoOverrides(OptionDict):
|
||||
"Manual +/-/normal odds for an orb. Put 0 if you don't want a certain nerf or buff to be a possibility. Orbs that you don't specify will roll with 1/1/1 as odds"
|
||||
schema = Schema({
|
||||
@@ -180,6 +196,7 @@ class DamageRandoOverrides(OptionDict):
|
||||
"Radiant": { "MinusOdds": 1, "NormalOdds": 1, "PlusOdds": 1 },
|
||||
}
|
||||
|
||||
|
||||
class HpCap(Range):
|
||||
"Sets the number that Lunais's HP maxes out at."
|
||||
display_name = "HP Cap"
|
||||
@@ -187,10 +204,12 @@ class HpCap(Range):
|
||||
range_end = 999
|
||||
default = 999
|
||||
|
||||
|
||||
class BossHealing(DefaultOnToggle):
|
||||
"Enables/disables healing after boss fights. NOTE: Currently only applicable when Boss Rando is enabled."
|
||||
display_name = "Heal After Bosses"
|
||||
|
||||
|
||||
class ShopFill(Choice):
|
||||
"""Sets the items for sale in Merchant Crow's shops.
|
||||
Default: No sunglasses or trendy jacket, but sand vials for sale.
|
||||
@@ -203,10 +222,12 @@ class ShopFill(Choice):
|
||||
option_vanilla = 2
|
||||
option_empty = 3
|
||||
|
||||
|
||||
class ShopWarpShards(DefaultOnToggle):
|
||||
"Shops always sell warp shards (when keys possessed), ignoring inventory setting."
|
||||
display_name = "Always Sell Warp Shards"
|
||||
|
||||
|
||||
class ShopMultiplier(Range):
|
||||
"Multiplier for the cost of items in the shop. Set to 0 for free shops."
|
||||
display_name = "Shop Price Multiplier"
|
||||
@@ -214,6 +235,7 @@ class ShopMultiplier(Range):
|
||||
range_end = 10
|
||||
default = 1
|
||||
|
||||
|
||||
class LootPool(Choice):
|
||||
"""Sets the items that drop from enemies (does not apply to boss reward checks)
|
||||
Vanilla: Drops are the same as the base game
|
||||
@@ -224,6 +246,7 @@ class LootPool(Choice):
|
||||
option_randomized = 1
|
||||
option_empty = 2
|
||||
|
||||
|
||||
class DropRateCategory(Choice):
|
||||
"""Sets the drop rate when 'Loot Pool' is set to 'Random'
|
||||
Tiered: Based on item rarity/value
|
||||
@@ -237,6 +260,7 @@ class DropRateCategory(Choice):
|
||||
option_randomized = 2
|
||||
option_fixed = 3
|
||||
|
||||
|
||||
class FixedDropRate(Range):
|
||||
"Base drop rate percentage when 'Drop Rate Category' is set to 'Fixed'"
|
||||
display_name = "Fixed Drop Rate"
|
||||
@@ -244,6 +268,7 @@ class FixedDropRate(Range):
|
||||
range_end = 100
|
||||
default = 5
|
||||
|
||||
|
||||
class LootTierDistro(Choice):
|
||||
"""Sets how often items of each rarity tier are placed when 'Loot Pool' is set to 'Random'
|
||||
Default Weight: Rarer items will be assigned to enemy drop slots less frequently than common items
|
||||
@@ -255,14 +280,17 @@ class LootTierDistro(Choice):
|
||||
option_full_random = 1
|
||||
option_inverted_weight = 2
|
||||
|
||||
|
||||
class ShowBestiary(Toggle):
|
||||
"All entries in the bestiary are visible, without needing to kill one of a given enemy first"
|
||||
display_name = "Show Bestiary Entries"
|
||||
|
||||
|
||||
class ShowDrops(Toggle):
|
||||
"All item drops in the bestiary are visible, without needing an enemy to drop one of a given item first"
|
||||
display_name = "Show Bestiary Item Drops"
|
||||
|
||||
|
||||
# Some options that are available in the timespinner randomizer arent currently implemented
|
||||
timespinner_options: Dict[str, Option] = {
|
||||
"StartWithJewelryBox": StartWithJewelryBox,
|
||||
@@ -296,9 +324,11 @@ timespinner_options: Dict[str, Option] = {
|
||||
"DeathLink": DeathLink,
|
||||
}
|
||||
|
||||
|
||||
def is_option_enabled(world: MultiWorld, player: int, name: str) -> bool:
|
||||
return get_option_value(world, player, name) > 0
|
||||
|
||||
|
||||
def get_option_value(world: MultiWorld, player: int, name: str) -> Union[int, dict]:
|
||||
option = getattr(world, name, None)
|
||||
if option == None:
|
||||
|
||||
@@ -15,9 +15,9 @@ class DisableNonRandomizedPuzzles(DefaultOnToggle):
|
||||
|
||||
|
||||
class EarlySecretArea(Toggle):
|
||||
"""Opens the Mountainside shortcut to the Mountain Secret Area from the start.
|
||||
"""Opens the Mountainside shortcut to the Caves from the start.
|
||||
(Otherwise known as "UTM", "Caves" or the "Challenge Area")"""
|
||||
display_name = "Early Secret Area"
|
||||
display_name = "Early Caves"
|
||||
|
||||
|
||||
class ShuffleSymbols(DefaultOnToggle):
|
||||
@@ -58,15 +58,9 @@ class ShuffleVaultBoxes(Toggle):
|
||||
display_name = "Shuffle Vault Boxes"
|
||||
|
||||
|
||||
class ShuffleUncommonLocations(Toggle):
|
||||
"""Adds some optional puzzles that are somewhat difficult or out of the way.
|
||||
Examples: Mountaintop River Shape, Tutorial Patio Floor, Theater Videos"""
|
||||
display_name = "Shuffle Uncommon Locations"
|
||||
|
||||
|
||||
class ShufflePostgame(Toggle):
|
||||
"""Adds locations into the pool that are guaranteed to be locked behind your goal. Use this if you don't play with
|
||||
forfeit on victory."""
|
||||
"""Adds locations into the pool that are guaranteed to become accessible before or at the same time as your goal.
|
||||
Use this if you don't play with forfeit on victory."""
|
||||
display_name = "Shuffle Postgame"
|
||||
|
||||
|
||||
@@ -90,7 +84,7 @@ class MountainLasers(Range):
|
||||
|
||||
|
||||
class ChallengeLasers(Range):
|
||||
"""Sets the amount of beams required to enter the secret area through the Mountain Bottom Layer Discard."""
|
||||
"""Sets the amount of beams required to enter the Caves through the Mountain Bottom Floor Discard."""
|
||||
display_name = "Required Lasers for Challenge"
|
||||
range_start = 1
|
||||
range_end = 11
|
||||
@@ -122,7 +116,6 @@ the_witness_options: Dict[str, type] = {
|
||||
"disable_non_randomized_puzzles": DisableNonRandomizedPuzzles,
|
||||
"shuffle_discarded_panels": ShuffleDiscardedPanels,
|
||||
"shuffle_vault_boxes": ShuffleVaultBoxes,
|
||||
"shuffle_uncommon": ShuffleUncommonLocations,
|
||||
"shuffle_postgame": ShufflePostgame,
|
||||
"victory_condition": VictoryCondition,
|
||||
"mountain_lasers": MountainLasers,
|
||||
|
||||
@@ -28,38 +28,38 @@ Traps:
|
||||
610 - Power Surge
|
||||
|
||||
Doors:
|
||||
1100 - Glass Factory Entry Door (Panel) - 0x01A54
|
||||
1105 - Door to Symmetry Island Lower (Panel) - 0x000B0
|
||||
1107 - Door to Symmetry Island Upper (Panel) - 0x1C349
|
||||
1110 - Door to Desert Flood Light Room (Panel) - 0x0C339
|
||||
1111 - Desert Flood Room Flood Controls (Panel) - 0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B
|
||||
1119 - Quarry Door to Mill (Panel) - 0x01E5A,0x01E59
|
||||
1100 - Glass Factory Entry (Panel) - 0x01A54
|
||||
1105 - Symmetry Island Lower (Panel) - 0x000B0
|
||||
1107 - Symmetry Island Upper (Panel) - 0x1C349
|
||||
1110 - Desert Light Room Entry (Panel) - 0x0C339
|
||||
1111 - Desert Flood Controls (Panel) - 0x1C2DF,0x1831E,0x1C260,0x1831C,0x1C2F3,0x1831D,0x1C2B1,0x1831B
|
||||
1119 - Quarry Mill Entry (Panel) - 0x01E5A,0x01E59
|
||||
1120 - Quarry Mill Ramp Controls (Panel) - 0x03678,0x03676
|
||||
1122 - Quarry Mill Elevator Controls (Panel) - 0x03679,0x03675
|
||||
1122 - Quarry Mill Lift Controls (Panel) - 0x03679,0x03675
|
||||
1125 - Quarry Boathouse Ramp Height Control (Panel) - 0x03852
|
||||
1127 - Quarry Boathouse Ramp Horizontal Control (Panel) - 0x03858
|
||||
1131 - Shadows Door Timer (Panel) - 0x334DB,0x334DC
|
||||
1150 - Monastery Entry Door Left (Panel) - 0x00B10
|
||||
1151 - Monastery Entry Door Right (Panel) - 0x00C92
|
||||
1162 - Town Door to RGB House (Panel) - 0x28998
|
||||
1163 - Town Door to Church (Panel) - 0x28A0D
|
||||
1150 - Monastery Entry Left (Panel) - 0x00B10
|
||||
1151 - Monastery Entry Right (Panel) - 0x00C92
|
||||
1162 - Town Tinted Glass Door (Panel) - 0x28998
|
||||
1163 - Town Church Entry (Panel) - 0x28A0D
|
||||
1166 - Town Maze Panel (Drop-Down Staircase) (Panel) - 0x28A79
|
||||
1169 - Windmill Door (Panel) - 0x17F5F
|
||||
1169 - Windmill Entry (Panel) - 0x17F5F
|
||||
1200 - Treehouse First & Second Doors (Panel) - 0x0288C,0x02886
|
||||
1202 - Treehouse Third Door (Panel) - 0x0A182
|
||||
1205 - Treehouse Laser House Door Timer (Panel) - 0x2700B,0x334DC
|
||||
1208 - Treehouse Shortcut Drop-Down Bridge (Panel) - 0x17CBC
|
||||
1208 - Treehouse Drawbridge (Panel) - 0x17CBC
|
||||
1175 - Jungle Popup Wall (Panel) - 0x17CAB
|
||||
1180 - Bunker Entry Door (Panel) - 0x17C2E
|
||||
1183 - Inside Bunker Door to Bunker Proper (Panel) - 0x0A099
|
||||
1180 - Bunker Entry (Panel) - 0x17C2E
|
||||
1183 - Bunker Tinted Glass Door (Panel) - 0x0A099
|
||||
1186 - Bunker Elevator Control (Panel) - 0x0A079
|
||||
1190 - Swamp Entry Door (Panel) - 0x0056E
|
||||
1190 - Swamp Entry (Panel) - 0x0056E
|
||||
1192 - Swamp Sliding Bridge (Panel) - 0x00609,0x18488
|
||||
1195 - Swamp Rotating Bridge (Panel) - 0x181F5
|
||||
1197 - Swamp Maze Control (Panel) - 0x17C0A
|
||||
1310 - Boat - 0x17CDF,0x17CC8,0x17CA6,0x09DB8,0x17C95,0x0A054
|
||||
|
||||
1400 - Caves Mountain Shortcut - 0x2D73F
|
||||
1400 - Caves Mountain Shortcut (Door) - 0x2D73F
|
||||
|
||||
1500 - Symmetry Laser - 0x00509
|
||||
1501 - Desert Laser - 0x012FB,0x01317
|
||||
@@ -73,101 +73,101 @@ Doors:
|
||||
1509 - Swamp Laser - 0x00BF6
|
||||
1510 - Treehouse Laser - 0x028A4
|
||||
|
||||
1600 - Outside Tutorial Optional Door - 0x03BA2
|
||||
1603 - Outside Tutorial Outpost Entry Door - 0x0A170
|
||||
1606 - Outside Tutorial Outpost Exit Door - 0x04CA3
|
||||
1609 - Glass Factory Entry Door - 0x01A29
|
||||
1612 - Glass Factory Back Wall - 0x0D7ED
|
||||
1615 - Symmetry Island Lower Door - 0x17F3E
|
||||
1618 - Symmetry Island Upper Door - 0x18269
|
||||
1619 - Orchard Middle Gate - 0x03307
|
||||
1620 - Orchard Final Gate - 0x03313
|
||||
1621 - Desert Door to Flood Light Room - 0x09FEE
|
||||
1624 - Desert Door to Pond Room - 0x0C2C3
|
||||
1627 - Desert Door to Water Levels Room - 0x0A24B
|
||||
1630 - Desert Door to Elevator Room - 0x0C316
|
||||
1633 - Quarry Main Entry 1 - 0x09D6F
|
||||
1636 - Quarry Main Entry 2 - 0x17C07
|
||||
1639 - Quarry Door to Mill - 0x02010
|
||||
1642 - Quarry Mill Side Door - 0x275FF
|
||||
1645 - Quarry Mill Rooftop Shortcut - 0x17CE8
|
||||
1648 - Quarry Mill Stairs - 0x0368A
|
||||
1651 - Quarry Boathouse Boat Staircase - 0x2769B,0x27163
|
||||
1653 - Quarry Boathouse First Barrier - 0x17C50
|
||||
1654 - Quarry Boathouse Shortcut - 0x3865F
|
||||
1656 - Shadows Timed Door - 0x19B24
|
||||
1657 - Shadows Laser Room Right Door - 0x194B2
|
||||
1660 - Shadows Laser Room Left Door - 0x19665
|
||||
1663 - Shadows Barrier to Quarry - 0x19865,0x0A2DF
|
||||
1666 - Shadows Barrier to Ledge - 0x1855B,0x19ADE
|
||||
1669 - Keep Hedge Maze 1 Exit Door - 0x01954
|
||||
1672 - Keep Pressure Plates 1 Exit Door - 0x01BEC
|
||||
1675 - Keep Hedge Maze 2 Shortcut - 0x018CE
|
||||
1678 - Keep Hedge Maze 2 Exit Door - 0x019D8
|
||||
1681 - Keep Hedge Maze 3 Shortcut - 0x019B5
|
||||
1684 - Keep Hedge Maze 3 Exit Door - 0x019E6
|
||||
1687 - Keep Hedge Maze 4 Shortcut - 0x0199A
|
||||
1690 - Keep Hedge Maze 4 Exit Door - 0x01A0E
|
||||
1693 - Keep Pressure Plates 2 Exit Door - 0x01BEA
|
||||
1696 - Keep Pressure Plates 3 Exit Door - 0x01CD5
|
||||
1699 - Keep Pressure Plates 4 Exit Door - 0x01D40
|
||||
1702 - Keep Shortcut to Shadows - 0x09E3D
|
||||
1705 - Keep Tower Shortcut - 0x04F8F
|
||||
1708 - Monastery Shortcut - 0x0364E
|
||||
1711 - Monastery Inner Door - 0x0C128
|
||||
1714 - Monastery Outer Door - 0x0C153
|
||||
1717 - Monastery Door to Garden - 0x03750
|
||||
1718 - Town Cargo Box Door - 0x0A0C9
|
||||
1720 - Town Wooden Roof Staircase - 0x034F5
|
||||
1723 - Town Tinted Door to RGB House - 0x28A61
|
||||
1726 - Town Door to Church - 0x03BB0
|
||||
1729 - Town Maze Staircase - 0x28AA2
|
||||
1732 - Town Windmill Door - 0x1845B
|
||||
1735 - Town RGB House Staircase - 0x2897B
|
||||
1738 - Town Tower Blue Panels Door - 0x27798
|
||||
1741 - Town Tower Lattice Door - 0x27799
|
||||
1744 - Town Tower Environmental Set Door - 0x2779A
|
||||
1747 - Town Tower Wooden Roof Set Door - 0x2779C
|
||||
1750 - Theater Entry Door - 0x17F88
|
||||
1753 - Theater Exit Door Left - 0x0A16D
|
||||
1756 - Theater Exit Door Right - 0x3CCDF
|
||||
1759 - Jungle Bamboo Shortcut to River - 0x3873B
|
||||
1760 - Jungle Popup Wall - 0x1475B
|
||||
1762 - River Shortcut to Monastery Garden - 0x0CF2A
|
||||
1765 - Bunker Bunker Entry Door - 0x0C2A4
|
||||
1768 - Bunker Tinted Glass Door - 0x17C79
|
||||
1771 - Bunker Door to Ultraviolet Room - 0x0C2A3
|
||||
1774 - Bunker Door to Elevator - 0x0A08D
|
||||
1777 - Swamp Entry Door - 0x00C1C
|
||||
1780 - Swamp Door to Broken Shapers - 0x184B7
|
||||
1600 - Outside Tutorial Outpost Path (Door) - 0x03BA2
|
||||
1603 - Outside Tutorial Outpost Entry (Door) - 0x0A170
|
||||
1606 - Outside Tutorial Outpost Exit (Door) - 0x04CA3
|
||||
1609 - Glass Factory Entry (Door) - 0x01A29
|
||||
1612 - Glass Factory Back Wall (Door) - 0x0D7ED
|
||||
1615 - Symmetry Island Lower (Door) - 0x17F3E
|
||||
1618 - Symmetry Island Upper (Door) - 0x18269
|
||||
1619 - Orchard First Gate (Door) - 0x03307
|
||||
1620 - Orchard Second Gate (Door) - 0x03313
|
||||
1621 - Desert Light Room Entry (Door) - 0x09FEE
|
||||
1624 - Desert Pond Room Entry (Door) - 0x0C2C3
|
||||
1627 - Desert Flood Room Entry (Door) - 0x0A24B
|
||||
1630 - Desert Elevator Room Entry (Door) - 0x0C316
|
||||
1633 - Quarry Entry 1 (Door) - 0x09D6F
|
||||
1636 - Quarry Entry 2 (Door) - 0x17C07
|
||||
1639 - Quarry Mill Entry (Door) - 0x02010
|
||||
1642 - Quarry Mill Side Exit (Door) - 0x275FF
|
||||
1645 - Quarry Mill Roof Exit (Door) - 0x17CE8
|
||||
1648 - Quarry Mill Stairs (Door) - 0x0368A
|
||||
1651 - Quarry Boathouse Dock (Door) - 0x2769B,0x27163
|
||||
1653 - Quarry Boathouse First Barrier (Door) - 0x17C50
|
||||
1654 - Quarry Boathouse Second Barrier (Door) - 0x3865F
|
||||
1656 - Shadows Timed Door (Door) - 0x19B24
|
||||
1657 - Shadows Laser Entry Right (Door) - 0x194B2
|
||||
1660 - Shadows Laser Entry Left (Door) - 0x19665
|
||||
1663 - Shadows Quarry Barrier (Door) - 0x19865,0x0A2DF
|
||||
1666 - Shadows Ledge Barrier (Door) - 0x1855B,0x19ADE
|
||||
1669 - Keep Hedge Maze 1 Exit (Door) - 0x01954
|
||||
1672 - Keep Pressure Plates 1 Exit (Door) - 0x01BEC
|
||||
1675 - Keep Hedge Maze 2 Shortcut (Door) - 0x018CE
|
||||
1678 - Keep Hedge Maze 2 Exit (Door) - 0x019D8
|
||||
1681 - Keep Hedge Maze 3 Shortcut (Door) - 0x019B5
|
||||
1684 - Keep Hedge Maze 3 Exit (Door) - 0x019E6
|
||||
1687 - Keep Hedge Maze 4 Shortcut (Door) - 0x0199A
|
||||
1690 - Keep Hedge Maze 4 Exit (Door) - 0x01A0E
|
||||
1693 - Keep Pressure Plates 2 Exit (Door) - 0x01BEA
|
||||
1696 - Keep Pressure Plates 3 Exit (Door) - 0x01CD5
|
||||
1699 - Keep Pressure Plates 4 Exit (Door) - 0x01D40
|
||||
1702 - Keep Shadows Shortcut (Door) - 0x09E3D
|
||||
1705 - Keep Tower Shortcut (Door) - 0x04F8F
|
||||
1708 - Monastery Shortcut (Door) - 0x0364E
|
||||
1711 - Monastery Entry Inner (Door) - 0x0C128
|
||||
1714 - Monastery Entry Outer (Door) - 0x0C153
|
||||
1717 - Monastery Garden Entry (Door) - 0x03750
|
||||
1718 - Town Cargo Box Entry (Door) - 0x0A0C9
|
||||
1720 - Town Wooden Roof Stairs (Door) - 0x034F5
|
||||
1723 - Town Tinted Glass Door (Door) - 0x28A61
|
||||
1726 - Town Church Entry (Door) - 0x03BB0
|
||||
1729 - Town Maze Stairs (Door) - 0x28AA2
|
||||
1732 - Town Windmill Entry (Door) - 0x1845B
|
||||
1735 - Town RGB House Stairs (Door) - 0x2897B
|
||||
1738 - Town Tower First Door (Door) - 0x27798
|
||||
1741 - Town Tower Third Door (Door) - 0x27799
|
||||
1744 - Town Tower Fourth Door (Door) - 0x2779A
|
||||
1747 - Town Tower Second Door (Door) - 0x2779C
|
||||
1750 - Theater Entry (Door) - 0x17F88
|
||||
1753 - Theater Exit Left (Door) - 0x0A16D
|
||||
1756 - Theater Exit Right (Door) - 0x3CCDF
|
||||
1759 - Jungle Bamboo Laser Shortcut (Door) - 0x3873B
|
||||
1760 - Jungle Popup Wall (Door) - 0x1475B
|
||||
1762 - River Monastery Shortcut (Door) - 0x0CF2A
|
||||
1765 - Bunker Entry (Door) - 0x0C2A4
|
||||
1768 - Bunker Tinted Glass Door (Door) - 0x17C79
|
||||
1771 - Bunker UV Room Entry (Door) - 0x0C2A3
|
||||
1774 - Bunker Elevator Room Entry (Door) - 0x0A08D
|
||||
1777 - Swamp Entry (Door) - 0x00C1C
|
||||
1780 - Swamp Between Bridges First Door - 0x184B7
|
||||
1783 - Swamp Platform Shortcut Door - 0x38AE6
|
||||
1786 - Swamp Cyan Water Pump - 0x04B7F
|
||||
1789 - Swamp Door to Rotated Shapers - 0x18507
|
||||
1792 - Swamp Red Water Pump - 0x183F2
|
||||
1795 - Swamp Red Underwater Exit - 0x305D5
|
||||
1798 - Swamp Blue Water Pump - 0x18482
|
||||
1801 - Swamp Purple Water Pump - 0x0A1D6
|
||||
1804 - Swamp Near Laser Shortcut - 0x2D880
|
||||
1807 - Treehouse First Door - 0x0C309
|
||||
1810 - Treehouse Second Door - 0x0C310
|
||||
1813 - Treehouse Beyond Yellow Bridge Door - 0x0A181
|
||||
1816 - Treehouse Drawbridge - 0x0C32D
|
||||
1819 - Treehouse Timed Door to Laser House - 0x0C323
|
||||
1822 - Inside Mountain First Layer Exit Door - 0x09E54
|
||||
1825 - Inside Mountain Second Layer Staircase Near - 0x09FFB
|
||||
1828 - Inside Mountain Second Layer Exit Door - 0x09EDD
|
||||
1831 - Inside Mountain Second Layer Staircase Far - 0x09E07
|
||||
1834 - Inside Mountain Giant Puzzle Exit Door - 0x09F89
|
||||
1840 - Inside Mountain Door to Final Room - 0x0C141
|
||||
1843 - Inside Mountain Bottom Layer Rock - 0x17F33
|
||||
1846 - Inside Mountain Door to Secret Area - 0x2D77D
|
||||
1849 - Caves Pillar Door - 0x019A5
|
||||
1855 - Caves Swamp Shortcut - 0x2D859
|
||||
1858 - Challenge Entry Door - 0x0A19A
|
||||
1861 - Challenge Door to Theater Walkway - 0x0348A
|
||||
1864 - Theater Walkway Door to Windmill Interior - 0x27739
|
||||
1867 - Theater Walkway Door to Desert Elevator Room - 0x27263
|
||||
1870 - Theater Walkway Door to Town - 0x09E87
|
||||
1786 - Swamp Cyan Water Pump (Door) - 0x04B7F
|
||||
1789 - Swamp Between Bridges Second Door - 0x18507
|
||||
1792 - Swamp Red Water Pump (Door) - 0x183F2
|
||||
1795 - Swamp Red Underwater Exit (Door) - 0x305D5
|
||||
1798 - Swamp Blue Water Pump (Door) - 0x18482
|
||||
1801 - Swamp Purple Water Pump (Door) - 0x0A1D6
|
||||
1804 - Swamp Laser Shortcut (Door) - 0x2D880
|
||||
1807 - Treehouse First Door (Door) - 0x0C309
|
||||
1810 - Treehouse Second Door (Door) - 0x0C310
|
||||
1813 - Treehouse Third Door (Door) - 0x0A181
|
||||
1816 - Treehouse Drawbridge (Door) - 0x0C32D
|
||||
1819 - Treehouse Laser House Entry (Door) - 0x0C323
|
||||
1822 - Mountain Floor 1 Exit (Door) - 0x09E54
|
||||
1825 - Mountain Floor 2 Staircase Near (Door) - 0x09FFB
|
||||
1828 - Mountain Floor 2 Exit (Door) - 0x09EDD
|
||||
1831 - Mountain Floor 2 Staircase Far (Door) - 0x09E07
|
||||
1834 - Mountain Bottom Floor Giant Puzzle Exit (Door) - 0x09F89
|
||||
1840 - Mountain Bottom Floor Final Room Entry (Door) - 0x0C141
|
||||
1843 - Mountain Bottom Floor Rock (Door) - 0x17F33
|
||||
1846 - Caves Entry (Door) - 0x2D77D
|
||||
1849 - Caves Pillar Door (Door) - 0x019A5
|
||||
1855 - Caves Swamp Shortcut (Door) - 0x2D859
|
||||
1858 - Challenge Entry (Door) - 0x0A19A
|
||||
1861 - Challenge Tunnels Entry (Door) - 0x0348A
|
||||
1864 - Tunnels Theater Shortcut (Door) - 0x27739
|
||||
1867 - Tunnels Desert Shortcut (Door) - 0x27263
|
||||
1870 - Tunnels Town Shortcut (Door) - 0x09E87
|
||||
|
||||
1903 - Outside Tutorial Outpost Doors - 0x03BA2,0x0A170,0x04CA3
|
||||
1906 - Symmetry Island Doors - 0x17F3E,0x18269
|
||||
@@ -181,18 +181,18 @@ Doors:
|
||||
1930 - Keep Hedge Maze Doors - 0x01954,0x018CE,0x019D8,0x019B5,0x019E6,0x0199A,0x01A0E
|
||||
1933 - Keep Pressure Plates Doors - 0x01BEC,0x01BEA,0x01CD5,0x01D40
|
||||
1936 - Keep Shortcuts - 0x09E3D,0x04F8F
|
||||
1939 - Monastery Entry Door - 0x0C128,0x0C153
|
||||
1939 - Monastery Entry - 0x0C128,0x0C153
|
||||
1942 - Monastery Shortcuts - 0x0364E,0x03750
|
||||
1945 - Town Doors - 0x0A0C9,0x034F5,0x28A61,0x03BB0,0x28AA2,0x1845B,0x2897B
|
||||
1948 - Town Tower Doors - 0x27798,0x27799,0x2779A,0x2779C
|
||||
1951 - Theater Exit Door - 0x0A16D,0x3CCDF
|
||||
1951 - Theater Exit - 0x0A16D,0x3CCDF
|
||||
1954 - Jungle & River Shortcuts - 0x3873B,0x0CF2A
|
||||
1957 - Bunker Doors - 0x0C2A4,0x17C79,0x0C2A3,0x0A08D
|
||||
1960 - Swamp Doors - 0x00C1C,0x184B7,0x38AE6,0x18507
|
||||
1963 - Swamp Water Pumps - 0x04B7F,0x183F2,0x305D5,0x18482,0x0A1D6
|
||||
1966 - Treehouse Entry Doors - 0x0C309,0x0C310,0x0A181
|
||||
1975 - Inside Mountain Second Layer Stairs & Doors - 0x09FFB,0x09EDD,0x09E07
|
||||
1978 - Inside Mountain Bottom Layer Doors to Caves - 0x17F33,0x2D77D
|
||||
1975 - Mountain Floor 2 Stairs & Doors - 0x09FFB,0x09EDD,0x09E07
|
||||
1978 - Mountain Bottom Floor Doors to Caves - 0x17F33,0x2D77D
|
||||
1981 - Caves Doors to Challenge - 0x019A5,0x0A19A
|
||||
1984 - Caves Exits to Main Island - 0x2D859,0x2D73F
|
||||
1987 - Theater Walkway Doors - 0x27739,0x27263,0x09E87
|
||||
1987 - Tunnels Doors - 0x27739,0x27263,0x09E87
|
||||
File diff suppressed because it is too large
Load Diff
@@ -13,6 +13,7 @@ from .rules import set_rules
|
||||
from .regions import WitnessRegions
|
||||
from .Options import is_option_enabled, the_witness_options, get_option_value
|
||||
from .utils import best_junk_to_add_based_on_weights
|
||||
from logging import warning
|
||||
|
||||
|
||||
class WitnessWebWorld(WebWorld):
|
||||
@@ -35,7 +36,7 @@ class WitnessWorld(World):
|
||||
"""
|
||||
game = "The Witness"
|
||||
topology_present = False
|
||||
data_version = 5
|
||||
data_version = 7
|
||||
|
||||
static_logic = StaticWitnessLogic()
|
||||
static_locat = StaticWitnessLocations()
|
||||
@@ -56,15 +57,20 @@ class WitnessWorld(World):
|
||||
'panelhex_to_id': self.locat.CHECK_PANELHEX_TO_ID,
|
||||
'item_id_to_door_hexes': self.items.ITEM_ID_TO_DOOR_HEX,
|
||||
'door_hexes': self.items.DOORS,
|
||||
'symbols_not_in_the_game': self.items.SYMBOLS_NOT_IN_THE_GAME
|
||||
'symbols_not_in_the_game': self.items.SYMBOLS_NOT_IN_THE_GAME,
|
||||
'disabled_panels': self.player_logic.COMPLETELY_DISABLED_CHECKS,
|
||||
}
|
||||
|
||||
def generate_early(self):
|
||||
if not (is_option_enabled(self.world, self.player, "shuffle_symbols")
|
||||
or get_option_value(self.world, self.player, "shuffle_doors")
|
||||
or is_option_enabled(self.world, self.player, "shuffle_lasers")):
|
||||
raise Exception("This Witness world doesn't have any progression items. Please turn on Symbol Shuffle, Door"
|
||||
" Shuffle or Laser Shuffle")
|
||||
if self.world.players == 1:
|
||||
warning("This Witness world doesn't have any progression items. Please turn on Symbol Shuffle, Door"
|
||||
" Shuffle or Laser Shuffle if that doesn't seem right.")
|
||||
else:
|
||||
raise Exception("This Witness world doesn't have any progression items. Please turn on Symbol Shuffle,"
|
||||
" Door Shuffle or Laser Shuffle.")
|
||||
|
||||
self.player_logic = WitnessPlayerLogic(self.world, self.player)
|
||||
self.locat = WitnessPlayerLocations(self.world, self.player, self.player_logic)
|
||||
|
||||
@@ -30,17 +30,17 @@ class StaticWitnessLocations:
|
||||
|
||||
"Outside Tutorial Vault Box",
|
||||
"Outside Tutorial Discard",
|
||||
"Outside Tutorial Dots Introduction 5",
|
||||
"Outside Tutorial Squares Introduction 9",
|
||||
"Outside Tutorial Shed Row 5",
|
||||
"Outside Tutorial Tree Row 9",
|
||||
|
||||
"Glass Factory Discard",
|
||||
"Glass Factory Vertical Symmetry 5",
|
||||
"Glass Factory Rotational Symmetry 3",
|
||||
"Glass Factory Back Wall 5",
|
||||
"Glass Factory Front 3",
|
||||
"Glass Factory Melting 3",
|
||||
|
||||
"Symmetry Island Black Dots 5",
|
||||
"Symmetry Island Colored Dots 6",
|
||||
"Symmetry Island Fading Lines 7",
|
||||
"Symmetry Island Right 5",
|
||||
"Symmetry Island Back 6",
|
||||
"Symmetry Island Left 7",
|
||||
"Symmetry Island Scenery Outlines 5",
|
||||
"Symmetry Island Laser Panel",
|
||||
|
||||
@@ -48,26 +48,28 @@ class StaticWitnessLocations:
|
||||
|
||||
"Desert Vault Box",
|
||||
"Desert Discard",
|
||||
"Desert Sun Reflection 8",
|
||||
"Desert Artificial Light Reflection 3",
|
||||
"Desert Pond Reflection 5",
|
||||
"Desert Flood Reflection 6",
|
||||
"Desert Surface 8",
|
||||
"Desert Light Room 3",
|
||||
"Desert Pond Room 5",
|
||||
"Desert Flood Room 6",
|
||||
"Desert Final Bent 3",
|
||||
"Desert Final Hexagonal",
|
||||
"Desert Laser Panel",
|
||||
|
||||
"Quarry Mill Eraser and Dots 6",
|
||||
"Quarry Mill Eraser and Squares 8",
|
||||
"Quarry Mill Small Squares & Dots & Eraser",
|
||||
"Quarry Boathouse Intro Shapers",
|
||||
"Quarry Boathouse Intro Stars",
|
||||
"Quarry Boathouse Eraser and Shapers 5",
|
||||
"Quarry Boathouse Stars & Eraser & Shapers 2",
|
||||
"Quarry Boathouse Stars & Eraser & Shapers 5",
|
||||
"Quarry Mill Lower Row 6",
|
||||
"Quarry Mill Upper Row 8",
|
||||
"Quarry Mill Control Room Right",
|
||||
"Quarry Boathouse Intro Right",
|
||||
"Quarry Boathouse Intro Left",
|
||||
"Quarry Boathouse Front Row 5",
|
||||
"Quarry Boathouse Back First Row 9",
|
||||
"Quarry Boathouse Back Second Row 3",
|
||||
"Quarry Discard",
|
||||
"Quarry Laser Panel",
|
||||
|
||||
"Shadows Lower Avoid 8",
|
||||
"Shadows Environmental Avoid 8",
|
||||
"Shadows Follow 5",
|
||||
"Shadows Intro 8",
|
||||
"Shadows Far 8",
|
||||
"Shadows Near 5",
|
||||
"Shadows Laser Panel",
|
||||
|
||||
"Keep Hedge Maze 4",
|
||||
@@ -79,44 +81,44 @@ class StaticWitnessLocations:
|
||||
"Shipwreck Vault Box",
|
||||
"Shipwreck Discard",
|
||||
|
||||
"Monastery Rhombic Avoid 3",
|
||||
"Monastery Branch Follow 2",
|
||||
"Monastery Outside 3",
|
||||
"Monastery Inside 4",
|
||||
"Monastery Laser Panel",
|
||||
|
||||
"Town Cargo Box Discard",
|
||||
"Town Hexagonal Reflection",
|
||||
"Town Tall Hexagonal",
|
||||
"Town Church Lattice",
|
||||
"Town Rooftop Discard",
|
||||
"Town Symmetry Squares 5 + Dots",
|
||||
"Town Full Dot Grid Shapers 5",
|
||||
"Town Shapers & Dots & Eraser",
|
||||
"Town Red Rooftop 5",
|
||||
"Town Wooden Roof Lower Row 5",
|
||||
"Town Wooden Rooftop",
|
||||
"Town Laser Panel",
|
||||
|
||||
"Theater Discard",
|
||||
|
||||
"Jungle Discard",
|
||||
"Jungle Waves 3",
|
||||
"Jungle Waves 7",
|
||||
"Jungle First Row 3",
|
||||
"Jungle Second Row 4",
|
||||
"Jungle Popup Wall 6",
|
||||
"Jungle Laser Panel",
|
||||
|
||||
"River Vault Box",
|
||||
|
||||
"Bunker Drawn Squares 5",
|
||||
"Bunker Drawn Squares 9",
|
||||
"Bunker Drawn Squares through Tinted Glass 3",
|
||||
"Bunker Drop-Down Door Squares 2",
|
||||
"Bunker Intro Left 5",
|
||||
"Bunker Intro Back 4",
|
||||
"Bunker Glass Room 3",
|
||||
"Bunker UV Room 2",
|
||||
"Bunker Laser Panel",
|
||||
|
||||
"Swamp Seperatable Shapers 6",
|
||||
"Swamp Combinable Shapers 8",
|
||||
"Swamp Broken Shapers 4",
|
||||
"Swamp Cyan Underwater Negative Shapers 5",
|
||||
"Swamp Platform Shapers 4",
|
||||
"Swamp Rotated Shapers 4",
|
||||
"Swamp Red Underwater Negative Shapers 4",
|
||||
"Swamp More Rotated Shapers 4",
|
||||
"Swamp Blue Underwater Negative Shapers 5",
|
||||
"Swamp Intro Front 6",
|
||||
"Swamp Intro Back 8",
|
||||
"Swamp Between Bridges Near Row 4",
|
||||
"Swamp Cyan Underwater 5",
|
||||
"Swamp Platform Row 4",
|
||||
"Swamp Between Bridges Far Row 4",
|
||||
"Swamp Red Underwater 4",
|
||||
"Swamp Beyond Rotating Bridge 4",
|
||||
"Swamp Blue Underwater 5",
|
||||
"Swamp Laser Panel",
|
||||
|
||||
"Treehouse Yellow Bridge 9",
|
||||
@@ -125,73 +127,77 @@ class StaticWitnessLocations:
|
||||
"Treehouse Green Bridge 7",
|
||||
"Treehouse Green Bridge Discard",
|
||||
"Treehouse Left Orange Bridge 15",
|
||||
"Treehouse Burnt House Discard",
|
||||
"Treehouse Laser Discard",
|
||||
"Treehouse Right Orange Bridge 12",
|
||||
"Treehouse Laser Panel",
|
||||
|
||||
"Mountaintop Discard",
|
||||
"Mountaintop Vault Box",
|
||||
}
|
||||
"Mountainside Discard",
|
||||
"Mountainside Vault Box",
|
||||
|
||||
UNCOMMON_LOCATIONS = {
|
||||
"Mountaintop River Shape",
|
||||
"Tutorial Patio Floor",
|
||||
"Quarry Mill Big Squares & Dots & Eraser",
|
||||
"Quarry Mill Control Room Left",
|
||||
"Theater Tutorial Video",
|
||||
"Theater Desert Video",
|
||||
"Theater Jungle Video",
|
||||
"Theater Shipwreck Video",
|
||||
"Theater Mountain Video",
|
||||
"Town RGB Squares",
|
||||
"Town RGB Stars",
|
||||
"Swamp Underwater Back Optional",
|
||||
"Town RGB Room Left",
|
||||
"Town RGB Room Right",
|
||||
"Swamp Purple Underwater",
|
||||
}
|
||||
|
||||
CAVES_LOCATIONS = {
|
||||
"Inside Mountain Caves Dot Grid Triangles 4",
|
||||
"Inside Mountain Caves Symmetry Triangles",
|
||||
"Inside Mountain Caves Stars & Squares and Triangles 2",
|
||||
"Inside Mountain Caves Shapers and Triangles 2",
|
||||
"Inside Mountain Caves Symmetry Shapers",
|
||||
"Inside Mountain Caves Broken and Negative Shapers",
|
||||
"Inside Mountain Caves Broken Shapers",
|
||||
"Caves Blue Tunnel Right First 4",
|
||||
"Caves Blue Tunnel Left First 1",
|
||||
"Caves Blue Tunnel Left Second 5",
|
||||
"Caves Blue Tunnel Right Second 5",
|
||||
"Caves Blue Tunnel Right Third 1",
|
||||
"Caves Blue Tunnel Left Fourth 1",
|
||||
"Caves Blue Tunnel Left Third 1",
|
||||
|
||||
"Inside Mountain Caves Rainbow Squares",
|
||||
"Inside Mountain Caves Squares & Stars and Colored Eraser",
|
||||
"Inside Mountain Caves Rotated Broken Shapers",
|
||||
"Inside Mountain Caves Stars and Squares",
|
||||
"Inside Mountain Caves Lone Pillar",
|
||||
"Inside Mountain Caves Wooden Beam Shapers",
|
||||
"Inside Mountain Caves Wooden Beam Squares and Shapers",
|
||||
"Inside Mountain Caves Wooden Beam Stars and Squares",
|
||||
"Inside Mountain Caves Wooden Beam Shapers and Stars",
|
||||
"Inside Mountain Caves Upstairs Invisible Dots 8",
|
||||
"Inside Mountain Caves Upstairs Invisible Dot Symmetry 3",
|
||||
"Inside Mountain Caves Upstairs Dot Grid Negative Shapers",
|
||||
"Inside Mountain Caves Upstairs Dot Grid Rotated Shapers",
|
||||
"Caves First Floor Middle",
|
||||
"Caves First Floor Right",
|
||||
"Caves First Floor Left",
|
||||
"Caves First Floor Grounded",
|
||||
"Caves Lone Pillar",
|
||||
"Caves First Wooden Beam",
|
||||
"Caves Second Wooden Beam",
|
||||
"Caves Third Wooden Beam",
|
||||
"Caves Fourth Wooden Beam",
|
||||
"Caves Right Upstairs Left Row 8",
|
||||
"Caves Right Upstairs Right Row 3",
|
||||
"Caves Left Upstairs Single",
|
||||
"Caves Left Upstairs Left Row 5",
|
||||
|
||||
"Theater Walkway Vault Box",
|
||||
"Inside Mountain Bottom Layer Discard",
|
||||
"Tunnels Vault Box",
|
||||
"Mountain Bottom Floor Discard",
|
||||
"Theater Challenge Video",
|
||||
}
|
||||
|
||||
MOUNTAIN_UNREACHABLE_FROM_BEHIND = {
|
||||
"Mountaintop Trap Door Triple Exit",
|
||||
|
||||
"Inside Mountain Obscured Vision 5",
|
||||
"Inside Mountain Moving Background 7",
|
||||
"Inside Mountain Physically Obstructed 3",
|
||||
"Inside Mountain Angled Inside Trash 2",
|
||||
"Inside Mountain Color Cycle 5",
|
||||
"Inside Mountain Same Solution 6",
|
||||
"Mountain Floor 1 Right Row 5",
|
||||
"Mountain Floor 1 Left Row 7",
|
||||
"Mountain Floor 1 Back Row 3",
|
||||
"Mountain Floor 1 Trash Pillar 2",
|
||||
"Mountain Floor 2 Near Row 5",
|
||||
"Mountain Floor 2 Far Row 6",
|
||||
}
|
||||
|
||||
MOUNTAIN_REACHABLE_FROM_BEHIND = {
|
||||
"Inside Mountain Elevator Discard",
|
||||
"Inside Mountain Giant Puzzle",
|
||||
"Mountain Floor 2 Elevator Discard",
|
||||
"Mountain Bottom Floor Giant Puzzle",
|
||||
|
||||
"Inside Mountain Final Room Left Pillar 4",
|
||||
"Inside Mountain Final Room Right Pillar 4",
|
||||
"Mountain Final Room Left Pillar 4",
|
||||
"Mountain Final Room Right Pillar 4",
|
||||
}
|
||||
|
||||
MOUNTAIN_EXTRAS = {
|
||||
"Challenge Vault Box",
|
||||
"Theater Challenge Video",
|
||||
"Mountain Bottom Floor Discard"
|
||||
}
|
||||
|
||||
ALL_LOCATIONS_TO_ID = dict()
|
||||
@@ -241,42 +247,51 @@ class WitnessPlayerLocations:
|
||||
StaticWitnessLocations.GENERAL_LOCATIONS
|
||||
)
|
||||
|
||||
doors = get_option_value(world, player, "shuffle_doors")
|
||||
doors = get_option_value(world, player, "shuffle_doors") >= 2
|
||||
earlyutm = is_option_enabled(world, player, "early_secret_area")
|
||||
victory = get_option_value(world, player, "victory_condition")
|
||||
lasers = get_option_value(world, player, "challenge_lasers")
|
||||
mount_lasers = get_option_value(world, player, "mountain_lasers")
|
||||
chal_lasers = get_option_value(world, player, "challenge_lasers")
|
||||
laser_shuffle = get_option_value(world, player, "shuffle_lasers")
|
||||
|
||||
postgame = set()
|
||||
postgame = postgame | StaticWitnessLocations.CAVES_LOCATIONS
|
||||
postgame = postgame | StaticWitnessLocations.MOUNTAIN_REACHABLE_FROM_BEHIND
|
||||
postgame = postgame | StaticWitnessLocations.MOUNTAIN_UNREACHABLE_FROM_BEHIND
|
||||
postgame = postgame | StaticWitnessLocations.MOUNTAIN_EXTRAS
|
||||
|
||||
self.CHECK_LOCATIONS = self.CHECK_LOCATIONS | postgame
|
||||
|
||||
if earlyutm or doors >= 2 or (victory == 1 and (lasers <= 11 or laser_shuffle)):
|
||||
mountain_enterable_from_top = victory == 0 or victory == 1 or (victory == 3 and chal_lasers > mount_lasers)
|
||||
|
||||
if earlyutm or doors: # in non-doors, there is no way to get symbol-locked by the final pillars (currently)
|
||||
postgame -= StaticWitnessLocations.CAVES_LOCATIONS
|
||||
|
||||
if doors >= 2:
|
||||
if (doors or earlyutm) and (victory == 0 or (victory == 2 and mount_lasers > chal_lasers)):
|
||||
postgame -= {"Challenge Vault Box", "Theater Challenge Video"}
|
||||
|
||||
if doors or mountain_enterable_from_top:
|
||||
postgame -= StaticWitnessLocations.MOUNTAIN_REACHABLE_FROM_BEHIND
|
||||
|
||||
if victory != 2:
|
||||
if mountain_enterable_from_top:
|
||||
postgame -= StaticWitnessLocations.MOUNTAIN_UNREACHABLE_FROM_BEHIND
|
||||
|
||||
if (victory == 0 and doors) or victory == 1 or (victory == 2 and mount_lasers > chal_lasers and doors):
|
||||
postgame -= {"Mountain Bottom Floor Discard"}
|
||||
|
||||
if is_option_enabled(world, player, "shuffle_discarded_panels"):
|
||||
self.PANEL_TYPES_TO_SHUFFLE.add("Discard")
|
||||
|
||||
if is_option_enabled(world, player, "shuffle_vault_boxes"):
|
||||
self.PANEL_TYPES_TO_SHUFFLE.add("Vault")
|
||||
|
||||
if is_option_enabled(world, player, "shuffle_uncommon"):
|
||||
self.CHECK_LOCATIONS = self.CHECK_LOCATIONS | StaticWitnessLocations.UNCOMMON_LOCATIONS
|
||||
|
||||
self.CHECK_LOCATIONS = self.CHECK_LOCATIONS | player_logic.ADDED_CHECKS
|
||||
|
||||
if not is_option_enabled(world, player, "shuffle_postgame"):
|
||||
self.CHECK_LOCATIONS -= postgame
|
||||
|
||||
self.CHECK_LOCATIONS.discard(StaticWitnessLogic.CHECKS_BY_HEX[player_logic.VICTORY_LOCATION]["checkName"])
|
||||
|
||||
self.CHECK_LOCATIONS = self.CHECK_LOCATIONS - {
|
||||
StaticWitnessLogic.CHECKS_BY_HEX[check_hex]["checkName"]
|
||||
for check_hex in player_logic.COMPLETELY_DISABLED_CHECKS
|
||||
|
||||
@@ -36,6 +36,9 @@ class WitnessPlayerLogic:
|
||||
Panels outside of the same region will still be checked manually.
|
||||
"""
|
||||
|
||||
if panel_hex in self.COMPLETELY_DISABLED_CHECKS:
|
||||
return frozenset()
|
||||
|
||||
check_obj = StaticWitnessLogic.CHECKS_BY_HEX[panel_hex]
|
||||
|
||||
these_items = frozenset({frozenset()})
|
||||
@@ -57,7 +60,10 @@ class WitnessPlayerLogic:
|
||||
for dependentItem in door_items:
|
||||
all_options.add(items_option.union(dependentItem))
|
||||
|
||||
return frozenset(all_options)
|
||||
if panel_hex != "0x28A0D":
|
||||
return frozenset(all_options)
|
||||
else: # 0x28A0D depends on another entity for *non-power* reasons -> This dependency needs to be preserved
|
||||
these_items = all_options
|
||||
|
||||
these_panels = self.DEPENDENT_REQUIREMENTS_BY_HEX[panel_hex]["panels"]
|
||||
|
||||
@@ -72,7 +78,9 @@ class WitnessPlayerLogic:
|
||||
for option_panel in option:
|
||||
dep_obj = StaticWitnessLogic.CHECKS_BY_HEX.get(option_panel)
|
||||
|
||||
if option_panel in {"7 Lasers", "11 Lasers"}:
|
||||
if option_panel in self.COMPLETELY_DISABLED_CHECKS:
|
||||
new_items = frozenset()
|
||||
elif option_panel in {"7 Lasers", "11 Lasers"}:
|
||||
new_items = frozenset({frozenset([option_panel])})
|
||||
# If a panel turns on when a panel in a different region turns on,
|
||||
# the latter panel will be an "event panel", unless it ends up being
|
||||
@@ -204,9 +212,11 @@ class WitnessPlayerLogic:
|
||||
elif get_option_value(world, player, "victory_condition") == 3:
|
||||
self.VICTORY_LOCATION = "0xFFF00"
|
||||
|
||||
self.COMPLETELY_DISABLED_CHECKS.add(
|
||||
self.VICTORY_LOCATION
|
||||
)
|
||||
if get_option_value(world, player, "challenge_lasers") <= 7:
|
||||
adjustment_linesets_in_order.append([
|
||||
"Requirement Changes:",
|
||||
"0xFFF00 - 11 Lasers - True",
|
||||
])
|
||||
|
||||
if is_option_enabled(world, player, "disable_non_randomized_puzzles"):
|
||||
adjustment_linesets_in_order.append(get_disable_unrandomized_list())
|
||||
@@ -314,7 +324,7 @@ class WitnessPlayerLogic:
|
||||
self.VICTORY_LOCATION = "0x0356B"
|
||||
self.EVENT_ITEM_NAMES = {
|
||||
"0x01A0F": "Keep Laser Panel (Hedge Mazes) Activates",
|
||||
"0x09D9B": "Monastery Overhead Doors Open",
|
||||
"0x09D9B": "Monastery Shutters Open",
|
||||
"0x193A6": "Monastery Laser Panel Activates",
|
||||
"0x00037": "Monastery Branch Panels Activate",
|
||||
"0x0A079": "Access to Bunker Laser",
|
||||
@@ -325,24 +335,24 @@ class WitnessPlayerLogic:
|
||||
"0x01D3F": "Keep Laser Panel (Pressure Plates) Activates",
|
||||
"0x09F7F": "Mountain Access",
|
||||
"0x0367C": "Quarry Laser Mill Requirement Met",
|
||||
"0x009A1": "Swamp Rotated Shapers 1 Activates",
|
||||
"0x009A1": "Swamp Between Bridges Far 1 Activates",
|
||||
"0x00006": "Swamp Cyan Water Drains",
|
||||
"0x00990": "Swamp Broken Shapers 1 Activates",
|
||||
"0x0A8DC": "Lower Avoid 6 Activates",
|
||||
"0x0000A": "Swamp More Rotated Shapers 1 Access",
|
||||
"0x09E86": "Inside Mountain Second Layer Blue Bridge Access",
|
||||
"0x09ED8": "Inside Mountain Second Layer Yellow Bridge Access",
|
||||
"0x00990": "Swamp Between Bridges Near Row 1 Activates",
|
||||
"0x0A8DC": "Intro 6 Activates",
|
||||
"0x0000A": "Swamp Beyond Rotating Bridge 1 Access",
|
||||
"0x09E86": "Mountain Floor 2 Blue Bridge Access",
|
||||
"0x09ED8": "Mountain Floor 2 Yellow Bridge Access",
|
||||
"0x0A3D0": "Quarry Laser Boathouse Requirement Met",
|
||||
"0x00596": "Swamp Red Water Drains",
|
||||
"0x00E3A": "Swamp Purple Water Drains",
|
||||
"0x0343A": "Door to Symmetry Island Powers On",
|
||||
"0xFFF00": "Inside Mountain Bottom Layer Discard Turns On",
|
||||
"0xFFF00": "Mountain Bottom Floor Discard Turns On",
|
||||
"0x17CA6": "All Boat Panels Turn On",
|
||||
"0x17CDF": "All Boat Panels Turn On",
|
||||
"0x09DB8": "All Boat Panels Turn On",
|
||||
"0x17C95": "All Boat Panels Turn On",
|
||||
"0x03BB0": "Town Church Lattice Vision From Outside",
|
||||
"0x28AC1": "Town Shapers & Dots & Eraser Turns On",
|
||||
"0x28AC1": "Town Wooden Rooftop Turns On",
|
||||
"0x28A69": "Town Tower 1st Door Opens",
|
||||
"0x28ACC": "Town Tower 2nd Door Opens",
|
||||
"0x28AD9": "Town Tower 3rd Door Opens",
|
||||
@@ -350,12 +360,20 @@ class WitnessPlayerLogic:
|
||||
"0x03675": "Quarry Mill Ramp Activation From Above",
|
||||
"0x03679": "Quarry Mill Lift Lowering While Standing On It",
|
||||
"0x2FAF6": "Tutorial Gate Secret Solution Knowledge",
|
||||
"0x079DF": "Town Hexagonal Reflection Turns On",
|
||||
"0x079DF": "Town Tall Hexagonal Turns On",
|
||||
"0x17DA2": "Right Orange Bridge Fully Extended",
|
||||
"0x19B24": "Shadows Lower Avoid Patterns Visible",
|
||||
"0x19B24": "Shadows Intro Patterns Visible",
|
||||
"0x2700B": "Open Door to Treehouse Laser House",
|
||||
"0x00055": "Orchard Apple Trees 4 Turns On",
|
||||
"0x17DDB": "Left Orange Bridge Fully Extended",
|
||||
"0x03535": "Shipwreck Video Pattern Knowledge",
|
||||
"0x03542": "Mountain Video Pattern Knowledge",
|
||||
"0x0339E": "Desert Video Pattern Knowledge",
|
||||
"0x03481": "Tutorial Video Pattern Knowledge",
|
||||
"0x03702": "Jungle Video Pattern Knowledge",
|
||||
"0x0356B": "Challenge Video Pattern Knowledge",
|
||||
"0x0A15F": "Desert Laser Panel Shutters Open (1)",
|
||||
"0x012D7": "Desert Laser Panel Shutters Open (2)",
|
||||
}
|
||||
|
||||
self.ALWAYS_EVENT_NAMES_BY_HEX = {
|
||||
@@ -371,12 +389,6 @@ class WitnessPlayerLogic:
|
||||
"0x0C2B2": "Bunker Laser Activation",
|
||||
"0x00BF6": "Swamp Laser Activation",
|
||||
"0x028A4": "Treehouse Laser Activation",
|
||||
"0x03535": "Shipwreck Video Pattern Knowledge",
|
||||
"0x03542": "Mountain Video Pattern Knowledge",
|
||||
"0x0339E": "Desert Video Pattern Knowledge",
|
||||
"0x03481": "Tutorial Video Pattern Knowledge",
|
||||
"0x03702": "Jungle Video Pattern Knowledge",
|
||||
"0x0356B": "Challenge Video Pattern Knowledge",
|
||||
"0x09F7F": "Mountaintop Trap Door Turns On",
|
||||
"0x17C34": "Mountain Access",
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ class WitnessRegions:
|
||||
all_locations = all_locations | set(locations_for_this_region)
|
||||
|
||||
world.regions += [
|
||||
create_region(world, player, region_name, self.locat,locations_for_this_region)
|
||||
create_region(world, player, region_name, self.locat, locations_for_this_region)
|
||||
]
|
||||
|
||||
for region_name, region in StaticWitnessLogic.ALL_REGIONS_BY_NAME.items():
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
Event Items:
|
||||
Town Tower 4th Door Opens - 0x17CFB,0x3C12B,0x00B8D,0x17CF7
|
||||
Monastery Laser Activation - 0x00A5B,0x17CE7,0x17FA9,0x17CA4
|
||||
Monastery Laser Activation - 0x00A5B,0x17CE7,0x17FA9
|
||||
Bunker Laser Activation - 0x00061,0x17D01,0x17C42
|
||||
Shadows Laser Activation - 0x00021,0x17D28,0x17C71
|
||||
|
||||
Requirement Changes:
|
||||
0x17C65 - 0x00A5B | 0x17CE7 | 0x17FA9 | 0x17CA4
|
||||
0x17C65 - 0x00A5B | 0x17CE7 | 0x17FA9
|
||||
0x0C2B2 - 0x00061 | 0x17D01 | 0x17C42
|
||||
0x181B3 - 0x00021 | 0x17D28 | 0x17C71
|
||||
0x28B39 - True - Reflection
|
||||
0x17CAB - True - True
|
||||
0x2779A - True - 0x17CFB | 0x3C12B | 0x17CF7
|
||||
|
||||
Disabled Locations:
|
||||
0x03505 (Tutorial Gate Close)
|
||||
@@ -25,82 +25,84 @@ Disabled Locations:
|
||||
0x00055 (Orchard Apple Tree 3)
|
||||
0x032F7 (Orchard Apple Tree 4)
|
||||
0x032FF (Orchard Apple Tree 5)
|
||||
0x198B5 (Shadows Lower Avoid 1)
|
||||
0x198BD (Shadows Lower Avoid 2)
|
||||
0x198BF (Shadows Lower Avoid 3)
|
||||
0x19771 (Shadows Lower Avoid 4)
|
||||
0x0A8DC (Shadows Lower Avoid 5)
|
||||
0x0AC74 (Shadows Lower Avoid 6)
|
||||
0x0AC7A (Shadows Lower Avoid 7)
|
||||
0x0A8E0 (Shadows Lower Avoid 8)
|
||||
0x386FA (Shadows Environmental Avoid 1)
|
||||
0x1C33F (Shadows Environmental Avoid 2)
|
||||
0x196E2 (Shadows Environmental Avoid 3)
|
||||
0x1972A (Shadows Environmental Avoid 4)
|
||||
0x19809 (Shadows Environmental Avoid 5)
|
||||
0x19806 (Shadows Environmental Avoid 6)
|
||||
0x196F8 (Shadows Environmental Avoid 7)
|
||||
0x1972F (Shadows Environmental Avoid 8)
|
||||
0x19797 (Shadows Follow 1)
|
||||
0x1979A (Shadows Follow 2)
|
||||
0x197E0 (Shadows Follow 3)
|
||||
0x197E8 (Shadows Follow 4)
|
||||
0x197E5 (Shadows Follow 5)
|
||||
0x198B5 (Shadows Intro 1)
|
||||
0x198BD (Shadows Intro 2)
|
||||
0x198BF (Shadows Intro 3)
|
||||
0x19771 (Shadows Intro 4)
|
||||
0x0A8DC (Shadows Intro 5)
|
||||
0x0AC74 (Shadows Intro 6)
|
||||
0x0AC7A (Shadows Intro 7)
|
||||
0x0A8E0 (Shadows Intro 8)
|
||||
0x386FA (Shadows Far 1)
|
||||
0x1C33F (Shadows Far 2)
|
||||
0x196E2 (Shadows Far 3)
|
||||
0x1972A (Shadows Far 4)
|
||||
0x19809 (Shadows Far 5)
|
||||
0x19806 (Shadows Far 6)
|
||||
0x196F8 (Shadows Far 7)
|
||||
0x1972F (Shadows Far 8)
|
||||
0x19797 (Shadows Near 1)
|
||||
0x1979A (Shadows Near 2)
|
||||
0x197E0 (Shadows Near 3)
|
||||
0x197E8 (Shadows Near 4)
|
||||
0x197E5 (Shadows Near 5)
|
||||
0x19650 (Shadows Laser)
|
||||
0x00139 (Keep Hedge Maze 1)
|
||||
0x019DC (Keep Hedge Maze 2)
|
||||
0x019E7 (Keep Hedge Maze 3)
|
||||
0x01A0F (Keep Hedge Maze 4)
|
||||
0x0360E (Laser Hedges)
|
||||
0x00B10 (Monastery Door Open Left)
|
||||
0x00C92 (Monastery Door Open Right)
|
||||
0x00290 (Monastery Rhombic Avoid 1)
|
||||
0x00038 (Monastery Rhombic Avoid 2)
|
||||
0x00037 (Monastery Rhombic Avoid 3)
|
||||
0x193A7 (Monastery Branch Avoid 1)
|
||||
0x193AA (Monastery Branch Avoid 2)
|
||||
0x193AB (Monastery Branch Follow 1)
|
||||
0x193A6 (Monastery Branch Follow 2)
|
||||
0x17CA4 (Monastery Laser) - 0x193A6 - True
|
||||
0x18590 (Tree Outlines) - True - Symmetry & Environment
|
||||
0x28AE3 (Vines Shadows Follow) - 0x18590 - Shadows Follow & Environment
|
||||
0x28938 (Four-way Apple Tree) - 0x28AE3 - Environment
|
||||
0x079DF (Triple Environmental Puzzle) - 0x28938 - Shadows Avoid & Environment & Reflection
|
||||
0x28B39 (Hexagonal Reflection) - 0x079DF & 0x2896A - Reflection
|
||||
0x00B10 (Monastery Entry Left)
|
||||
0x00C92 (Monastery Entry Right)
|
||||
0x00290 (Monastery Outside 1)
|
||||
0x00038 (Monastery Outside 2)
|
||||
0x00037 (Monastery Outside 3)
|
||||
0x193A7 (Monastery Inside 1)
|
||||
0x193AA (Monastery Inside 2)
|
||||
0x193AB (Monastery Inside 3)
|
||||
0x193A6 (Monastery Inside 4)
|
||||
0x17CA4 (Monastery Laser)
|
||||
0x18590 (Transparent) - True - Symmetry & Environment
|
||||
0x28AE3 (Vines) - 0x18590 - Shadows Follow & Environment
|
||||
0x28938 (Apple Tree) - 0x28AE3 - Environment
|
||||
0x079DF (Triple Exit) - 0x28938 - Shadows Avoid & Environment & Reflection
|
||||
0x28B39 (Tall Hexagonal) - 0x079DF & 0x2896A - Reflection
|
||||
0x03553 (Theater Tutorial Video)
|
||||
0x03552 (Theater Desert Video)
|
||||
0x0354E (Theater Jungle Video)
|
||||
0x03549 (Theater Challenge Video)
|
||||
0x0354F (Theater Shipwreck Video)
|
||||
0x03545 (Theater Mountain Video)
|
||||
0x002C4 (Waves 1)
|
||||
0x00767 (Waves 2)
|
||||
0x002C6 (Waves 3)
|
||||
0x0070E (Waves 4)
|
||||
0x0070F (Waves 5)
|
||||
0x0087D (Waves 6)
|
||||
0x002C7 (Waves 7)
|
||||
0x15ADD (River Rhombic Avoid Vault)
|
||||
0x002C4 (First Row 1)
|
||||
0x00767 (First Row 2)
|
||||
0x002C6 (First Row 3)
|
||||
0x0070E (Second Row 1)
|
||||
0x0070F (Second Row 2)
|
||||
0x0087D (Second Row 3)
|
||||
0x002C7 (Second Row 4)
|
||||
0x15ADD (River Outside Vault)
|
||||
0x03702 (River Vault Box)
|
||||
0x17CAA (Rhombic Avoid to Monastery Garden)
|
||||
0x17CAA (Monastery Shortcut Panel)
|
||||
0x17C2E (Door to Bunker)
|
||||
0x09F7D (Bunker Drawn Squares 1)
|
||||
0x09FDC (Bunker Drawn Squares 2)
|
||||
0x09FF7 (Bunker Drawn Squares 3)
|
||||
0x09F82 (Bunker Drawn Squares 4)
|
||||
0x09FF8 (Bunker Drawn Squares 5)
|
||||
0x09D9F (Bunker Drawn Squares 6)
|
||||
0x09DA1 (Bunker Drawn Squares 7)
|
||||
0x09DA2 (Bunker Drawn Squares 8)
|
||||
0x09DAF (Bunker Drawn Squares 9)
|
||||
0x0A010 (Bunker Drawn Squares through Tinted Glass 1)
|
||||
0x0A01B (Bunker Drawn Squares through Tinted Glass 2)
|
||||
0x0A01F (Bunker Drawn Squares through Tinted Glass 3)
|
||||
0x0A099 (Door to Bunker Proper)
|
||||
0x09F7D (Bunker Intro Left 1)
|
||||
0x09FDC (Bunker Intro Left 2)
|
||||
0x09FF7 (Bunker Intro Left 3)
|
||||
0x09F82 (Bunker Intro Left 4)
|
||||
0x09FF8 (Bunker Intro Left 5)
|
||||
0x09D9F (Bunker Intro Back 1)
|
||||
0x09DA1 (Bunker Intro Back 2)
|
||||
0x09DA2 (Bunker Intro Back 3)
|
||||
0x09DAF (Bunker Intro Back 4)
|
||||
0x0A010 (Bunker Glass Room 1)
|
||||
0x0A01B (Bunker Glass Room 2)
|
||||
0x0A01F (Bunker Glass Room 3)
|
||||
0x0A099 (Tinted Glass Door)
|
||||
0x34BC5 (Bunker Drop-Down Door Open)
|
||||
0x34BC6 (Bunker Drop-Down Door Close)
|
||||
0x17E63 (Bunker Drop-Down Door Squares 1)
|
||||
0x17E67 (Bunker Drop-Down Door Squares 2)
|
||||
0x17E63 (Bunker UV Room 1)
|
||||
0x17E67 (Bunker UV Room 2)
|
||||
0x09DE0 (Bunker Laser)
|
||||
0x0A079 (Bunker Elevator Control)
|
||||
0x0042D (Mountaintop River Shape)
|
||||
0x0042D (Mountaintop River Shape)
|
||||
|
||||
0x17CAA (River Garden Entry Panel)
|
||||
|
||||
@@ -1,31 +1,31 @@
|
||||
Items:
|
||||
Glass Factory Entry Door (Panel)
|
||||
Door to Symmetry Island Lower (Panel)
|
||||
Door to Symmetry Island Upper (Panel)
|
||||
Door to Desert Flood Light Room (Panel)
|
||||
Desert Flood Room Flood Controls (Panel)
|
||||
Quarry Door to Mill (Panel)
|
||||
Glass Factory Entry (Panel)
|
||||
Symmetry Island Lower (Panel)
|
||||
Symmetry Island Upper (Panel)
|
||||
Desert Light Room Entry (Panel)
|
||||
Desert Flood Controls (Panel)
|
||||
Quarry Mill Entry (Panel)
|
||||
Quarry Mill Ramp Controls (Panel)
|
||||
Quarry Mill Elevator Controls (Panel)
|
||||
Quarry Mill Lift Controls (Panel)
|
||||
Quarry Boathouse Ramp Height Control (Panel)
|
||||
Quarry Boathouse Ramp Horizontal Control (Panel)
|
||||
Shadows Door Timer (Panel)
|
||||
Monastery Entry Door Left (Panel)
|
||||
Monastery Entry Door Right (Panel)
|
||||
Town Door to RGB House (Panel)
|
||||
Town Door to Church (Panel)
|
||||
Monastery Entry Left (Panel)
|
||||
Monastery Entry Right (Panel)
|
||||
Town Tinted Glass Door (Panel)
|
||||
Town Church Entry (Panel)
|
||||
Town Maze Panel (Drop-Down Staircase) (Panel)
|
||||
Windmill Door (Panel)
|
||||
Windmill Entry (Panel)
|
||||
Treehouse First & Second Doors (Panel)
|
||||
Treehouse Third Door (Panel)
|
||||
Treehouse Laser House Door Timer (Panel)
|
||||
Treehouse Shortcut Drop-Down Bridge (Panel)
|
||||
Treehouse Drawbridge (Panel)
|
||||
Jungle Popup Wall (Panel)
|
||||
Bunker Entry Door (Panel)
|
||||
Inside Bunker Door to Bunker Proper (Panel)
|
||||
Bunker Entry (Panel)
|
||||
Bunker Tinted Glass Door (Panel)
|
||||
Bunker Elevator Control (Panel)
|
||||
Swamp Entry Door (Panel)
|
||||
Swamp Entry (Panel)
|
||||
Swamp Sliding Bridge (Panel)
|
||||
Swamp Rotating Bridge (Panel)
|
||||
Swamp Maze Control (Panel)
|
||||
Boat
|
||||
Boat
|
||||
@@ -1,128 +1,128 @@
|
||||
Items:
|
||||
Outside Tutorial Optional Door
|
||||
Outside Tutorial Outpost Entry Door
|
||||
Outside Tutorial Outpost Exit Door
|
||||
Glass Factory Entry Door
|
||||
Glass Factory Back Wall
|
||||
Symmetry Island Lower Door
|
||||
Symmetry Island Upper Door
|
||||
Orchard Middle Gate
|
||||
Orchard Final Gate
|
||||
Desert Door to Flood Light Room
|
||||
Desert Door to Pond Room
|
||||
Desert Door to Water Levels Room
|
||||
Desert Door to Elevator Room
|
||||
Quarry Main Entry 1
|
||||
Quarry Main Entry 2
|
||||
Quarry Door to Mill
|
||||
Quarry Mill Side Door
|
||||
Quarry Mill Rooftop Shortcut
|
||||
Quarry Mill Stairs
|
||||
Quarry Boathouse Boat Staircase
|
||||
Quarry Boathouse First Barrier
|
||||
Quarry Boathouse Shortcut
|
||||
Shadows Timed Door
|
||||
Shadows Laser Room Right Door
|
||||
Shadows Laser Room Left Door
|
||||
Shadows Barrier to Quarry
|
||||
Shadows Barrier to Ledge
|
||||
Keep Hedge Maze 1 Exit Door
|
||||
Keep Pressure Plates 1 Exit Door
|
||||
Keep Hedge Maze 2 Shortcut
|
||||
Keep Hedge Maze 2 Exit Door
|
||||
Keep Hedge Maze 3 Shortcut
|
||||
Keep Hedge Maze 3 Exit Door
|
||||
Keep Hedge Maze 4 Shortcut
|
||||
Keep Hedge Maze 4 Exit Door
|
||||
Keep Pressure Plates 2 Exit Door
|
||||
Keep Pressure Plates 3 Exit Door
|
||||
Keep Pressure Plates 4 Exit Door
|
||||
Keep Shortcut to Shadows
|
||||
Keep Tower Shortcut
|
||||
Monastery Shortcut
|
||||
Monastery Inner Door
|
||||
Monastery Outer Door
|
||||
Monastery Door to Garden
|
||||
Town Cargo Box Door
|
||||
Town Wooden Roof Staircase
|
||||
Town Tinted Door to RGB House
|
||||
Town Door to Church
|
||||
Town Maze Staircase
|
||||
Town Windmill Door
|
||||
Town RGB House Staircase
|
||||
Town Tower Blue Panels Door
|
||||
Town Tower Lattice Door
|
||||
Town Tower Environmental Set Door
|
||||
Town Tower Wooden Roof Set Door
|
||||
Theater Entry Door
|
||||
Theater Exit Door Left
|
||||
Theater Exit Door Right
|
||||
Jungle Bamboo Shortcut to River
|
||||
Jungle Popup Wall
|
||||
River Shortcut to Monastery Garden
|
||||
Bunker Bunker Entry Door
|
||||
Bunker Tinted Glass Door
|
||||
Bunker Door to Ultraviolet Room
|
||||
Bunker Door to Elevator
|
||||
Swamp Entry Door
|
||||
Swamp Door to Broken Shapers
|
||||
Outside Tutorial Outpost Path (Door)
|
||||
Outside Tutorial Outpost Entry (Door)
|
||||
Outside Tutorial Outpost Exit (Door)
|
||||
Glass Factory Entry (Door)
|
||||
Glass Factory Back Wall (Door)
|
||||
Symmetry Island Lower (Door)
|
||||
Symmetry Island Upper (Door)
|
||||
Orchard First Gate (Door)
|
||||
Orchard Second Gate (Door)
|
||||
Desert Light Room Entry (Door)
|
||||
Desert Pond Room Entry (Door)
|
||||
Desert Flood Room Entry (Door)
|
||||
Desert Elevator Room Entry (Door)
|
||||
Quarry Entry 1 (Door)
|
||||
Quarry Entry 2 (Door)
|
||||
Quarry Mill Entry (Door)
|
||||
Quarry Mill Side Exit (Door)
|
||||
Quarry Mill Roof Exit (Door)
|
||||
Quarry Mill Stairs (Door)
|
||||
Quarry Boathouse Dock (Door)
|
||||
Quarry Boathouse First Barrier (Door)
|
||||
Quarry Boathouse Second Barrier (Door)
|
||||
Shadows Timed Door (Door)
|
||||
Shadows Laser Entry Right (Door)
|
||||
Shadows Laser Entry Left (Door)
|
||||
Shadows Quarry Barrier (Door)
|
||||
Shadows Ledge Barrier (Door)
|
||||
Keep Hedge Maze 1 Exit (Door)
|
||||
Keep Pressure Plates 1 Exit (Door)
|
||||
Keep Hedge Maze 2 Shortcut (Door)
|
||||
Keep Hedge Maze 2 Exit (Door)
|
||||
Keep Hedge Maze 3 Shortcut (Door)
|
||||
Keep Hedge Maze 3 Exit (Door)
|
||||
Keep Hedge Maze 4 Shortcut (Door)
|
||||
Keep Hedge Maze 4 Exit (Door)
|
||||
Keep Pressure Plates 2 Exit (Door)
|
||||
Keep Pressure Plates 3 Exit (Door)
|
||||
Keep Pressure Plates 4 Exit (Door)
|
||||
Keep Shadows Shortcut (Door)
|
||||
Keep Tower Shortcut (Door)
|
||||
Monastery Shortcut (Door)
|
||||
Monastery Entry Inner (Door)
|
||||
Monastery Entry Outer (Door)
|
||||
Monastery Garden Entry (Door)
|
||||
Town Cargo Box Entry (Door)
|
||||
Town Wooden Roof Stairs (Door)
|
||||
Town Tinted Glass Door (Door)
|
||||
Town Church Entry (Door)
|
||||
Town Maze Stairs (Door)
|
||||
Town Windmill Entry (Door)
|
||||
Town RGB House Stairs (Door)
|
||||
Town Tower First Door (Door)
|
||||
Town Tower Third Door (Door)
|
||||
Town Tower Fourth Door (Door)
|
||||
Town Tower Second Door (Door)
|
||||
Theater Entry (Door)
|
||||
Theater Exit Left (Door)
|
||||
Theater Exit Right (Door)
|
||||
Jungle Bamboo Laser Shortcut (Door)
|
||||
Jungle Popup Wall (Door)
|
||||
River Monastery Shortcut (Door)
|
||||
Bunker Entry (Door)
|
||||
Bunker Tinted Glass Door (Door)
|
||||
Bunker UV Room Entry (Door)
|
||||
Bunker Elevator Room Entry (Door)
|
||||
Swamp Entry (Door)
|
||||
Swamp Between Bridges First Door
|
||||
Swamp Platform Shortcut Door
|
||||
Swamp Cyan Water Pump
|
||||
Swamp Door to Rotated Shapers
|
||||
Swamp Red Water Pump
|
||||
Swamp Red Underwater Exit
|
||||
Swamp Blue Water Pump
|
||||
Swamp Purple Water Pump
|
||||
Swamp Near Laser Shortcut
|
||||
Treehouse First Door
|
||||
Treehouse Second Door
|
||||
Treehouse Beyond Yellow Bridge Door
|
||||
Treehouse Drawbridge
|
||||
Treehouse Timed Door to Laser House
|
||||
Inside Mountain First Layer Exit Door
|
||||
Inside Mountain Second Layer Staircase Near
|
||||
Inside Mountain Second Layer Exit Door
|
||||
Inside Mountain Second Layer Staircase Far
|
||||
Inside Mountain Giant Puzzle Exit Door
|
||||
Inside Mountain Door to Final Room
|
||||
Inside Mountain Bottom Layer Rock
|
||||
Inside Mountain Door to Secret Area
|
||||
Caves Pillar Door
|
||||
Caves Mountain Shortcut
|
||||
Caves Swamp Shortcut
|
||||
Challenge Entry Door
|
||||
Challenge Door to Theater Walkway
|
||||
Theater Walkway Door to Windmill Interior
|
||||
Theater Walkway Door to Desert Elevator Room
|
||||
Theater Walkway Door to Town
|
||||
Swamp Cyan Water Pump (Door)
|
||||
Swamp Between Bridges Second Door
|
||||
Swamp Red Water Pump (Door)
|
||||
Swamp Red Underwater Exit (Door)
|
||||
Swamp Blue Water Pump (Door)
|
||||
Swamp Purple Water Pump (Door)
|
||||
Swamp Laser Shortcut (Door)
|
||||
Treehouse First Door (Door)
|
||||
Treehouse Second Door (Door)
|
||||
Treehouse Third Door (Door)
|
||||
Treehouse Drawbridge (Door)
|
||||
Treehouse Laser House Entry (Door)
|
||||
Mountain Floor 1 Exit (Door)
|
||||
Mountain Floor 2 Staircase Near (Door)
|
||||
Mountain Floor 2 Exit (Door)
|
||||
Mountain Floor 2 Staircase Far (Door)
|
||||
Mountain Bottom Floor Giant Puzzle Exit (Door)
|
||||
Mountain Bottom Floor Final Room Entry (Door)
|
||||
Mountain Bottom Floor Rock (Door)
|
||||
Caves Entry (Door)
|
||||
Caves Pillar Door (Door)
|
||||
Caves Mountain Shortcut (Door)
|
||||
Caves Swamp Shortcut (Door)
|
||||
Challenge Entry (Door)
|
||||
Challenge Tunnels Entry (Door)
|
||||
Tunnels Theater Shortcut (Door)
|
||||
Tunnels Desert Shortcut (Door)
|
||||
Tunnels Town Shortcut (Door)
|
||||
|
||||
Added Locations:
|
||||
Outside Tutorial Door to Outpost Panel
|
||||
Outside Tutorial Exit Door from Outpost Panel
|
||||
Glass Factory Entry Door Panel
|
||||
Glass Factory Vertical Symmetry 5
|
||||
Symmetry Island Door to Symmetry Island Lower Panel
|
||||
Symmetry Island Door to Symmetry Island Upper Panel
|
||||
Outside Tutorial Outpost Entry Panel
|
||||
Outside Tutorial Outpost Exit Panel
|
||||
Glass Factory Entry Panel
|
||||
Glass Factory Back Wall 5
|
||||
Symmetry Island Lower Panel
|
||||
Symmetry Island Upper Panel
|
||||
Orchard Apple Tree 3
|
||||
Orchard Apple Tree 5
|
||||
Desert Door to Desert Flood Light Room Panel
|
||||
Desert Artificial Light Reflection 3
|
||||
Desert Door to Water Levels Room Panel
|
||||
Desert Flood Reflection 6
|
||||
Quarry Door to Quarry 1 Panel
|
||||
Quarry Door to Quarry 2 Panel
|
||||
Quarry Door to Mill Right
|
||||
Quarry Door to Mill Left
|
||||
Quarry Mill Ground Floor Shortcut Door Panel
|
||||
Quarry Mill Door to Outside Quarry Stairs Panel
|
||||
Desert Light Room Entry Panel
|
||||
Desert Light Room 3
|
||||
Desert Flood Room Entry Panel
|
||||
Desert Flood Room 6
|
||||
Quarry Entry 1 Panel
|
||||
Quarry Entry 2 Panel
|
||||
Quarry Mill Entry Right Panel
|
||||
Quarry Mill Entry Left Panel
|
||||
Quarry Mill Side Exit Panel
|
||||
Quarry Mill Roof Exit Panel
|
||||
Quarry Mill Stair Control
|
||||
Quarry Boathouse Shortcut Door Panel
|
||||
Quarry Boathouse Second Barrier Panel
|
||||
Shadows Door Timer Inside
|
||||
Shadows Door Timer Outside
|
||||
Shadows Environmental Avoid 8
|
||||
Shadows Follow 5
|
||||
Shadows Lower Avoid 3
|
||||
Shadows Lower Avoid 5
|
||||
Shadows Far 8
|
||||
Shadows Near 5
|
||||
Shadows Intro 3
|
||||
Shadows Intro 5
|
||||
Keep Hedge Maze 1
|
||||
Keep Pressure Plates 1
|
||||
Keep Hedge Maze 2
|
||||
@@ -131,71 +131,70 @@ Keep Hedge Maze 4
|
||||
Keep Pressure Plates 2
|
||||
Keep Pressure Plates 3
|
||||
Keep Pressure Plates 4
|
||||
Keep Shortcut to Shadows Panel
|
||||
Keep Tower Shortcut to Keep Panel
|
||||
Monastery Shortcut Door Panel
|
||||
Monastery Door Open Left
|
||||
Monastery Door Open Right
|
||||
Monastery Rhombic Avoid 3
|
||||
Town Cargo Box Panel
|
||||
Town Full Dot Grid Shapers 5
|
||||
Town Tinted Door Panel
|
||||
Town Door to Church Stars Panel
|
||||
Keep Shadows Shortcut Panel
|
||||
Keep Tower Shortcut Panel
|
||||
Monastery Shortcut Panel
|
||||
Monastery Entry Left
|
||||
Monastery Entry Right
|
||||
Monastery Outside 3
|
||||
Town Cargo Box Entry Panel
|
||||
Town Wooden Roof Lower Row 5
|
||||
Town Tinted Glass Door Panel
|
||||
Town Church Entry Panel
|
||||
Town Maze Stair Control
|
||||
Town Windmill Door Panel
|
||||
Town Sound Room Left
|
||||
Town Windmill Entry Panel
|
||||
Town Sound Room Right
|
||||
Town Symmetry Squares 5 + Dots
|
||||
Town Red Rooftop 5
|
||||
Town Church Lattice
|
||||
Town Hexagonal Reflection
|
||||
Town Shapers & Dots & Eraser
|
||||
Windmill Door to Front of Theater Panel
|
||||
Theater Door to Cargo Box Left Panel
|
||||
Theater Door to Cargo Box Right Panel
|
||||
Jungle Shortcut to River Panel
|
||||
Town Tall Hexagonal
|
||||
Town Wooden Rooftop
|
||||
Windmill Theater Entry Panel
|
||||
Theater Exit Left Panel
|
||||
Theater Exit Right Panel
|
||||
Jungle Laser Shortcut Panel
|
||||
Jungle Popup Wall Control
|
||||
River Rhombic Avoid to Monastery Garden
|
||||
Bunker Bunker Entry Panel
|
||||
Bunker Door to Bunker Proper Panel
|
||||
Bunker Drawn Squares through Tinted Glass 3
|
||||
Bunker Drop-Down Door Squares 2
|
||||
River Monastery Shortcut Panel
|
||||
Bunker Entry Panel
|
||||
Bunker Tinted Glass Door Panel
|
||||
Bunker Glass Room 3
|
||||
Bunker UV Room 2
|
||||
Swamp Entry Panel
|
||||
Swamp Platform Shapers 4
|
||||
Swamp Platform Row 4
|
||||
Swamp Platform Shortcut Right Panel
|
||||
Swamp Blue Underwater Negative Shapers 5
|
||||
Swamp Broken Shapers 4
|
||||
Swamp Cyan Underwater Negative Shapers 5
|
||||
Swamp Red Underwater Negative Shapers 4
|
||||
Swamp More Rotated Shapers 4
|
||||
Swamp More Rotated Shapers 4
|
||||
Swamp Near Laser Shortcut Right Panel
|
||||
Swamp Blue Underwater 5
|
||||
Swamp Between Bridges Near Row 4
|
||||
Swamp Cyan Underwater 5
|
||||
Swamp Red Underwater 4
|
||||
Swamp Beyond Rotating Bridge 4
|
||||
Swamp Beyond Rotating Bridge 4
|
||||
Swamp Laser Shortcut Right Panel
|
||||
Treehouse First Door Panel
|
||||
Treehouse Second Door Panel
|
||||
Treehouse Beyond Yellow Bridge Door Panel
|
||||
Treehouse Third Door Panel
|
||||
Treehouse Bridge Control
|
||||
Treehouse Left Orange Bridge 15
|
||||
Treehouse Right Orange Bridge 12
|
||||
Treehouse Laser House Door Timer Outside Control
|
||||
Treehouse Laser House Door Timer Inside Control
|
||||
Inside Mountain Moving Background 7
|
||||
Inside Mountain Obscured Vision 5
|
||||
Inside Mountain Physically Obstructed 3
|
||||
Inside Mountain Angled Inside Trash 2
|
||||
Inside Mountain Color Cycle 5
|
||||
Inside Mountain Light Bridge Controller 2
|
||||
Inside Mountain Light Bridge Controller 3
|
||||
Inside Mountain Same Solution 6
|
||||
Inside Mountain Giant Puzzle
|
||||
Inside Mountain Door to Final Room Left
|
||||
Inside Mountain Door to Final Room Right
|
||||
Inside Mountain Bottom Layer Discard
|
||||
Inside Mountain Rock Control
|
||||
Inside Mountain Secret Area Entry Panel
|
||||
Inside Mountain Caves Lone Pillar
|
||||
Inside Mountain Caves Shortcut to Mountain Panel
|
||||
Inside Mountain Caves Shortcut to Swamp Panel
|
||||
Inside Mountain Caves Challenge Entry Panel
|
||||
Challenge Door to Theater Walkway Panel
|
||||
Theater Walkway Theater Shortcut Panel
|
||||
Theater Walkway Desert Shortcut Panel
|
||||
Theater Walkway Town Shortcut Panel
|
||||
Treehouse Laser House Door Timer Inside
|
||||
Mountain Floor 1 Left Row 7
|
||||
Mountain Floor 1 Right Row 5
|
||||
Mountain Floor 1 Back Row 3
|
||||
Mountain Floor 1 Trash Pillar 2
|
||||
Mountain Floor 2 Near Row 5
|
||||
Mountain Floor 2 Light Bridge Controller Near
|
||||
Mountain Floor 2 Light Bridge Controller Far
|
||||
Mountain Floor 2 Far Row 6
|
||||
Mountain Bottom Floor Giant Puzzle
|
||||
Mountain Bottom Floor Final Room Entry Left
|
||||
Mountain Bottom Floor Final Room Entry Right
|
||||
Mountain Bottom Floor Discard
|
||||
Mountain Bottom Floor Rock Control
|
||||
Mountain Bottom Floor Caves Entry Panel
|
||||
Caves Lone Pillar
|
||||
Caves Mountain Shortcut Panel
|
||||
Caves Swamp Shortcut Panel
|
||||
Caves Challenge Entry Panel
|
||||
Challenge Tunnels Entry Panel
|
||||
Tunnels Theater Shortcut Panel
|
||||
Tunnels Desert Shortcut Panel
|
||||
Tunnels Town Shortcut Panel
|
||||
@@ -1,104 +1,104 @@
|
||||
Items:
|
||||
Outside Tutorial Optional Door
|
||||
Outside Tutorial Outpost Entry Door
|
||||
Outside Tutorial Outpost Exit Door
|
||||
Glass Factory Entry Door
|
||||
Glass Factory Back Wall
|
||||
Symmetry Island Lower Door
|
||||
Symmetry Island Upper Door
|
||||
Orchard Middle Gate
|
||||
Orchard Final Gate
|
||||
Desert Door to Flood Light Room
|
||||
Desert Door to Pond Room
|
||||
Desert Door to Water Levels Room
|
||||
Desert Door to Elevator Room
|
||||
Quarry Main Entry 1
|
||||
Quarry Main Entry 2
|
||||
Quarry Door to Mill
|
||||
Quarry Mill Side Door
|
||||
Quarry Mill Rooftop Shortcut
|
||||
Quarry Mill Stairs
|
||||
Quarry Boathouse Boat Staircase
|
||||
Quarry Boathouse First Barrier
|
||||
Quarry Boathouse Shortcut
|
||||
Shadows Timed Door
|
||||
Shadows Laser Room Right Door
|
||||
Shadows Laser Room Left Door
|
||||
Shadows Barrier to Quarry
|
||||
Shadows Barrier to Ledge
|
||||
Keep Hedge Maze 1 Exit Door
|
||||
Keep Pressure Plates 1 Exit Door
|
||||
Keep Hedge Maze 2 Shortcut
|
||||
Keep Hedge Maze 2 Exit Door
|
||||
Keep Hedge Maze 3 Shortcut
|
||||
Keep Hedge Maze 3 Exit Door
|
||||
Keep Hedge Maze 4 Shortcut
|
||||
Keep Hedge Maze 4 Exit Door
|
||||
Keep Pressure Plates 2 Exit Door
|
||||
Keep Pressure Plates 3 Exit Door
|
||||
Keep Pressure Plates 4 Exit Door
|
||||
Keep Shortcut to Shadows
|
||||
Keep Tower Shortcut
|
||||
Monastery Shortcut
|
||||
Monastery Inner Door
|
||||
Monastery Outer Door
|
||||
Monastery Door to Garden
|
||||
Town Cargo Box Door
|
||||
Town Wooden Roof Staircase
|
||||
Town Tinted Door to RGB House
|
||||
Town Door to Church
|
||||
Town Maze Staircase
|
||||
Town Windmill Door
|
||||
Town RGB House Staircase
|
||||
Town Tower Blue Panels Door
|
||||
Town Tower Lattice Door
|
||||
Town Tower Environmental Set Door
|
||||
Town Tower Wooden Roof Set Door
|
||||
Theater Entry Door
|
||||
Theater Exit Door Left
|
||||
Theater Exit Door Right
|
||||
Jungle Bamboo Shortcut to River
|
||||
Jungle Popup Wall
|
||||
River Shortcut to Monastery Garden
|
||||
Bunker Bunker Entry Door
|
||||
Bunker Tinted Glass Door
|
||||
Bunker Door to Ultraviolet Room
|
||||
Bunker Door to Elevator
|
||||
Swamp Entry Door
|
||||
Swamp Door to Broken Shapers
|
||||
Outside Tutorial Outpost Path (Door)
|
||||
Outside Tutorial Outpost Entry (Door)
|
||||
Outside Tutorial Outpost Exit (Door)
|
||||
Glass Factory Entry (Door)
|
||||
Glass Factory Back Wall (Door)
|
||||
Symmetry Island Lower (Door)
|
||||
Symmetry Island Upper (Door)
|
||||
Orchard First Gate (Door)
|
||||
Orchard Second Gate (Door)
|
||||
Desert Light Room Entry (Door)
|
||||
Desert Pond Room Entry (Door)
|
||||
Desert Flood Room Entry (Door)
|
||||
Desert Elevator Room Entry (Door)
|
||||
Quarry Entry 1 (Door)
|
||||
Quarry Entry 2 (Door)
|
||||
Quarry Mill Entry (Door)
|
||||
Quarry Mill Side Exit (Door)
|
||||
Quarry Mill Roof Exit (Door)
|
||||
Quarry Mill Stairs (Door)
|
||||
Quarry Boathouse Dock (Door)
|
||||
Quarry Boathouse First Barrier (Door)
|
||||
Quarry Boathouse Second Barrier (Door)
|
||||
Shadows Timed Door (Door)
|
||||
Shadows Laser Entry Right (Door)
|
||||
Shadows Laser Entry Left (Door)
|
||||
Shadows Quarry Barrier (Door)
|
||||
Shadows Ledge Barrier (Door)
|
||||
Keep Hedge Maze 1 Exit (Door)
|
||||
Keep Pressure Plates 1 Exit (Door)
|
||||
Keep Hedge Maze 2 Shortcut (Door)
|
||||
Keep Hedge Maze 2 Exit (Door)
|
||||
Keep Hedge Maze 3 Shortcut (Door)
|
||||
Keep Hedge Maze 3 Exit (Door)
|
||||
Keep Hedge Maze 4 Shortcut (Door)
|
||||
Keep Hedge Maze 4 Exit (Door)
|
||||
Keep Pressure Plates 2 Exit (Door)
|
||||
Keep Pressure Plates 3 Exit (Door)
|
||||
Keep Pressure Plates 4 Exit (Door)
|
||||
Keep Shadows Shortcut (Door)
|
||||
Keep Tower Shortcut (Door)
|
||||
Monastery Shortcut (Door)
|
||||
Monastery Entry Inner (Door)
|
||||
Monastery Entry Outer (Door)
|
||||
Monastery Garden Entry (Door)
|
||||
Town Cargo Box Entry (Door)
|
||||
Town Wooden Roof Stairs (Door)
|
||||
Town Tinted Glass Door (Door)
|
||||
Town Church Entry (Door)
|
||||
Town Maze Stairs (Door)
|
||||
Town Windmill Entry (Door)
|
||||
Town RGB House Stairs (Door)
|
||||
Town Tower First Door (Door)
|
||||
Town Tower Third Door (Door)
|
||||
Town Tower Fourth Door (Door)
|
||||
Town Tower Second Door (Door)
|
||||
Theater Entry (Door)
|
||||
Theater Exit Left (Door)
|
||||
Theater Exit Right (Door)
|
||||
Jungle Bamboo Laser Shortcut (Door)
|
||||
Jungle Popup Wall (Door)
|
||||
River Monastery Shortcut (Door)
|
||||
Bunker Entry (Door)
|
||||
Bunker Tinted Glass Door (Door)
|
||||
Bunker UV Room Entry (Door)
|
||||
Bunker Elevator Room Entry (Door)
|
||||
Swamp Entry (Door)
|
||||
Swamp Between Bridges First Door
|
||||
Swamp Platform Shortcut Door
|
||||
Swamp Cyan Water Pump
|
||||
Swamp Door to Rotated Shapers
|
||||
Swamp Red Water Pump
|
||||
Swamp Red Underwater Exit
|
||||
Swamp Blue Water Pump
|
||||
Swamp Purple Water Pump
|
||||
Swamp Near Laser Shortcut
|
||||
Treehouse First Door
|
||||
Treehouse Second Door
|
||||
Treehouse Beyond Yellow Bridge Door
|
||||
Treehouse Drawbridge
|
||||
Treehouse Timed Door to Laser House
|
||||
Inside Mountain First Layer Exit Door
|
||||
Inside Mountain Second Layer Staircase Near
|
||||
Inside Mountain Second Layer Exit Door
|
||||
Inside Mountain Second Layer Staircase Far
|
||||
Inside Mountain Giant Puzzle Exit Door
|
||||
Inside Mountain Door to Final Room
|
||||
Inside Mountain Bottom Layer Rock
|
||||
Inside Mountain Door to Secret Area
|
||||
Caves Pillar Door
|
||||
Caves Mountain Shortcut
|
||||
Caves Swamp Shortcut
|
||||
Challenge Entry Door
|
||||
Challenge Door to Theater Walkway
|
||||
Theater Walkway Door to Windmill Interior
|
||||
Theater Walkway Door to Desert Elevator Room
|
||||
Theater Walkway Door to Town
|
||||
Swamp Cyan Water Pump (Door)
|
||||
Swamp Between Bridges Second Door
|
||||
Swamp Red Water Pump (Door)
|
||||
Swamp Red Underwater Exit (Door)
|
||||
Swamp Blue Water Pump (Door)
|
||||
Swamp Purple Water Pump (Door)
|
||||
Swamp Laser Shortcut (Door)
|
||||
Treehouse First Door (Door)
|
||||
Treehouse Second Door (Door)
|
||||
Treehouse Third Door (Door)
|
||||
Treehouse Drawbridge (Door)
|
||||
Treehouse Laser House Entry (Door)
|
||||
Mountain Floor 1 Exit (Door)
|
||||
Mountain Floor 2 Staircase Near (Door)
|
||||
Mountain Floor 2 Exit (Door)
|
||||
Mountain Floor 2 Staircase Far (Door)
|
||||
Mountain Bottom Floor Giant Puzzle Exit (Door)
|
||||
Mountain Bottom Floor Final Room Entry (Door)
|
||||
Mountain Bottom Floor Rock (Door)
|
||||
Caves Entry (Door)
|
||||
Caves Pillar Door (Door)
|
||||
Caves Mountain Shortcut (Door)
|
||||
Caves Swamp Shortcut (Door)
|
||||
Challenge Entry (Door)
|
||||
Challenge Tunnels Entry (Door)
|
||||
Tunnels Theater Shortcut (Door)
|
||||
Tunnels Desert Shortcut (Door)
|
||||
Tunnels Town Shortcut (Door)
|
||||
|
||||
Desert Flood Room Flood Controls (Panel)
|
||||
Desert Flood Controls (Panel)
|
||||
Quarry Mill Ramp Controls (Panel)
|
||||
Quarry Mill Elevator Controls (Panel)
|
||||
Quarry Mill Lift Controls (Panel)
|
||||
Quarry Boathouse Ramp Height Control (Panel)
|
||||
Quarry Boathouse Ramp Horizontal Control (Panel)
|
||||
Bunker Elevator Control (Panel)
|
||||
@@ -108,32 +108,32 @@ Swamp Maze Control (Panel)
|
||||
Boat
|
||||
|
||||
Added Locations:
|
||||
Outside Tutorial Door to Outpost Panel
|
||||
Outside Tutorial Exit Door from Outpost Panel
|
||||
Glass Factory Entry Door Panel
|
||||
Glass Factory Vertical Symmetry 5
|
||||
Symmetry Island Door to Symmetry Island Lower Panel
|
||||
Symmetry Island Door to Symmetry Island Upper Panel
|
||||
Outside Tutorial Outpost Entry Panel
|
||||
Outside Tutorial Outpost Exit Panel
|
||||
Glass Factory Entry Panel
|
||||
Glass Factory Back Wall 5
|
||||
Symmetry Island Lower Panel
|
||||
Symmetry Island Upper Panel
|
||||
Orchard Apple Tree 3
|
||||
Orchard Apple Tree 5
|
||||
Desert Door to Desert Flood Light Room Panel
|
||||
Desert Artificial Light Reflection 3
|
||||
Desert Door to Water Levels Room Panel
|
||||
Desert Flood Reflection 6
|
||||
Quarry Door to Quarry 1 Panel
|
||||
Quarry Door to Quarry 2 Panel
|
||||
Quarry Door to Mill Right
|
||||
Quarry Door to Mill Left
|
||||
Quarry Mill Ground Floor Shortcut Door Panel
|
||||
Quarry Mill Door to Outside Quarry Stairs Panel
|
||||
Desert Light Room Entry Panel
|
||||
Desert Light Room 3
|
||||
Desert Flood Room Entry Panel
|
||||
Desert Flood Room 6
|
||||
Quarry Entry 1 Panel
|
||||
Quarry Entry 2 Panel
|
||||
Quarry Mill Entry Right Panel
|
||||
Quarry Mill Entry Left Panel
|
||||
Quarry Mill Side Exit Panel
|
||||
Quarry Mill Roof Exit Panel
|
||||
Quarry Mill Stair Control
|
||||
Quarry Boathouse Shortcut Door Panel
|
||||
Quarry Boathouse Second Barrier Panel
|
||||
Shadows Door Timer Inside
|
||||
Shadows Door Timer Outside
|
||||
Shadows Environmental Avoid 8
|
||||
Shadows Follow 5
|
||||
Shadows Lower Avoid 3
|
||||
Shadows Lower Avoid 5
|
||||
Shadows Far 8
|
||||
Shadows Near 5
|
||||
Shadows Intro 3
|
||||
Shadows Intro 5
|
||||
Keep Hedge Maze 1
|
||||
Keep Pressure Plates 1
|
||||
Keep Hedge Maze 2
|
||||
@@ -142,71 +142,70 @@ Keep Hedge Maze 4
|
||||
Keep Pressure Plates 2
|
||||
Keep Pressure Plates 3
|
||||
Keep Pressure Plates 4
|
||||
Keep Shortcut to Shadows Panel
|
||||
Keep Tower Shortcut to Keep Panel
|
||||
Monastery Shortcut Door Panel
|
||||
Monastery Door Open Left
|
||||
Monastery Door Open Right
|
||||
Monastery Rhombic Avoid 3
|
||||
Town Cargo Box Panel
|
||||
Town Full Dot Grid Shapers 5
|
||||
Town Tinted Door Panel
|
||||
Town Door to Church Stars Panel
|
||||
Keep Shadows Shortcut Panel
|
||||
Keep Tower Shortcut Panel
|
||||
Monastery Shortcut Panel
|
||||
Monastery Entry Left
|
||||
Monastery Entry Right
|
||||
Monastery Outside 3
|
||||
Town Cargo Box Entry Panel
|
||||
Town Wooden Roof Lower Row 5
|
||||
Town Tinted Glass Door Panel
|
||||
Town Church Entry Panel
|
||||
Town Maze Stair Control
|
||||
Town Windmill Door Panel
|
||||
Town Sound Room Left
|
||||
Town Windmill Entry Panel
|
||||
Town Sound Room Right
|
||||
Town Symmetry Squares 5 + Dots
|
||||
Town Red Rooftop 5
|
||||
Town Church Lattice
|
||||
Town Hexagonal Reflection
|
||||
Town Shapers & Dots & Eraser
|
||||
Windmill Door to Front of Theater Panel
|
||||
Theater Door to Cargo Box Left Panel
|
||||
Theater Door to Cargo Box Right Panel
|
||||
Jungle Shortcut to River Panel
|
||||
Town Tall Hexagonal
|
||||
Town Wooden Rooftop
|
||||
Windmill Theater Entry Panel
|
||||
Theater Exit Left Panel
|
||||
Theater Exit Right Panel
|
||||
Jungle Laser Shortcut Panel
|
||||
Jungle Popup Wall Control
|
||||
River Rhombic Avoid to Monastery Garden
|
||||
Bunker Bunker Entry Panel
|
||||
Bunker Door to Bunker Proper Panel
|
||||
Bunker Drawn Squares through Tinted Glass 3
|
||||
Bunker Drop-Down Door Squares 2
|
||||
River Monastery Shortcut Panel
|
||||
Bunker Entry Panel
|
||||
Bunker Tinted Glass Door Panel
|
||||
Bunker Glass Room 3
|
||||
Bunker UV Room 2
|
||||
Swamp Entry Panel
|
||||
Swamp Platform Shapers 4
|
||||
Swamp Platform Row 4
|
||||
Swamp Platform Shortcut Right Panel
|
||||
Swamp Blue Underwater Negative Shapers 5
|
||||
Swamp Broken Shapers 4
|
||||
Swamp Cyan Underwater Negative Shapers 5
|
||||
Swamp Red Underwater Negative Shapers 4
|
||||
Swamp More Rotated Shapers 4
|
||||
Swamp More Rotated Shapers 4
|
||||
Swamp Near Laser Shortcut Right Panel
|
||||
Swamp Blue Underwater 5
|
||||
Swamp Between Bridges Near Row 4
|
||||
Swamp Cyan Underwater 5
|
||||
Swamp Red Underwater 4
|
||||
Swamp Beyond Rotating Bridge 4
|
||||
Swamp Beyond Rotating Bridge 4
|
||||
Swamp Laser Shortcut Right Panel
|
||||
Treehouse First Door Panel
|
||||
Treehouse Second Door Panel
|
||||
Treehouse Beyond Yellow Bridge Door Panel
|
||||
Treehouse Third Door Panel
|
||||
Treehouse Bridge Control
|
||||
Treehouse Left Orange Bridge 15
|
||||
Treehouse Right Orange Bridge 12
|
||||
Treehouse Laser House Door Timer Outside Control
|
||||
Treehouse Laser House Door Timer Inside Control
|
||||
Inside Mountain Moving Background 7
|
||||
Inside Mountain Obscured Vision 5
|
||||
Inside Mountain Physically Obstructed 3
|
||||
Inside Mountain Angled Inside Trash 2
|
||||
Inside Mountain Color Cycle 5
|
||||
Inside Mountain Light Bridge Controller 2
|
||||
Inside Mountain Light Bridge Controller 3
|
||||
Inside Mountain Same Solution 6
|
||||
Inside Mountain Giant Puzzle
|
||||
Inside Mountain Door to Final Room Left
|
||||
Inside Mountain Door to Final Room Right
|
||||
Inside Mountain Bottom Layer Discard
|
||||
Inside Mountain Rock Control
|
||||
Inside Mountain Secret Area Entry Panel
|
||||
Inside Mountain Caves Lone Pillar
|
||||
Inside Mountain Caves Shortcut to Mountain Panel
|
||||
Inside Mountain Caves Shortcut to Swamp Panel
|
||||
Inside Mountain Caves Challenge Entry Panel
|
||||
Challenge Door to Theater Walkway Panel
|
||||
Theater Walkway Theater Shortcut Panel
|
||||
Theater Walkway Desert Shortcut Panel
|
||||
Theater Walkway Town Shortcut Panel
|
||||
Treehouse Laser House Door Timer Inside
|
||||
Mountain Floor 1 Left Row 7
|
||||
Mountain Floor 1 Right Row 5
|
||||
Mountain Floor 1 Back Row 3
|
||||
Mountain Floor 1 Trash Pillar 2
|
||||
Mountain Floor 2 Near Row 5
|
||||
Mountain Floor 2 Light Bridge Controller Near
|
||||
Mountain Floor 2 Light Bridge Controller Far
|
||||
Mountain Floor 2 Far Row 6
|
||||
Mountain Bottom Floor Giant Puzzle
|
||||
Mountain Bottom Floor Final Room Entry Left
|
||||
Mountain Bottom Floor Final Room Entry Right
|
||||
Mountain Bottom Floor Discard
|
||||
Mountain Bottom Floor Rock Control
|
||||
Mountain Bottom Floor Caves Entry Panel
|
||||
Caves Lone Pillar
|
||||
Caves Mountain Shortcut Panel
|
||||
Caves Swamp Shortcut Panel
|
||||
Caves Challenge Entry Panel
|
||||
Challenge Tunnels Entry Panel
|
||||
Tunnels Theater Shortcut Panel
|
||||
Tunnels Desert Shortcut Panel
|
||||
Tunnels Town Shortcut Panel
|
||||
@@ -1,73 +1,73 @@
|
||||
Items:
|
||||
Glass Factory Back Wall
|
||||
Quarry Boathouse Boat Staircase
|
||||
Glass Factory Back Wall (Door)
|
||||
Quarry Boathouse Dock (Door)
|
||||
Outside Tutorial Outpost Doors
|
||||
Glass Factory Entry Door
|
||||
Glass Factory Entry (Door)
|
||||
Symmetry Island Doors
|
||||
Orchard Gates
|
||||
Desert Doors
|
||||
Quarry Main Entry
|
||||
Quarry Door to Mill
|
||||
Quarry Mill Entry (Door)
|
||||
Quarry Mill Shortcuts
|
||||
Quarry Boathouse Barriers
|
||||
Shadows Timed Door
|
||||
Shadows Timed Door (Door)
|
||||
Shadows Laser Room Door
|
||||
Shadows Barriers
|
||||
Keep Hedge Maze Doors
|
||||
Keep Pressure Plates Doors
|
||||
Keep Shortcuts
|
||||
Monastery Entry Door
|
||||
Monastery Entry
|
||||
Monastery Shortcuts
|
||||
Town Doors
|
||||
Town Tower Doors
|
||||
Theater Entry Door
|
||||
Theater Exit Door
|
||||
Theater Entry (Door)
|
||||
Theater Exit
|
||||
Jungle & River Shortcuts
|
||||
Jungle Popup Wall
|
||||
Jungle Popup Wall (Door)
|
||||
Bunker Doors
|
||||
Swamp Doors
|
||||
Swamp Near Laser Shortcut
|
||||
Swamp Laser Shortcut (Door)
|
||||
Swamp Water Pumps
|
||||
Treehouse Entry Doors
|
||||
Treehouse Drawbridge
|
||||
Treehouse Timed Door to Laser House
|
||||
Inside Mountain First Layer Exit Door
|
||||
Inside Mountain Second Layer Stairs & Doors
|
||||
Inside Mountain Giant Puzzle Exit Door
|
||||
Inside Mountain Door to Final Room
|
||||
Inside Mountain Bottom Layer Doors to Caves
|
||||
Treehouse Drawbridge (Door)
|
||||
Treehouse Laser House Entry (Door)
|
||||
Mountain Floor 1 Exit (Door)
|
||||
Mountain Floor 2 Stairs & Doors
|
||||
Mountain Bottom Floor Giant Puzzle Exit (Door)
|
||||
Mountain Bottom Floor Final Room Entry (Door)
|
||||
Mountain Bottom Floor Doors to Caves
|
||||
Caves Doors to Challenge
|
||||
Caves Exits to Main Island
|
||||
Challenge Door to Theater Walkway
|
||||
Theater Walkway Doors
|
||||
Challenge Tunnels Entry (Door)
|
||||
Tunnels Doors
|
||||
|
||||
Added Locations:
|
||||
Outside Tutorial Door to Outpost Panel
|
||||
Outside Tutorial Exit Door from Outpost Panel
|
||||
Glass Factory Entry Door Panel
|
||||
Glass Factory Vertical Symmetry 5
|
||||
Symmetry Island Door to Symmetry Island Lower Panel
|
||||
Symmetry Island Door to Symmetry Island Upper Panel
|
||||
Outside Tutorial Outpost Entry Panel
|
||||
Outside Tutorial Outpost Exit Panel
|
||||
Glass Factory Entry Panel
|
||||
Glass Factory Back Wall 5
|
||||
Symmetry Island Lower Panel
|
||||
Symmetry Island Upper Panel
|
||||
Orchard Apple Tree 3
|
||||
Orchard Apple Tree 5
|
||||
Desert Door to Desert Flood Light Room Panel
|
||||
Desert Artificial Light Reflection 3
|
||||
Desert Door to Water Levels Room Panel
|
||||
Desert Flood Reflection 6
|
||||
Quarry Door to Quarry 1 Panel
|
||||
Quarry Door to Quarry 2 Panel
|
||||
Quarry Door to Mill Right
|
||||
Quarry Door to Mill Left
|
||||
Quarry Mill Ground Floor Shortcut Door Panel
|
||||
Quarry Mill Door to Outside Quarry Stairs Panel
|
||||
Desert Light Room Entry Panel
|
||||
Desert Light Room 3
|
||||
Desert Flood Room Entry Panel
|
||||
Desert Flood Room 6
|
||||
Quarry Entry 1 Panel
|
||||
Quarry Entry 2 Panel
|
||||
Quarry Mill Entry Right Panel
|
||||
Quarry Mill Entry Left Panel
|
||||
Quarry Mill Side Exit Panel
|
||||
Quarry Mill Roof Exit Panel
|
||||
Quarry Mill Stair Control
|
||||
Quarry Boathouse Shortcut Door Panel
|
||||
Quarry Boathouse Second Barrier Panel
|
||||
Shadows Door Timer Inside
|
||||
Shadows Door Timer Outside
|
||||
Shadows Environmental Avoid 8
|
||||
Shadows Follow 5
|
||||
Shadows Lower Avoid 3
|
||||
Shadows Lower Avoid 5
|
||||
Shadows Far 8
|
||||
Shadows Near 5
|
||||
Shadows Intro 3
|
||||
Shadows Intro 5
|
||||
Keep Hedge Maze 1
|
||||
Keep Pressure Plates 1
|
||||
Keep Hedge Maze 2
|
||||
@@ -76,71 +76,70 @@ Keep Hedge Maze 4
|
||||
Keep Pressure Plates 2
|
||||
Keep Pressure Plates 3
|
||||
Keep Pressure Plates 4
|
||||
Keep Shortcut to Shadows Panel
|
||||
Keep Tower Shortcut to Keep Panel
|
||||
Monastery Shortcut Door Panel
|
||||
Monastery Door Open Left
|
||||
Monastery Door Open Right
|
||||
Monastery Rhombic Avoid 3
|
||||
Town Cargo Box Panel
|
||||
Town Full Dot Grid Shapers 5
|
||||
Town Tinted Door Panel
|
||||
Town Door to Church Stars Panel
|
||||
Keep Shadows Shortcut Panel
|
||||
Keep Tower Shortcut Panel
|
||||
Monastery Shortcut Panel
|
||||
Monastery Entry Left
|
||||
Monastery Entry Right
|
||||
Monastery Outside 3
|
||||
Town Cargo Box Entry Panel
|
||||
Town Wooden Roof Lower Row 5
|
||||
Town Tinted Glass Door Panel
|
||||
Town Church Entry Panel
|
||||
Town Maze Stair Control
|
||||
Town Windmill Door Panel
|
||||
Town Sound Room Left
|
||||
Town Windmill Entry Panel
|
||||
Town Sound Room Right
|
||||
Town Symmetry Squares 5 + Dots
|
||||
Town Red Rooftop 5
|
||||
Town Church Lattice
|
||||
Town Hexagonal Reflection
|
||||
Town Shapers & Dots & Eraser
|
||||
Windmill Door to Front of Theater Panel
|
||||
Theater Door to Cargo Box Left Panel
|
||||
Theater Door to Cargo Box Right Panel
|
||||
Jungle Shortcut to River Panel
|
||||
Town Tall Hexagonal
|
||||
Town Wooden Rooftop
|
||||
Windmill Theater Entry Panel
|
||||
Theater Exit Left Panel
|
||||
Theater Exit Right Panel
|
||||
Jungle Laser Shortcut Panel
|
||||
Jungle Popup Wall Control
|
||||
River Rhombic Avoid to Monastery Garden
|
||||
Bunker Bunker Entry Panel
|
||||
Bunker Door to Bunker Proper Panel
|
||||
Bunker Drawn Squares through Tinted Glass 3
|
||||
Bunker Drop-Down Door Squares 2
|
||||
River Monastery Shortcut Panel
|
||||
Bunker Entry Panel
|
||||
Bunker Tinted Glass Door Panel
|
||||
Bunker Glass Room 3
|
||||
Bunker UV Room 2
|
||||
Swamp Entry Panel
|
||||
Swamp Platform Shapers 4
|
||||
Swamp Platform Row 4
|
||||
Swamp Platform Shortcut Right Panel
|
||||
Swamp Blue Underwater Negative Shapers 5
|
||||
Swamp Broken Shapers 4
|
||||
Swamp Cyan Underwater Negative Shapers 5
|
||||
Swamp Red Underwater Negative Shapers 4
|
||||
Swamp More Rotated Shapers 4
|
||||
Swamp More Rotated Shapers 4
|
||||
Swamp Near Laser Shortcut Right Panel
|
||||
Swamp Blue Underwater 5
|
||||
Swamp Between Bridges Near Row 4
|
||||
Swamp Cyan Underwater 5
|
||||
Swamp Red Underwater 4
|
||||
Swamp Beyond Rotating Bridge 4
|
||||
Swamp Beyond Rotating Bridge 4
|
||||
Swamp Laser Shortcut Right Panel
|
||||
Treehouse First Door Panel
|
||||
Treehouse Second Door Panel
|
||||
Treehouse Beyond Yellow Bridge Door Panel
|
||||
Treehouse Third Door Panel
|
||||
Treehouse Bridge Control
|
||||
Treehouse Left Orange Bridge 15
|
||||
Treehouse Right Orange Bridge 12
|
||||
Treehouse Laser House Door Timer Outside Control
|
||||
Treehouse Laser House Door Timer Inside Control
|
||||
Inside Mountain Moving Background 7
|
||||
Inside Mountain Obscured Vision 5
|
||||
Inside Mountain Physically Obstructed 3
|
||||
Inside Mountain Angled Inside Trash 2
|
||||
Inside Mountain Color Cycle 5
|
||||
Inside Mountain Light Bridge Controller 2
|
||||
Inside Mountain Light Bridge Controller 3
|
||||
Inside Mountain Same Solution 6
|
||||
Inside Mountain Giant Puzzle
|
||||
Inside Mountain Door to Final Room Left
|
||||
Inside Mountain Door to Final Room Right
|
||||
Inside Mountain Bottom Layer Discard
|
||||
Inside Mountain Rock Control
|
||||
Inside Mountain Secret Area Entry Panel
|
||||
Inside Mountain Caves Lone Pillar
|
||||
Inside Mountain Caves Shortcut to Mountain Panel
|
||||
Inside Mountain Caves Shortcut to Swamp Panel
|
||||
Inside Mountain Caves Challenge Entry Panel
|
||||
Challenge Door to Theater Walkway Panel
|
||||
Theater Walkway Theater Shortcut Panel
|
||||
Theater Walkway Desert Shortcut Panel
|
||||
Theater Walkway Town Shortcut Panel
|
||||
Treehouse Laser House Door Timer Inside
|
||||
Mountain Floor 1 Left Row 7
|
||||
Mountain Floor 1 Right Row 5
|
||||
Mountain Floor 1 Back Row 3
|
||||
Mountain Floor 1 Trash Pillar 2
|
||||
Mountain Floor 2 Near Row 5
|
||||
Mountain Floor 2 Light Bridge Controller Near
|
||||
Mountain Floor 2 Light Bridge Controller Far
|
||||
Mountain Floor 2 Far Row 6
|
||||
Mountain Bottom Floor Giant Puzzle
|
||||
Mountain Bottom Floor Final Room Entry Left
|
||||
Mountain Bottom Floor Final Room Entry Right
|
||||
Mountain Bottom Floor Discard
|
||||
Mountain Bottom Floor Rock Control
|
||||
Mountain Bottom Floor Caves Entry Panel
|
||||
Caves Lone Pillar
|
||||
Caves Mountain Shortcut Panel
|
||||
Caves Swamp Shortcut Panel
|
||||
Caves Challenge Entry Panel
|
||||
Challenge Tunnels Entry Panel
|
||||
Tunnels Theater Shortcut Panel
|
||||
Tunnels Desert Shortcut Panel
|
||||
Tunnels Town Shortcut Panel
|
||||
@@ -5,5 +5,5 @@ Starting Inventory:
|
||||
Caves Exits to Main Island
|
||||
|
||||
Remove Items:
|
||||
Caves Mountain Shortcut
|
||||
Caves Swamp Shortcut
|
||||
Caves Mountain Shortcut (Door)
|
||||
Caves Swamp Shortcut (Door)
|
||||
Reference in New Issue
Block a user