Compare commits

..

1 Commits

Author SHA1 Message Date
NewSoupVi
4762865d0b The Witness: Bump Required Client Version to 0.6.0
The beta client releases already report this.
2025-03-22 20:51:18 +01:00
121 changed files with 1095 additions and 9790 deletions

1
.gitignore vendored
View File

@@ -10,7 +10,6 @@
*.apmc
*.apz5
*.aptloz
*.aptww
*.apemerald
*.pyc
*.pyd

View File

@@ -81,7 +81,6 @@ Currently, the following games are supported:
* Castlevania: Circle of the Moon
* Inscryption
* Civilization VI
* The Legend of Zelda: The Wind Waker
For setup and instructions check out our [tutorials page](https://archipelago.gg/tutorial/).
Downloads can be found at [Releases](https://github.com/ArchipelagoMW/Archipelago/releases), including compiled

View File

@@ -1,11 +1,11 @@
flask>=3.1.0
werkzeug>=3.1.3
flask>=3.0.3
werkzeug>=3.0.6
pony>=0.7.19
waitress>=3.0.2
waitress>=3.0.0
Flask-Caching>=2.3.0
Flask-Compress>=1.17
Flask-Limiter>=3.12
bokeh>=3.6.3
markupsafe>=3.0.2
Flask-Compress>=1.15
Flask-Limiter>=3.8.0
bokeh>=3.5.2
markupsafe>=2.1.5
Markdown>=3.7
mdx-breakless-lists>=1.0.1

View File

@@ -214,9 +214,6 @@
# Wargroove
/worlds/wargroove/ @FlySniper
# The Wind Waker
/worlds/tww/ @tanjo3
# The Witness
/worlds/witness/ @NewSoupVi @blastron

View File

@@ -265,19 +265,14 @@ def bake_target_group_lookup(world: World, get_target_groups: Callable[[int], li
return { group: get_target_groups(group) for group in unique_groups }
def disconnect_entrance_for_randomization(entrance: Entrance, target_group: int | None = None,
one_way_target_name: str | None = None) -> None:
def disconnect_entrance_for_randomization(entrance: Entrance, target_group: int | None = None) -> None:
"""
Given an entrance in a "vanilla" region graph, splits that entrance to prepare it for randomization
in randomize_entrances. This should be done after setting the type and group of the entrance. Because it attempts
to meet strict entrance naming requirements for coupled mode, this function may produce unintuitive results when
called only on a single entrance; it produces eventually-correct outputs only after calling it on all entrances.
in randomize_entrances. This should be done after setting the type and group of the entrance.
:param entrance: The entrance which will be disconnected in preparation for randomization.
:param target_group: The group to assign to the created ER target. If not specified, the group from
the original entrance will be copied.
:param one_way_target_name: The name of the created ER target if `entrance` is one-way. This argument
is required for one-way entrances and is ignored otherwise.
"""
child_region = entrance.connected_region
parent_region = entrance.parent_region
@@ -292,11 +287,8 @@ def disconnect_entrance_for_randomization(entrance: Entrance, target_group: int
# targets in the child region will be created when the other direction edge is disconnected
target = parent_region.create_er_target(entrance.name)
else:
# for 1-ways, the child region needs a target. naming is not a concern for coupling so we
# allow it to be user provided (and require it, to prevent an unhelpful assumed name in pairings)
if not one_way_target_name:
raise ValueError("Cannot disconnect a one-way entrance without a target name specified")
target = child_region.create_er_target(one_way_target_name)
# for 1-ways, the child region needs a target and coupling/naming is not a concern
target = child_region.create_er_target(child_region.name)
target.randomization_type = entrance.randomization_type
target.randomization_group = target_group or entrance.randomization_group

View File

@@ -817,12 +817,6 @@ class HintLayout(BoxLayout):
boxlayout.add_widget(AutocompleteHintInput())
self.add_widget(boxlayout)
def fix_heights(self):
for child in self.children:
fix_func = getattr(child, "fix_heights", None)
if fix_func:
fix_func()
status_names: typing.Dict[HintStatus, str] = {
HintStatus.HINT_FOUND: "Found",

View File

@@ -1,14 +1,14 @@
colorama>=0.4.6
websockets>=13.0.1,<14
PyYAML>=6.0.2
jellyfish>=1.1.3
jinja2>=3.1.6
jellyfish>=1.1.0
jinja2>=3.1.4
schema>=0.7.7
kivy>=2.3.1
bsdiff4>=1.2.6
platformdirs>=4.3.6
certifi>=2025.1.31
cython>=3.0.12
cymem>=2.0.11
orjson>=3.10.15
kivy>=2.3.0
bsdiff4>=1.2.4
platformdirs>=4.2.2
certifi>=2024.12.14
cython>=3.0.11
cymem>=2.0.8
orjson>=3.10.7
typing_extensions>=4.12.2

View File

@@ -19,7 +19,7 @@ from typing import Dict, Iterable, List, Optional, Sequence, Set, Tuple, Union
# This is a bit jank. We need cx-Freeze to be able to run anything from this script, so install it
requirement = 'cx-Freeze==8.0.0'
requirement = 'cx-Freeze==7.2.0'
try:
import pkg_resources
try:

View File

@@ -148,7 +148,7 @@ class TestDisconnectForRandomization(unittest.TestCase):
e.randomization_group = 1
e.connect(r2)
disconnect_entrance_for_randomization(e, one_way_target_name="foo")
disconnect_entrance_for_randomization(e)
self.assertIsNone(e.connected_region)
self.assertEqual([], r1.entrances)
@@ -158,22 +158,10 @@ class TestDisconnectForRandomization(unittest.TestCase):
self.assertEqual(1, len(r2.entrances))
self.assertIsNone(r2.entrances[0].parent_region)
self.assertEqual("foo", r2.entrances[0].name)
self.assertEqual("r2", r2.entrances[0].name)
self.assertEqual(EntranceType.ONE_WAY, r2.entrances[0].randomization_type)
self.assertEqual(1, r2.entrances[0].randomization_group)
def test_disconnect_default_1way_no_vanilla_target_raises(self):
multiworld = generate_test_multiworld()
r1 = Region("r1", 1, multiworld)
r2 = Region("r2", 1, multiworld)
e = r1.create_exit("e")
e.randomization_type = EntranceType.ONE_WAY
e.randomization_group = 1
e.connect(r2)
with self.assertRaises(ValueError):
disconnect_entrance_for_randomization(e)
def test_disconnect_uses_alternate_group(self):
multiworld = generate_test_multiworld()
r1 = Region("r1", 1, multiworld)
@@ -183,7 +171,7 @@ class TestDisconnectForRandomization(unittest.TestCase):
e.randomization_group = 1
e.connect(r2)
disconnect_entrance_for_randomization(e, 2, "foo")
disconnect_entrance_for_randomization(e, 2)
self.assertIsNone(e.connected_region)
self.assertEqual([], r1.entrances)
@@ -193,7 +181,7 @@ class TestDisconnectForRandomization(unittest.TestCase):
self.assertEqual(1, len(r2.entrances))
self.assertIsNone(r2.entrances[0].parent_region)
self.assertEqual("foo", r2.entrances[0].name)
self.assertEqual("r2", r2.entrances[0].name)
self.assertEqual(EntranceType.ONE_WAY, r2.entrances[0].randomization_type)
self.assertEqual(2, r2.entrances[0].randomization_group)

View File

@@ -1,14 +0,0 @@
import unittest
import os
class TestPackages(unittest.TestCase):
def test_packages_have_init(self):
"""Test that all world folders containing .py files also have a __init__.py file,
to indicate full package rather than namespace package."""
import Utils
worlds_path = Utils.local_path("worlds")
for dirpath, dirnames, filenames in os.walk(worlds_path):
with self.subTest(directory=dirpath):
self.assertEqual("__init__.py" in filenames, any(file.endswith(".py") for file in filenames))

View File

@@ -1,11 +0,0 @@
import unittest
from worlds.AutoWorld import AutoWorldRegister
from worlds.Files import AutoPatchRegister
class TestPatches(unittest.TestCase):
def test_patch_name_matches_game(self) -> None:
for game_name in AutoPatchRegister.patch_types:
with self.subTest(game=game_name):
self.assertIn(game_name, AutoWorldRegister.world_types.keys(),
f"Patch '{game_name}' does not match the name of any world.")

View File

@@ -1,19 +0,0 @@
import unittest
import os
class TestBase(unittest.TestCase):
def test_requirements_file_ends_on_newline(self):
"""Test that all requirements files end on a newline"""
import Utils
requirements_files = [Utils.local_path("requirements.txt"),
Utils.local_path("WebHostLib", "requirements.txt")]
worlds_path = Utils.local_path("worlds")
for entry in os.listdir(worlds_path):
requirements_path = os.path.join(worlds_path, entry, "requirements.txt")
if os.path.isfile(requirements_path):
requirements_files.append(requirements_path)
for requirements_file in requirements_files:
with self.subTest(path=requirements_file):
with open(requirements_file) as f:
self.assertEqual(f.read()[-1], "\n")

View File

@@ -3,4 +3,4 @@ mpyq>=0.2.5
portpicker>=1.5.2
aiohttp>=3.8.4
loguru>=0.7.0
protobuf==3.20.3
protobuf==3.20.3

View File

@@ -1,2 +1,2 @@
maseya-z3pr>=1.0.0rc1
xxtea>=3.0.0
xxtea>=3.0.0

View File

@@ -48,10 +48,6 @@ class CivVIContainer(APContainer, metaclass=AutoPatchRegister):
opened_zipfile.writestr(filename, yml)
super().write_contents(opened_zipfile)
def sanitize_value(value: str) -> str:
"""Removes values that can cause issues in XML"""
return value.replace('"', "'").replace('&', 'and')
def get_cost(world: 'CivVIWorld', location: CivVILocationData) -> int:
"""
@@ -67,7 +63,7 @@ def get_formatted_player_name(world: 'CivVIWorld', player: int) -> str:
Returns the name of the player in the world
"""
if player != world.player:
return sanitize_value(f"{world.multiworld.player_name[player]}{apo}s")
return f"{world.multiworld.player_name[player]}{apo}s"
return "Your"
@@ -110,7 +106,7 @@ def generate_new_items(world: 'CivVIWorld') -> str:
<Row TechnologyType="TECH_BLOCKER" Name="TECH_BLOCKER" EraType="ERA_ANCIENT" UITreeRow="0" Cost="99999" AdvisorType="ADVISOR_GENERIC" Description="Archipelago Tech created to prevent players from researching their own tech. If you can read this, then congrats you have reached the end of your tree before beating the game!"/>
{"".join([f'{tab}<Row TechnologyType="{location.name}" '
f'Name="{get_formatted_player_name(world, location.item.player)} '
f'{sanitize_value(location.item.name)}" '
f'{location.item.name}" '
f'EraType="{world.location_table[location.name].era_type}" '
f'UITreeRow="{world.location_table[location.name].uiTreeRow}" '
f'Cost="{get_cost(world, world.location_table[location.name])}" '
@@ -126,7 +122,7 @@ def generate_new_items(world: 'CivVIWorld') -> str:
<Row CivicType="CIVIC_BLOCKER" Name="CIVIC_BLOCKER" EraType="ERA_ANCIENT" UITreeRow="0" Cost="99999" AdvisorType="ADVISOR_GENERIC" Description="Archipelago Civic created to prevent players from researching their own civics. If you can read this, then congrats you have reached the end of your tree before beating the game!"/>
{"".join([f'{tab}<Row CivicType="{location.name}" '
f'Name="{get_formatted_player_name(world, location.item.player)} '
f'{sanitize_value(location.item.name)}" '
f'{location.item.name}" '
f'EraType="{world.location_table[location.name].era_type}" '
f'UITreeRow="{world.location_table[location.name].uiTreeRow}" '
f'Cost="{get_cost(world, world.location_table[location.name])}" '

View File

@@ -644,9 +644,6 @@ class CV64PatchExtensions(APPatchExtension):
# Replace the PowerUp in the Forest Special1 Bridge 3HB rock with an L jewel if 3HBs aren't randomized
if not options["multi_hit_breakables"]:
rom_data.write_byte(0x10C7A1, 0x03)
# Replace the PowerUp in one of the lizard lockers if the lizard locker items aren't randomized.
if not options["lizard_locker_items"]:
rom_data.write_byte(0xBFCA07, 0x03)
# Change the appearance of the Pot-Pourri to that of a larger PowerUp regardless of the above setting, so other
# game PermaUps are distinguishable.
rom_data.write_int32s(0xEE558, [0x06005F08, 0x3FB00000, 0xFFFFFF00])
@@ -717,11 +714,7 @@ class CV64PatchExtensions(APPatchExtension):
rom_data.write_int32(0x10CF38, 0x8000FF4D) # CT final room door slab
rom_data.write_int32(0x10CF44, 0x1000FF4D) # CT Renon slab
rom_data.write_int32(0x10C908, 0x0008FF4D) # Villa foyer chandelier
# Change the pointer to the Clock Tower final room 3HB door slab drops to not share its values with those of the
# 3HB slab near Renon at the top of the room.
if options["multi_hit_breakables"]:
rom_data.write_byte(0x10CF37, 0x04)
rom_data.write_byte(0x10CF37, 0x04) # pointer for CT final room door slab item data
# Once-per-frame gameplay checks
rom_data.write_int32(0x6C848, 0x080FF40D) # J 0x803FD034
@@ -1007,7 +1000,6 @@ def write_patch(world: "CV64World", patch: CV64ProcedurePatch, offset_data: Dict
"multi_hit_breakables": world.options.multi_hit_breakables.value,
"drop_previous_sub_weapon": world.options.drop_previous_sub_weapon.value,
"countdown": world.options.countdown.value,
"lizard_locker_items": world.options.lizard_locker_items.value,
"shopsanity": world.options.shopsanity.value,
"panther_dash": world.options.panther_dash.value,
"big_toss": world.options.big_toss.value,

View File

@@ -126,7 +126,7 @@ location_table: Dict[int, LocationDict] = {
'map': 3,
'index': 64,
'doom_type': 2001,
'region': "Toxin Refinery (E1M3) Start"},
'region': "Toxin Refinery (E1M3) Main"},
351019: {'name': 'Toxin Refinery (E1M3) - Shotgun 2',
'episode': 1,
'map': 3,
@@ -234,7 +234,7 @@ location_table: Dict[int, LocationDict] = {
'map': 4,
'index': 107,
'doom_type': 8,
'region': "Command Control (E1M4) Start"},
'region': "Command Control (E1M4) Main"},
351037: {'name': 'Command Control (E1M4) - Shotgun',
'episode': 1,
'map': 4,
@@ -504,7 +504,7 @@ location_table: Dict[int, LocationDict] = {
'map': 7,
'index': 122,
'doom_type': 2001,
'region': "Computer Station (E1M7) Start"},
'region': "Computer Station (E1M7) Main"},
351082: {'name': 'Computer Station (E1M7) - Rocket launcher',
'episode': 1,
'map': 7,
@@ -912,7 +912,7 @@ location_table: Dict[int, LocationDict] = {
'map': 4,
'index': 109,
'doom_type': 2001,
'region': "Deimos Lab (E2M4) Start"},
'region': "Deimos Lab (E2M4) Main"},
351150: {'name': 'Deimos Lab (E2M4) - Mega Armor',
'episode': 2,
'map': 4,
@@ -1242,7 +1242,7 @@ location_table: Dict[int, LocationDict] = {
'map': 8,
'index': 36,
'doom_type': 2019,
'region': "Tower of Babel (E2M8) Start"},
'region': "Tower of Babel (E2M8) Main"},
351205: {'name': 'Fortress of Mystery (E2M9) - Supercharge',
'episode': 2,
'map': 9,
@@ -1638,7 +1638,7 @@ location_table: Dict[int, LocationDict] = {
'map': 5,
'index': 187,
'doom_type': 2001,
'region': "Unholy Cathedral (E3M5) Start"},
'region': "Unholy Cathedral (E3M5) Main"},
351271: {'name': 'Unholy Cathedral (E3M5) - Shotgun 2',
'episode': 3,
'map': 5,

View File

@@ -33,11 +33,9 @@ regions:List[RegionDict] = [
# Toxin Refinery (E1M3)
{"name":"Toxin Refinery (E1M3) Main",
"connects_to_hub":False,
"connects_to_hub":True,
"episode":1,
"connections":[
{"target":"Toxin Refinery (E1M3) Blue","pro":False},
{"target":"Toxin Refinery (E1M3) Start","pro":False}]},
"connections":[{"target":"Toxin Refinery (E1M3) Blue","pro":False}]},
{"name":"Toxin Refinery (E1M3) Blue",
"connects_to_hub":False,
"episode":1,
@@ -48,20 +46,15 @@ regions:List[RegionDict] = [
"connects_to_hub":False,
"episode":1,
"connections":[{"target":"Toxin Refinery (E1M3) Blue","pro":False}]},
{"name":"Toxin Refinery (E1M3) Start",
"connects_to_hub":True,
"episode":1,
"connections":[{"target":"Toxin Refinery (E1M3) Main","pro":False}]},
# Command Control (E1M4)
{"name":"Command Control (E1M4) Main",
"connects_to_hub":False,
"connects_to_hub":True,
"episode":1,
"connections":[
{"target":"Command Control (E1M4) Blue","pro":False},
{"target":"Command Control (E1M4) Yellow","pro":False},
{"target":"Command Control (E1M4) Ledge","pro":True},
{"target":"Command Control (E1M4) Start","pro":False}]},
{"target":"Command Control (E1M4) Ledge","pro":True}]},
{"name":"Command Control (E1M4) Blue",
"connects_to_hub":False,
"episode":1,
@@ -79,10 +72,6 @@ regions:List[RegionDict] = [
{"target":"Command Control (E1M4) Main","pro":False},
{"target":"Command Control (E1M4) Blue","pro":False},
{"target":"Command Control (E1M4) Yellow","pro":False}]},
{"name":"Command Control (E1M4) Start",
"connects_to_hub":True,
"episode":1,
"connections":[{"target":"Command Control (E1M4) Main","pro":False}]},
# Phobos Lab (E1M5)
{"name":"Phobos Lab (E1M5) Main",
@@ -137,12 +126,11 @@ regions:List[RegionDict] = [
# Computer Station (E1M7)
{"name":"Computer Station (E1M7) Main",
"connects_to_hub":False,
"connects_to_hub":True,
"episode":1,
"connections":[
{"target":"Computer Station (E1M7) Red","pro":False},
{"target":"Computer Station (E1M7) Yellow","pro":False},
{"target":"Computer Station (E1M7) Start","pro":False}]},
{"target":"Computer Station (E1M7) Yellow","pro":False}]},
{"name":"Computer Station (E1M7) Blue",
"connects_to_hub":False,
"episode":1,
@@ -162,10 +150,6 @@ regions:List[RegionDict] = [
"connects_to_hub":False,
"episode":1,
"connections":[{"target":"Computer Station (E1M7) Yellow","pro":False}]},
{"name":"Computer Station (E1M7) Start",
"connects_to_hub":True,
"episode":1,
"connections":[{"target":"Computer Station (E1M7) Main","pro":False}]},
# Phobos Anomaly (E1M8)
{"name":"Phobos Anomaly (E1M8) Main",
@@ -254,11 +238,9 @@ regions:List[RegionDict] = [
# Deimos Lab (E2M4)
{"name":"Deimos Lab (E2M4) Main",
"connects_to_hub":False,
"connects_to_hub":True,
"episode":2,
"connections":[
{"target":"Deimos Lab (E2M4) Blue","pro":False},
{"target":"Deimos Lab (E2M4) Start","pro":False}]},
"connections":[{"target":"Deimos Lab (E2M4) Blue","pro":False}]},
{"name":"Deimos Lab (E2M4) Blue",
"connects_to_hub":False,
"episode":2,
@@ -269,10 +251,6 @@ regions:List[RegionDict] = [
"connects_to_hub":False,
"episode":2,
"connections":[{"target":"Deimos Lab (E2M4) Blue","pro":False}]},
{"name":"Deimos Lab (E2M4) Start",
"connects_to_hub":True,
"episode":2,
"connections":[{"target":"Deimos Lab (E2M4) Main","pro":False}]},
# Command Center (E2M5)
{"name":"Command Center (E2M5) Main",
@@ -336,13 +314,9 @@ regions:List[RegionDict] = [
# Tower of Babel (E2M8)
{"name":"Tower of Babel (E2M8) Main",
"connects_to_hub":False,
"episode":2,
"connections":[{"target":"Tower of Babel (E2M8) Start","pro":False}]},
{"name":"Tower of Babel (E2M8) Start",
"connects_to_hub":True,
"episode":2,
"connections":[{"target":"Tower of Babel (E2M8) Main","pro":False}]},
"connections":[]},
# Fortress of Mystery (E2M9)
{"name":"Fortress of Mystery (E2M9) Main",
@@ -418,12 +392,11 @@ regions:List[RegionDict] = [
# Unholy Cathedral (E3M5)
{"name":"Unholy Cathedral (E3M5) Main",
"connects_to_hub":False,
"connects_to_hub":True,
"episode":3,
"connections":[
{"target":"Unholy Cathedral (E3M5) Yellow","pro":False},
{"target":"Unholy Cathedral (E3M5) Blue","pro":False},
{"target":"Unholy Cathedral (E3M5) Start","pro":False}]},
{"target":"Unholy Cathedral (E3M5) Blue","pro":False}]},
{"name":"Unholy Cathedral (E3M5) Blue",
"connects_to_hub":False,
"episode":3,
@@ -432,10 +405,6 @@ regions:List[RegionDict] = [
"connects_to_hub":False,
"episode":3,
"connections":[{"target":"Unholy Cathedral (E3M5) Main","pro":False}]},
{"name":"Unholy Cathedral (E3M5) Start",
"connects_to_hub":True,
"episode":3,
"connections":[{"target":"Unholy Cathedral (E3M5) Main","pro":False}]},
# Mt. Erebus (E3M6)
{"name":"Mt. Erebus (E3M6) Main",

View File

@@ -23,6 +23,10 @@ def set_episode1_rules(player, multiworld, pro):
state.has("Nuclear Plant (E1M2) - Red keycard", player, 1))
# Toxin Refinery (E1M3)
set_rule(multiworld.get_entrance("Hub -> Toxin Refinery (E1M3) Main", player), lambda state:
(state.has("Toxin Refinery (E1M3)", player, 1)) and
(state.has("Shotgun", player, 1) or
state.has("Chaingun", player, 1)))
set_rule(multiworld.get_entrance("Toxin Refinery (E1M3) Main -> Toxin Refinery (E1M3) Blue", player), lambda state:
state.has("Toxin Refinery (E1M3) - Blue keycard", player, 1))
set_rule(multiworld.get_entrance("Toxin Refinery (E1M3) Blue -> Toxin Refinery (E1M3) Yellow", player), lambda state:
@@ -31,13 +35,12 @@ def set_episode1_rules(player, multiworld, pro):
state.has("Toxin Refinery (E1M3) - Blue keycard", player, 1))
set_rule(multiworld.get_entrance("Toxin Refinery (E1M3) Yellow -> Toxin Refinery (E1M3) Blue", player), lambda state:
state.has("Toxin Refinery (E1M3) - Yellow keycard", player, 1))
set_rule(multiworld.get_entrance("Hub -> Toxin Refinery (E1M3) Start", player), lambda state:
state.has("Toxin Refinery (E1M3)", player, 1))
set_rule(multiworld.get_entrance("Toxin Refinery (E1M3) Start -> Toxin Refinery (E1M3) Main", player), lambda state:
state.has("Shotgun", player, 1) or
state.has("Chaingun", player, 1))
# Command Control (E1M4)
set_rule(multiworld.get_entrance("Hub -> Command Control (E1M4) Main", player), lambda state:
state.has("Command Control (E1M4)", player, 1) and
state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1))
set_rule(multiworld.get_entrance("Command Control (E1M4) Main -> Command Control (E1M4) Blue", player), lambda state:
state.has("Command Control (E1M4) - Blue keycard", player, 1) or
state.has("Command Control (E1M4) - Yellow keycard", player, 1))
@@ -47,11 +50,6 @@ def set_episode1_rules(player, multiworld, pro):
set_rule(multiworld.get_entrance("Command Control (E1M4) Blue -> Command Control (E1M4) Main", player), lambda state:
state.has("Command Control (E1M4) - Yellow keycard", player, 1) or
state.has("Command Control (E1M4) - Blue keycard", player, 1))
set_rule(multiworld.get_entrance("Hub -> Command Control (E1M4) Start", player), lambda state:
state.has("Command Control (E1M4)", player, 1))
set_rule(multiworld.get_entrance("Command Control (E1M4) Start -> Command Control (E1M4) Main", player), lambda state:
state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1))
# Phobos Lab (E1M5)
set_rule(multiworld.get_entrance("Hub -> Phobos Lab (E1M5) Main", player), lambda state:
@@ -85,6 +83,11 @@ def set_episode1_rules(player, multiworld, pro):
state.has("Central Processing (E1M6) - Yellow keycard", player, 1))
# Computer Station (E1M7)
set_rule(multiworld.get_entrance("Hub -> Computer Station (E1M7) Main", player), lambda state:
state.has("Computer Station (E1M7)", player, 1) and
state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Rocket launcher", player, 1))
set_rule(multiworld.get_entrance("Computer Station (E1M7) Main -> Computer Station (E1M7) Red", player), lambda state:
state.has("Computer Station (E1M7) - Red keycard", player, 1))
set_rule(multiworld.get_entrance("Computer Station (E1M7) Main -> Computer Station (E1M7) Yellow", player), lambda state:
@@ -100,12 +103,6 @@ def set_episode1_rules(player, multiworld, pro):
state.has("Computer Station (E1M7) - Red keycard", player, 1))
set_rule(multiworld.get_entrance("Computer Station (E1M7) Courtyard -> Computer Station (E1M7) Yellow", player), lambda state:
state.has("Computer Station (E1M7) - Yellow keycard", player, 1))
set_rule(multiworld.get_entrance("Hub -> Computer Station (E1M7) Start", player), lambda state:
state.has("Computer Station (E1M7)", player, 1))
set_rule(multiworld.get_entrance("Computer Station (E1M7) Start -> Computer Station (E1M7) Main", player), lambda state:
state.has("Shotgun", player, 1) and
state.has("Rocket launcher", player, 1) and
state.has("Chaingun", player, 1))
# Phobos Anomaly (E1M8)
set_rule(multiworld.get_entrance("Hub -> Phobos Anomaly (E1M8) Start", player), lambda state:
@@ -175,16 +172,15 @@ def set_episode2_rules(player, multiworld, pro):
state.has("Refinery (E2M3) - Blue keycard", player, 1))
# Deimos Lab (E2M4)
set_rule(multiworld.get_entrance("Hub -> Deimos Lab (E2M4) Main", player), lambda state:
state.has("Deimos Lab (E2M4)", player, 1) and
state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Plasma gun", player, 1))
set_rule(multiworld.get_entrance("Deimos Lab (E2M4) Main -> Deimos Lab (E2M4) Blue", player), lambda state:
state.has("Deimos Lab (E2M4) - Blue keycard", player, 1))
set_rule(multiworld.get_entrance("Deimos Lab (E2M4) Blue -> Deimos Lab (E2M4) Yellow", player), lambda state:
state.has("Deimos Lab (E2M4) - Yellow keycard", player, 1))
set_rule(multiworld.get_entrance("Hub -> Deimos Lab (E2M4) Start", player), lambda state:
state.has("Deimos Lab (E2M4)", player, 1))
set_rule(multiworld.get_entrance("Deimos Lab (E2M4) Start -> Deimos Lab (E2M4) Main", player), lambda state:
state.has("Shotgun", player, 1) and
state.has("Plasma gun", player, 1) and
state.has("Chaingun", player, 1))
# Command Center (E2M5)
set_rule(multiworld.get_entrance("Hub -> Command Center (E2M5) Main", player), lambda state:
@@ -242,11 +238,11 @@ def set_episode2_rules(player, multiworld, pro):
state.has("Spawning Vats (E2M7) - Red keycard", player, 1))
# Tower of Babel (E2M8)
set_rule(multiworld.get_entrance("Hub -> Tower of Babel (E2M8) Start", player), lambda state:
state.has("Tower of Babel (E2M8)", player, 1))
set_rule(multiworld.get_entrance("Tower of Babel (E2M8) Start -> Tower of Babel (E2M8) Main", player), lambda state:
(state.has("Chaingun", player, 1) and
state.has("Shotgun", player, 1)) and (state.has("Rocket launcher", player, 1) or
set_rule(multiworld.get_entrance("Hub -> Tower of Babel (E2M8) Main", player), lambda state:
(state.has("Tower of Babel (E2M8)", player, 1) and
state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1)) and
(state.has("Rocket launcher", player, 1) or
state.has("Plasma gun", player, 1) or
state.has("BFG9000", player, 1)))
@@ -325,6 +321,13 @@ def set_episode3_rules(player, multiworld, pro):
state.has("House of Pain (E3M4) - Yellow skull key", player, 1))
# Unholy Cathedral (E3M5)
set_rule(multiworld.get_entrance("Hub -> Unholy Cathedral (E3M5) Main", player), lambda state:
(state.has("Unholy Cathedral (E3M5)", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Shotgun", player, 1)) and
(state.has("Rocket launcher", player, 1) or
state.has("Plasma gun", player, 1) or
state.has("BFG9000", player, 1)))
set_rule(multiworld.get_entrance("Unholy Cathedral (E3M5) Main -> Unholy Cathedral (E3M5) Yellow", player), lambda state:
state.has("Unholy Cathedral (E3M5) - Yellow skull key", player, 1))
set_rule(multiworld.get_entrance("Unholy Cathedral (E3M5) Main -> Unholy Cathedral (E3M5) Blue", player), lambda state:
@@ -333,13 +336,6 @@ def set_episode3_rules(player, multiworld, pro):
state.has("Unholy Cathedral (E3M5) - Blue skull key", player, 1))
set_rule(multiworld.get_entrance("Unholy Cathedral (E3M5) Yellow -> Unholy Cathedral (E3M5) Main", player), lambda state:
state.has("Unholy Cathedral (E3M5) - Yellow skull key", player, 1))
set_rule(multiworld.get_entrance("Hub -> Unholy Cathedral (E3M5) Start", player), lambda state:
state.has("Unholy Cathedral (E3M5)", player, 1))
set_rule(multiworld.get_entrance("Unholy Cathedral (E3M5) Start -> Unholy Cathedral (E3M5) Main", player), lambda state:
(state.has("Chaingun", player, 1) and
state.has("Shotgun", player, 1)) and (state.has("Plasma gun", player, 1) or
state.has("Rocket launcher", player, 1) or
state.has("BFG9000", player, 1)))
# Mt. Erebus (E3M6)
set_rule(multiworld.get_entrance("Hub -> Mt. Erebus (E3M6) Main", player), lambda state:

View File

@@ -50,14 +50,14 @@ class DOOM1993World(World):
location_name_to_id = {data["name"]: loc_id for loc_id, data in Locations.location_table.items()}
location_name_groups = Locations.location_name_groups
starting_level_for_episode: Dict[int, str] = {
1: "Hangar (E1M1)",
2: "Deimos Anomaly (E2M1)",
3: "Hell Keep (E3M1)",
4: "Hell Beneath (E4M1)"
}
starting_level_for_episode: List[str] = [
"Hangar (E1M1)",
"Deimos Anomaly (E2M1)",
"Hell Keep (E3M1)",
"Hell Beneath (E4M1)"
]
all_boss_levels: List[str] = [
boss_level_for_espidoes: List[str] = [
"Phobos Anomaly (E1M8)",
"Tower of Babel (E2M8)",
"Dis (E3M8)",
@@ -82,7 +82,6 @@ class DOOM1993World(World):
def __init__(self, multiworld: MultiWorld, player: int):
self.included_episodes = [1, 1, 1, 0]
self.location_count = 0
self.starting_levels = []
super().__init__(multiworld, player)
@@ -100,16 +99,6 @@ class DOOM1993World(World):
if self.get_episode_count() == 0:
self.included_episodes[0] = 1
self.starting_levels = [level_name for (episode, level_name) in self.starting_level_for_episode.items()
if self.included_episodes[episode - 1]]
# Solo Episode 3 presents a problem, because Hell Keep has only two locations.
# We have to give the player Slough of Despair (E3M2), and also mark a weapon early.
if self.get_episode_count() == 1 and self.included_episodes[2]:
early_weapon = self.random.choice(["Shotgun", "Chaingun"])
self.multiworld.early_items[self.player][early_weapon] = 1
self.starting_levels.append("Slough of Despair (E3M2)")
def create_regions(self):
pro = self.options.pro.value
@@ -163,7 +152,7 @@ class DOOM1993World(World):
def completion_rule(self, state: CollectionState):
goal_levels = Maps.map_names
if self.options.goal.value:
goal_levels = self.all_boss_levels
goal_levels = self.boss_level_for_espidoes
for map_name in goal_levels:
if map_name + " - Exit" not in self.location_name_to_id:
@@ -212,7 +201,7 @@ class DOOM1993World(World):
if item["episode"] != -1 and not self.included_episodes[item["episode"] - 1]:
continue
count = item["count"] if item["name"] not in self.starting_levels else item["count"] - 1
count = item["count"] if item["name"] not in self.starting_level_for_episode else item["count"] - 1
itempool += [self.create_item(item["name"]) for _ in range(count)]
# Backpack(s) based on options
@@ -243,8 +232,9 @@ class DOOM1993World(World):
self.location_count -= 1
# Give starting levels right away
for map_name in self.starting_levels:
self.multiworld.push_precollected(self.create_item(map_name))
for i in range(len(self.included_episodes)):
if self.included_episodes[i]:
self.multiworld.push_precollected(self.create_item(self.starting_level_for_episode[i]))
# Give Computer area maps if option selected
if self.options.start_with_computer_area_maps.value:

View File

@@ -412,7 +412,7 @@ item_table: Dict[int, ItemDict] = {
'map': 2},
360246: {'classification': ItemClassification.progression,
'count': 1,
'name': "Barrels o' Fun (MAP23) - Yellow skull key",
'name': 'Barrels o Fun (MAP23) - Yellow skull key',
'doom_type': 39,
'episode': 3,
'map': 3},
@@ -880,19 +880,19 @@ item_table: Dict[int, ItemDict] = {
'map': 2},
360466: {'classification': ItemClassification.progression,
'count': 1,
'name': "Barrels o' Fun (MAP23)",
'name': 'Barrels o Fun (MAP23)',
'doom_type': -1,
'episode': 3,
'map': 3},
360467: {'classification': ItemClassification.progression,
'count': 1,
'name': "Barrels o' Fun (MAP23) - Complete",
'name': 'Barrels o Fun (MAP23) - Complete',
'doom_type': -2,
'episode': 3,
'map': 3},
360468: {'classification': ItemClassification.filler,
'count': 1,
'name': "Barrels o' Fun (MAP23) - Computer area map",
'name': 'Barrels o Fun (MAP23) - Computer area map',
'doom_type': 2026,
'episode': 3,
'map': 3},
@@ -1024,37 +1024,37 @@ item_table: Dict[int, ItemDict] = {
'map': 10},
360490: {'classification': ItemClassification.progression,
'count': 1,
'name': 'Wolfenstein (MAP31)',
'name': 'Wolfenstein2 (MAP31)',
'doom_type': -1,
'episode': 4,
'map': 1},
360491: {'classification': ItemClassification.progression,
'count': 1,
'name': 'Wolfenstein (MAP31) - Complete',
'name': 'Wolfenstein2 (MAP31) - Complete',
'doom_type': -2,
'episode': 4,
'map': 1},
360492: {'classification': ItemClassification.filler,
'count': 1,
'name': 'Wolfenstein (MAP31) - Computer area map',
'name': 'Wolfenstein2 (MAP31) - Computer area map',
'doom_type': 2026,
'episode': 4,
'map': 1},
360493: {'classification': ItemClassification.progression,
'count': 1,
'name': 'Grosse (MAP32)',
'name': 'Grosse2 (MAP32)',
'doom_type': -1,
'episode': 4,
'map': 2},
360494: {'classification': ItemClassification.progression,
'count': 1,
'name': 'Grosse (MAP32) - Complete',
'name': 'Grosse2 (MAP32) - Complete',
'doom_type': -2,
'episode': 4,
'map': 2},
360495: {'classification': ItemClassification.filler,
'count': 1,
'name': 'Grosse (MAP32) - Computer area map',
'name': 'Grosse2 (MAP32) - Computer area map',
'doom_type': 2026,
'episode': 4,
'map': 2},
@@ -1087,9 +1087,9 @@ item_table: Dict[int, ItemDict] = {
item_name_groups: Dict[str, Set[str]] = {
'Ammos': {'Box of bullets', 'Box of rockets', 'Box of shotgun shells', 'Energy cell pack', },
'Computer area maps': {"Barrels o' Fun (MAP23) - Computer area map", 'Bloodfalls (MAP25) - Computer area map', 'Circle of Death (MAP11) - Computer area map', 'Dead Simple (MAP07) - Computer area map', 'Downtown (MAP13) - Computer area map', 'Entryway (MAP01) - Computer area map', 'Gotcha! (MAP20) - Computer area map', 'Grosse (MAP32) - Computer area map', 'Icon of Sin (MAP30) - Computer area map', 'Industrial Zone (MAP15) - Computer area map', 'Monster Condo (MAP27) - Computer area map', 'Nirvana (MAP21) - Computer area map', 'Refueling Base (MAP10) - Computer area map', 'Suburbs (MAP16) - Computer area map', 'Tenements (MAP17) - Computer area map', 'The Abandoned Mines (MAP26) - Computer area map', 'The Catacombs (MAP22) - Computer area map', 'The Chasm (MAP24) - Computer area map', 'The Citadel (MAP19) - Computer area map', 'The Courtyard (MAP18) - Computer area map', 'The Crusher (MAP06) - Computer area map', 'The Factory (MAP12) - Computer area map', 'The Focus (MAP04) - Computer area map', 'The Gantlet (MAP03) - Computer area map', 'The Inmost Dens (MAP14) - Computer area map', 'The Living End (MAP29) - Computer area map', 'The Pit (MAP09) - Computer area map', 'The Spirit World (MAP28) - Computer area map', 'The Waste Tunnels (MAP05) - Computer area map', 'Tricks and Traps (MAP08) - Computer area map', 'Underhalls (MAP02) - Computer area map', 'Wolfenstein (MAP31) - Computer area map', },
'Keys': {"Barrels o' Fun (MAP23) - Yellow skull key", 'Bloodfalls (MAP25) - Blue skull key', 'Circle of Death (MAP11) - Blue keycard', 'Circle of Death (MAP11) - Red keycard', 'Downtown (MAP13) - Blue keycard', 'Downtown (MAP13) - Red keycard', 'Downtown (MAP13) - Yellow keycard', 'Industrial Zone (MAP15) - Blue keycard', 'Industrial Zone (MAP15) - Red keycard', 'Industrial Zone (MAP15) - Yellow keycard', 'Monster Condo (MAP27) - Blue skull key', 'Monster Condo (MAP27) - Red skull key', 'Monster Condo (MAP27) - Yellow skull key', 'Nirvana (MAP21) - Blue skull key', 'Nirvana (MAP21) - Red skull key', 'Nirvana (MAP21) - Yellow skull key', 'Refueling Base (MAP10) - Blue keycard', 'Refueling Base (MAP10) - Yellow keycard', 'Suburbs (MAP16) - Blue skull key', 'Suburbs (MAP16) - Red skull key', 'Tenements (MAP17) - Blue keycard', 'Tenements (MAP17) - Red keycard', 'Tenements (MAP17) - Yellow skull key', 'The Abandoned Mines (MAP26) - Blue keycard', 'The Abandoned Mines (MAP26) - Red keycard', 'The Abandoned Mines (MAP26) - Yellow keycard', 'The Catacombs (MAP22) - Blue skull key', 'The Catacombs (MAP22) - Red skull key', 'The Chasm (MAP24) - Blue keycard', 'The Chasm (MAP24) - Red keycard', 'The Citadel (MAP19) - Blue skull key', 'The Citadel (MAP19) - Red skull key', 'The Citadel (MAP19) - Yellow skull key', 'The Courtyard (MAP18) - Blue skull key', 'The Courtyard (MAP18) - Yellow skull key', 'The Crusher (MAP06) - Blue keycard', 'The Crusher (MAP06) - Red keycard', 'The Crusher (MAP06) - Yellow keycard', 'The Factory (MAP12) - Blue keycard', 'The Factory (MAP12) - Yellow keycard', 'The Focus (MAP04) - Blue keycard', 'The Focus (MAP04) - Red keycard', 'The Focus (MAP04) - Yellow keycard', 'The Gantlet (MAP03) - Blue keycard', 'The Gantlet (MAP03) - Red keycard', 'The Inmost Dens (MAP14) - Blue skull key', 'The Inmost Dens (MAP14) - Red skull key', 'The Pit (MAP09) - Blue keycard', 'The Pit (MAP09) - Yellow keycard', 'The Spirit World (MAP28) - Red skull key', 'The Spirit World (MAP28) - Yellow skull key', 'The Waste Tunnels (MAP05) - Blue keycard', 'The Waste Tunnels (MAP05) - Red keycard', 'The Waste Tunnels (MAP05) - Yellow keycard', 'Tricks and Traps (MAP08) - Red skull key', 'Tricks and Traps (MAP08) - Yellow skull key', 'Underhalls (MAP02) - Blue keycard', 'Underhalls (MAP02) - Red keycard', },
'Levels': {"Barrels o' Fun (MAP23)", 'Bloodfalls (MAP25)', 'Circle of Death (MAP11)', 'Dead Simple (MAP07)', 'Downtown (MAP13)', 'Entryway (MAP01)', 'Gotcha! (MAP20)', 'Grosse (MAP32)', 'Icon of Sin (MAP30)', 'Industrial Zone (MAP15)', 'Monster Condo (MAP27)', 'Nirvana (MAP21)', 'Refueling Base (MAP10)', 'Suburbs (MAP16)', 'Tenements (MAP17)', 'The Abandoned Mines (MAP26)', 'The Catacombs (MAP22)', 'The Chasm (MAP24)', 'The Citadel (MAP19)', 'The Courtyard (MAP18)', 'The Crusher (MAP06)', 'The Factory (MAP12)', 'The Focus (MAP04)', 'The Gantlet (MAP03)', 'The Inmost Dens (MAP14)', 'The Living End (MAP29)', 'The Pit (MAP09)', 'The Spirit World (MAP28)', 'The Waste Tunnels (MAP05)', 'Tricks and Traps (MAP08)', 'Underhalls (MAP02)', 'Wolfenstein (MAP31)', },
'Computer area maps': {'Barrels o Fun (MAP23) - Computer area map', 'Bloodfalls (MAP25) - Computer area map', 'Circle of Death (MAP11) - Computer area map', 'Dead Simple (MAP07) - Computer area map', 'Downtown (MAP13) - Computer area map', 'Entryway (MAP01) - Computer area map', 'Gotcha! (MAP20) - Computer area map', 'Grosse2 (MAP32) - Computer area map', 'Icon of Sin (MAP30) - Computer area map', 'Industrial Zone (MAP15) - Computer area map', 'Monster Condo (MAP27) - Computer area map', 'Nirvana (MAP21) - Computer area map', 'Refueling Base (MAP10) - Computer area map', 'Suburbs (MAP16) - Computer area map', 'Tenements (MAP17) - Computer area map', 'The Abandoned Mines (MAP26) - Computer area map', 'The Catacombs (MAP22) - Computer area map', 'The Chasm (MAP24) - Computer area map', 'The Citadel (MAP19) - Computer area map', 'The Courtyard (MAP18) - Computer area map', 'The Crusher (MAP06) - Computer area map', 'The Factory (MAP12) - Computer area map', 'The Focus (MAP04) - Computer area map', 'The Gantlet (MAP03) - Computer area map', 'The Inmost Dens (MAP14) - Computer area map', 'The Living End (MAP29) - Computer area map', 'The Pit (MAP09) - Computer area map', 'The Spirit World (MAP28) - Computer area map', 'The Waste Tunnels (MAP05) - Computer area map', 'Tricks and Traps (MAP08) - Computer area map', 'Underhalls (MAP02) - Computer area map', 'Wolfenstein2 (MAP31) - Computer area map', },
'Keys': {'Barrels o Fun (MAP23) - Yellow skull key', 'Bloodfalls (MAP25) - Blue skull key', 'Circle of Death (MAP11) - Blue keycard', 'Circle of Death (MAP11) - Red keycard', 'Downtown (MAP13) - Blue keycard', 'Downtown (MAP13) - Red keycard', 'Downtown (MAP13) - Yellow keycard', 'Industrial Zone (MAP15) - Blue keycard', 'Industrial Zone (MAP15) - Red keycard', 'Industrial Zone (MAP15) - Yellow keycard', 'Monster Condo (MAP27) - Blue skull key', 'Monster Condo (MAP27) - Red skull key', 'Monster Condo (MAP27) - Yellow skull key', 'Nirvana (MAP21) - Blue skull key', 'Nirvana (MAP21) - Red skull key', 'Nirvana (MAP21) - Yellow skull key', 'Refueling Base (MAP10) - Blue keycard', 'Refueling Base (MAP10) - Yellow keycard', 'Suburbs (MAP16) - Blue skull key', 'Suburbs (MAP16) - Red skull key', 'Tenements (MAP17) - Blue keycard', 'Tenements (MAP17) - Red keycard', 'Tenements (MAP17) - Yellow skull key', 'The Abandoned Mines (MAP26) - Blue keycard', 'The Abandoned Mines (MAP26) - Red keycard', 'The Abandoned Mines (MAP26) - Yellow keycard', 'The Catacombs (MAP22) - Blue skull key', 'The Catacombs (MAP22) - Red skull key', 'The Chasm (MAP24) - Blue keycard', 'The Chasm (MAP24) - Red keycard', 'The Citadel (MAP19) - Blue skull key', 'The Citadel (MAP19) - Red skull key', 'The Citadel (MAP19) - Yellow skull key', 'The Courtyard (MAP18) - Blue skull key', 'The Courtyard (MAP18) - Yellow skull key', 'The Crusher (MAP06) - Blue keycard', 'The Crusher (MAP06) - Red keycard', 'The Crusher (MAP06) - Yellow keycard', 'The Factory (MAP12) - Blue keycard', 'The Factory (MAP12) - Yellow keycard', 'The Focus (MAP04) - Blue keycard', 'The Focus (MAP04) - Red keycard', 'The Focus (MAP04) - Yellow keycard', 'The Gantlet (MAP03) - Blue keycard', 'The Gantlet (MAP03) - Red keycard', 'The Inmost Dens (MAP14) - Blue skull key', 'The Inmost Dens (MAP14) - Red skull key', 'The Pit (MAP09) - Blue keycard', 'The Pit (MAP09) - Yellow keycard', 'The Spirit World (MAP28) - Red skull key', 'The Spirit World (MAP28) - Yellow skull key', 'The Waste Tunnels (MAP05) - Blue keycard', 'The Waste Tunnels (MAP05) - Red keycard', 'The Waste Tunnels (MAP05) - Yellow keycard', 'Tricks and Traps (MAP08) - Red skull key', 'Tricks and Traps (MAP08) - Yellow skull key', 'Underhalls (MAP02) - Blue keycard', 'Underhalls (MAP02) - Red keycard', },
'Levels': {'Barrels o Fun (MAP23)', 'Bloodfalls (MAP25)', 'Circle of Death (MAP11)', 'Dead Simple (MAP07)', 'Downtown (MAP13)', 'Entryway (MAP01)', 'Gotcha! (MAP20)', 'Grosse2 (MAP32)', 'Icon of Sin (MAP30)', 'Industrial Zone (MAP15)', 'Monster Condo (MAP27)', 'Nirvana (MAP21)', 'Refueling Base (MAP10)', 'Suburbs (MAP16)', 'Tenements (MAP17)', 'The Abandoned Mines (MAP26)', 'The Catacombs (MAP22)', 'The Chasm (MAP24)', 'The Citadel (MAP19)', 'The Courtyard (MAP18)', 'The Crusher (MAP06)', 'The Factory (MAP12)', 'The Focus (MAP04)', 'The Gantlet (MAP03)', 'The Inmost Dens (MAP14)', 'The Living End (MAP29)', 'The Pit (MAP09)', 'The Spirit World (MAP28)', 'The Waste Tunnels (MAP05)', 'Tricks and Traps (MAP08)', 'Underhalls (MAP02)', 'Wolfenstein2 (MAP31)', },
'Powerups': {'Armor', 'Berserk', 'Invulnerability', 'Mega Armor', 'Megasphere', 'Partial invisibility', 'Supercharge', },
'Weapons': {'BFG9000', 'Chaingun', 'Chainsaw', 'Plasma gun', 'Rocket launcher', 'Shotgun', 'Super Shotgun', },
}

View File

@@ -180,7 +180,7 @@ location_table: Dict[int, LocationDict] = {
'map': 5,
'index': 46,
'doom_type': 82,
'region': "The Waste Tunnels (MAP05) Start"},
'region': "The Waste Tunnels (MAP05) Main"},
361028: {'name': 'The Waste Tunnels (MAP05) - Blue keycard',
'episode': 1,
'map': 5,
@@ -234,7 +234,7 @@ location_table: Dict[int, LocationDict] = {
'map': 5,
'index': 202,
'doom_type': 2001,
'region': "The Waste Tunnels (MAP05) Start"},
'region': "The Waste Tunnels (MAP05) Main"},
361037: {'name': 'The Waste Tunnels (MAP05) - Berserk',
'episode': 1,
'map': 5,
@@ -360,7 +360,7 @@ location_table: Dict[int, LocationDict] = {
'map': 7,
'index': 8,
'doom_type': 82,
'region': "Dead Simple (MAP07) Start"},
'region': "Dead Simple (MAP07) Main"},
361058: {'name': 'Dead Simple (MAP07) - Chaingun',
'episode': 1,
'map': 7,
@@ -378,7 +378,7 @@ location_table: Dict[int, LocationDict] = {
'map': 7,
'index': 43,
'doom_type': 8,
'region': "Dead Simple (MAP07) Start"},
'region': "Dead Simple (MAP07) Main"},
361061: {'name': 'Dead Simple (MAP07) - Berserk',
'episode': 1,
'map': 7,
@@ -570,7 +570,7 @@ location_table: Dict[int, LocationDict] = {
'map': 9,
'index': 26,
'doom_type': 2019,
'region': "The Pit (MAP09) Start"},
'region': "The Pit (MAP09) Main"},
361093: {'name': 'The Pit (MAP09) - Supercharge',
'episode': 1,
'map': 9,
@@ -678,7 +678,7 @@ location_table: Dict[int, LocationDict] = {
'map': 10,
'index': 99,
'doom_type': 2001,
'region': "Refueling Base (MAP10) Start"},
'region': "Refueling Base (MAP10) Main"},
361111: {'name': 'Refueling Base (MAP10) - Chaingun',
'episode': 1,
'map': 10,
@@ -846,31 +846,31 @@ location_table: Dict[int, LocationDict] = {
'map': 11,
'index': 88,
'doom_type': 8,
'region': "Circle of Death (MAP11) Ending"},
'region': "Circle of Death (MAP11) Red"},
361139: {'name': 'Circle of Death (MAP11) - Supercharge 2',
'episode': 1,
'map': 11,
'index': 108,
'doom_type': 2013,
'region': "Circle of Death (MAP11) Ending"},
'region': "Circle of Death (MAP11) Red"},
361140: {'name': 'Circle of Death (MAP11) - BFG9000',
'episode': 1,
'map': 11,
'index': 110,
'doom_type': 2006,
'region': "Circle of Death (MAP11) Ending"},
'region': "Circle of Death (MAP11) Red"},
361141: {'name': 'Circle of Death (MAP11) - Exit',
'episode': 1,
'map': 11,
'index': -1,
'doom_type': -1,
'region': "Circle of Death (MAP11) Ending"},
'region': "Circle of Death (MAP11) Red"},
361142: {'name': 'The Factory (MAP12) - Shotgun',
'episode': 2,
'map': 1,
'index': 14,
'doom_type': 2001,
'region': "The Factory (MAP12) Outdoors"},
'region': "The Factory (MAP12) Main"},
361143: {'name': 'The Factory (MAP12) - Berserk',
'episode': 2,
'map': 1,
@@ -888,13 +888,13 @@ location_table: Dict[int, LocationDict] = {
'map': 1,
'index': 52,
'doom_type': 2013,
'region': "The Factory (MAP12) Indoors"},
'region': "The Factory (MAP12) Main"},
361146: {'name': 'The Factory (MAP12) - Blue keycard',
'episode': 2,
'map': 1,
'index': 54,
'doom_type': 5,
'region': "The Factory (MAP12) Indoors"},
'region': "The Factory (MAP12) Main"},
361147: {'name': 'The Factory (MAP12) - Armor',
'episode': 2,
'map': 1,
@@ -912,31 +912,31 @@ location_table: Dict[int, LocationDict] = {
'map': 1,
'index': 83,
'doom_type': 2013,
'region': "The Factory (MAP12) Indoors"},
'region': "The Factory (MAP12) Main"},
361150: {'name': 'The Factory (MAP12) - Armor 2',
'episode': 2,
'map': 1,
'index': 92,
'doom_type': 2018,
'region': "The Factory (MAP12) Outdoors"},
'region': "The Factory (MAP12) Main"},
361151: {'name': 'The Factory (MAP12) - Partial invisibility',
'episode': 2,
'map': 1,
'index': 93,
'doom_type': 2024,
'region': "The Factory (MAP12) Outdoors"},
'region': "The Factory (MAP12) Main"},
361152: {'name': 'The Factory (MAP12) - Berserk 2',
'episode': 2,
'map': 1,
'index': 107,
'doom_type': 2023,
'region': "The Factory (MAP12) Indoors"},
'region': "The Factory (MAP12) Main"},
361153: {'name': 'The Factory (MAP12) - Yellow keycard',
'episode': 2,
'map': 1,
'index': 123,
'doom_type': 6,
'region': "The Factory (MAP12) Indoors"},
'region': "The Factory (MAP12) Main"},
361154: {'name': 'The Factory (MAP12) - BFG9000',
'episode': 2,
'map': 1,
@@ -954,7 +954,7 @@ location_table: Dict[int, LocationDict] = {
'map': 1,
'index': 192,
'doom_type': 82,
'region': "The Factory (MAP12) Indoors"},
'region': "The Factory (MAP12) Main"},
361157: {'name': 'The Factory (MAP12) - Exit',
'episode': 2,
'map': 1,
@@ -1812,7 +1812,7 @@ location_table: Dict[int, LocationDict] = {
'map': 1,
'index': 70,
'doom_type': 82,
'region': "Nirvana (MAP21) Start"},
'region': "Nirvana (MAP21) Main"},
361300: {'name': 'Nirvana (MAP21) - Rocket launcher',
'episode': 3,
'map': 1,
@@ -1884,7 +1884,7 @@ location_table: Dict[int, LocationDict] = {
'map': 2,
'index': 28,
'doom_type': 2001,
'region': "The Catacombs (MAP22) Early"},
'region': "The Catacombs (MAP22) Main"},
361312: {'name': 'The Catacombs (MAP22) - Berserk',
'episode': 3,
'map': 2,
@@ -1896,103 +1896,103 @@ location_table: Dict[int, LocationDict] = {
'map': 2,
'index': 83,
'doom_type': 2004,
'region': "The Catacombs (MAP22) Early"},
'region': "The Catacombs (MAP22) Main"},
361314: {'name': 'The Catacombs (MAP22) - Supercharge',
'episode': 3,
'map': 2,
'index': 118,
'doom_type': 2013,
'region': "The Catacombs (MAP22) Early"},
'region': "The Catacombs (MAP22) Main"},
361315: {'name': 'The Catacombs (MAP22) - Armor',
'episode': 3,
'map': 2,
'index': 119,
'doom_type': 2018,
'region': "The Catacombs (MAP22) Early"},
'region': "The Catacombs (MAP22) Main"},
361316: {'name': 'The Catacombs (MAP22) - Exit',
'episode': 3,
'map': 2,
'index': -1,
'doom_type': -1,
'region': "The Catacombs (MAP22) Red"},
361317: {'name': "Barrels o' Fun (MAP23) - Shotgun",
361317: {'name': 'Barrels o Fun (MAP23) - Shotgun',
'episode': 3,
'map': 3,
'index': 136,
'doom_type': 2001,
'region': "Barrels o' Fun (MAP23) Main"},
361318: {'name': "Barrels o' Fun (MAP23) - Berserk",
'region': "Barrels o Fun (MAP23) Main"},
361318: {'name': 'Barrels o Fun (MAP23) - Berserk',
'episode': 3,
'map': 3,
'index': 222,
'doom_type': 2023,
'region': "Barrels o' Fun (MAP23) Main"},
361319: {'name': "Barrels o' Fun (MAP23) - Backpack",
'region': "Barrels o Fun (MAP23) Main"},
361319: {'name': 'Barrels o Fun (MAP23) - Backpack',
'episode': 3,
'map': 3,
'index': 223,
'doom_type': 8,
'region': "Barrels o' Fun (MAP23) Main"},
361320: {'name': "Barrels o' Fun (MAP23) - Computer area map",
'region': "Barrels o Fun (MAP23) Main"},
361320: {'name': 'Barrels o Fun (MAP23) - Computer area map',
'episode': 3,
'map': 3,
'index': 224,
'doom_type': 2026,
'region': "Barrels o' Fun (MAP23) Main"},
361321: {'name': "Barrels o' Fun (MAP23) - Armor",
'region': "Barrels o Fun (MAP23) Main"},
361321: {'name': 'Barrels o Fun (MAP23) - Armor',
'episode': 3,
'map': 3,
'index': 249,
'doom_type': 2018,
'region': "Barrels o' Fun (MAP23) Main"},
361322: {'name': "Barrels o' Fun (MAP23) - Rocket launcher",
'region': "Barrels o Fun (MAP23) Main"},
361322: {'name': 'Barrels o Fun (MAP23) - Rocket launcher',
'episode': 3,
'map': 3,
'index': 264,
'doom_type': 2003,
'region': "Barrels o' Fun (MAP23) Main"},
361323: {'name': "Barrels o' Fun (MAP23) - Megasphere",
'region': "Barrels o Fun (MAP23) Main"},
361323: {'name': 'Barrels o Fun (MAP23) - Megasphere',
'episode': 3,
'map': 3,
'index': 266,
'doom_type': 83,
'region': "Barrels o' Fun (MAP23) Main"},
361324: {'name': "Barrels o' Fun (MAP23) - Supercharge",
'region': "Barrels o Fun (MAP23) Main"},
361324: {'name': 'Barrels o Fun (MAP23) - Supercharge',
'episode': 3,
'map': 3,
'index': 277,
'doom_type': 2013,
'region': "Barrels o' Fun (MAP23) Main"},
361325: {'name': "Barrels o' Fun (MAP23) - Backpack 2",
'region': "Barrels o Fun (MAP23) Main"},
361325: {'name': 'Barrels o Fun (MAP23) - Backpack 2',
'episode': 3,
'map': 3,
'index': 301,
'doom_type': 8,
'region': "Barrels o' Fun (MAP23) Main"},
361326: {'name': "Barrels o' Fun (MAP23) - Yellow skull key",
'region': "Barrels o Fun (MAP23) Main"},
361326: {'name': 'Barrels o Fun (MAP23) - Yellow skull key',
'episode': 3,
'map': 3,
'index': 307,
'doom_type': 39,
'region': "Barrels o' Fun (MAP23) Main"},
361327: {'name': "Barrels o' Fun (MAP23) - BFG9000",
'region': "Barrels o Fun (MAP23) Main"},
361327: {'name': 'Barrels o Fun (MAP23) - BFG9000',
'episode': 3,
'map': 3,
'index': 342,
'doom_type': 2006,
'region': "Barrels o' Fun (MAP23) Main"},
361328: {'name': "Barrels o' Fun (MAP23) - Exit",
'region': "Barrels o Fun (MAP23) Main"},
361328: {'name': 'Barrels o Fun (MAP23) - Exit',
'episode': 3,
'map': 3,
'index': -1,
'doom_type': -1,
'region': "Barrels o' Fun (MAP23) Yellow"},
'region': "Barrels o Fun (MAP23) Yellow"},
361329: {'name': 'The Chasm (MAP24) - Plasma gun',
'episode': 3,
'map': 4,
'index': 5,
'doom_type': 2004,
'region': "The Chasm (MAP24) Blue"},
'region': "The Chasm (MAP24) Main"},
361330: {'name': 'The Chasm (MAP24) - Shotgun',
'episode': 3,
'map': 4,
@@ -2004,7 +2004,7 @@ location_table: Dict[int, LocationDict] = {
'map': 4,
'index': 12,
'doom_type': 2022,
'region': "The Chasm (MAP24) Blue"},
'region': "The Chasm (MAP24) Main"},
361332: {'name': 'The Chasm (MAP24) - Rocket launcher',
'episode': 3,
'map': 4,
@@ -2022,7 +2022,7 @@ location_table: Dict[int, LocationDict] = {
'map': 4,
'index': 31,
'doom_type': 8,
'region': "The Chasm (MAP24) Blue"},
'region': "The Chasm (MAP24) Main"},
361335: {'name': 'The Chasm (MAP24) - Berserk',
'episode': 3,
'map': 4,
@@ -2034,19 +2034,19 @@ location_table: Dict[int, LocationDict] = {
'map': 4,
'index': 155,
'doom_type': 2023,
'region': "The Chasm (MAP24) Blue"},
'region': "The Chasm (MAP24) Main"},
361337: {'name': 'The Chasm (MAP24) - Armor',
'episode': 3,
'map': 4,
'index': 169,
'doom_type': 2018,
'region': "The Chasm (MAP24) Blue"},
'region': "The Chasm (MAP24) Main"},
361338: {'name': 'The Chasm (MAP24) - Red keycard',
'episode': 3,
'map': 4,
'index': 261,
'doom_type': 13,
'region': "The Chasm (MAP24) Blue"},
'region': "The Chasm (MAP24) Main"},
361339: {'name': 'The Chasm (MAP24) - BFG9000',
'episode': 3,
'map': 4,
@@ -2064,7 +2064,7 @@ location_table: Dict[int, LocationDict] = {
'map': 4,
'index': 355,
'doom_type': 83,
'region': "The Chasm (MAP24) Blue"},
'region': "The Chasm (MAP24) Main"},
361342: {'name': 'The Chasm (MAP24) - Megasphere 2',
'episode': 3,
'map': 4,
@@ -2082,7 +2082,7 @@ location_table: Dict[int, LocationDict] = {
'map': 5,
'index': 6,
'doom_type': 82,
'region': "Bloodfalls (MAP25) Start"},
'region': "Bloodfalls (MAP25) Main"},
361345: {'name': 'Bloodfalls (MAP25) - Partial invisibility',
'episode': 3,
'map': 5,
@@ -2664,55 +2664,55 @@ location_table: Dict[int, LocationDict] = {
'map': 10,
'index': 40,
'doom_type': 2006,
'region': "Icon of Sin (MAP30) Start"},
'region': "Icon of Sin (MAP30) Main"},
361442: {'name': 'Icon of Sin (MAP30) - Chaingun',
'episode': 3,
'map': 10,
'index': 41,
'doom_type': 2002,
'region': "Icon of Sin (MAP30) Start"},
'region': "Icon of Sin (MAP30) Main"},
361443: {'name': 'Icon of Sin (MAP30) - Chainsaw',
'episode': 3,
'map': 10,
'index': 42,
'doom_type': 2005,
'region': "Icon of Sin (MAP30) Start"},
'region': "Icon of Sin (MAP30) Main"},
361444: {'name': 'Icon of Sin (MAP30) - Plasma gun',
'episode': 3,
'map': 10,
'index': 43,
'doom_type': 2004,
'region': "Icon of Sin (MAP30) Start"},
'region': "Icon of Sin (MAP30) Main"},
361445: {'name': 'Icon of Sin (MAP30) - Rocket launcher',
'episode': 3,
'map': 10,
'index': 44,
'doom_type': 2003,
'region': "Icon of Sin (MAP30) Start"},
'region': "Icon of Sin (MAP30) Main"},
361446: {'name': 'Icon of Sin (MAP30) - Shotgun',
'episode': 3,
'map': 10,
'index': 45,
'doom_type': 2001,
'region': "Icon of Sin (MAP30) Start"},
'region': "Icon of Sin (MAP30) Main"},
361447: {'name': 'Icon of Sin (MAP30) - Super Shotgun',
'episode': 3,
'map': 10,
'index': 46,
'doom_type': 82,
'region': "Icon of Sin (MAP30) Start"},
'region': "Icon of Sin (MAP30) Main"},
361448: {'name': 'Icon of Sin (MAP30) - Backpack',
'episode': 3,
'map': 10,
'index': 47,
'doom_type': 8,
'region': "Icon of Sin (MAP30) Start"},
'region': "Icon of Sin (MAP30) Main"},
361449: {'name': 'Icon of Sin (MAP30) - Megasphere',
'episode': 3,
'map': 10,
'index': 64,
'doom_type': 83,
'region': "Icon of Sin (MAP30) Start"},
'region': "Icon of Sin (MAP30) Main"},
361450: {'name': 'Icon of Sin (MAP30) - Megasphere 2',
'episode': 3,
'map': 10,
@@ -2731,179 +2731,179 @@ location_table: Dict[int, LocationDict] = {
'index': -1,
'doom_type': -1,
'region': "Icon of Sin (MAP30) Main"},
361453: {'name': 'Wolfenstein (MAP31) - Rocket launcher',
361453: {'name': 'Wolfenstein2 (MAP31) - Rocket launcher',
'episode': 4,
'map': 1,
'index': 110,
'doom_type': 2003,
'region': "Wolfenstein (MAP31) Main"},
361454: {'name': 'Wolfenstein (MAP31) - Shotgun',
'region': "Wolfenstein2 (MAP31) Main"},
361454: {'name': 'Wolfenstein2 (MAP31) - Shotgun',
'episode': 4,
'map': 1,
'index': 139,
'doom_type': 2001,
'region': "Wolfenstein (MAP31) Main"},
361455: {'name': 'Wolfenstein (MAP31) - Berserk',
'region': "Wolfenstein2 (MAP31) Main"},
361455: {'name': 'Wolfenstein2 (MAP31) - Berserk',
'episode': 4,
'map': 1,
'index': 263,
'doom_type': 2023,
'region': "Wolfenstein (MAP31) Main"},
361456: {'name': 'Wolfenstein (MAP31) - Supercharge',
'region': "Wolfenstein2 (MAP31) Main"},
361456: {'name': 'Wolfenstein2 (MAP31) - Supercharge',
'episode': 4,
'map': 1,
'index': 278,
'doom_type': 2013,
'region': "Wolfenstein (MAP31) Main"},
361457: {'name': 'Wolfenstein (MAP31) - Chaingun',
'region': "Wolfenstein2 (MAP31) Main"},
361457: {'name': 'Wolfenstein2 (MAP31) - Chaingun',
'episode': 4,
'map': 1,
'index': 305,
'doom_type': 2002,
'region': "Wolfenstein (MAP31) Main"},
361458: {'name': 'Wolfenstein (MAP31) - Super Shotgun',
'region': "Wolfenstein2 (MAP31) Main"},
361458: {'name': 'Wolfenstein2 (MAP31) - Super Shotgun',
'episode': 4,
'map': 1,
'index': 308,
'doom_type': 82,
'region': "Wolfenstein (MAP31) Main"},
361459: {'name': 'Wolfenstein (MAP31) - Partial invisibility',
'region': "Wolfenstein2 (MAP31) Main"},
361459: {'name': 'Wolfenstein2 (MAP31) - Partial invisibility',
'episode': 4,
'map': 1,
'index': 309,
'doom_type': 2024,
'region': "Wolfenstein (MAP31) Main"},
361460: {'name': 'Wolfenstein (MAP31) - Megasphere',
'region': "Wolfenstein2 (MAP31) Main"},
361460: {'name': 'Wolfenstein2 (MAP31) - Megasphere',
'episode': 4,
'map': 1,
'index': 310,
'doom_type': 83,
'region': "Wolfenstein (MAP31) Main"},
361461: {'name': 'Wolfenstein (MAP31) - Backpack',
'region': "Wolfenstein2 (MAP31) Main"},
361461: {'name': 'Wolfenstein2 (MAP31) - Backpack',
'episode': 4,
'map': 1,
'index': 311,
'doom_type': 8,
'region': "Wolfenstein (MAP31) Main"},
361462: {'name': 'Wolfenstein (MAP31) - Backpack 2',
'region': "Wolfenstein2 (MAP31) Main"},
361462: {'name': 'Wolfenstein2 (MAP31) - Backpack 2',
'episode': 4,
'map': 1,
'index': 312,
'doom_type': 8,
'region': "Wolfenstein (MAP31) Main"},
361463: {'name': 'Wolfenstein (MAP31) - Backpack 3',
'region': "Wolfenstein2 (MAP31) Main"},
361463: {'name': 'Wolfenstein2 (MAP31) - Backpack 3',
'episode': 4,
'map': 1,
'index': 313,
'doom_type': 8,
'region': "Wolfenstein (MAP31) Main"},
361464: {'name': 'Wolfenstein (MAP31) - Backpack 4',
'region': "Wolfenstein2 (MAP31) Main"},
361464: {'name': 'Wolfenstein2 (MAP31) - Backpack 4',
'episode': 4,
'map': 1,
'index': 314,
'doom_type': 8,
'region': "Wolfenstein (MAP31) Main"},
361465: {'name': 'Wolfenstein (MAP31) - BFG9000',
'region': "Wolfenstein2 (MAP31) Main"},
361465: {'name': 'Wolfenstein2 (MAP31) - BFG9000',
'episode': 4,
'map': 1,
'index': 315,
'doom_type': 2006,
'region': "Wolfenstein (MAP31) Main"},
361466: {'name': 'Wolfenstein (MAP31) - Plasma gun',
'region': "Wolfenstein2 (MAP31) Main"},
361466: {'name': 'Wolfenstein2 (MAP31) - Plasma gun',
'episode': 4,
'map': 1,
'index': 316,
'doom_type': 2004,
'region': "Wolfenstein (MAP31) Main"},
361467: {'name': 'Wolfenstein (MAP31) - Exit',
'region': "Wolfenstein2 (MAP31) Main"},
361467: {'name': 'Wolfenstein2 (MAP31) - Exit',
'episode': 4,
'map': 1,
'index': -1,
'doom_type': -1,
'region': "Wolfenstein (MAP31) Main"},
361468: {'name': 'Grosse (MAP32) - Plasma gun',
'region': "Wolfenstein2 (MAP31) Main"},
361468: {'name': 'Grosse2 (MAP32) - Plasma gun',
'episode': 4,
'map': 2,
'index': 33,
'doom_type': 2004,
'region': "Grosse (MAP32) Main"},
361469: {'name': 'Grosse (MAP32) - Rocket launcher',
'region': "Grosse2 (MAP32) Main"},
361469: {'name': 'Grosse2 (MAP32) - Rocket launcher',
'episode': 4,
'map': 2,
'index': 57,
'doom_type': 2003,
'region': "Grosse (MAP32) Start"},
361470: {'name': 'Grosse (MAP32) - Invulnerability',
'region': "Grosse2 (MAP32) Main"},
361470: {'name': 'Grosse2 (MAP32) - Invulnerability',
'episode': 4,
'map': 2,
'index': 70,
'doom_type': 2022,
'region': "Grosse (MAP32) Main"},
361471: {'name': 'Grosse (MAP32) - Super Shotgun',
'region': "Grosse2 (MAP32) Main"},
361471: {'name': 'Grosse2 (MAP32) - Super Shotgun',
'episode': 4,
'map': 2,
'index': 74,
'doom_type': 82,
'region': "Grosse (MAP32) Main"},
361472: {'name': 'Grosse (MAP32) - BFG9000',
'region': "Grosse2 (MAP32) Main"},
361472: {'name': 'Grosse2 (MAP32) - BFG9000',
'episode': 4,
'map': 2,
'index': 75,
'doom_type': 2006,
'region': "Grosse (MAP32) Main"},
361473: {'name': 'Grosse (MAP32) - Megasphere',
'region': "Grosse2 (MAP32) Main"},
361473: {'name': 'Grosse2 (MAP32) - Megasphere',
'episode': 4,
'map': 2,
'index': 78,
'doom_type': 83,
'region': "Grosse (MAP32) Main"},
361474: {'name': 'Grosse (MAP32) - Chaingun',
'region': "Grosse2 (MAP32) Main"},
361474: {'name': 'Grosse2 (MAP32) - Chaingun',
'episode': 4,
'map': 2,
'index': 79,
'doom_type': 2002,
'region': "Grosse (MAP32) Main"},
361475: {'name': 'Grosse (MAP32) - Chaingun 2',
'region': "Grosse2 (MAP32) Main"},
361475: {'name': 'Grosse2 (MAP32) - Chaingun 2',
'episode': 4,
'map': 2,
'index': 80,
'doom_type': 2002,
'region': "Grosse (MAP32) Main"},
361476: {'name': 'Grosse (MAP32) - Chaingun 3',
'region': "Grosse2 (MAP32) Main"},
361476: {'name': 'Grosse2 (MAP32) - Chaingun 3',
'episode': 4,
'map': 2,
'index': 81,
'doom_type': 2002,
'region': "Grosse (MAP32) Main"},
361477: {'name': 'Grosse (MAP32) - Berserk',
'region': "Grosse2 (MAP32) Main"},
361477: {'name': 'Grosse2 (MAP32) - Berserk',
'episode': 4,
'map': 2,
'index': 82,
'doom_type': 2023,
'region': "Grosse (MAP32) Start"},
361478: {'name': 'Grosse (MAP32) - Exit',
'region': "Grosse2 (MAP32) Main"},
361478: {'name': 'Grosse2 (MAP32) - Exit',
'episode': 4,
'map': 2,
'index': -1,
'doom_type': -1,
'region': "Grosse (MAP32) Main"},
'region': "Grosse2 (MAP32) Main"},
}
location_name_groups: Dict[str, Set[str]] = {
"Barrels o' Fun (MAP23)": {
"Barrels o' Fun (MAP23) - Armor",
"Barrels o' Fun (MAP23) - BFG9000",
"Barrels o' Fun (MAP23) - Backpack",
"Barrels o' Fun (MAP23) - Backpack 2",
"Barrels o' Fun (MAP23) - Berserk",
"Barrels o' Fun (MAP23) - Computer area map",
"Barrels o' Fun (MAP23) - Exit",
"Barrels o' Fun (MAP23) - Megasphere",
"Barrels o' Fun (MAP23) - Rocket launcher",
"Barrels o' Fun (MAP23) - Shotgun",
"Barrels o' Fun (MAP23) - Supercharge",
"Barrels o' Fun (MAP23) - Yellow skull key",
'Barrels o Fun (MAP23)': {
'Barrels o Fun (MAP23) - Armor',
'Barrels o Fun (MAP23) - BFG9000',
'Barrels o Fun (MAP23) - Backpack',
'Barrels o Fun (MAP23) - Backpack 2',
'Barrels o Fun (MAP23) - Berserk',
'Barrels o Fun (MAP23) - Computer area map',
'Barrels o Fun (MAP23) - Exit',
'Barrels o Fun (MAP23) - Megasphere',
'Barrels o Fun (MAP23) - Rocket launcher',
'Barrels o Fun (MAP23) - Shotgun',
'Barrels o Fun (MAP23) - Supercharge',
'Barrels o Fun (MAP23) - Yellow skull key',
},
'Bloodfalls (MAP25)': {
'Bloodfalls (MAP25) - Armor',
@@ -2998,18 +2998,18 @@ location_name_groups: Dict[str, Set[str]] = {
'Gotcha! (MAP20) - Supercharge 3',
'Gotcha! (MAP20) - Supercharge 4',
},
'Grosse (MAP32)': {
'Grosse (MAP32) - BFG9000',
'Grosse (MAP32) - Berserk',
'Grosse (MAP32) - Chaingun',
'Grosse (MAP32) - Chaingun 2',
'Grosse (MAP32) - Chaingun 3',
'Grosse (MAP32) - Exit',
'Grosse (MAP32) - Invulnerability',
'Grosse (MAP32) - Megasphere',
'Grosse (MAP32) - Plasma gun',
'Grosse (MAP32) - Rocket launcher',
'Grosse (MAP32) - Super Shotgun',
'Grosse2 (MAP32)': {
'Grosse2 (MAP32) - BFG9000',
'Grosse2 (MAP32) - Berserk',
'Grosse2 (MAP32) - Chaingun',
'Grosse2 (MAP32) - Chaingun 2',
'Grosse2 (MAP32) - Chaingun 3',
'Grosse2 (MAP32) - Exit',
'Grosse2 (MAP32) - Invulnerability',
'Grosse2 (MAP32) - Megasphere',
'Grosse2 (MAP32) - Plasma gun',
'Grosse2 (MAP32) - Rocket launcher',
'Grosse2 (MAP32) - Super Shotgun',
},
'Icon of Sin (MAP30)': {
'Icon of Sin (MAP30) - BFG9000',
@@ -3417,22 +3417,22 @@ location_name_groups: Dict[str, Set[str]] = {
'Underhalls (MAP02) - Red keycard',
'Underhalls (MAP02) - Super Shotgun',
},
'Wolfenstein (MAP31)': {
'Wolfenstein (MAP31) - BFG9000',
'Wolfenstein (MAP31) - Backpack',
'Wolfenstein (MAP31) - Backpack 2',
'Wolfenstein (MAP31) - Backpack 3',
'Wolfenstein (MAP31) - Backpack 4',
'Wolfenstein (MAP31) - Berserk',
'Wolfenstein (MAP31) - Chaingun',
'Wolfenstein (MAP31) - Exit',
'Wolfenstein (MAP31) - Megasphere',
'Wolfenstein (MAP31) - Partial invisibility',
'Wolfenstein (MAP31) - Plasma gun',
'Wolfenstein (MAP31) - Rocket launcher',
'Wolfenstein (MAP31) - Shotgun',
'Wolfenstein (MAP31) - Super Shotgun',
'Wolfenstein (MAP31) - Supercharge',
'Wolfenstein2 (MAP31)': {
'Wolfenstein2 (MAP31) - BFG9000',
'Wolfenstein2 (MAP31) - Backpack',
'Wolfenstein2 (MAP31) - Backpack 2',
'Wolfenstein2 (MAP31) - Backpack 3',
'Wolfenstein2 (MAP31) - Backpack 4',
'Wolfenstein2 (MAP31) - Berserk',
'Wolfenstein2 (MAP31) - Chaingun',
'Wolfenstein2 (MAP31) - Exit',
'Wolfenstein2 (MAP31) - Megasphere',
'Wolfenstein2 (MAP31) - Partial invisibility',
'Wolfenstein2 (MAP31) - Plasma gun',
'Wolfenstein2 (MAP31) - Rocket launcher',
'Wolfenstein2 (MAP31) - Shotgun',
'Wolfenstein2 (MAP31) - Super Shotgun',
'Wolfenstein2 (MAP31) - Supercharge',
},
}

View File

@@ -26,7 +26,7 @@ map_names: List[str] = [
'Gotcha! (MAP20)',
'Nirvana (MAP21)',
'The Catacombs (MAP22)',
"Barrels o' Fun (MAP23)",
'Barrels o Fun (MAP23)',
'The Chasm (MAP24)',
'Bloodfalls (MAP25)',
'The Abandoned Mines (MAP26)',
@@ -34,6 +34,6 @@ map_names: List[str] = [
'The Spirit World (MAP28)',
'The Living End (MAP29)',
'Icon of Sin (MAP30)',
'Wolfenstein (MAP31)',
'Grosse (MAP32)',
'Wolfenstein2 (MAP31)',
'Grosse2 (MAP32)',
]

View File

@@ -84,12 +84,11 @@ regions:List[RegionDict] = [
# The Waste Tunnels (MAP05)
{"name":"The Waste Tunnels (MAP05) Main",
"connects_to_hub":False,
"connects_to_hub":True,
"episode":1,
"connections":[
{"target":"The Waste Tunnels (MAP05) Red","pro":False},
{"target":"The Waste Tunnels (MAP05) Blue","pro":False},
{"target":"The Waste Tunnels (MAP05) Start","pro":False}]},
{"target":"The Waste Tunnels (MAP05) Blue","pro":False}]},
{"name":"The Waste Tunnels (MAP05) Blue",
"connects_to_hub":False,
"episode":1,
@@ -104,10 +103,6 @@ regions:List[RegionDict] = [
"connects_to_hub":False,
"episode":1,
"connections":[{"target":"The Waste Tunnels (MAP05) Main","pro":False}]},
{"name":"The Waste Tunnels (MAP05) Start",
"connects_to_hub":True,
"episode":1,
"connections":[{"target":"The Waste Tunnels (MAP05) Main","pro":False}]},
# The Crusher (MAP06)
{"name":"The Crusher (MAP06) Main",
@@ -134,13 +129,9 @@ regions:List[RegionDict] = [
# Dead Simple (MAP07)
{"name":"Dead Simple (MAP07) Main",
"connects_to_hub":False,
"episode":1,
"connections":[{"target":"Dead Simple (MAP07) Start","pro":False}]},
{"name":"Dead Simple (MAP07) Start",
"connects_to_hub":True,
"episode":1,
"connections":[{"target":"Dead Simple (MAP07) Main","pro":False}]},
"connections":[]},
# Tricks and Traps (MAP08)
{"name":"Tricks and Traps (MAP08) Main",
@@ -160,12 +151,11 @@ regions:List[RegionDict] = [
# The Pit (MAP09)
{"name":"The Pit (MAP09) Main",
"connects_to_hub":False,
"connects_to_hub":True,
"episode":1,
"connections":[
{"target":"The Pit (MAP09) Yellow","pro":False},
{"target":"The Pit (MAP09) Blue","pro":False},
{"target":"The Pit (MAP09) Start","pro":False}]},
{"target":"The Pit (MAP09) Blue","pro":False}]},
{"name":"The Pit (MAP09) Blue",
"connects_to_hub":False,
"episode":1,
@@ -174,18 +164,12 @@ regions:List[RegionDict] = [
"connects_to_hub":False,
"episode":1,
"connections":[{"target":"The Pit (MAP09) Main","pro":False}]},
{"name":"The Pit (MAP09) Start",
"connects_to_hub":True,
"episode":1,
"connections":[{"target":"The Pit (MAP09) Main","pro":False}]},
# Refueling Base (MAP10)
{"name":"Refueling Base (MAP10) Main",
"connects_to_hub":False,
"connects_to_hub":True,
"episode":1,
"connections":[
{"target":"Refueling Base (MAP10) Yellow","pro":False},
{"target":"Refueling Base (MAP10) Start","pro":False}]},
"connections":[{"target":"Refueling Base (MAP10) Yellow","pro":False}]},
{"name":"Refueling Base (MAP10) Yellow",
"connects_to_hub":False,
"episode":1,
@@ -196,10 +180,6 @@ regions:List[RegionDict] = [
"connects_to_hub":False,
"episode":1,
"connections":[{"target":"Refueling Base (MAP10) Yellow","pro":False}]},
{"name":"Refueling Base (MAP10) Start",
"connects_to_hub":True,
"episode":1,
"connections":[{"target":"Refueling Base (MAP10) Main","pro":False}]},
# Circle of Death (MAP11)
{"name":"Circle of Death (MAP11) Main",
@@ -207,49 +187,31 @@ regions:List[RegionDict] = [
"episode":1,
"connections":[
{"target":"Circle of Death (MAP11) Blue","pro":False},
{"target":"Circle of Death (MAP11) Red","pro":False},
{"target":"Circle of Death (MAP11) Ending","pro":True}]},
{"target":"Circle of Death (MAP11) Red","pro":False}]},
{"name":"Circle of Death (MAP11) Blue",
"connects_to_hub":False,
"episode":1,
"connections":[{"target":"Circle of Death (MAP11) Main","pro":False}]},
{"name":"Circle of Death (MAP11) Red",
"connects_to_hub":False,
"episode":1,
"connections":[
{"target":"Circle of Death (MAP11) Main","pro":False},
{"target":"Circle of Death (MAP11) Ending","pro":False}]},
{"name":"Circle of Death (MAP11) Ending",
"connects_to_hub":False,
"episode":1,
"connections":[{"target":"Circle of Death (MAP11) Main","pro":False}]},
# The Factory (MAP12)
{"name":"The Factory (MAP12) Indoors",
"connects_to_hub":False,
{"name":"The Factory (MAP12) Main",
"connects_to_hub":True,
"episode":2,
"connections":[
{"target":"The Factory (MAP12) Yellow","pro":False},
{"target":"The Factory (MAP12) Blue","pro":False},
{"target":"The Factory (MAP12) Main","pro":False}]},
{"target":"The Factory (MAP12) Blue","pro":False}]},
{"name":"The Factory (MAP12) Blue",
"connects_to_hub":False,
"episode":2,
"connections":[{"target":"The Factory (MAP12) Indoors","pro":False}]},
"connections":[{"target":"The Factory (MAP12) Main","pro":False}]},
{"name":"The Factory (MAP12) Yellow",
"connects_to_hub":False,
"episode":2,
"connections":[]},
{"name":"The Factory (MAP12) Outdoors",
"connects_to_hub":True,
"episode":2,
"connections":[{"target":"The Factory (MAP12) Main","pro":False}]},
{"name":"The Factory (MAP12) Main",
"connects_to_hub":False,
"episode":2,
"connections":[
{"target":"The Factory (MAP12) Indoors","pro":False},
{"target":"The Factory (MAP12) Outdoors","pro":False}]},
# Downtown (MAP13)
{"name":"Downtown (MAP13) Main",
@@ -329,8 +291,7 @@ regions:List[RegionDict] = [
"episode":2,
"connections":[
{"target":"Suburbs (MAP16) Red","pro":False},
{"target":"Suburbs (MAP16) Blue","pro":False},
{"target":"Suburbs (MAP16) Pro Exit","pro":True}]},
{"target":"Suburbs (MAP16) Blue","pro":False}]},
{"name":"Suburbs (MAP16) Blue",
"connects_to_hub":False,
"episode":2,
@@ -338,13 +299,7 @@ regions:List[RegionDict] = [
{"name":"Suburbs (MAP16) Red",
"connects_to_hub":False,
"episode":2,
"connections":[
{"target":"Suburbs (MAP16) Main","pro":False},
{"target":"Suburbs (MAP16) Pro Exit","pro":False}]},
{"name":"Suburbs (MAP16) Pro Exit",
"connects_to_hub":False,
"episode":2,
"connections":[{"target":"Suburbs (MAP16) Red","pro":False}]},
"connections":[{"target":"Suburbs (MAP16) Main","pro":False}]},
# Tenements (MAP17)
{"name":"Tenements (MAP17) Main",
@@ -403,7 +358,7 @@ regions:List[RegionDict] = [
# Nirvana (MAP21)
{"name":"Nirvana (MAP21) Main",
"connects_to_hub":False,
"connects_to_hub":True,
"episode":3,
"connections":[{"target":"Nirvana (MAP21) Yellow","pro":False}]},
{"name":"Nirvana (MAP21) Yellow",
@@ -411,31 +366,19 @@ regions:List[RegionDict] = [
"episode":3,
"connections":[
{"target":"Nirvana (MAP21) Main","pro":False},
{"target":"Nirvana (MAP21) Magenta","pro":False},
{"target":"Nirvana (MAP21) Pro Magenta","pro":True}]},
{"target":"Nirvana (MAP21) Magenta","pro":False}]},
{"name":"Nirvana (MAP21) Magenta",
"connects_to_hub":False,
"episode":3,
"connections":[
{"target":"Nirvana (MAP21) Yellow","pro":False},
{"target":"Nirvana (MAP21) Pro Magenta","pro":False}]},
{"name":"Nirvana (MAP21) Start",
"connects_to_hub":True,
"episode":3,
"connections":[{"target":"Nirvana (MAP21) Main","pro":False}]},
{"name":"Nirvana (MAP21) Pro Magenta",
"connects_to_hub":False,
"episode":3,
"connections":[{"target":"Nirvana (MAP21) Magenta","pro":False}]},
"connections":[{"target":"Nirvana (MAP21) Yellow","pro":False}]},
# The Catacombs (MAP22)
{"name":"The Catacombs (MAP22) Main",
"connects_to_hub":False,
"connects_to_hub":True,
"episode":3,
"connections":[
{"target":"The Catacombs (MAP22) Blue","pro":False},
{"target":"The Catacombs (MAP22) Red","pro":False},
{"target":"The Catacombs (MAP22) Early","pro":False}]},
{"target":"The Catacombs (MAP22) Red","pro":False}]},
{"name":"The Catacombs (MAP22) Blue",
"connects_to_hub":False,
"episode":3,
@@ -444,59 +387,36 @@ regions:List[RegionDict] = [
"connects_to_hub":False,
"episode":3,
"connections":[{"target":"The Catacombs (MAP22) Main","pro":False}]},
{"name":"The Catacombs (MAP22) Early",
"connects_to_hub":True,
"episode":3,
"connections":[{"target":"The Catacombs (MAP22) Main","pro":False}]},
# Barrels o' Fun (MAP23)
{"name":"Barrels o' Fun (MAP23) Main",
# Barrels o Fun (MAP23)
{"name":"Barrels o Fun (MAP23) Main",
"connects_to_hub":True,
"episode":3,
"connections":[{"target":"Barrels o' Fun (MAP23) Yellow","pro":False}]},
{"name":"Barrels o' Fun (MAP23) Yellow",
"connections":[{"target":"Barrels o Fun (MAP23) Yellow","pro":False}]},
{"name":"Barrels o Fun (MAP23) Yellow",
"connects_to_hub":False,
"episode":3,
"connections":[{"target":"Barrels o' Fun (MAP23) Main","pro":False}]},
"connections":[{"target":"Barrels o Fun (MAP23) Main","pro":False}]},
# The Chasm (MAP24)
{"name":"The Chasm (MAP24) Main",
"connects_to_hub":True,
"episode":3,
"connections":[
{"target":"The Chasm (MAP24) Blue","pro":False},
{"target":"The Chasm (MAP24) Blue Pro","pro":True}]},
"connections":[{"target":"The Chasm (MAP24) Red","pro":False}]},
{"name":"The Chasm (MAP24) Red",
"connects_to_hub":False,
"episode":3,
"connections":[{"target":"The Chasm (MAP24) Blue","pro":False}]},
{"name":"The Chasm (MAP24) Blue",
"connects_to_hub":False,
"episode":3,
"connections":[
{"target":"The Chasm (MAP24) Red","pro":False},
{"target":"The Chasm (MAP24) Main","pro":False},
{"target":"The Chasm (MAP24) Blue Pro","pro":False}]},
{"name":"The Chasm (MAP24) Blue Pro",
"connects_to_hub":False,
"episode":3,
"connections":[{"target":"The Chasm (MAP24) Blue","pro":False}]},
"connections":[{"target":"The Chasm (MAP24) Main","pro":False}]},
# Bloodfalls (MAP25)
{"name":"Bloodfalls (MAP25) Main",
"connects_to_hub":False,
"connects_to_hub":True,
"episode":3,
"connections":[
{"target":"Bloodfalls (MAP25) Blue","pro":False},
{"target":"Bloodfalls (MAP25) Start","pro":False}]},
"connections":[{"target":"Bloodfalls (MAP25) Blue","pro":False}]},
{"name":"Bloodfalls (MAP25) Blue",
"connects_to_hub":False,
"episode":3,
"connections":[{"target":"Bloodfalls (MAP25) Main","pro":False}]},
{"name":"Bloodfalls (MAP25) Start",
"connects_to_hub":True,
"episode":3,
"connections":[{"target":"Bloodfalls (MAP25) Main","pro":False}]},
# The Abandoned Mines (MAP26)
{"name":"The Abandoned Mines (MAP26) Main",
@@ -564,27 +484,19 @@ regions:List[RegionDict] = [
# Icon of Sin (MAP30)
{"name":"Icon of Sin (MAP30) Main",
"connects_to_hub":False,
"episode":3,
"connections":[{"target":"Icon of Sin (MAP30) Start","pro":False}]},
{"name":"Icon of Sin (MAP30) Start",
"connects_to_hub":True,
"episode":3,
"connections":[{"target":"Icon of Sin (MAP30) Main","pro":False}]},
"connections":[]},
# Wolfenstein (MAP31)
{"name":"Wolfenstein (MAP31) Main",
# Wolfenstein2 (MAP31)
{"name":"Wolfenstein2 (MAP31) Main",
"connects_to_hub":True,
"episode":4,
"connections":[]},
# Grosse (MAP32)
{"name":"Grosse (MAP32) Main",
"connects_to_hub":False,
"episode":4,
"connections":[{"target":"Grosse (MAP32) Start","pro":False}]},
{"name":"Grosse (MAP32) Start",
# Grosse2 (MAP32)
{"name":"Grosse2 (MAP32) Main",
"connects_to_hub":True,
"episode":4,
"connections":[{"target":"Grosse (MAP32) Main","pro":False}]},
"connections":[]},
]

View File

@@ -53,6 +53,14 @@ def set_episode1_rules(player, multiworld, pro):
state.has("The Focus (MAP04) - Red keycard", player, 1))
# The Waste Tunnels (MAP05)
set_rule(multiworld.get_entrance("Hub -> The Waste Tunnels (MAP05) Main", player), lambda state:
(state.has("The Waste Tunnels (MAP05)", player, 1) and
state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Super Shotgun", player, 1)) and
(state.has("Rocket launcher", player, 1) or
state.has("Plasma gun", player, 1) or
state.has("BFG9000", player, 1)))
set_rule(multiworld.get_entrance("The Waste Tunnels (MAP05) Main -> The Waste Tunnels (MAP05) Red", player), lambda state:
state.has("The Waste Tunnels (MAP05) - Red keycard", player, 1))
set_rule(multiworld.get_entrance("The Waste Tunnels (MAP05) Main -> The Waste Tunnels (MAP05) Blue", player), lambda state:
@@ -63,22 +71,18 @@ def set_episode1_rules(player, multiworld, pro):
state.has("The Waste Tunnels (MAP05) - Blue keycard", player, 1))
set_rule(multiworld.get_entrance("The Waste Tunnels (MAP05) Yellow -> The Waste Tunnels (MAP05) Blue", player), lambda state:
state.has("The Waste Tunnels (MAP05) - Yellow keycard", player, 1))
set_rule(multiworld.get_entrance("Hub -> The Waste Tunnels (MAP05) Start", player), lambda state:
state.has("The Waste Tunnels (MAP05)", player, 1))
set_rule(multiworld.get_entrance("The Waste Tunnels (MAP05) Start -> The Waste Tunnels (MAP05) Main", player), lambda state:
(state.has("Shotgun", player, 1) and
state.has("Super Shotgun", player, 1)) and (state.has("Chaingun", player, 1) or
state.has("Plasma gun", player, 1)))
# The Crusher (MAP06)
set_rule(multiworld.get_entrance("Hub -> The Crusher (MAP06) Main", player), lambda state:
(state.has("The Crusher (MAP06)", player, 1) and
state.has("Shotgun", player, 1)) and
(state.has("Plasma gun", player, 1) or
state.has("Chaingun", player, 1)))
state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Super Shotgun", player, 1)) and
(state.has("Rocket launcher", player, 1) or
state.has("Plasma gun", player, 1) or
state.has("BFG9000", player, 1)))
set_rule(multiworld.get_entrance("The Crusher (MAP06) Main -> The Crusher (MAP06) Blue", player), lambda state:
state.has("The Crusher (MAP06) - Blue keycard", player, 1) and
state.has("Super Shotgun", player, 1))
state.has("The Crusher (MAP06) - Blue keycard", player, 1))
set_rule(multiworld.get_entrance("The Crusher (MAP06) Blue -> The Crusher (MAP06) Red", player), lambda state:
state.has("The Crusher (MAP06) - Red keycard", player, 1))
set_rule(multiworld.get_entrance("The Crusher (MAP06) Blue -> The Crusher (MAP06) Main", player), lambda state:
@@ -91,14 +95,14 @@ def set_episode1_rules(player, multiworld, pro):
state.has("The Crusher (MAP06) - Red keycard", player, 1))
# Dead Simple (MAP07)
set_rule(multiworld.get_entrance("Hub -> Dead Simple (MAP07) Start", player), lambda state:
state.has("Dead Simple (MAP07)", player, 1))
set_rule(multiworld.get_entrance("Dead Simple (MAP07) Start -> Dead Simple (MAP07) Main", player), lambda state:
(state.has("Shotgun", player, 1) and
set_rule(multiworld.get_entrance("Hub -> Dead Simple (MAP07) Main", player), lambda state:
(state.has("Dead Simple (MAP07)", player, 1) and
state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Super Shotgun", player, 1)) and (state.has("BFG9000", player, 1) or
state.has("Super Shotgun", player, 1)) and
(state.has("Rocket launcher", player, 1) or
state.has("Plasma gun", player, 1) or
state.has("Rocket launcher", player, 1)))
state.has("BFG9000", player, 1)))
# Tricks and Traps (MAP08)
set_rule(multiworld.get_entrance("Hub -> Tricks and Traps (MAP08) Main", player), lambda state:
@@ -115,34 +119,34 @@ def set_episode1_rules(player, multiworld, pro):
state.has("Tricks and Traps (MAP08) - Yellow skull key", player, 1))
# The Pit (MAP09)
set_rule(multiworld.get_entrance("Hub -> The Pit (MAP09) Main", player), lambda state:
(state.has("The Pit (MAP09)", player, 1) and
state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Super Shotgun", player, 1)) and
(state.has("Rocket launcher", player, 1) or
state.has("Plasma gun", player, 1) or
state.has("BFG9000", player, 1)))
set_rule(multiworld.get_entrance("The Pit (MAP09) Main -> The Pit (MAP09) Yellow", player), lambda state:
state.has("The Pit (MAP09) - Yellow keycard", player, 1))
set_rule(multiworld.get_entrance("The Pit (MAP09) Main -> The Pit (MAP09) Blue", player), lambda state:
state.has("The Pit (MAP09) - Blue keycard", player, 1))
set_rule(multiworld.get_entrance("The Pit (MAP09) Yellow -> The Pit (MAP09) Main", player), lambda state:
state.has("The Pit (MAP09) - Yellow keycard", player, 1))
set_rule(multiworld.get_entrance("Hub -> The Pit (MAP09) Start", player), lambda state:
state.has("The Pit (MAP09)", player, 1))
set_rule(multiworld.get_entrance("The Pit (MAP09) Start -> The Pit (MAP09) Main", player), lambda state:
(state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Super Shotgun", player, 1)) and (state.has("BFG9000", player, 1) or
state.has("Plasma gun", player, 1) or
state.has("Rocket launcher", player, 1)))
# Refueling Base (MAP10)
set_rule(multiworld.get_entrance("Hub -> Refueling Base (MAP10) Main", player), lambda state:
(state.has("Refueling Base (MAP10)", player, 1) and
state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Super Shotgun", player, 1)) and
(state.has("Rocket launcher", player, 1) or
state.has("Plasma gun", player, 1) or
state.has("BFG9000", player, 1)))
set_rule(multiworld.get_entrance("Refueling Base (MAP10) Main -> Refueling Base (MAP10) Yellow", player), lambda state:
state.has("Refueling Base (MAP10) - Yellow keycard", player, 1))
set_rule(multiworld.get_entrance("Refueling Base (MAP10) Yellow -> Refueling Base (MAP10) Yellow Blue", player), lambda state:
state.has("Refueling Base (MAP10) - Blue keycard", player, 1))
set_rule(multiworld.get_entrance("Hub -> Refueling Base (MAP10) Start", player), lambda state:
state.has("Refueling Base (MAP10)", player, 1))
set_rule(multiworld.get_entrance("Refueling Base (MAP10) Start -> Refueling Base (MAP10) Main", player), lambda state:
(state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Super Shotgun", player, 1)) and (state.has("BFG9000", player, 1) or
state.has("Plasma gun", player, 1) or
state.has("Rocket launcher", player, 1)))
# Circle of Death (MAP11)
set_rule(multiworld.get_entrance("Hub -> Circle of Death (MAP11) Main", player), lambda state:
@@ -161,19 +165,18 @@ def set_episode1_rules(player, multiworld, pro):
def set_episode2_rules(player, multiworld, pro):
# The Factory (MAP12)
set_rule(multiworld.get_entrance("The Factory (MAP12) Indoors -> The Factory (MAP12) Yellow", player), lambda state:
set_rule(multiworld.get_entrance("Hub -> The Factory (MAP12) Main", player), lambda state:
(state.has("The Factory (MAP12)", player, 1) and
state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Super Shotgun", player, 1)) and
(state.has("Rocket launcher", player, 1) or
state.has("Plasma gun", player, 1) or
state.has("BFG9000", player, 1)))
set_rule(multiworld.get_entrance("The Factory (MAP12) Main -> The Factory (MAP12) Yellow", player), lambda state:
state.has("The Factory (MAP12) - Yellow keycard", player, 1))
set_rule(multiworld.get_entrance("The Factory (MAP12) Indoors -> The Factory (MAP12) Blue", player), lambda state:
set_rule(multiworld.get_entrance("The Factory (MAP12) Main -> The Factory (MAP12) Blue", player), lambda state:
state.has("The Factory (MAP12) - Blue keycard", player, 1))
set_rule(multiworld.get_entrance("Hub -> The Factory (MAP12) Outdoors", player), lambda state:
state.has("The Factory (MAP12)", player, 1))
set_rule(multiworld.get_entrance("The Factory (MAP12) Outdoors -> The Factory (MAP12) Main", player), lambda state:
state.has("Super Shotgun", player, 1) or
state.has("Plasma gun", player, 1))
set_rule(multiworld.get_entrance("The Factory (MAP12) Main -> The Factory (MAP12) Indoors", player), lambda state:
(state.has("Super Shotgun", player, 1) and
state.has("Chaingun", player, 1)) and (state.has("BFG9000", player, 1) or
state.has("Plasma gun", player, 1)))
# Downtown (MAP13)
set_rule(multiworld.get_entrance("Hub -> Downtown (MAP13) Main", player), lambda state:
@@ -304,56 +307,54 @@ def set_episode2_rules(player, multiworld, pro):
def set_episode3_rules(player, multiworld, pro):
# Nirvana (MAP21)
set_rule(multiworld.get_entrance("Nirvana (MAP21) Main -> Nirvana (MAP21) Yellow", player), lambda state:
(state.has("Super Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Nirvana (MAP21) - Yellow skull key", player, 1)) and (state.has("Plasma gun", player, 1) or
state.has("BFG9000", player, 1)))
set_rule(multiworld.get_entrance("Nirvana (MAP21) Yellow -> Nirvana (MAP21) Main", player), lambda state:
state.has("Nirvana (MAP21) - Yellow skull key", player, 1))
set_rule(multiworld.get_entrance("Nirvana (MAP21) Yellow -> Nirvana (MAP21) Magenta", player), lambda state:
state.has("Nirvana (MAP21) - Red skull key", player, 1) and
state.has("Nirvana (MAP21) - Blue skull key", player, 1))
set_rule(multiworld.get_entrance("Hub -> Nirvana (MAP21) Start", player), lambda state:
state.has("Nirvana (MAP21)", player, 1))
set_rule(multiworld.get_entrance("Nirvana (MAP21) Start -> Nirvana (MAP21) Main", player), lambda state:
state.has("Super Shotgun", player, 1) or
state.has("Plasma gun", player, 1))
set_rule(multiworld.get_entrance("Nirvana (MAP21) Pro Magenta -> Nirvana (MAP21) Magenta", player), lambda state:
state.has("Nirvana (MAP21) - Red skull key", player, 1))
# The Catacombs (MAP22)
set_rule(multiworld.get_entrance("The Catacombs (MAP22) Main -> The Catacombs (MAP22) Blue", player), lambda state:
state.has("The Catacombs (MAP22) - Blue skull key", player, 1))
set_rule(multiworld.get_entrance("The Catacombs (MAP22) Main -> The Catacombs (MAP22) Red", player), lambda state:
state.has("The Catacombs (MAP22) - Red skull key", player, 1))
set_rule(multiworld.get_entrance("The Catacombs (MAP22) Red -> The Catacombs (MAP22) Main", player), lambda state:
state.has("The Catacombs (MAP22) - Red skull key", player, 1))
set_rule(multiworld.get_entrance("Hub -> The Catacombs (MAP22) Early", player), lambda state:
(state.has("The Catacombs (MAP22)", player, 1)) and
(state.has("Shotgun", player, 1) or
state.has("Super Shotgun", player, 1) or
state.has("Plasma gun", player, 1)))
set_rule(multiworld.get_entrance("The Catacombs (MAP22) Early -> The Catacombs (MAP22) Main", player), lambda state:
(state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Super Shotgun", player, 1)) and (state.has("BFG9000", player, 1) or
state.has("Plasma gun", player, 1) or
state.has("Rocket launcher", player, 1)))
# Barrels o' Fun (MAP23)
set_rule(multiworld.get_entrance("Hub -> Barrels o' Fun (MAP23) Main", player), lambda state:
(state.has("Barrels o' Fun (MAP23)", player, 1) and
set_rule(multiworld.get_entrance("Hub -> Nirvana (MAP21) Main", player), lambda state:
(state.has("Nirvana (MAP21)", player, 1) and
state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Super Shotgun", player, 1)) and
(state.has("Rocket launcher", player, 1) or
state.has("Plasma gun", player, 1) or
state.has("BFG9000", player, 1)))
set_rule(multiworld.get_entrance("Barrels o' Fun (MAP23) Main -> Barrels o' Fun (MAP23) Yellow", player), lambda state:
state.has("Barrels o' Fun (MAP23) - Yellow skull key", player, 1))
set_rule(multiworld.get_entrance("Barrels o' Fun (MAP23) Yellow -> Barrels o' Fun (MAP23) Main", player), lambda state:
state.has("Barrels o' Fun (MAP23) - Yellow skull key", player, 1))
set_rule(multiworld.get_entrance("Nirvana (MAP21) Main -> Nirvana (MAP21) Yellow", player), lambda state:
state.has("Nirvana (MAP21) - Yellow skull key", player, 1))
set_rule(multiworld.get_entrance("Nirvana (MAP21) Yellow -> Nirvana (MAP21) Main", player), lambda state:
state.has("Nirvana (MAP21) - Yellow skull key", player, 1))
set_rule(multiworld.get_entrance("Nirvana (MAP21) Yellow -> Nirvana (MAP21) Magenta", player), lambda state:
state.has("Nirvana (MAP21) - Red skull key", player, 1) and
state.has("Nirvana (MAP21) - Blue skull key", player, 1))
set_rule(multiworld.get_entrance("Nirvana (MAP21) Magenta -> Nirvana (MAP21) Yellow", player), lambda state:
state.has("Nirvana (MAP21) - Red skull key", player, 1) and
state.has("Nirvana (MAP21) - Blue skull key", player, 1))
# The Catacombs (MAP22)
set_rule(multiworld.get_entrance("Hub -> The Catacombs (MAP22) Main", player), lambda state:
(state.has("The Catacombs (MAP22)", player, 1) and
state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Super Shotgun", player, 1)) and
(state.has("BFG9000", player, 1) or
state.has("Rocket launcher", player, 1) or
state.has("Plasma gun", player, 1)))
set_rule(multiworld.get_entrance("The Catacombs (MAP22) Main -> The Catacombs (MAP22) Blue", player), lambda state:
state.has("The Catacombs (MAP22) - Blue skull key", player, 1))
set_rule(multiworld.get_entrance("The Catacombs (MAP22) Main -> The Catacombs (MAP22) Red", player), lambda state:
state.has("The Catacombs (MAP22) - Red skull key", player, 1))
set_rule(multiworld.get_entrance("The Catacombs (MAP22) Red -> The Catacombs (MAP22) Main", player), lambda state:
state.has("The Catacombs (MAP22) - Red skull key", player, 1))
# Barrels o Fun (MAP23)
set_rule(multiworld.get_entrance("Hub -> Barrels o Fun (MAP23) Main", player), lambda state:
(state.has("Barrels o Fun (MAP23)", player, 1) and
state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Super Shotgun", player, 1)) and
(state.has("Rocket launcher", player, 1) or
state.has("Plasma gun", player, 1) or
state.has("BFG9000", player, 1)))
set_rule(multiworld.get_entrance("Barrels o Fun (MAP23) Main -> Barrels o Fun (MAP23) Yellow", player), lambda state:
state.has("Barrels o Fun (MAP23) - Yellow skull key", player, 1))
set_rule(multiworld.get_entrance("Barrels o Fun (MAP23) Yellow -> Barrels o Fun (MAP23) Main", player), lambda state:
state.has("Barrels o Fun (MAP23) - Yellow skull key", player, 1))
# The Chasm (MAP24)
set_rule(multiworld.get_entrance("Hub -> The Chasm (MAP24) Main", player), lambda state:
@@ -364,26 +365,24 @@ def set_episode3_rules(player, multiworld, pro):
state.has("Plasma gun", player, 1) and
state.has("BFG9000", player, 1) and
state.has("Super Shotgun", player, 1))
set_rule(multiworld.get_entrance("The Chasm (MAP24) Main -> The Chasm (MAP24) Blue", player), lambda state:
state.has("The Chasm (MAP24) - Blue keycard", player, 1))
set_rule(multiworld.get_entrance("The Chasm (MAP24) Red -> The Chasm (MAP24) Blue", player), lambda state:
set_rule(multiworld.get_entrance("The Chasm (MAP24) Main -> The Chasm (MAP24) Red", player), lambda state:
state.has("The Chasm (MAP24) - Red keycard", player, 1))
set_rule(multiworld.get_entrance("The Chasm (MAP24) Blue -> The Chasm (MAP24) Red", player), lambda state:
set_rule(multiworld.get_entrance("The Chasm (MAP24) Red -> The Chasm (MAP24) Main", player), lambda state:
state.has("The Chasm (MAP24) - Red keycard", player, 1))
# Bloodfalls (MAP25)
set_rule(multiworld.get_entrance("Hub -> Bloodfalls (MAP25) Main", player), lambda state:
state.has("Bloodfalls (MAP25)", player, 1) and
state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Rocket launcher", player, 1) and
state.has("Plasma gun", player, 1) and
state.has("BFG9000", player, 1) and
state.has("Super Shotgun", player, 1))
set_rule(multiworld.get_entrance("Bloodfalls (MAP25) Main -> Bloodfalls (MAP25) Blue", player), lambda state:
(state.has("Bloodfalls (MAP25) - Blue skull key", player, 1)) and (state.has("Rocket launcher", player, 1) or
state.has("Plasma gun", player, 1) or
state.has("BFG9000", player, 1)))
state.has("Bloodfalls (MAP25) - Blue skull key", player, 1))
set_rule(multiworld.get_entrance("Bloodfalls (MAP25) Blue -> Bloodfalls (MAP25) Main", player), lambda state:
state.has("Bloodfalls (MAP25) - Blue skull key", player, 1))
set_rule(multiworld.get_entrance("Hub -> Bloodfalls (MAP25) Start", player), lambda state:
state.has("Bloodfalls (MAP25)", player, 1))
set_rule(multiworld.get_entrance("Bloodfalls (MAP25) Start -> Bloodfalls (MAP25) Main", player), lambda state:
state.has("Super Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Shotgun", player, 1))
# The Abandoned Mines (MAP26)
set_rule(multiworld.get_entrance("Hub -> The Abandoned Mines (MAP26) Main", player), lambda state:
@@ -452,34 +451,36 @@ def set_episode3_rules(player, multiworld, pro):
state.has("Super Shotgun", player, 1))
# Icon of Sin (MAP30)
set_rule(multiworld.get_entrance("Hub -> Icon of Sin (MAP30) Start", player), lambda state:
state.has("Icon of Sin (MAP30)", player, 1))
set_rule(multiworld.get_entrance("Icon of Sin (MAP30) Start -> Icon of Sin (MAP30) Main", player), lambda state:
state.has("Shotgun", player, 1) and
set_rule(multiworld.get_entrance("Hub -> Icon of Sin (MAP30) Main", player), lambda state:
state.has("Icon of Sin (MAP30)", player, 1) and
state.has("Rocket launcher", player, 1) and
state.has("Plasma gun", player, 1) and
state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Plasma gun", player, 1) and
state.has("BFG9000", player, 1) and
state.has("Super Shotgun", player, 1))
def set_episode4_rules(player, multiworld, pro):
# Wolfenstein (MAP31)
set_rule(multiworld.get_entrance("Hub -> Wolfenstein (MAP31) Main", player), lambda state:
(state.has("Wolfenstein (MAP31)", player, 1) and
state.has("Chaingun", player, 1)) and
(state.has("Shotgun", player, 1) or
state.has("Super Shotgun", player, 1)))
# Grosse (MAP32)
set_rule(multiworld.get_entrance("Hub -> Grosse (MAP32) Start", player), lambda state:
state.has("Grosse (MAP32)", player, 1))
set_rule(multiworld.get_entrance("Grosse (MAP32) Start -> Grosse (MAP32) Main", player), lambda state:
(state.has("Shotgun", player, 1) and
# Wolfenstein2 (MAP31)
set_rule(multiworld.get_entrance("Hub -> Wolfenstein2 (MAP31) Main", player), lambda state:
(state.has("Wolfenstein2 (MAP31)", player, 1) and
state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Super Shotgun", player, 1)) and (state.has("BFG9000", player, 1) or
state.has("Super Shotgun", player, 1)) and
(state.has("Rocket launcher", player, 1) or
state.has("Plasma gun", player, 1) or
state.has("Rocket launcher", player, 1)))
state.has("BFG9000", player, 1)))
# Grosse2 (MAP32)
set_rule(multiworld.get_entrance("Hub -> Grosse2 (MAP32) Main", player), lambda state:
(state.has("Grosse2 (MAP32)", player, 1) and
state.has("Shotgun", player, 1) and
state.has("Chaingun", player, 1) and
state.has("Super Shotgun", player, 1)) and
(state.has("Rocket launcher", player, 1) or
state.has("Plasma gun", player, 1) or
state.has("BFG9000", player, 1)))
def set_rules(doom_ii_world: "DOOM2World", included_episodes, pro):

View File

@@ -51,11 +51,11 @@ class DOOM2World(World):
location_name_to_id = {data["name"]: loc_id for loc_id, data in Locations.location_table.items()}
location_name_groups = Locations.location_name_groups
starting_level_for_episode: Dict[int, str] = {
1: "Entryway (MAP01)",
2: "The Factory (MAP12)",
3: "Nirvana (MAP21)"
}
starting_level_for_episode: List[str] = [
"Entryway (MAP01)",
"The Factory (MAP12)",
"Nirvana (MAP21)"
]
# Item ratio that scales depending on episode count. These are the ratio for 3 episode. In DOOM1.
# The ratio have been tweaked seem, and feel good.
@@ -77,7 +77,6 @@ class DOOM2World(World):
def __init__(self, multiworld: MultiWorld, player: int):
self.included_episodes = [1, 1, 1, 0]
self.location_count = 0
self.starting_levels = []
super().__init__(multiworld, player)
@@ -96,14 +95,6 @@ class DOOM2World(World):
if self.get_episode_count() == 0:
self.included_episodes[0] = 1
self.starting_levels = [level_name for (episode, level_name) in self.starting_level_for_episode.items()
if self.included_episodes[episode - 1]]
# If soloing MAP21-MAP30, we need to mark a weapon as early to help generation succeed
if self.get_episode_count() == 1 and self.included_episodes[2]:
early_weapon = self.random.choice(["Super Shotgun", "Plasma gun"])
self.multiworld.early_items[self.player][early_weapon] = 1
def create_regions(self):
pro = self.options.pro.value
@@ -202,7 +193,7 @@ class DOOM2World(World):
if item["episode"] != -1 and not self.included_episodes[item["episode"] - 1]:
continue
count = item["count"] if item["name"] not in self.starting_levels else item["count"] - 1
count = item["count"] if item["name"] not in self.starting_level_for_episode else item["count"] - 1
itempool += [self.create_item(item["name"]) for _ in range(count)]
# Backpack(s) based on options
@@ -233,8 +224,9 @@ class DOOM2World(World):
self.location_count -= 1
# Give starting levels right away
for map_name in self.starting_levels:
self.multiworld.push_precollected(self.create_item(map_name))
for i in range(len(self.starting_level_for_episode)):
if self.included_episodes[i]:
self.multiworld.push_precollected(self.create_item(self.starting_level_for_episode[i]))
# Give Computer area maps if option selected
if start_with_computer_area_maps:

View File

@@ -255,8 +255,7 @@ async def game_watcher(ctx: FactorioContext):
if "DeathLink" in ctx.tags:
async_start(ctx.send_death())
if ctx.energy_link_increment:
# 1 + quality * 0.3 for each bridge
in_world_bridges: float = data["energy_bridges"]
in_world_bridges = data["energy_bridges"]
if in_world_bridges:
in_world_energy = data["energy"]
if in_world_energy < (ctx.energy_link_increment * in_world_bridges):
@@ -264,14 +263,14 @@ async def game_watcher(ctx: FactorioContext):
ctx.last_deplete = time.time()
async_start(ctx.send_msgs([{
"cmd": "Set", "key": ctx.energylink_key, "operations":
[{"operation": "add", "value": int(-ctx.energy_link_increment * in_world_bridges)},
[{"operation": "add", "value": -ctx.energy_link_increment * in_world_bridges},
{"operation": "max", "value": 0}],
"last_deplete": ctx.last_deplete
}]))
# Above Capacity - (len(Bridges) * ENERGY_INCREMENT)
elif in_world_energy > (in_world_bridges * ctx.energy_link_increment * 5) - \
ctx.energy_link_increment * in_world_bridges:
value = int(ctx.energy_link_increment * in_world_bridges)
value = ctx.energy_link_increment * in_world_bridges
async_start(ctx.send_msgs([{
"cmd": "Set", "key": ctx.energylink_key, "operations":
[{"operation": "add", "value": value}]
@@ -407,7 +406,7 @@ async def get_info(ctx: FactorioContext, rcon_client: factorio_rcon.RCONClient):
ctx.auth = info["slot_name"]
ctx.seed_name = info["seed_name"]
death_link = info["death_link"]
ctx.energy_link_increment = int(info.get("energy_link", 0))
ctx.energy_link_increment = info.get("energy_link", 0)
logger.debug(f"Energy Link Increment: {ctx.energy_link_increment}")
if ctx.energy_link_increment and ctx.ui:
ctx.ui.enable_energy_link()

View File

@@ -102,7 +102,7 @@ class Factorio(World):
item_name_groups = {
"Progressive": set(progressive_tech_table.keys()),
}
required_client_version = (0, 6, 0)
required_client_version = (0, 5, 1)
if Utils.version_tuple < required_client_version:
raise Exception(f"Update Archipelago to use this world ({game}).")
ordered_science_packs: typing.List[str] = MaxSciencePack.get_ordered_science_packs()

View File

@@ -3,6 +3,7 @@ import settings
import base64
import threading
import requests
import yaml
from worlds.AutoWorld import World, WebWorld
from BaseClasses import Tutorial
from .Regions import create_regions, location_table, set_rules, stage_set_rules, rooms, non_dead_end_crest_rooms,\
@@ -133,7 +134,7 @@ class FFMQWorld(World):
errors.append([api_url, err])
else:
if response.ok:
world.rooms = rooms_data[query] = Utils.parse_yaml(response.text)
world.rooms = rooms_data[query] = yaml.load(response.text, yaml.Loader)
break
else:
api_urls.remove(api_url)

View File

@@ -514,19 +514,19 @@ item_table: Dict[int, ItemDict] = {
'map': 7},
370259: {'classification': ItemClassification.progression,
'count': 1,
'name': 'The Aquifer (E3M9) - Blue key',
'name': 'The Aquifier (E3M9) - Blue key',
'doom_type': 79,
'episode': 3,
'map': 9},
370260: {'classification': ItemClassification.progression,
'count': 1,
'name': 'The Aquifer (E3M9) - Green key',
'name': 'The Aquifier (E3M9) - Green key',
'doom_type': 73,
'episode': 3,
'map': 9},
370261: {'classification': ItemClassification.progression,
'count': 1,
'name': 'The Aquifer (E3M9) - Yellow key',
'name': 'The Aquifier (E3M9) - Yellow key',
'doom_type': 80,
'episode': 3,
'map': 9},
@@ -1234,37 +1234,37 @@ item_table: Dict[int, ItemDict] = {
'map': 7},
370475: {'classification': ItemClassification.progression,
'count': 1,
'name': "D'Sparil's Keep (E3M8)",
'name': "D'Sparil'S Keep (E3M8)",
'doom_type': -1,
'episode': 3,
'map': 8},
370476: {'classification': ItemClassification.progression,
'count': 1,
'name': "D'Sparil's Keep (E3M8) - Complete",
'name': "D'Sparil'S Keep (E3M8) - Complete",
'doom_type': -2,
'episode': 3,
'map': 8},
370477: {'classification': ItemClassification.filler,
'count': 1,
'name': "D'Sparil's Keep (E3M8) - Map Scroll",
'name': "D'Sparil'S Keep (E3M8) - Map Scroll",
'doom_type': 35,
'episode': 3,
'map': 8},
370478: {'classification': ItemClassification.progression,
'count': 1,
'name': 'The Aquifer (E3M9)',
'name': 'The Aquifier (E3M9)',
'doom_type': -1,
'episode': 3,
'map': 9},
370479: {'classification': ItemClassification.progression,
'count': 1,
'name': 'The Aquifer (E3M9) - Complete',
'name': 'The Aquifier (E3M9) - Complete',
'doom_type': -2,
'episode': 3,
'map': 9},
370480: {'classification': ItemClassification.filler,
'count': 1,
'name': 'The Aquifer (E3M9) - Map Scroll',
'name': 'The Aquifier (E3M9) - Map Scroll',
'doom_type': 35,
'episode': 3,
'map': 9},
@@ -1635,8 +1635,8 @@ item_name_groups: Dict[str, Set[str]] = {
'Ammos': {'Crystal Geode', 'Energy Orb', 'Greater Runes', 'Inferno Orb', 'Pile of Mace Spheres', 'Quiver of Ethereal Arrows', },
'Armors': {'Enchanted Shield', 'Silver Shield', },
'Artifacts': {'Chaos Device', 'Morph Ovum', 'Mystic Urn', 'Quartz Flask', 'Ring of Invincibility', 'Shadowsphere', 'Timebomb of the Ancients', 'Tome of Power', 'Torch', },
'Keys': {'Ambulatory (E4M3) - Blue key', 'Ambulatory (E4M3) - Green key', 'Ambulatory (E4M3) - Yellow key', 'Blockhouse (E4M2) - Blue key', 'Blockhouse (E4M2) - Green key', 'Blockhouse (E4M2) - Yellow key', 'Catafalque (E4M1) - Green key', 'Catafalque (E4M1) - Yellow key', 'Colonnade (E5M6) - Blue key', 'Colonnade (E5M6) - Green key', 'Colonnade (E5M6) - Yellow key', 'Courtyard (E5M4) - Blue key', 'Courtyard (E5M4) - Green key', 'Courtyard (E5M4) - Yellow key', 'Foetid Manse (E5M7) - Blue key', 'Foetid Manse (E5M7) - Green key', 'Foetid Manse (E5M7) - Yellow key', 'Great Stair (E4M5) - Blue key', 'Great Stair (E4M5) - Green key', 'Great Stair (E4M5) - Yellow key', 'Halls of the Apostate (E4M6) - Blue key', 'Halls of the Apostate (E4M6) - Green key', 'Halls of the Apostate (E4M6) - Yellow key', 'Hydratyr (E5M5) - Blue key', 'Hydratyr (E5M5) - Green key', 'Hydratyr (E5M5) - Yellow key', 'Mausoleum (E4M9) - Yellow key', 'Ochre Cliffs (E5M1) - Blue key', 'Ochre Cliffs (E5M1) - Green key', 'Ochre Cliffs (E5M1) - Yellow key', 'Quay (E5M3) - Blue key', 'Quay (E5M3) - Green key', 'Quay (E5M3) - Yellow key', 'Ramparts of Perdition (E4M7) - Blue key', 'Ramparts of Perdition (E4M7) - Green key', 'Ramparts of Perdition (E4M7) - Yellow key', 'Rapids (E5M2) - Green key', 'Rapids (E5M2) - Yellow key', 'Shattered Bridge (E4M8) - Yellow key', "Skein of D'Sparil (E5M9) - Blue key", "Skein of D'Sparil (E5M9) - Green key", "Skein of D'Sparil (E5M9) - Yellow key", 'The Aquifer (E3M9) - Blue key', 'The Aquifer (E3M9) - Green key', 'The Aquifer (E3M9) - Yellow key', 'The Azure Fortress (E3M4) - Green key', 'The Azure Fortress (E3M4) - Yellow key', 'The Catacombs (E2M5) - Blue key', 'The Catacombs (E2M5) - Green key', 'The Catacombs (E2M5) - Yellow key', 'The Cathedral (E1M6) - Green key', 'The Cathedral (E1M6) - Yellow key', 'The Cesspool (E3M2) - Blue key', 'The Cesspool (E3M2) - Green key', 'The Cesspool (E3M2) - Yellow key', 'The Chasm (E3M7) - Blue key', 'The Chasm (E3M7) - Green key', 'The Chasm (E3M7) - Yellow key', 'The Citadel (E1M5) - Blue key', 'The Citadel (E1M5) - Green key', 'The Citadel (E1M5) - Yellow key', 'The Confluence (E3M3) - Blue key', 'The Confluence (E3M3) - Green key', 'The Confluence (E3M3) - Yellow key', 'The Crater (E2M1) - Green key', 'The Crater (E2M1) - Yellow key', 'The Crypts (E1M7) - Blue key', 'The Crypts (E1M7) - Green key', 'The Crypts (E1M7) - Yellow key', 'The Docks (E1M1) - Yellow key', 'The Dungeons (E1M2) - Blue key', 'The Dungeons (E1M2) - Green key', 'The Dungeons (E1M2) - Yellow key', 'The Gatehouse (E1M3) - Green key', 'The Gatehouse (E1M3) - Yellow key', 'The Glacier (E2M9) - Blue key', 'The Glacier (E2M9) - Green key', 'The Glacier (E2M9) - Yellow key', 'The Graveyard (E1M9) - Blue key', 'The Graveyard (E1M9) - Green key', 'The Graveyard (E1M9) - Yellow key', 'The Great Hall (E2M7) - Blue key', 'The Great Hall (E2M7) - Green key', 'The Great Hall (E2M7) - Yellow key', 'The Guard Tower (E1M4) - Green key', 'The Guard Tower (E1M4) - Yellow key', 'The Halls of Fear (E3M6) - Blue key', 'The Halls of Fear (E3M6) - Green key', 'The Halls of Fear (E3M6) - Yellow key', 'The Ice Grotto (E2M4) - Blue key', 'The Ice Grotto (E2M4) - Green key', 'The Ice Grotto (E2M4) - Yellow key', 'The Labyrinth (E2M6) - Blue key', 'The Labyrinth (E2M6) - Green key', 'The Labyrinth (E2M6) - Yellow key', 'The Lava Pits (E2M2) - Green key', 'The Lava Pits (E2M2) - Yellow key', 'The Ophidian Lair (E3M5) - Green key', 'The Ophidian Lair (E3M5) - Yellow key', 'The River of Fire (E2M3) - Blue key', 'The River of Fire (E2M3) - Green key', 'The River of Fire (E2M3) - Yellow key', 'The Storehouse (E3M1) - Green key', 'The Storehouse (E3M1) - Yellow key', },
'Levels': {'Ambulatory (E4M3)', 'Blockhouse (E4M2)', 'Catafalque (E4M1)', 'Colonnade (E5M6)', 'Courtyard (E5M4)', "D'Sparil's Keep (E3M8)", 'Field of Judgement (E5M8)', 'Foetid Manse (E5M7)', 'Great Stair (E4M5)', 'Halls of the Apostate (E4M6)', "Hell's Maw (E1M8)", 'Hydratyr (E5M5)', 'Mausoleum (E4M9)', 'Ochre Cliffs (E5M1)', 'Quay (E5M3)', 'Ramparts of Perdition (E4M7)', 'Rapids (E5M2)', 'Sepulcher (E4M4)', 'Shattered Bridge (E4M8)', "Skein of D'Sparil (E5M9)", 'The Aquifer (E3M9)', 'The Azure Fortress (E3M4)', 'The Catacombs (E2M5)', 'The Cathedral (E1M6)', 'The Cesspool (E3M2)', 'The Chasm (E3M7)', 'The Citadel (E1M5)', 'The Confluence (E3M3)', 'The Crater (E2M1)', 'The Crypts (E1M7)', 'The Docks (E1M1)', 'The Dungeons (E1M2)', 'The Gatehouse (E1M3)', 'The Glacier (E2M9)', 'The Graveyard (E1M9)', 'The Great Hall (E2M7)', 'The Guard Tower (E1M4)', 'The Halls of Fear (E3M6)', 'The Ice Grotto (E2M4)', 'The Labyrinth (E2M6)', 'The Lava Pits (E2M2)', 'The Ophidian Lair (E3M5)', 'The Portals of Chaos (E2M8)', 'The River of Fire (E2M3)', 'The Storehouse (E3M1)', },
'Map Scrolls': {'Ambulatory (E4M3) - Map Scroll', 'Blockhouse (E4M2) - Map Scroll', 'Catafalque (E4M1) - Map Scroll', 'Colonnade (E5M6) - Map Scroll', 'Courtyard (E5M4) - Map Scroll', "D'Sparil's Keep (E3M8) - Map Scroll", 'Field of Judgement (E5M8) - Map Scroll', 'Foetid Manse (E5M7) - Map Scroll', 'Great Stair (E4M5) - Map Scroll', 'Halls of the Apostate (E4M6) - Map Scroll', "Hell's Maw (E1M8) - Map Scroll", 'Hydratyr (E5M5) - Map Scroll', 'Mausoleum (E4M9) - Map Scroll', 'Ochre Cliffs (E5M1) - Map Scroll', 'Quay (E5M3) - Map Scroll', 'Ramparts of Perdition (E4M7) - Map Scroll', 'Rapids (E5M2) - Map Scroll', 'Sepulcher (E4M4) - Map Scroll', 'Shattered Bridge (E4M8) - Map Scroll', "Skein of D'Sparil (E5M9) - Map Scroll", 'The Aquifer (E3M9) - Map Scroll', 'The Azure Fortress (E3M4) - Map Scroll', 'The Catacombs (E2M5) - Map Scroll', 'The Cathedral (E1M6) - Map Scroll', 'The Cesspool (E3M2) - Map Scroll', 'The Chasm (E3M7) - Map Scroll', 'The Citadel (E1M5) - Map Scroll', 'The Confluence (E3M3) - Map Scroll', 'The Crater (E2M1) - Map Scroll', 'The Crypts (E1M7) - Map Scroll', 'The Docks (E1M1) - Map Scroll', 'The Dungeons (E1M2) - Map Scroll', 'The Gatehouse (E1M3) - Map Scroll', 'The Glacier (E2M9) - Map Scroll', 'The Graveyard (E1M9) - Map Scroll', 'The Great Hall (E2M7) - Map Scroll', 'The Guard Tower (E1M4) - Map Scroll', 'The Halls of Fear (E3M6) - Map Scroll', 'The Ice Grotto (E2M4) - Map Scroll', 'The Labyrinth (E2M6) - Map Scroll', 'The Lava Pits (E2M2) - Map Scroll', 'The Ophidian Lair (E3M5) - Map Scroll', 'The Portals of Chaos (E2M8) - Map Scroll', 'The River of Fire (E2M3) - Map Scroll', 'The Storehouse (E3M1) - Map Scroll', },
'Keys': {'Ambulatory (E4M3) - Blue key', 'Ambulatory (E4M3) - Green key', 'Ambulatory (E4M3) - Yellow key', 'Blockhouse (E4M2) - Blue key', 'Blockhouse (E4M2) - Green key', 'Blockhouse (E4M2) - Yellow key', 'Catafalque (E4M1) - Green key', 'Catafalque (E4M1) - Yellow key', 'Colonnade (E5M6) - Blue key', 'Colonnade (E5M6) - Green key', 'Colonnade (E5M6) - Yellow key', 'Courtyard (E5M4) - Blue key', 'Courtyard (E5M4) - Green key', 'Courtyard (E5M4) - Yellow key', 'Foetid Manse (E5M7) - Blue key', 'Foetid Manse (E5M7) - Green key', 'Foetid Manse (E5M7) - Yellow key', 'Great Stair (E4M5) - Blue key', 'Great Stair (E4M5) - Green key', 'Great Stair (E4M5) - Yellow key', 'Halls of the Apostate (E4M6) - Blue key', 'Halls of the Apostate (E4M6) - Green key', 'Halls of the Apostate (E4M6) - Yellow key', 'Hydratyr (E5M5) - Blue key', 'Hydratyr (E5M5) - Green key', 'Hydratyr (E5M5) - Yellow key', 'Mausoleum (E4M9) - Yellow key', 'Ochre Cliffs (E5M1) - Blue key', 'Ochre Cliffs (E5M1) - Green key', 'Ochre Cliffs (E5M1) - Yellow key', 'Quay (E5M3) - Blue key', 'Quay (E5M3) - Green key', 'Quay (E5M3) - Yellow key', 'Ramparts of Perdition (E4M7) - Blue key', 'Ramparts of Perdition (E4M7) - Green key', 'Ramparts of Perdition (E4M7) - Yellow key', 'Rapids (E5M2) - Green key', 'Rapids (E5M2) - Yellow key', 'Shattered Bridge (E4M8) - Yellow key', "Skein of D'Sparil (E5M9) - Blue key", "Skein of D'Sparil (E5M9) - Green key", "Skein of D'Sparil (E5M9) - Yellow key", 'The Aquifier (E3M9) - Blue key', 'The Aquifier (E3M9) - Green key', 'The Aquifier (E3M9) - Yellow key', 'The Azure Fortress (E3M4) - Green key', 'The Azure Fortress (E3M4) - Yellow key', 'The Catacombs (E2M5) - Blue key', 'The Catacombs (E2M5) - Green key', 'The Catacombs (E2M5) - Yellow key', 'The Cathedral (E1M6) - Green key', 'The Cathedral (E1M6) - Yellow key', 'The Cesspool (E3M2) - Blue key', 'The Cesspool (E3M2) - Green key', 'The Cesspool (E3M2) - Yellow key', 'The Chasm (E3M7) - Blue key', 'The Chasm (E3M7) - Green key', 'The Chasm (E3M7) - Yellow key', 'The Citadel (E1M5) - Blue key', 'The Citadel (E1M5) - Green key', 'The Citadel (E1M5) - Yellow key', 'The Confluence (E3M3) - Blue key', 'The Confluence (E3M3) - Green key', 'The Confluence (E3M3) - Yellow key', 'The Crater (E2M1) - Green key', 'The Crater (E2M1) - Yellow key', 'The Crypts (E1M7) - Blue key', 'The Crypts (E1M7) - Green key', 'The Crypts (E1M7) - Yellow key', 'The Docks (E1M1) - Yellow key', 'The Dungeons (E1M2) - Blue key', 'The Dungeons (E1M2) - Green key', 'The Dungeons (E1M2) - Yellow key', 'The Gatehouse (E1M3) - Green key', 'The Gatehouse (E1M3) - Yellow key', 'The Glacier (E2M9) - Blue key', 'The Glacier (E2M9) - Green key', 'The Glacier (E2M9) - Yellow key', 'The Graveyard (E1M9) - Blue key', 'The Graveyard (E1M9) - Green key', 'The Graveyard (E1M9) - Yellow key', 'The Great Hall (E2M7) - Blue key', 'The Great Hall (E2M7) - Green key', 'The Great Hall (E2M7) - Yellow key', 'The Guard Tower (E1M4) - Green key', 'The Guard Tower (E1M4) - Yellow key', 'The Halls of Fear (E3M6) - Blue key', 'The Halls of Fear (E3M6) - Green key', 'The Halls of Fear (E3M6) - Yellow key', 'The Ice Grotto (E2M4) - Blue key', 'The Ice Grotto (E2M4) - Green key', 'The Ice Grotto (E2M4) - Yellow key', 'The Labyrinth (E2M6) - Blue key', 'The Labyrinth (E2M6) - Green key', 'The Labyrinth (E2M6) - Yellow key', 'The Lava Pits (E2M2) - Green key', 'The Lava Pits (E2M2) - Yellow key', 'The Ophidian Lair (E3M5) - Green key', 'The Ophidian Lair (E3M5) - Yellow key', 'The River of Fire (E2M3) - Blue key', 'The River of Fire (E2M3) - Green key', 'The River of Fire (E2M3) - Yellow key', 'The Storehouse (E3M1) - Green key', 'The Storehouse (E3M1) - Yellow key', },
'Levels': {'Ambulatory (E4M3)', 'Blockhouse (E4M2)', 'Catafalque (E4M1)', 'Colonnade (E5M6)', 'Courtyard (E5M4)', "D'Sparil'S Keep (E3M8)", 'Field of Judgement (E5M8)', 'Foetid Manse (E5M7)', 'Great Stair (E4M5)', 'Halls of the Apostate (E4M6)', "Hell's Maw (E1M8)", 'Hydratyr (E5M5)', 'Mausoleum (E4M9)', 'Ochre Cliffs (E5M1)', 'Quay (E5M3)', 'Ramparts of Perdition (E4M7)', 'Rapids (E5M2)', 'Sepulcher (E4M4)', 'Shattered Bridge (E4M8)', "Skein of D'Sparil (E5M9)", 'The Aquifier (E3M9)', 'The Azure Fortress (E3M4)', 'The Catacombs (E2M5)', 'The Cathedral (E1M6)', 'The Cesspool (E3M2)', 'The Chasm (E3M7)', 'The Citadel (E1M5)', 'The Confluence (E3M3)', 'The Crater (E2M1)', 'The Crypts (E1M7)', 'The Docks (E1M1)', 'The Dungeons (E1M2)', 'The Gatehouse (E1M3)', 'The Glacier (E2M9)', 'The Graveyard (E1M9)', 'The Great Hall (E2M7)', 'The Guard Tower (E1M4)', 'The Halls of Fear (E3M6)', 'The Ice Grotto (E2M4)', 'The Labyrinth (E2M6)', 'The Lava Pits (E2M2)', 'The Ophidian Lair (E3M5)', 'The Portals of Chaos (E2M8)', 'The River of Fire (E2M3)', 'The Storehouse (E3M1)', },
'Map Scrolls': {'Ambulatory (E4M3) - Map Scroll', 'Blockhouse (E4M2) - Map Scroll', 'Catafalque (E4M1) - Map Scroll', 'Colonnade (E5M6) - Map Scroll', 'Courtyard (E5M4) - Map Scroll', "D'Sparil'S Keep (E3M8) - Map Scroll", 'Field of Judgement (E5M8) - Map Scroll', 'Foetid Manse (E5M7) - Map Scroll', 'Great Stair (E4M5) - Map Scroll', 'Halls of the Apostate (E4M6) - Map Scroll', "Hell's Maw (E1M8) - Map Scroll", 'Hydratyr (E5M5) - Map Scroll', 'Mausoleum (E4M9) - Map Scroll', 'Ochre Cliffs (E5M1) - Map Scroll', 'Quay (E5M3) - Map Scroll', 'Ramparts of Perdition (E4M7) - Map Scroll', 'Rapids (E5M2) - Map Scroll', 'Sepulcher (E4M4) - Map Scroll', 'Shattered Bridge (E4M8) - Map Scroll', "Skein of D'Sparil (E5M9) - Map Scroll", 'The Aquifier (E3M9) - Map Scroll', 'The Azure Fortress (E3M4) - Map Scroll', 'The Catacombs (E2M5) - Map Scroll', 'The Cathedral (E1M6) - Map Scroll', 'The Cesspool (E3M2) - Map Scroll', 'The Chasm (E3M7) - Map Scroll', 'The Citadel (E1M5) - Map Scroll', 'The Confluence (E3M3) - Map Scroll', 'The Crater (E2M1) - Map Scroll', 'The Crypts (E1M7) - Map Scroll', 'The Docks (E1M1) - Map Scroll', 'The Dungeons (E1M2) - Map Scroll', 'The Gatehouse (E1M3) - Map Scroll', 'The Glacier (E2M9) - Map Scroll', 'The Graveyard (E1M9) - Map Scroll', 'The Great Hall (E2M7) - Map Scroll', 'The Guard Tower (E1M4) - Map Scroll', 'The Halls of Fear (E3M6) - Map Scroll', 'The Ice Grotto (E2M4) - Map Scroll', 'The Labyrinth (E2M6) - Map Scroll', 'The Lava Pits (E2M2) - Map Scroll', 'The Ophidian Lair (E3M5) - Map Scroll', 'The Portals of Chaos (E2M8) - Map Scroll', 'The River of Fire (E2M3) - Map Scroll', 'The Storehouse (E3M1) - Map Scroll', },
'Weapons': {'Dragon Claw', 'Ethereal Crossbow', 'Firemace', 'Gauntlets of the Necromancer', 'Hellstaff', 'Phoenix Rod', },
}

View File

@@ -3633,300 +3633,300 @@ location_table: Dict[int, LocationDict] = {
'index': -1,
'doom_type': -1,
'region': "The Chasm (E3M7) Blue"},
371517: {'name': "D'Sparil's Keep (E3M8) - Phoenix Rod",
371517: {'name': "D'Sparil'S Keep (E3M8) - Phoenix Rod",
'episode': 3,
'check_sanity': False,
'map': 8,
'index': 55,
'doom_type': 2003,
'region': "D'Sparil's Keep (E3M8) Main"},
371518: {'name': "D'Sparil's Keep (E3M8) - Ethereal Crossbow",
'region': "D'Sparil'S Keep (E3M8) Main"},
371518: {'name': "D'Sparil'S Keep (E3M8) - Ethereal Crossbow",
'episode': 3,
'check_sanity': True,
'map': 8,
'index': 56,
'doom_type': 2001,
'region': "D'Sparil's Keep (E3M8) Main"},
371519: {'name': "D'Sparil's Keep (E3M8) - Dragon Claw",
'region': "D'Sparil'S Keep (E3M8) Main"},
371519: {'name': "D'Sparil'S Keep (E3M8) - Dragon Claw",
'episode': 3,
'check_sanity': False,
'map': 8,
'index': 57,
'doom_type': 53,
'region': "D'Sparil's Keep (E3M8) Main"},
371520: {'name': "D'Sparil's Keep (E3M8) - Gauntlets of the Necromancer",
'region': "D'Sparil'S Keep (E3M8) Main"},
371520: {'name': "D'Sparil'S Keep (E3M8) - Gauntlets of the Necromancer",
'episode': 3,
'check_sanity': False,
'map': 8,
'index': 58,
'doom_type': 2005,
'region': "D'Sparil's Keep (E3M8) Main"},
371521: {'name': "D'Sparil's Keep (E3M8) - Hellstaff",
'region': "D'Sparil'S Keep (E3M8) Main"},
371521: {'name': "D'Sparil'S Keep (E3M8) - Hellstaff",
'episode': 3,
'check_sanity': False,
'map': 8,
'index': 59,
'doom_type': 2004,
'region': "D'Sparil's Keep (E3M8) Main"},
371522: {'name': "D'Sparil's Keep (E3M8) - Bag of Holding",
'region': "D'Sparil'S Keep (E3M8) Main"},
371522: {'name': "D'Sparil'S Keep (E3M8) - Bag of Holding",
'episode': 3,
'check_sanity': False,
'map': 8,
'index': 63,
'doom_type': 8,
'region': "D'Sparil's Keep (E3M8) Main"},
371523: {'name': "D'Sparil's Keep (E3M8) - Mystic Urn",
'region': "D'Sparil'S Keep (E3M8) Main"},
371523: {'name': "D'Sparil'S Keep (E3M8) - Mystic Urn",
'episode': 3,
'check_sanity': False,
'map': 8,
'index': 64,
'doom_type': 32,
'region': "D'Sparil's Keep (E3M8) Main"},
371524: {'name': "D'Sparil's Keep (E3M8) - Ring of Invincibility",
'region': "D'Sparil'S Keep (E3M8) Main"},
371524: {'name': "D'Sparil'S Keep (E3M8) - Ring of Invincibility",
'episode': 3,
'check_sanity': False,
'map': 8,
'index': 65,
'doom_type': 84,
'region': "D'Sparil's Keep (E3M8) Main"},
371525: {'name': "D'Sparil's Keep (E3M8) - Shadowsphere",
'region': "D'Sparil'S Keep (E3M8) Main"},
371525: {'name': "D'Sparil'S Keep (E3M8) - Shadowsphere",
'episode': 3,
'check_sanity': False,
'map': 8,
'index': 66,
'doom_type': 75,
'region': "D'Sparil's Keep (E3M8) Main"},
371526: {'name': "D'Sparil's Keep (E3M8) - Silver Shield",
'region': "D'Sparil'S Keep (E3M8) Main"},
371526: {'name': "D'Sparil'S Keep (E3M8) - Silver Shield",
'episode': 3,
'check_sanity': False,
'map': 8,
'index': 67,
'doom_type': 85,
'region': "D'Sparil's Keep (E3M8) Main"},
371527: {'name': "D'Sparil's Keep (E3M8) - Enchanted Shield",
'region': "D'Sparil'S Keep (E3M8) Main"},
371527: {'name': "D'Sparil'S Keep (E3M8) - Enchanted Shield",
'episode': 3,
'check_sanity': False,
'map': 8,
'index': 68,
'doom_type': 31,
'region': "D'Sparil's Keep (E3M8) Main"},
371528: {'name': "D'Sparil's Keep (E3M8) - Tome of Power",
'region': "D'Sparil'S Keep (E3M8) Main"},
371528: {'name': "D'Sparil'S Keep (E3M8) - Tome of Power",
'episode': 3,
'check_sanity': False,
'map': 8,
'index': 69,
'doom_type': 86,
'region': "D'Sparil's Keep (E3M8) Main"},
371529: {'name': "D'Sparil's Keep (E3M8) - Tome of Power 2",
'region': "D'Sparil'S Keep (E3M8) Main"},
371529: {'name': "D'Sparil'S Keep (E3M8) - Tome of Power 2",
'episode': 3,
'check_sanity': True,
'map': 8,
'index': 70,
'doom_type': 86,
'region': "D'Sparil's Keep (E3M8) Main"},
371530: {'name': "D'Sparil's Keep (E3M8) - Chaos Device",
'region': "D'Sparil'S Keep (E3M8) Main"},
371530: {'name': "D'Sparil'S Keep (E3M8) - Chaos Device",
'episode': 3,
'check_sanity': True,
'map': 8,
'index': 71,
'doom_type': 36,
'region': "D'Sparil's Keep (E3M8) Main"},
371531: {'name': "D'Sparil's Keep (E3M8) - Tome of Power 3",
'region': "D'Sparil'S Keep (E3M8) Main"},
371531: {'name': "D'Sparil'S Keep (E3M8) - Tome of Power 3",
'episode': 3,
'check_sanity': True,
'map': 8,
'index': 245,
'doom_type': 86,
'region': "D'Sparil's Keep (E3M8) Main"},
371532: {'name': "D'Sparil's Keep (E3M8) - Exit",
'region': "D'Sparil'S Keep (E3M8) Main"},
371532: {'name': "D'Sparil'S Keep (E3M8) - Exit",
'episode': 3,
'check_sanity': False,
'map': 8,
'index': -1,
'doom_type': -1,
'region': "D'Sparil's Keep (E3M8) Main"},
371533: {'name': 'The Aquifer (E3M9) - Blue key',
'region': "D'Sparil'S Keep (E3M8) Main"},
371533: {'name': 'The Aquifier (E3M9) - Blue key',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 12,
'doom_type': 79,
'region': "The Aquifer (E3M9) Green"},
371534: {'name': 'The Aquifer (E3M9) - Green key',
'region': "The Aquifier (E3M9) Green"},
371534: {'name': 'The Aquifier (E3M9) - Green key',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 13,
'doom_type': 73,
'region': "The Aquifer (E3M9) Yellow"},
371535: {'name': 'The Aquifer (E3M9) - Yellow key',
'region': "The Aquifier (E3M9) Yellow"},
371535: {'name': 'The Aquifier (E3M9) - Yellow key',
'episode': 3,
'check_sanity': True,
'map': 9,
'index': 14,
'doom_type': 80,
'region': "The Aquifer (E3M9) Main"},
371536: {'name': 'The Aquifer (E3M9) - Ethereal Crossbow',
'region': "The Aquifier (E3M9) Main"},
371536: {'name': 'The Aquifier (E3M9) - Ethereal Crossbow',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 141,
'doom_type': 2001,
'region': "The Aquifer (E3M9) Main"},
371537: {'name': 'The Aquifer (E3M9) - Phoenix Rod',
'region': "The Aquifier (E3M9) Main"},
371537: {'name': 'The Aquifier (E3M9) - Phoenix Rod',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 142,
'doom_type': 2003,
'region': "The Aquifer (E3M9) Yellow"},
371538: {'name': 'The Aquifer (E3M9) - Dragon Claw',
'region': "The Aquifier (E3M9) Yellow"},
371538: {'name': 'The Aquifier (E3M9) - Dragon Claw',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 143,
'doom_type': 53,
'region': "The Aquifer (E3M9) Green"},
371539: {'name': 'The Aquifer (E3M9) - Hellstaff',
'region': "The Aquifier (E3M9) Green"},
371539: {'name': 'The Aquifier (E3M9) - Hellstaff',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 144,
'doom_type': 2004,
'region': "The Aquifer (E3M9) Green"},
371540: {'name': 'The Aquifer (E3M9) - Gauntlets of the Necromancer',
'region': "The Aquifier (E3M9) Green"},
371540: {'name': 'The Aquifier (E3M9) - Gauntlets of the Necromancer',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 145,
'doom_type': 2005,
'region': "The Aquifer (E3M9) Green"},
371541: {'name': 'The Aquifer (E3M9) - Ring of Invincibility',
'region': "The Aquifier (E3M9) Green"},
371541: {'name': 'The Aquifier (E3M9) - Ring of Invincibility',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 148,
'doom_type': 84,
'region': "The Aquifer (E3M9) Yellow"},
371542: {'name': 'The Aquifer (E3M9) - Mystic Urn',
'region': "The Aquifier (E3M9) Yellow"},
371542: {'name': 'The Aquifier (E3M9) - Mystic Urn',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 149,
'doom_type': 32,
'region': "The Aquifer (E3M9) Green"},
371543: {'name': 'The Aquifer (E3M9) - Silver Shield',
'region': "The Aquifier (E3M9) Green"},
371543: {'name': 'The Aquifier (E3M9) - Silver Shield',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 151,
'doom_type': 85,
'region': "The Aquifer (E3M9) Main"},
371544: {'name': 'The Aquifer (E3M9) - Tome of Power',
'region': "The Aquifier (E3M9) Main"},
371544: {'name': 'The Aquifier (E3M9) - Tome of Power',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 152,
'doom_type': 86,
'region': "The Aquifer (E3M9) Main"},
371545: {'name': 'The Aquifer (E3M9) - Bag of Holding',
'region': "The Aquifier (E3M9) Main"},
371545: {'name': 'The Aquifier (E3M9) - Bag of Holding',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 153,
'doom_type': 8,
'region': "The Aquifer (E3M9) Yellow"},
371546: {'name': 'The Aquifer (E3M9) - Morph Ovum',
'region': "The Aquifier (E3M9) Yellow"},
371546: {'name': 'The Aquifier (E3M9) - Morph Ovum',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 154,
'doom_type': 30,
'region': "The Aquifer (E3M9) Green"},
371547: {'name': 'The Aquifer (E3M9) - Map Scroll',
'region': "The Aquifier (E3M9) Green"},
371547: {'name': 'The Aquifier (E3M9) - Map Scroll',
'episode': 3,
'check_sanity': True,
'map': 9,
'index': 155,
'doom_type': 35,
'region': "The Aquifer (E3M9) Green"},
371548: {'name': 'The Aquifer (E3M9) - Chaos Device',
'region': "The Aquifier (E3M9) Green"},
371548: {'name': 'The Aquifier (E3M9) - Chaos Device',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 156,
'doom_type': 36,
'region': "The Aquifer (E3M9) Yellow"},
371549: {'name': 'The Aquifer (E3M9) - Enchanted Shield',
'region': "The Aquifier (E3M9) Yellow"},
371549: {'name': 'The Aquifier (E3M9) - Enchanted Shield',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 157,
'doom_type': 31,
'region': "The Aquifer (E3M9) Green"},
371550: {'name': 'The Aquifer (E3M9) - Tome of Power 2',
'region': "The Aquifier (E3M9) Green"},
371550: {'name': 'The Aquifier (E3M9) - Tome of Power 2',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 158,
'doom_type': 86,
'region': "The Aquifer (E3M9) Green"},
371551: {'name': 'The Aquifer (E3M9) - Torch',
'region': "The Aquifier (E3M9) Green"},
371551: {'name': 'The Aquifier (E3M9) - Torch',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 159,
'doom_type': 33,
'region': "The Aquifer (E3M9) Main"},
371552: {'name': 'The Aquifer (E3M9) - Shadowsphere',
'region': "The Aquifier (E3M9) Main"},
371552: {'name': 'The Aquifier (E3M9) - Shadowsphere',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 160,
'doom_type': 75,
'region': "The Aquifer (E3M9) Green"},
371553: {'name': 'The Aquifer (E3M9) - Silver Shield 2',
'region': "The Aquifier (E3M9) Green"},
371553: {'name': 'The Aquifier (E3M9) - Silver Shield 2',
'episode': 3,
'check_sanity': True,
'map': 9,
'index': 374,
'doom_type': 85,
'region': "The Aquifer (E3M9) Green"},
371554: {'name': 'The Aquifer (E3M9) - Firemace',
'region': "The Aquifier (E3M9) Green"},
371554: {'name': 'The Aquifier (E3M9) - Firemace',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 478,
'doom_type': 2002,
'region': "The Aquifer (E3M9) Green"},
371555: {'name': 'The Aquifer (E3M9) - Firemace 2',
'region': "The Aquifier (E3M9) Green"},
371555: {'name': 'The Aquifier (E3M9) - Firemace 2',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 526,
'doom_type': 2002,
'region': "The Aquifer (E3M9) Green"},
371556: {'name': 'The Aquifer (E3M9) - Firemace 3',
'region': "The Aquifier (E3M9) Green"},
371556: {'name': 'The Aquifier (E3M9) - Firemace 3',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': 527,
'doom_type': 2002,
'region': "The Aquifer (E3M9) Green"},
371557: {'name': 'The Aquifer (E3M9) - Firemace 4',
'region': "The Aquifier (E3M9) Green"},
371557: {'name': 'The Aquifier (E3M9) - Firemace 4',
'episode': 3,
'check_sanity': True,
'map': 9,
'index': 528,
'doom_type': 2002,
'region': "The Aquifer (E3M9) Yellow"},
371558: {'name': 'The Aquifer (E3M9) - Exit',
'region': "The Aquifier (E3M9) Yellow"},
371558: {'name': 'The Aquifier (E3M9) - Exit',
'episode': 3,
'check_sanity': False,
'map': 9,
'index': -1,
'doom_type': -1,
'region': "The Aquifer (E3M9) Blue"},
'region': "The Aquifier (E3M9) Blue"},
371559: {'name': 'Catafalque (E4M1) - Yellow key',
'episode': 4,
'check_sanity': False,
@@ -5963,7 +5963,7 @@ location_table: Dict[int, LocationDict] = {
'map': 3,
'index': 213,
'doom_type': 2005,
'region': "Quay (E5M3) Blue"},
'region': "Quay (E5M3) Main"},
371850: {'name': 'Quay (E5M3) - Dragon Claw',
'episode': 5,
'check_sanity': False,
@@ -6145,7 +6145,7 @@ location_table: Dict[int, LocationDict] = {
'map': 4,
'index': 3,
'doom_type': 79,
'region': "Courtyard (E5M4) Green"},
'region': "Courtyard (E5M4) Main"},
371876: {'name': 'Courtyard (E5M4) - Yellow key',
'episode': 5,
'check_sanity': False,
@@ -6159,7 +6159,7 @@ location_table: Dict[int, LocationDict] = {
'map': 4,
'index': 21,
'doom_type': 73,
'region': "Courtyard (E5M4) Yellow"},
'region': "Courtyard (E5M4) Kakis"},
371878: {'name': 'Courtyard (E5M4) - Gauntlets of the Necromancer',
'episode': 5,
'check_sanity': False,
@@ -6187,14 +6187,14 @@ location_table: Dict[int, LocationDict] = {
'map': 4,
'index': 87,
'doom_type': 2004,
'region': "Courtyard (E5M4) Yellow"},
'region': "Courtyard (E5M4) Kakis"},
371882: {'name': 'Courtyard (E5M4) - Phoenix Rod',
'episode': 5,
'check_sanity': False,
'map': 4,
'index': 88,
'doom_type': 2003,
'region': "Courtyard (E5M4) Green"},
'region': "Courtyard (E5M4) Main"},
371883: {'name': 'Courtyard (E5M4) - Morph Ovum',
'episode': 5,
'check_sanity': False,
@@ -6229,7 +6229,7 @@ location_table: Dict[int, LocationDict] = {
'map': 4,
'index': 104,
'doom_type': 84,
'region': "Courtyard (E5M4) Yellow"},
'region': "Courtyard (E5M4) Kakis"},
371888: {'name': 'Courtyard (E5M4) - Shadowsphere',
'episode': 5,
'check_sanity': False,
@@ -6250,14 +6250,14 @@ location_table: Dict[int, LocationDict] = {
'map': 4,
'index': 107,
'doom_type': 35,
'region': "Courtyard (E5M4) Yellow"},
'region': "Courtyard (E5M4) Kakis"},
371891: {'name': 'Courtyard (E5M4) - Chaos Device',
'episode': 5,
'check_sanity': False,
'map': 4,
'index': 108,
'doom_type': 36,
'region': "Courtyard (E5M4) Green"},
'region': "Courtyard (E5M4) Main"},
371892: {'name': 'Courtyard (E5M4) - Tome of Power',
'episode': 5,
'check_sanity': False,
@@ -6278,7 +6278,7 @@ location_table: Dict[int, LocationDict] = {
'map': 4,
'index': 111,
'doom_type': 86,
'region': "Courtyard (E5M4) Yellow"},
'region': "Courtyard (E5M4) Kakis"},
371895: {'name': 'Courtyard (E5M4) - Torch',
'episode': 5,
'check_sanity': False,
@@ -6299,7 +6299,7 @@ location_table: Dict[int, LocationDict] = {
'map': 4,
'index': 219,
'doom_type': 85,
'region': "Courtyard (E5M4) Yellow"},
'region': "Courtyard (E5M4) Kakis"},
371898: {'name': 'Courtyard (E5M4) - Bag of Holding 3',
'episode': 5,
'check_sanity': False,
@@ -7247,23 +7247,23 @@ location_name_groups: Dict[str, Set[str]] = {
'Courtyard (E5M4) - Torch',
'Courtyard (E5M4) - Yellow key',
},
"D'Sparil's Keep (E3M8)": {
"D'Sparil's Keep (E3M8) - Bag of Holding",
"D'Sparil's Keep (E3M8) - Chaos Device",
"D'Sparil's Keep (E3M8) - Dragon Claw",
"D'Sparil's Keep (E3M8) - Enchanted Shield",
"D'Sparil's Keep (E3M8) - Ethereal Crossbow",
"D'Sparil's Keep (E3M8) - Exit",
"D'Sparil's Keep (E3M8) - Gauntlets of the Necromancer",
"D'Sparil's Keep (E3M8) - Hellstaff",
"D'Sparil's Keep (E3M8) - Mystic Urn",
"D'Sparil's Keep (E3M8) - Phoenix Rod",
"D'Sparil's Keep (E3M8) - Ring of Invincibility",
"D'Sparil's Keep (E3M8) - Shadowsphere",
"D'Sparil's Keep (E3M8) - Silver Shield",
"D'Sparil's Keep (E3M8) - Tome of Power",
"D'Sparil's Keep (E3M8) - Tome of Power 2",
"D'Sparil's Keep (E3M8) - Tome of Power 3",
"D'Sparil'S Keep (E3M8)": {
"D'Sparil'S Keep (E3M8) - Bag of Holding",
"D'Sparil'S Keep (E3M8) - Chaos Device",
"D'Sparil'S Keep (E3M8) - Dragon Claw",
"D'Sparil'S Keep (E3M8) - Enchanted Shield",
"D'Sparil'S Keep (E3M8) - Ethereal Crossbow",
"D'Sparil'S Keep (E3M8) - Exit",
"D'Sparil'S Keep (E3M8) - Gauntlets of the Necromancer",
"D'Sparil'S Keep (E3M8) - Hellstaff",
"D'Sparil'S Keep (E3M8) - Mystic Urn",
"D'Sparil'S Keep (E3M8) - Phoenix Rod",
"D'Sparil'S Keep (E3M8) - Ring of Invincibility",
"D'Sparil'S Keep (E3M8) - Shadowsphere",
"D'Sparil'S Keep (E3M8) - Silver Shield",
"D'Sparil'S Keep (E3M8) - Tome of Power",
"D'Sparil'S Keep (E3M8) - Tome of Power 2",
"D'Sparil'S Keep (E3M8) - Tome of Power 3",
},
'Field of Judgement (E5M8)': {
'Field of Judgement (E5M8) - Bag of Holding',
@@ -7641,33 +7641,33 @@ location_name_groups: Dict[str, Set[str]] = {
"Skein of D'Sparil (E5M9) - Torch",
"Skein of D'Sparil (E5M9) - Yellow key",
},
'The Aquifer (E3M9)': {
'The Aquifer (E3M9) - Bag of Holding',
'The Aquifer (E3M9) - Blue key',
'The Aquifer (E3M9) - Chaos Device',
'The Aquifer (E3M9) - Dragon Claw',
'The Aquifer (E3M9) - Enchanted Shield',
'The Aquifer (E3M9) - Ethereal Crossbow',
'The Aquifer (E3M9) - Exit',
'The Aquifer (E3M9) - Firemace',
'The Aquifer (E3M9) - Firemace 2',
'The Aquifer (E3M9) - Firemace 3',
'The Aquifer (E3M9) - Firemace 4',
'The Aquifer (E3M9) - Gauntlets of the Necromancer',
'The Aquifer (E3M9) - Green key',
'The Aquifer (E3M9) - Hellstaff',
'The Aquifer (E3M9) - Map Scroll',
'The Aquifer (E3M9) - Morph Ovum',
'The Aquifer (E3M9) - Mystic Urn',
'The Aquifer (E3M9) - Phoenix Rod',
'The Aquifer (E3M9) - Ring of Invincibility',
'The Aquifer (E3M9) - Shadowsphere',
'The Aquifer (E3M9) - Silver Shield',
'The Aquifer (E3M9) - Silver Shield 2',
'The Aquifer (E3M9) - Tome of Power',
'The Aquifer (E3M9) - Tome of Power 2',
'The Aquifer (E3M9) - Torch',
'The Aquifer (E3M9) - Yellow key',
'The Aquifier (E3M9)': {
'The Aquifier (E3M9) - Bag of Holding',
'The Aquifier (E3M9) - Blue key',
'The Aquifier (E3M9) - Chaos Device',
'The Aquifier (E3M9) - Dragon Claw',
'The Aquifier (E3M9) - Enchanted Shield',
'The Aquifier (E3M9) - Ethereal Crossbow',
'The Aquifier (E3M9) - Exit',
'The Aquifier (E3M9) - Firemace',
'The Aquifier (E3M9) - Firemace 2',
'The Aquifier (E3M9) - Firemace 3',
'The Aquifier (E3M9) - Firemace 4',
'The Aquifier (E3M9) - Gauntlets of the Necromancer',
'The Aquifier (E3M9) - Green key',
'The Aquifier (E3M9) - Hellstaff',
'The Aquifier (E3M9) - Map Scroll',
'The Aquifier (E3M9) - Morph Ovum',
'The Aquifier (E3M9) - Mystic Urn',
'The Aquifier (E3M9) - Phoenix Rod',
'The Aquifier (E3M9) - Ring of Invincibility',
'The Aquifier (E3M9) - Shadowsphere',
'The Aquifier (E3M9) - Silver Shield',
'The Aquifier (E3M9) - Silver Shield 2',
'The Aquifier (E3M9) - Tome of Power',
'The Aquifier (E3M9) - Tome of Power 2',
'The Aquifier (E3M9) - Torch',
'The Aquifier (E3M9) - Yellow key',
},
'The Azure Fortress (E3M4)': {
'The Azure Fortress (E3M4) - Bag of Holding',

View File

@@ -29,8 +29,8 @@ map_names: List[str] = [
'The Ophidian Lair (E3M5)',
'The Halls of Fear (E3M6)',
'The Chasm (E3M7)',
"D'Sparil's Keep (E3M8)",
'The Aquifer (E3M9)',
"D'Sparil'S Keep (E3M8)",
'The Aquifier (E3M9)',
'Catafalque (E4M1)',
'Blockhouse (E4M2)',
'Ambulatory (E4M3)',

View File

@@ -520,34 +520,34 @@ regions:List[RegionDict] = [
"episode":3,
"connections":[{"target":"The Chasm (E3M7) Yellow","pro":False}]},
# D'Sparil's Keep (E3M8)
{"name":"D'Sparil's Keep (E3M8) Main",
# D'Sparil'S Keep (E3M8)
{"name":"D'Sparil'S Keep (E3M8) Main",
"connects_to_hub":True,
"episode":3,
"connections":[]},
# The Aquifer (E3M9)
{"name":"The Aquifer (E3M9) Main",
# The Aquifier (E3M9)
{"name":"The Aquifier (E3M9) Main",
"connects_to_hub":True,
"episode":3,
"connections":[{"target":"The Aquifer (E3M9) Yellow","pro":False}]},
{"name":"The Aquifer (E3M9) Blue",
"connections":[{"target":"The Aquifier (E3M9) Yellow","pro":False}]},
{"name":"The Aquifier (E3M9) Blue",
"connects_to_hub":False,
"episode":3,
"connections":[]},
{"name":"The Aquifer (E3M9) Yellow",
{"name":"The Aquifier (E3M9) Yellow",
"connects_to_hub":False,
"episode":3,
"connections":[
{"target":"The Aquifer (E3M9) Green","pro":False},
{"target":"The Aquifer (E3M9) Main","pro":False}]},
{"name":"The Aquifer (E3M9) Green",
{"target":"The Aquifier (E3M9) Green","pro":False},
{"target":"The Aquifier (E3M9) Main","pro":False}]},
{"name":"The Aquifier (E3M9) Green",
"connects_to_hub":False,
"episode":3,
"connections":[
{"target":"The Aquifer (E3M9) Yellow","pro":False},
{"target":"The Aquifer (E3M9) Main","pro":False},
{"target":"The Aquifer (E3M9) Blue","pro":False}]},
{"target":"The Aquifier (E3M9) Yellow","pro":False},
{"target":"The Aquifier (E3M9) Main","pro":False},
{"target":"The Aquifier (E3M9) Blue","pro":False}]},
# Catafalque (E4M1)
{"name":"Catafalque (E4M1) Main",
@@ -795,22 +795,16 @@ regions:List[RegionDict] = [
"connects_to_hub":True,
"episode":5,
"connections":[
{"target":"Courtyard (E5M4) Yellow","pro":False},
{"target":"Courtyard (E5M4) Kakis","pro":False},
{"target":"Courtyard (E5M4) Blue","pro":False}]},
{"name":"Courtyard (E5M4) Blue",
"connects_to_hub":False,
"episode":5,
"connections":[{"target":"Courtyard (E5M4) Main","pro":False}]},
{"name":"Courtyard (E5M4) Yellow",
{"name":"Courtyard (E5M4) Kakis",
"connects_to_hub":False,
"episode":5,
"connections":[
{"target":"Courtyard (E5M4) Main","pro":False},
{"target":"Courtyard (E5M4) Green","pro":False}]},
{"name":"Courtyard (E5M4) Green",
"connects_to_hub":False,
"episode":5,
"connections":[{"target":"Courtyard (E5M4) Yellow","pro":False}]},
"connections":[{"target":"Courtyard (E5M4) Main","pro":False}]},
# Hydratyr (E5M5)
{"name":"Hydratyr (E5M5) Main",

View File

@@ -388,9 +388,9 @@ def set_episode3_rules(player, multiworld, pro):
set_rule(multiworld.get_entrance("The Chasm (E3M7) Green -> The Chasm (E3M7) Yellow", player), lambda state:
state.has("The Chasm (E3M7) - Green key", player, 1))
# D'Sparil's Keep (E3M8)
set_rule(multiworld.get_entrance("Hub -> D'Sparil's Keep (E3M8) Main", player), lambda state:
state.has("D'Sparil's Keep (E3M8)", player, 1) and
# D'Sparil'S Keep (E3M8)
set_rule(multiworld.get_entrance("Hub -> D'Sparil'S Keep (E3M8) Main", player), lambda state:
state.has("D'Sparil'S Keep (E3M8)", player, 1) and
state.has("Gauntlets of the Necromancer", player, 1) and
state.has("Ethereal Crossbow", player, 1) and
state.has("Dragon Claw", player, 1) and
@@ -398,23 +398,23 @@ def set_episode3_rules(player, multiworld, pro):
state.has("Firemace", player, 1) and
state.has("Hellstaff", player, 1))
# The Aquifer (E3M9)
set_rule(multiworld.get_entrance("Hub -> The Aquifer (E3M9) Main", player), lambda state:
state.has("The Aquifer (E3M9)", player, 1) and
# The Aquifier (E3M9)
set_rule(multiworld.get_entrance("Hub -> The Aquifier (E3M9) Main", player), lambda state:
state.has("The Aquifier (E3M9)", player, 1) and
state.has("Gauntlets of the Necromancer", player, 1) and
state.has("Ethereal Crossbow", player, 1) and
state.has("Dragon Claw", player, 1) and
state.has("Phoenix Rod", player, 1) and
state.has("Firemace", player, 1) and
state.has("Hellstaff", player, 1))
set_rule(multiworld.get_entrance("The Aquifer (E3M9) Main -> The Aquifer (E3M9) Yellow", player), lambda state:
state.has("The Aquifer (E3M9) - Yellow key", player, 1))
set_rule(multiworld.get_entrance("The Aquifer (E3M9) Yellow -> The Aquifer (E3M9) Green", player), lambda state:
state.has("The Aquifer (E3M9) - Green key", player, 1))
set_rule(multiworld.get_entrance("The Aquifer (E3M9) Yellow -> The Aquifer (E3M9) Main", player), lambda state:
state.has("The Aquifer (E3M9) - Yellow key", player, 1))
set_rule(multiworld.get_entrance("The Aquifer (E3M9) Green -> The Aquifer (E3M9) Yellow", player), lambda state:
state.has("The Aquifer (E3M9) - Green key", player, 1))
set_rule(multiworld.get_entrance("The Aquifier (E3M9) Main -> The Aquifier (E3M9) Yellow", player), lambda state:
state.has("The Aquifier (E3M9) - Yellow key", player, 1))
set_rule(multiworld.get_entrance("The Aquifier (E3M9) Yellow -> The Aquifier (E3M9) Green", player), lambda state:
state.has("The Aquifier (E3M9) - Green key", player, 1))
set_rule(multiworld.get_entrance("The Aquifier (E3M9) Yellow -> The Aquifier (E3M9) Main", player), lambda state:
state.has("The Aquifier (E3M9) - Yellow key", player, 1))
set_rule(multiworld.get_entrance("The Aquifier (E3M9) Green -> The Aquifier (E3M9) Yellow", player), lambda state:
state.has("The Aquifier (E3M9) - Green key", player, 1))
def set_episode4_rules(player, multiworld, pro):
@@ -623,17 +623,15 @@ def set_episode5_rules(player, multiworld, pro):
(state.has("Phoenix Rod", player, 1) or
state.has("Firemace", player, 1) or
state.has("Hellstaff", player, 1)))
set_rule(multiworld.get_entrance("Courtyard (E5M4) Main -> Courtyard (E5M4) Yellow", player), lambda state:
state.has("Courtyard (E5M4) - Yellow key", player, 1))
set_rule(multiworld.get_entrance("Courtyard (E5M4) Main -> Courtyard (E5M4) Kakis", player), lambda state:
state.has("Courtyard (E5M4) - Yellow key", player, 1) or
state.has("Courtyard (E5M4) - Green key", player, 1))
set_rule(multiworld.get_entrance("Courtyard (E5M4) Main -> Courtyard (E5M4) Blue", player), lambda state:
state.has("Courtyard (E5M4) - Blue key", player, 1))
set_rule(multiworld.get_entrance("Courtyard (E5M4) Blue -> Courtyard (E5M4) Main", player), lambda state:
state.has("Courtyard (E5M4) - Blue key", player, 1))
set_rule(multiworld.get_entrance("Courtyard (E5M4) Yellow -> Courtyard (E5M4) Main", player), lambda state:
state.has("Courtyard (E5M4) - Yellow key", player, 1))
set_rule(multiworld.get_entrance("Courtyard (E5M4) Yellow -> Courtyard (E5M4) Green", player), lambda state:
state.has("Courtyard (E5M4) - Green key", player, 1))
set_rule(multiworld.get_entrance("Courtyard (E5M4) Green -> Courtyard (E5M4) Yellow", player), lambda state:
set_rule(multiworld.get_entrance("Courtyard (E5M4) Kakis -> Courtyard (E5M4) Main", player), lambda state:
state.has("Courtyard (E5M4) - Yellow key", player, 1) or
state.has("Courtyard (E5M4) - Green key", player, 1))
# Hydratyr (E5M5)

View File

@@ -49,18 +49,18 @@ class HereticWorld(World):
location_name_to_id = {data["name"]: loc_id for loc_id, data in Locations.location_table.items()}
location_name_groups = Locations.location_name_groups
starting_level_for_episode: Dict[int, str] = {
1: "The Docks (E1M1)",
2: "The Crater (E2M1)",
3: "The Storehouse (E3M1)",
4: "Catafalque (E4M1)",
5: "Ochre Cliffs (E5M1)"
}
starting_level_for_episode: List[str] = [
"The Docks (E1M1)",
"The Crater (E2M1)",
"The Storehouse (E3M1)",
"Catafalque (E4M1)",
"Ochre Cliffs (E5M1)"
]
all_boss_levels: List[str] = [
boss_level_for_episode: List[str] = [
"Hell's Maw (E1M8)",
"The Portals of Chaos (E2M8)",
"D'Sparil's Keep (E3M8)",
"D'Sparil'S Keep (E3M8)",
"Shattered Bridge (E4M8)",
"Field of Judgement (E5M8)"
]
@@ -82,7 +82,6 @@ class HereticWorld(World):
def __init__(self, multiworld: MultiWorld, player: int):
self.included_episodes = [1, 1, 1, 0, 0]
self.location_count = 0
self.starting_levels = []
super().__init__(multiworld, player)
@@ -101,14 +100,6 @@ class HereticWorld(World):
if self.get_episode_count() == 0:
self.included_episodes[0] = 1
self.starting_levels = [level_name for (episode, level_name) in self.starting_level_for_episode.items()
if self.included_episodes[episode - 1]]
# For Solo Episode 1, place the Yellow Key for E1M1 early.
# Gives the generator five potential placements (plus the forced key) instead of only two.
if self.get_episode_count() == 1 and self.included_episodes[0]:
self.multiworld.early_items[self.player]["The Docks (E1M1) - Yellow key"] = 1
def create_regions(self):
pro = self.options.pro.value
check_sanity = self.options.check_sanity.value
@@ -163,7 +154,7 @@ class HereticWorld(World):
def completion_rule(self, state: CollectionState):
goal_levels = Maps.map_names
if self.options.goal.value:
goal_levels = self.all_boss_levels
goal_levels = self.boss_level_for_episode
for map_name in goal_levels:
if map_name + " - Exit" not in self.location_name_to_id:
@@ -212,7 +203,7 @@ class HereticWorld(World):
if item["episode"] != -1 and not self.included_episodes[item["episode"] - 1]:
continue
count = item["count"] if item["name"] not in self.starting_levels else item["count"] - 1
count = item["count"] if item["name"] not in self.starting_level_for_episode else item["count"] - 1
itempool += [self.create_item(item["name"]) for _ in range(count)]
# Bag(s) of Holding based on options
@@ -245,8 +236,9 @@ class HereticWorld(World):
self.location_count -= 1
# Give starting levels right away
for map_name in self.starting_levels:
self.multiworld.push_precollected(self.create_item(map_name))
for i in range(len(self.included_episodes)):
if self.included_episodes[i]:
self.multiworld.push_precollected(self.create_item(self.starting_level_for_episode[i]))
# Give Computer area maps if option selected
if self.options.start_with_map_scrolls.value:

View File

@@ -1 +1 @@
Pymem>=1.10.0
Pymem>=1.10.0

View File

@@ -310,8 +310,7 @@ class LinksAwakeningWorld(World):
def opens_new_regions(item):
collection_state = base_collection_state.copy()
collection_state.collect(item, prevent_sweep=True)
collection_state.sweep_for_advancements(self.get_locations())
collection_state.collect(item)
return len(collection_state.reachable_regions[self.player]) > reachable_count
start_items = [item for item in itempool if is_possible_start_item(item)]
@@ -330,7 +329,7 @@ class LinksAwakeningWorld(World):
if entrance_mapping['start_house'] not in ['start_house', 'shop']:
start_items = [item for item in start_items if item.name != 'Shovel']
base_collection_state = CollectionState(self.multiworld)
base_collection_state.sweep_for_advancements(self.get_locations())
base_collection_state.update_reachable_regions(self.player)
reachable_count = len(base_collection_state.reachable_regions[self.player])
start_item = next((item for item in start_items if opens_new_regions(item)), None)

View File

@@ -100,8 +100,6 @@
# paintings is an array of paintings in the room. This is used for painting
# shuffling.
# - id: The internal painting ID from the LINGO map.
# - display_name: The name of the painting location when showed in the
# tracker. Not needed for disabled paintings.
# - enter_only: If true, painting shuffling will not place a warp exit on
# this painting.
# - exit_only: If true, painting shuffling will not place a warp entrance
@@ -228,7 +226,6 @@
- HIDDEN
paintings:
- id: arrows_painting
display_name: Overhead Painting
exit_only: True
orientation: south
- id: arrows_painting2
@@ -237,24 +234,7 @@
- id: arrows_painting3
disable: True
move: True
- id: symmetry_painting_a_starter
display_name: Left Near Painting
enter_only: True
orientation: west
move: True
required_door:
room: The Wondrous (Doorknob)
door: Painting Shortcut
- id: eyes_yellow_painting2
display_name: Left Far Painting
enter_only: True
orientation: west
move: True
required_door:
room: Outside The Agreeable
door: Painting Shortcut
- id: garden_painting_tower2
display_name: Front Left Painting
enter_only: True
orientation: north
move: True
@@ -262,15 +242,20 @@
room: Hedge Maze
door: Painting Shortcut
- id: flower_painting_8
display_name: Front Right Painting
enter_only: True
orientation: north
move: True
required_door:
room: Courtyard
door: Painting Shortcut
- id: symmetry_painting_a_starter
enter_only: True
orientation: west
move: True
required_door:
room: The Wondrous (Doorknob)
door: Painting Shortcut
- id: pencil_painting6
display_name: Right Far Painting
enter_only: True
orientation: east
move: True
@@ -278,13 +263,19 @@
room: Outside The Bold
door: Painting Shortcut
- id: blueman_painting_3
display_name: Right Near Painting
enter_only: True
orientation: east
move: True
required_door:
room: Outside The Undeterred
door: Painting Shortcut
- id: eyes_yellow_painting2
enter_only: True
orientation: west
move: True
required_door:
room: Outside The Agreeable
door: Painting Shortcut
Hidden Room:
entrances:
Starting Room:
@@ -349,7 +340,6 @@
- OPEN
paintings:
- id: owl_painting
display_name: Painting
orientation: north
The Seeker:
entrances:
@@ -609,7 +599,6 @@
- OPEN
paintings:
- id: maze_painting
display_name: Near Traveled Painting
orientation: west
sunwarps:
- dots: 1
@@ -641,7 +630,6 @@
door: Eights
paintings:
- id: smile_painting_6
display_name: Painting
orientation: north
Sunwarps:
# This is a special, meta-ish room.
@@ -980,7 +968,6 @@
required_door:
door: Eye Wall
- id: smile_painting_4
display_name: Near Discerning Painting
orientation: south
sunwarps:
- dots: 1
@@ -1081,7 +1068,6 @@
tag: midwhite
paintings:
- id: west_afar
display_name: Painting
orientation: south
The Tenacious:
entrances:
@@ -1406,7 +1392,6 @@
- RIGHT
paintings:
- id: eyes_yellow_painting
display_name: Near Hallway Painting
orientation: east
sunwarps:
- dots: 6
@@ -1466,7 +1451,6 @@
- FIRE
paintings:
- id: pencil_painting7
display_name: Compass Room Painting
orientation: north
Dread Hallway:
entrances:
@@ -1714,7 +1698,6 @@
- GAZE
paintings:
- id: garden_painting_tower
display_name: Painting
orientation: north
The Fearless (First Floor):
entrances:
@@ -2094,7 +2077,6 @@
panel: A
paintings:
- id: crown_painting
display_name: Near Achievement Painting
orientation: east
Eight Alcove:
entrances:
@@ -2106,7 +2088,6 @@
door: Eight Door (Outside The Initiated)
paintings:
- id: eight_painting2
display_name: Eight Alcove Painting
orientation: north
Eight Room:
entrances:
@@ -2127,7 +2108,6 @@
tag: forbid
paintings:
- id: eight_painting
display_name: Eight Room Painting
orientation: south
exit_only: True
required: True
@@ -2360,10 +2340,8 @@
panel: YELLOW
paintings:
- id: arrows_painting_6
display_name: Left Painting
orientation: east
- id: flower_painting_5
display_name: Right Painting
orientation: south
sunwarps:
- dots: 2
@@ -2452,7 +2430,6 @@
door: Eights
paintings:
- id: smile_painting_8
display_name: Hot Crusts Painting
orientation: north
sunwarps:
- dots: 2
@@ -2554,13 +2531,10 @@
- SIZE (Big)
paintings:
- id: hi_solved_painting3
display_name: Cellar Replica Painting
orientation: south
- id: hi_solved_painting2
display_name: Cellar Painting
orientation: south
- id: east_afar
display_name: Seasons Area Painting
orientation: north
Orange Tower Sixth Floor:
entrances:
@@ -2572,35 +2546,25 @@
painting: True
paintings:
- id: arrows_painting_10
display_name: Back Left Painting
orientation: east
- id: scenery_painting_5d_2
display_name: Left Near Painting
orientation: south
- id: panda_painting_2
display_name: Left Middle Painting
orientation: south
- id: colors_painting2
display_name: Left Far Painting
orientation: south
- id: clock_painting
display_name: Front Left Painting
orientation: west
- id: hi_solved_painting
display_name: Front Right Painting
orientation: west
- id: crown_painting2
display_name: Right Far Painting
orientation: north
- id: owl_painting_3
display_name: Right Middle Painting
orientation: north
- id: clock_painting
orientation: west
- id: scenery_painting_5d_2
orientation: south
- id: symmetry_painting_b_7
display_name: Right Near Painting
orientation: north
- id: panda_painting_2
orientation: south
- id: crown_painting2
orientation: north
- id: colors_painting2
orientation: south
- id: cherry_painting2
display_name: Back Right Painting
orientation: east
- id: hi_solved_painting
orientation: west
Ending Area:
entrances:
Orange Tower Sixth Floor:
@@ -2696,7 +2660,6 @@
panel: MASTERY
paintings:
- id: map_painting2
display_name: Painting
orientation: north
enter_only: True # otherwise you might just skip the whole game!
req_blocked_when_no_doors: True # owl hallway in vanilla doors
@@ -2792,7 +2755,6 @@
non_counting: True
paintings:
- id: arrows_painting_11
display_name: Painting
orientation: east
req_blocked_when_no_doors: True # owl hallway in vanilla doors
Courtyard:
@@ -2855,7 +2817,6 @@
panel: GREEN
paintings:
- id: flower_painting_7
display_name: Courtyard Painting
orientation: north
Yellow Backside Area:
entrances:
@@ -2877,7 +2838,6 @@
door: Nines
paintings:
- id: blueman_painting
display_name: Near Nine Painting
orientation: east
First Second Third Fourth:
# We are separating this door + its panels into its own room because they
@@ -3213,7 +3173,6 @@
achievement: The Colorful
paintings:
- id: arrows_painting_12
display_name: Painting
orientation: north
progression:
Progressive Colorful:
@@ -3337,17 +3296,13 @@
- STRAYS
paintings:
- id: arrows_painting_8
display_name: Near Maze Painting
orientation: south
- id: maze_painting_2
display_name: Maze Side Middle Painting
orientation: north
- id: owl_painting_2
display_name: Orange Side Middle Painting
orientation: south
required_when_no_doors: True
- id: clock_painting_4
display_name: Near Orange Painting
orientation: north
Outside The Initiated:
entrances:
@@ -3535,10 +3490,8 @@
- OXEN
paintings:
- id: clock_painting_5
display_name: Brown Puzzles Painting
orientation: east
- id: smile_painting_1
display_name: Near Eight Painting
orientation: north
sunwarps:
- dots: 3
@@ -3913,10 +3866,8 @@
- BEGIN
paintings:
- id: pencil_painting2
display_name: Near Bold Painting
orientation: west
- id: north_missing2
display_name: Directions Area Painting
orientation: north
The Bold:
entrances:
@@ -4238,14 +4189,12 @@
panel: FOUR
paintings:
- id: maze_painting_3
display_name: Near Four Painting
enter_only: True
orientation: north
move: True
required_door:
door: Green Painting
- id: blueman_painting_2
display_name: Near Undeterred Painting
orientation: east
sunwarps:
- dots: 4
@@ -4608,7 +4557,6 @@
panel: NINE
paintings:
- id: smile_painting_5
display_name: Near Eight Painting
enter_only: True
orientation: east
required_door:
@@ -4794,13 +4742,10 @@
- LEARN
paintings:
- id: smile_painting_7
display_name: Near Turn/Return Painting
orientation: south
- id: flower_painting_4
display_name: Back Area Right Painting
orientation: south
- id: pencil_painting3
display_name: Back Area Left Painting
enter_only: True
orientation: east
move: True
@@ -4808,10 +4753,8 @@
room: Number Hunt
door: First Six
- id: boxes_painting
display_name: Near Directions Painting
orientation: south
- id: cherry_painting
display_name: Alcove Painting
orientation: east
sunwarps:
- dots: 6
@@ -4905,10 +4848,8 @@
- GREEN
paintings:
- id: arrows_painting_7
display_name: Near Sunwarp Painting
orientation: east
- id: fruitbowl_painting3
display_name: Hidden Painting
orientation: west
enter_only: True
required_door:
@@ -4947,7 +4888,6 @@
tag: forbid
paintings:
- id: colors_painting
display_name: Painting
orientation: south
The Bearer:
entrances:
@@ -5429,7 +5369,6 @@
panel: ANTECHAMBER
paintings:
- id: pencil_painting5
display_name: Left Painting
orientation: south
The Steady (Lemon):
entrances:
@@ -5452,7 +5391,6 @@
- MELON
paintings:
- id: pencil_painting4
display_name: Right Painting
orientation: south
The Steady (Topaz):
entrances:
@@ -6074,7 +6012,6 @@
panel: NIGHT
paintings:
- id: smile_painting_9
display_name: Smiley Painting
orientation: north
exit_only: True
The Artistic (Panda):
@@ -6187,7 +6124,6 @@
panel: BOWELS
paintings:
- id: panda_painting_3
display_name: Panda Painting
exit_only: True
orientation: south
required_when_no_doors: True
@@ -6299,7 +6235,6 @@
panel: THING
paintings:
- id: boxes_painting2
display_name: Lattice Painting
orientation: south
exit_only: True
required_when_no_doors: True
@@ -6409,7 +6344,6 @@
panel: ROOT
paintings:
- id: cherry_painting3
display_name: Apple Painting
orientation: north
exit_only: True
required_when_no_doors: True
@@ -6556,10 +6490,8 @@
- NEAR
paintings:
- id: eye_painting_2
display_name: Near Pillar Painting
orientation: west
- id: smile_painting_2
display_name: Near Window Painting
orientation: north
Far Window:
entrances:
@@ -6580,7 +6512,6 @@
door: Exit
paintings:
- id: arrows_painting_5
display_name: Lobby Painting
orientation: east
Outside The Wondrous:
entrances:
@@ -6631,11 +6562,9 @@
panel: SHRINK
paintings:
- id: symmetry_painting_a_1
display_name: Doorknob Upper Painting
orientation: east
exit_only: True
- id: symmetry_painting_b_1
display_name: Doorknob Lower Painting
orientation: south
The Wondrous (Bookcase):
entrances:
@@ -6647,7 +6576,6 @@
tag: midblue
paintings:
- id: symmetry_painting_a_3
display_name: Bookcase Painting
orientation: west
exit_only: True
- id: symmetry_painting_b_3
@@ -6662,7 +6590,6 @@
tag: midyellow
paintings:
- id: symmetry_painting_a_5
display_name: Chandelier Painting
orientation: east
- id: symmetry_painting_b_5
disable: True
@@ -6676,7 +6603,6 @@
tag: botbrown
paintings:
- id: symmetry_painting_b_4
display_name: Window Painting
orientation: north
exit_only: True
- id: symmetry_painting_a_4
@@ -6701,10 +6627,8 @@
tag: midyellow
paintings:
- id: symmetry_painting_a_2
display_name: Table Lower Painting
orientation: west
- id: symmetry_painting_b_2
display_name: Table Upper Painting
orientation: south
exit_only: True
required: True
@@ -6745,7 +6669,6 @@
- Achievement
paintings:
- id: arrows_painting_9
display_name: Exit Painting
enter_only: True
orientation: south
move: True
@@ -6753,11 +6676,9 @@
door: Exit
req_blocked_when_no_doors: True # the wondrous (table) in vanilla doors
- id: symmetry_painting_a_6
display_name: Fireplace Upper Painting
orientation: west
exit_only: True
- id: symmetry_painting_b_6
display_name: Fireplace Lower Painting
orientation: north
req_blocked_when_no_doors: True # the wondrous (table) in vanilla doors
Arrow Garden:
@@ -6779,7 +6700,6 @@
tag: midwhite
paintings:
- id: flower_painting_6
display_name: Painting
orientation: south
Hallway Room (1):
entrances:
@@ -6838,7 +6758,6 @@
- TOWER
paintings:
- id: panda_painting
display_name: Painting
orientation: south
progression:
Progressive Hallway Room:
@@ -7026,7 +6945,6 @@
tag: midwhite
paintings:
- id: south_afar
display_name: Painting
orientation: south
Outside The Wanderer:
entrances:
@@ -7205,21 +7123,16 @@
panels:
- ORDER
paintings:
- id: smile_painting_3
orientation: west
- id: flower_painting_2
display_name: Left Near Painting
orientation: east
- id: scenery_painting_0a
orientation: north
- id: map_painting
display_name: Left Far Painting
orientation: east
- id: fruitbowl_painting4
display_name: Center Front Painting
orientation: south
- id: scenery_painting_0a
display_name: Center Back Painting
orientation: north
- id: smile_painting_3
display_name: Right Far Painting
orientation: west
progression:
Progressive Art Gallery:
doors:
@@ -7580,7 +7493,6 @@
panel: WORD
paintings:
- id: arrows_painting_3
display_name: Circle Painting
orientation: north
Rhyme Room (Looped Square):
entrances:
@@ -7763,7 +7675,6 @@
- INNOVATIVE (Bottom)
paintings:
- id: arrows_painting_4
display_name: Target Painting
orientation: north
Room Room:
# This is a bit of a weird room. You can't really get to it from the roof.
@@ -8033,10 +7944,8 @@
- CAT
paintings:
- id: arrows_painting_2
display_name: Left Painting
orientation: east
- id: clock_painting_2
display_name: Right Painting
orientation: east
exit_only: True
required: True
@@ -8113,7 +8022,6 @@
tag: midbrown
paintings:
- id: clock_painting_3
display_name: Painting
orientation: east
req_blocked: True # outside the wise (with or without door shuffle)
The Red:
@@ -8584,7 +8492,6 @@
- OPTICS
paintings:
- id: hi_solved_painting4
display_name: Painting
orientation: south
req_blocked_when_no_doors: True # owl hallway in vanilla doors
Challenge Room:

Binary file not shown.

View File

@@ -50,7 +50,7 @@ directives = Set["entrances", "panels", "doors", "panel_doors", "paintings", "su
panel_directives = Set["id", "required_room", "required_door", "required_panel", "colors", "check", "exclude_reduce", "tag", "link", "subtag", "achievement", "copy_to_sign", "non_counting", "hunt", "location_name"]
door_directives = Set["id", "painting_id", "panels", "item_name", "item_group", "location_name", "skip_location", "skip_item", "door_group", "include_reduce", "event", "warp_id"]
panel_door_directives = Set["panels", "item_name", "panel_group"]
painting_directives = Set["id", "display_name", "enter_only", "exit_only", "orientation", "required_door", "required", "required_when_no_doors", "move", "req_blocked", "req_blocked_when_no_doors"]
painting_directives = Set["id", "enter_only", "exit_only", "orientation", "required_door", "required", "required_when_no_doors", "move", "req_blocked", "req_blocked_when_no_doors"]
non_counting = 0
@@ -314,10 +314,6 @@ config.each do |room_name, room|
next
end
unless painting.include? "display_name" then
puts "#{room_name} - #{painting["id"] || "painting"} :::: Missing display name"
end
if painting.include?("orientation") then
unless ["north", "south", "east", "west"].include? painting["orientation"] then
puts "#{room_name} - #{painting["id"] || "painting"} :::: Invalid orientation #{painting["orientation"]}"

View File

@@ -1 +1 @@
requests >= 2.28.1 # used by client
requests >= 2.28.1 # used by client

Binary file not shown.

View File

@@ -52,13 +52,11 @@ class MuseDashCollections:
"Nyaa SFX Trap": STARTING_CODE + 8,
"Error SFX Trap": STARTING_CODE + 9,
"Focus Line Trap": STARTING_CODE + 10,
"Beefcake SFX Trap": STARTING_CODE + 11,
}
sfx_trap_items: List[str] = [
"Nyaa SFX Trap",
"Error SFX Trap",
"Beefcake SFX Trap",
]
filler_items: Dict[str, int] = {

View File

@@ -627,10 +627,4 @@ SONG_DATA: Dict[str, SongData] = {
"Sharp Bubbles": SongData(2900751, "83-3", "Cosmic Radio 2024", True, 7, 9, 11),
"Replay": SongData(2900752, "83-4", "Cosmic Radio 2024", True, 5, 7, 9),
"Cosmic Dusty Girl": SongData(2900753, "83-5", "Cosmic Radio 2024", True, 5, 7, 9),
"Meow Rock feat. Chun Ge, Yuan Shen": SongData(2900754, "84-0", "Muse Dash Legend", True, None, None, None),
"Even if you make an old radio song with AI": SongData(2900755, "84-1", "Muse Dash Legend", False, 3, 6, 8),
"Unusual Sketchbook": SongData(2900756, "84-2", "Muse Dash Legend", True, 6, 8, 11),
"TransientTears": SongData(2900757, "84-3", "Muse Dash Legend", True, 6, 8, 11),
"SHOOTING*STAR": SongData(2900758, "84-4", "Muse Dash Legend", False, 5, 7, 9),
"But the Blue Bird is Already Dead": SongData(2900759, "84-5", "Muse Dash Legend", False, 6, 8, 10),
}

View File

@@ -20,7 +20,7 @@
2. Choose the automated tab, click the select button and browse to `MuseDash.exe`.
- You can find the folder in steam by finding the game in your library, right clicking it and choosing *Manage→Browse Local Files*.
- If you click the bar at the top telling you your current folder, this will give you a path you can copy. If you paste that into the window popped up by **MelonLoader**, it will automatically go to the same folder.
3. Select v0.7.0. Then click install.
3. Uncheck "Latest" and select v0.6.1. Then click install.
4. Run the game once, and wait until you get to the Muse Dash start screen before exiting.
5. Download the latest [Muse Dash Archipelago Mod](https://github.com/DeamonHunter/ArchipelagoMuseDash/releases/latest) and then extract that into the newly created `/Mods/` folder in MuseDash's install location.
- All files must be under the `/Mods/` folder and not within a sub folder inside of `/Mods/`

View File

@@ -20,7 +20,7 @@
2. Elije la pestaña "automated", haz clic en el botón "select" y busca tu `MuseDash.exe`.
- Puedes encontrar la carpeta en Steam buscando el juego en tu biblioteca, haciendo clic derecho sobre el y elegir *Administrar→Ver archivos locales*.
- Si haces clic en la barra superior que te indica la carpeta en la que estas, te dará la dirección de ésta para que puedas copiarla. Al pegar esa dirección en la ventana que **MelonLoader** abre, irá automaticamente a esa carpeta.
3. Selecciona v0.7.0. Luego haz clic en "install".
3. Desmarca "Latest" y selecciona v0.6.1. Luego haz clic en "install".
4. Ejecuta el juego una vez, y espera hasta que aparezca la pantalla de inicio de Muse Dash antes de cerrarlo.
5. Descarga la última version de [Muse Dash Archipelago Mod](https://github.com/DeamonHunter/ArchipelagoMuseDash/releases/latest) y extraelo en la nueva carpeta creada llamada `/Mods/`, localizada en la carpeta de instalación de Muse Dash.
- Todos los archivos deben ir directamente en la carpeta `/Mods/`, y NO en una subcarpeta dentro de la carpeta `/Mods/`

View File

@@ -14,9 +14,6 @@ _not_ used for logical access (the seed will never require you to catch somethin
- Now excludes the location "Navel Rock Top - Hidden Item Sacred Ash" if your goal is Champion and you didn't randomize
event tickets.
- Fixed handling of shuffle option for badges/HMs in the case that the player sets those items to nonlocal or uses
plando to put an item in one of those locations, or in the case that fill gets itself stuck on these items and has to
retry.
# 2.3.0

View File

@@ -8,7 +8,7 @@ import os
import pkgutil
from typing import Any, Set, List, Dict, Optional, Tuple, ClassVar, TextIO, Union
from BaseClasses import CollectionState, ItemClassification, MultiWorld, Tutorial, LocationProgressType
from BaseClasses import ItemClassification, MultiWorld, Tutorial, LocationProgressType
from Fill import FillError, fill_restrictive
from Options import OptionError, Toggle
import settings
@@ -100,7 +100,6 @@ class PokemonEmeraldWorld(World):
required_client_version = (0, 4, 6)
item_pool: List[PokemonEmeraldItem]
badge_shuffle_info: Optional[List[Tuple[PokemonEmeraldLocation, PokemonEmeraldItem]]]
hm_shuffle_info: Optional[List[Tuple[PokemonEmeraldLocation, PokemonEmeraldItem]]]
free_fly_location_id: int
@@ -186,7 +185,7 @@ class PokemonEmeraldWorld(World):
# In race mode we don't patch any item location information into the ROM
if self.multiworld.is_race and not self.options.remote_items:
logging.warning("Pokemon Emerald: Forcing player %s (%s) to use remote items due to race mode.",
logging.warning("Pokemon Emerald: Forcing Player %s (%s) to use remote items due to race mode.",
self.player, self.player_name)
self.options.remote_items.value = Toggle.option_true
@@ -198,7 +197,7 @@ class PokemonEmeraldWorld(World):
# Prevent setting the number of required legendaries higher than the number of enabled legendaries
if self.options.legendary_hunt_count.value > len(self.options.allowed_legendary_hunt_encounters.value):
logging.warning("Pokemon Emerald: Legendary hunt count for player %s (%s) higher than number of allowed "
logging.warning("Pokemon Emerald: Legendary hunt count for Player %s (%s) higher than number of allowed "
"legendary encounters. Reducing to number of allowed encounters.", self.player,
self.player_name)
self.options.legendary_hunt_count.value = len(self.options.allowed_legendary_hunt_encounters.value)
@@ -235,17 +234,10 @@ class PokemonEmeraldWorld(World):
max_norman_count = 4
if self.options.norman_count.value > max_norman_count:
logging.warning("Pokemon Emerald: Norman requirements for player %s (%s) are unsafe in combination with "
logging.warning("Pokemon Emerald: Norman requirements for Player %s (%s) are unsafe in combination with "
"other settings. Reducing to 4.", self.player, self.player_name)
self.options.norman_count.value = max_norman_count
# Shuffled badges/hms will always be placed locally, so add them to local_items
if self.options.badges == RandomizeBadges.option_shuffle:
self.options.local_items.value.update(self.item_name_groups["Badge"])
if self.options.hms == RandomizeHms.option_shuffle:
self.options.local_items.value.update(self.item_name_groups["HM"])
def create_regions(self) -> None:
from .regions import create_regions
all_regions = create_regions(self)
@@ -385,11 +377,12 @@ class PokemonEmeraldWorld(World):
item_locations = [location for location in item_locations if emerald_data.locations[location.key].category not in filter_categories]
default_itempool = [self.create_item_by_code(location.default_item_code) for location in item_locations]
# Take the itempool as-is
if self.options.item_pool_type == ItemPoolType.option_shuffled:
# Take the itempool as-is
self.item_pool = default_itempool
self.multiworld.itempool += default_itempool
# Recreate the itempool from random items
elif self.options.item_pool_type in (ItemPoolType.option_diverse, ItemPoolType.option_diverse_balanced):
# Recreate the itempool from random items
item_categories = ["Ball", "Healing", "Rare Candy", "Vitamin", "Evolution Stone",
"Money", "TM", "Held", "Misc", "Berry"]
@@ -399,7 +392,6 @@ class PokemonEmeraldWorld(World):
if not item.advancement:
item_category_counter.update([tag for tag in item.tags if tag in item_categories])
self.item_pool = []
item_category_weights = [item_category_counter.get(category) for category in item_categories]
item_category_weights = [weight if weight is not None else 0 for weight in item_category_weights]
@@ -444,10 +436,19 @@ class PokemonEmeraldWorld(World):
item_code = self.random.choice(fill_item_candidates_by_category[category])
item = self.create_item_by_code(item_code)
self.item_pool.append(item)
self.multiworld.itempool.append(item)
self.multiworld.itempool += self.item_pool
def set_rules(self) -> None:
from .rules import set_rules
set_rules(self)
def generate_basic(self) -> None:
# Create auth
# self.auth = self.random.randbytes(16) # Requires >=3.9
self.auth = self.random.getrandbits(16 * 8).to_bytes(16, "little")
randomize_types(self)
randomize_wild_encounters(self)
set_free_fly(self)
set_legendary_cave_entrances(self)
@@ -474,20 +475,9 @@ class PokemonEmeraldWorld(World):
if not self.options.key_items:
convert_unrandomized_items_to_events(LocationCategory.KEY)
def set_rules(self):
from .rules import set_rules
set_rules(self)
def connect_entrances(self):
randomize_wild_encounters(self)
self.shuffle_badges_hms()
# For entrance randomization, disconnect entrances here, randomize map, then
# undo badge/HM placement and re-shuffle them in the new map.
def shuffle_badges_hms(self) -> None:
my_progression_items = [item for item in self.item_pool if item.advancement]
my_locations = list(self.get_locations())
def pre_fill(self) -> None:
# Badges and HMs that are set to shuffle need to be placed at
# their own subset of locations
if self.options.badges == RandomizeBadges.option_shuffle:
badge_locations: List[PokemonEmeraldLocation]
badge_items: List[PokemonEmeraldItem]
@@ -512,20 +502,41 @@ class PokemonEmeraldWorld(World):
badge_priority["Knuckle Badge"] = 0
badge_items.sort(key=lambda item: badge_priority.get(item.name, 0))
# Build state
state = CollectionState(self.multiworld)
for item in my_progression_items:
state.collect(item, True)
# If HM shuffle is on, HMs are neither placed in locations nor in
# the item pool, so we also need to collect them.
# Un-exclude badge locations, since we need to put progression items on them
for location in badge_locations:
location.progress_type = LocationProgressType.DEFAULT \
if location.progress_type == LocationProgressType.EXCLUDED \
else location.progress_type
collection_state = self.multiworld.get_all_state(False)
# If HM shuffle is on, HMs are not placed and not in the pool, so
# `get_all_state` did not contain them. Collect them manually for
# this fill. We know that they will be included in all state after
# this stage.
if self.hm_shuffle_info is not None:
for _, item in self.hm_shuffle_info:
state.collect(item, True)
state.sweep_for_advancements(my_locations)
collection_state.collect(item)
# Shuffle badges
self.fill_subset_with_retries(badge_items, badge_locations, state)
# In specific very constrained conditions, fill_restrictive may run
# out of swaps before it finds a valid solution if it gets unlucky.
# This is a band-aid until fill/swap can reliably find those solutions.
attempts_remaining = 2
while attempts_remaining > 0:
attempts_remaining -= 1
self.random.shuffle(badge_locations)
try:
fill_restrictive(self.multiworld, collection_state, badge_locations, badge_items,
single_player_placement=True, lock=True, allow_excluded=True)
break
except FillError as exc:
if attempts_remaining == 0:
raise exc
logging.debug(f"Failed to shuffle badges for player {self.player}. Retrying.")
continue
# Badges are guaranteed to be either placed or in the multiworld's itempool now
if self.options.hms == RandomizeHms.option_shuffle:
hm_locations: List[PokemonEmeraldLocation]
hm_items: List[PokemonEmeraldItem]
@@ -548,56 +559,33 @@ class PokemonEmeraldWorld(World):
if self.options.badges == RandomizeBadges.option_vanilla and \
self.options.require_flash in (DarkCavesRequireFlash.option_both, DarkCavesRequireFlash.option_only_granite_cave):
hm_priority["HM05 Flash"] = 0
hm_items.sort(key=lambda item: hm_priority.get(item.name, 0), reverse=True)
hm_items.sort(key=lambda item: hm_priority.get(item.name, 0))
# Build state
# Badges are either in the item pool, or already placed and collected during sweep
state = CollectionState(self.multiworld)
for item in my_progression_items:
state.collect(item, True)
state.sweep_for_advancements(my_locations)
# Un-exclude HM locations, since we need to put progression items on them
for location in hm_locations:
location.progress_type = LocationProgressType.DEFAULT \
if location.progress_type == LocationProgressType.EXCLUDED \
else location.progress_type
# Shuffle HMs
self.fill_subset_with_retries(hm_items, hm_locations, state)
collection_state = self.multiworld.get_all_state(False)
def fill_subset_with_retries(self, items: list[PokemonEmeraldItem], locations: list[PokemonEmeraldLocation], state: CollectionState):
# Un-exclude locations, since we need to put progression items on them
for location in locations:
location.progress_type = LocationProgressType.DEFAULT \
if location.progress_type == LocationProgressType.EXCLUDED \
else location.progress_type
# In specific very constrained conditions, fill_restrictive may run
# out of swaps before it finds a valid solution if it gets unlucky.
# This is a band-aid until fill/swap can reliably find those solutions.
attempts_remaining = 2
while attempts_remaining > 0:
attempts_remaining -= 1
self.random.shuffle(hm_locations)
try:
fill_restrictive(self.multiworld, collection_state, hm_locations, hm_items,
single_player_placement=True, lock=True, allow_excluded=True)
break
except FillError as exc:
if attempts_remaining == 0:
raise exc
# In specific very constrained conditions, `fill_restrictive` may run
# out of swaps before it finds a valid solution if it gets unlucky.
attempts_remaining = 2
while attempts_remaining > 0:
attempts_remaining -= 1
locations_copy = locations.copy()
items_copy = items.copy()
self.random.shuffle(locations_copy)
try:
fill_restrictive(self.multiworld, state, locations_copy, items_copy, single_player_placement=True,
lock=True)
break
except FillError as exc:
if attempts_remaining <= 0:
raise exc
# Undo partial item placement
for location in locations:
location.locked = False
if location.item is not None:
location.item.location = None
location.item = None
logging.debug(f"Failed to shuffle items for player {self.player} ({self.player_name}). Retrying.")
continue
def generate_basic(self) -> None:
# Create auth
self.auth = self.random.randbytes(16)
randomize_types(self)
logging.debug(f"Failed to shuffle HMs for player {self.player}. Retrying.")
continue
def generate_output(self, output_directory: str) -> None:
self.modified_trainers = copy.deepcopy(emerald_data.trainers)

View File

@@ -59,8 +59,6 @@
- Going into submenus from the pause menu should no longer reset traps
- `Sonic - Magic Gloves` are now plural
- Junk items will no longer cause a crash when in a falling state
- Saves should no longer incorrectly be marked as not matching the connected server
- Fixed miscellaneous crashes
- Chao Garden:
- Prevent races from occasionally becoming uncompletable when using the "Prize Only" option
- Properly allow Hero Chao to participate in Dark Races

View File

@@ -1439,9 +1439,7 @@ def set_mission_upgrade_rules_standard(multiworld: MultiWorld, world: World, pla
lambda state: state.has(ItemName.rouge_iron_boots, player))
add_rule(multiworld.get_location(LocationName.pyramid_cave_lifebox_5, player),
lambda state: (state.has(ItemName.sonic_light_shoes, player) and
state.has(ItemName.sonic_bounce_bracelet, player) and
state.has(ItemName.sonic_mystic_melody, player)))
lambda state: state.has(ItemName.sonic_bounce_bracelet, player))
add_rule(multiworld.get_location(LocationName.pyramid_cave_lifebox_6, player),
lambda state: state.has(ItemName.sonic_bounce_bracelet, player))
@@ -1747,8 +1745,6 @@ def set_mission_upgrade_rules_standard(multiworld: MultiWorld, world: World, pla
state.has(ItemName.sonic_bounce_bracelet, player) and
state.has(ItemName.sonic_flame_ring, player)))
add_rule(multiworld.get_location(LocationName.egg_quarters_itembox_9, player),
lambda state: state.has(ItemName.rouge_mystic_melody, player))
add_rule(multiworld.get_location(LocationName.lost_colony_itembox_9, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(multiworld.get_location(LocationName.security_hall_itembox_9, player),
@@ -2468,6 +2464,10 @@ def set_mission_upgrade_rules_hard(multiworld: MultiWorld, world: World, player:
add_rule(multiworld.get_location(LocationName.eternal_engine_omo_2, player),
lambda state: state.has(ItemName.tails_booster, player))
add_rule(multiworld.get_location(LocationName.weapons_bed_omo_2, player),
lambda state: state.has(ItemName.eggman_jet_engine, player) or
state.has(ItemName.eggman_large_cannon, player))
add_rule(multiworld.get_location(LocationName.hidden_base_omo_3, player),
lambda state: state.has(ItemName.tails_booster, player))
add_rule(multiworld.get_location(LocationName.eternal_engine_omo_3, player),
@@ -3214,8 +3214,6 @@ def set_mission_upgrade_rules_hard(multiworld: MultiWorld, world: World, player:
lambda state: (state.has(ItemName.sonic_light_shoes, player) and
state.has(ItemName.sonic_flame_ring, player)))
add_rule(multiworld.get_location(LocationName.egg_quarters_itembox_9, player),
lambda state: state.has(ItemName.rouge_mystic_melody, player))
add_rule(multiworld.get_location(LocationName.lost_colony_itembox_9, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(multiworld.get_location(LocationName.security_hall_itembox_9, player),
@@ -3437,6 +3435,8 @@ def set_mission_upgrade_rules_hard(multiworld: MultiWorld, world: World, player:
# Big Upgrade Requirements
if world.options.bigsanity:
add_rule(multiworld.get_location(LocationName.metal_harbor_big, player),
lambda state: state.has(ItemName.sonic_bounce_bracelet, player))
add_rule(multiworld.get_location(LocationName.mission_street_big, player),
lambda state: state.has(ItemName.tails_booster, player))
add_rule(multiworld.get_location(LocationName.hidden_base_big, player),
@@ -3454,6 +3454,8 @@ def set_mission_upgrade_rules_hard(multiworld: MultiWorld, world: World, player:
add_rule(multiworld.get_location(LocationName.lost_colony_big, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(multiworld.get_location(LocationName.weapons_bed_big, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(multiworld.get_location(LocationName.cannon_core_big_2, player),
lambda state: state.has(ItemName.tails_booster, player))
@@ -3698,6 +3700,10 @@ def set_mission_upgrade_rules_expert(multiworld: MultiWorld, world: World, playe
add_rule(multiworld.get_location(LocationName.eternal_engine_omo_2, player),
lambda state: state.has(ItemName.tails_booster, player))
add_rule(multiworld.get_location(LocationName.weapons_bed_omo_2, player),
lambda state: state.has(ItemName.eggman_jet_engine, player) or
state.has(ItemName.eggman_large_cannon, player))
add_rule(multiworld.get_location(LocationName.hidden_base_omo_3, player),
lambda state: state.has(ItemName.tails_booster, player))
add_rule(multiworld.get_location(LocationName.eternal_engine_omo_3, player),
@@ -4204,8 +4210,7 @@ def set_mission_upgrade_rules_expert(multiworld: MultiWorld, world: World, playe
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(multiworld.get_location(LocationName.eternal_engine_itembox_13, player),
lambda state: (state.has(ItemName.tails_booster, player) and
state.has(ItemName.tails_bazooka, player)))
lambda state: state.has(ItemName.tails_booster, player))
add_rule(multiworld.get_location(LocationName.mad_space_itembox_13, player),
lambda state: state.has(ItemName.rouge_treasure_scope, player))
@@ -4213,8 +4218,7 @@ def set_mission_upgrade_rules_expert(multiworld: MultiWorld, world: World, playe
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(multiworld.get_location(LocationName.eternal_engine_itembox_14, player),
lambda state: (state.has(ItemName.tails_booster, player) and
state.has(ItemName.tails_bazooka, player)))
lambda state: state.has(ItemName.tails_booster, player))
add_rule(multiworld.get_location(LocationName.cosmic_wall_itembox_14, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
@@ -4260,6 +4264,8 @@ def set_mission_upgrade_rules_expert(multiworld: MultiWorld, world: World, playe
# Big Upgrade Requirements
if world.options.bigsanity:
add_rule(multiworld.get_location(LocationName.metal_harbor_big, player),
lambda state: state.has(ItemName.sonic_bounce_bracelet, player))
add_rule(multiworld.get_location(LocationName.mission_street_big, player),
lambda state: state.has(ItemName.tails_booster, player))
add_rule(multiworld.get_location(LocationName.hidden_base_big, player),
@@ -4270,6 +4276,8 @@ def set_mission_upgrade_rules_expert(multiworld: MultiWorld, world: World, playe
add_rule(multiworld.get_location(LocationName.lost_colony_big, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
add_rule(multiworld.get_location(LocationName.weapons_bed_big, player),
lambda state: state.has(ItemName.eggman_jet_engine, player))
def set_boss_gate_rules(multiworld: MultiWorld, player: int, gate_bosses: typing.Dict[int, int]):

View File

@@ -129,10 +129,7 @@ If you wish to use the `SADX Music` option of the Randomizer, you must own a cop
- If you enabled an `SADX Music` option, then most likely the music data was not copied properly into the mod folder (See Additional Options for instructions).
- Mission 1 is missing a texture in the stage select UI.
- Most likely another mod is conflicting and overwriting the texture pack. It is recommended to have the SA2B Archipelago mod load last in the mod manager.
- Minigame trap is un-winnable
- If you are using the SA2 Input Controls mod, it conflicts with certain minigames such as the Input Sequence Trap and medium difficulty Fishing Trap. Disabling the SA2 Input Controls mod should resolve the issue.
- Most likely another mod is conflicting and overwriting the texture pack. It is recommeded to have the SA2B Archipelago mod load last in the mod manager.
## Save File Safeguard (Advanced Option)

View File

@@ -124,7 +124,7 @@ class SMWorld(World):
Logic.factory('vanilla')
dummy_rom_file = Utils.user_path(SMSettings.RomFile.copy_to) # actual rom set in generate_output
self.variaRando = VariaRandomizer(self.options, dummy_rom_file, self.player, self.multiworld.seed, self.random)
self.variaRando = VariaRandomizer(self.options, dummy_rom_file, self.player)
self.multiworld.state.smbm[self.player] = SMBoolManager(self.player, self.variaRando.maxDifficulty)
# keeps Nothing items local so no player will ever pickup Nothing
@@ -314,11 +314,11 @@ class SMWorld(World):
raise KeyError(f"Item {name} for {self.player_name} is invalid.")
def get_filler_item_name(self) -> str:
if self.random.randint(0, 100) < self.options.minor_qty.value:
if self.multiworld.random.randint(0, 100) < self.options.minor_qty.value:
power_bombs = self.options.power_bomb_qty.value
missiles = self.options.missile_qty.value
super_missiles = self.options.super_qty.value
roll = self.random.randint(1, power_bombs + missiles + super_missiles)
roll = self.multiworld.random.randint(1, power_bombs + missiles + super_missiles)
if roll <= power_bombs:
return "Power Bomb"
elif roll <= power_bombs + missiles:
@@ -340,8 +340,8 @@ class SMWorld(World):
else:
nonChozoLoc.append(loc)
self.random.shuffle(nonChozoLoc)
self.random.shuffle(chozoLoc)
self.multiworld.random.shuffle(nonChozoLoc)
self.multiworld.random.shuffle(chozoLoc)
missingCount = len(self.NothingPool) - len(nonChozoLoc)
locations = nonChozoLoc
if (missingCount > 0):

View File

@@ -1,4 +1,5 @@
import copy
import random
from ..logic.logic import Logic
from ..utils.parameters import Knows
from ..graph.location import locationsDict
@@ -135,8 +136,7 @@ class GraphUtils:
refused[apName] = cause
return ret, refused
@staticmethod
def updateLocClassesStart(startGraphArea, split, possibleMajLocs, preserveMajLocs, nLocs, random):
def updateLocClassesStart(startGraphArea, split, possibleMajLocs, preserveMajLocs, nLocs):
locs = locationsDict
preserveMajLocs = [locs[locName] for locName in preserveMajLocs if locs[locName].isClass(split)]
possLocs = [locs[locName] for locName in possibleMajLocs][:nLocs]
@@ -160,8 +160,7 @@ class GraphUtils:
ap = getAccessPoint(startApName)
return ap.Start['patches'] if 'patches' in ap.Start else []
@staticmethod
def createBossesTransitions(random):
def createBossesTransitions():
transitions = vanillaBossesTransitions
def isVanilla():
for t in vanillaBossesTransitions:
@@ -181,15 +180,13 @@ class GraphUtils:
transitions.append((src,dst))
return transitions
@staticmethod
def createAreaTransitions(lightAreaRando=False, *, random):
def createAreaTransitions(lightAreaRando=False):
if lightAreaRando:
return GraphUtils.createLightAreaTransitions(random=random)
return GraphUtils.createLightAreaTransitions()
else:
return GraphUtils.createRegularAreaTransitions(random=random)
return GraphUtils.createRegularAreaTransitions()
@staticmethod
def createRegularAreaTransitions(apList=None, apPred=None, *, random):
def createRegularAreaTransitions(apList=None, apPred=None):
if apList is None:
apList = Logic.accessPoints
if apPred is None:
@@ -242,8 +239,7 @@ class GraphUtils:
transitions.append((ap.Name, ap.Name))
# crateria can be forced in corner cases
@staticmethod
def createMinimizerTransitions(startApName, locLimit, forcedAreas=None, *, random):
def createMinimizerTransitions(startApName, locLimit, forcedAreas=None):
if forcedAreas is None:
forcedAreas = []
if startApName == 'Ceres':
@@ -320,8 +316,7 @@ class GraphUtils:
GraphUtils.log.debug("FINAL MINIMIZER areas: "+str(areas))
return transitions
@staticmethod
def createLightAreaTransitions(random):
def createLightAreaTransitions():
# group APs by area
aps = {}
totalCount = 0
@@ -412,8 +407,7 @@ class GraphUtils:
return rooms
@staticmethod
def escapeAnimalsTransitions(graph, possibleTargets, firstEscape, random):
def escapeAnimalsTransitions(graph, possibleTargets, firstEscape):
n = len(possibleTargets)
assert (n < 4 and firstEscape is not None) or (n <= 4 and firstEscape is None), "Invalid possibleTargets list: " + str(possibleTargets)
GraphUtils.log.debug("escapeAnimalsTransitions. possibleTargets="+str(possibleTargets)+", firstEscape="+str(firstEscape))

View File

@@ -1,3 +1,4 @@
import random
from ..utils import log
from ..utils.utils import getRangeDict, chooseFromRange
from ..rando.ItemLocContainer import ItemLocation
@@ -22,9 +23,8 @@ class Choice(object):
# simple random choice, that chooses an item first, then a locatio to put it in
class ItemThenLocChoice(Choice):
def __init__(self, restrictions, random):
def __init__(self, restrictions):
super(ItemThenLocChoice, self).__init__(restrictions)
self.random = random
def chooseItemLoc(self, itemLocDict, isProg):
itemList = self.getItemList(itemLocDict)
@@ -49,7 +49,7 @@ class ItemThenLocChoice(Choice):
return self.chooseItemRandom(itemList)
def chooseItemRandom(self, itemList):
return self.random.choice(itemList)
return random.choice(itemList)
def chooseLocation(self, locList, item, isProg):
if len(locList) == 0:
@@ -63,12 +63,12 @@ class ItemThenLocChoice(Choice):
return self.chooseLocationRandom(locList)
def chooseLocationRandom(self, locList):
return self.random.choice(locList)
return random.choice(locList)
# Choice specialization for prog speed based filler
class ItemThenLocChoiceProgSpeed(ItemThenLocChoice):
def __init__(self, restrictions, progSpeedParams, distanceProp, services, random):
super(ItemThenLocChoiceProgSpeed, self).__init__(restrictions, random)
def __init__(self, restrictions, progSpeedParams, distanceProp, services):
super(ItemThenLocChoiceProgSpeed, self).__init__(restrictions)
self.progSpeedParams = progSpeedParams
self.distanceProp = distanceProp
self.services = services
@@ -104,7 +104,7 @@ class ItemThenLocChoiceProgSpeed(ItemThenLocChoice):
if self.restrictions.isLateMorph() and canRollback and len(itemLocDict) == 1:
item, locList = list(itemLocDict.items())[0]
if item.Type == 'Morph':
morphLocs = self.restrictions.lateMorphCheck(container, locList, self.random)
morphLocs = self.restrictions.lateMorphCheck(container, locList)
if morphLocs is not None:
itemLocDict[item] = morphLocs
else:
@@ -115,7 +115,7 @@ class ItemThenLocChoiceProgSpeed(ItemThenLocChoice):
assert len(locs) == 1 and locs[0].Name == item.Name
return ItemLocation(item, locs[0])
# late doors check for random door colors
if self.restrictions.isLateDoors() and self.random.random() < self.lateDoorsProb:
if self.restrictions.isLateDoors() and random.random() < self.lateDoorsProb:
self.processLateDoors(itemLocDict, ap, container)
self.progressionItemLocs = progressionItemLocs
self.ap = ap
@@ -145,14 +145,14 @@ class ItemThenLocChoiceProgSpeed(ItemThenLocChoice):
def chooseLocationProg(self, locs, item):
locs = self.getLocsSpreadProgression(locs)
self.random.shuffle(locs)
random.shuffle(locs)
ret = self.getChooseFunc(self.chooseLocRanges, self.chooseLocFuncs)(locs)
self.log.debug('chooseLocationProg. ret='+ret.Name)
return ret
# get choose function from a weighted dict
def getChooseFunc(self, rangeDict, funcDict):
v = chooseFromRange(rangeDict, self.random)
v = chooseFromRange(rangeDict)
return funcDict[v]
@@ -209,6 +209,6 @@ class ItemThenLocChoiceProgSpeed(ItemThenLocChoice):
for i in range(len(availableLocations)):
loc = availableLocations[i]
d = distances[i]
if d == maxDist or self.random.random() >= self.spreadProb:
if d == maxDist or random.random() >= self.spreadProb:
locs.append(loc)
return locs

View File

@@ -1,5 +1,5 @@
import copy, time
import copy, time, random
from ..utils import log
from ..logic.cache import RequestCache
from ..rando.RandoServices import RandoServices
@@ -15,11 +15,11 @@ from ..graph.graph_utils import GraphUtils
# item pool is not empty).
# entry point is generateItems
class Filler(object):
def __init__(self, startAP, graph, restrictions, emptyContainer, endDate=infinity, *, random):
def __init__(self, startAP, graph, restrictions, emptyContainer, endDate=infinity):
self.startAP = startAP
self.cache = RequestCache()
self.graph = graph
self.services = RandoServices(graph, restrictions, self.cache, random=random)
self.services = RandoServices(graph, restrictions, self.cache)
self.restrictions = restrictions
self.settings = restrictions.settings
self.endDate = endDate
@@ -108,9 +108,9 @@ class Filler(object):
# very simple front fill algorithm with no rollback and no "softlock checks" (== dessy algorithm)
class FrontFiller(Filler):
def __init__(self, startAP, graph, restrictions, emptyContainer, endDate=infinity, *, random):
super(FrontFiller, self).__init__(startAP, graph, restrictions, emptyContainer, endDate, random=random)
self.choice = ItemThenLocChoice(restrictions, random)
def __init__(self, startAP, graph, restrictions, emptyContainer, endDate=infinity):
super(FrontFiller, self).__init__(startAP, graph, restrictions, emptyContainer, endDate)
self.choice = ItemThenLocChoice(restrictions)
self.stdStart = GraphUtils.isStandardStart(self.startAP)
def isEarlyGame(self):

View File

@@ -1,5 +1,5 @@
import copy
import random, copy
from ..utils import log
from ..graph.graph_utils import GraphUtils, vanillaTransitions, vanillaBossesTransitions, escapeSource, escapeTargets, graphAreas, getAccessPoint
from ..logic.logic import Logic
@@ -11,14 +11,13 @@ from collections import defaultdict
# creates graph and handles randomized escape
class GraphBuilder(object):
def __init__(self, graphSettings, random):
def __init__(self, graphSettings):
self.graphSettings = graphSettings
self.areaRando = graphSettings.areaRando
self.bossRando = graphSettings.bossRando
self.escapeRando = graphSettings.escapeRando
self.minimizerN = graphSettings.minimizerN
self.log = log.get('GraphBuilder')
self.random = random
# builds everything but escape transitions
def createGraph(self, maxDiff):
@@ -49,18 +48,18 @@ class GraphBuilder(object):
objForced = forcedAreas.intersection(escAreas)
escAreasList = sorted(list(escAreas))
while len(objForced) < n and len(escAreasList) > 0:
objForced.add(escAreasList.pop(self.random.randint(0, len(escAreasList)-1)))
objForced.add(escAreasList.pop(random.randint(0, len(escAreasList)-1)))
forcedAreas = forcedAreas.union(objForced)
transitions = GraphUtils.createMinimizerTransitions(self.graphSettings.startAP, self.minimizerN, sorted(list(forcedAreas)), random=self.random)
transitions = GraphUtils.createMinimizerTransitions(self.graphSettings.startAP, self.minimizerN, sorted(list(forcedAreas)))
else:
if not self.bossRando:
transitions += vanillaBossesTransitions
else:
transitions += GraphUtils.createBossesTransitions(self.random)
transitions += GraphUtils.createBossesTransitions()
if not self.areaRando:
transitions += vanillaTransitions
else:
transitions += GraphUtils.createAreaTransitions(self.graphSettings.lightAreaRando, random=self.random)
transitions += GraphUtils.createAreaTransitions(self.graphSettings.lightAreaRando)
ret = AccessGraph(Logic.accessPoints, transitions, self.graphSettings.dotFile)
Objectives.objDict[self.graphSettings.player].setGraph(ret, maxDiff)
return ret
@@ -101,7 +100,7 @@ class GraphBuilder(object):
self.escapeTimer(graph, paths, self.areaRando or escapeTrigger is not None)
self.log.debug("escapeGraph: ({}, {}) timer: {}".format(escapeSource, dst, graph.EscapeAttributes['Timer']))
# animals
GraphUtils.escapeAnimalsTransitions(graph, possibleTargets, dst, self.random)
GraphUtils.escapeAnimalsTransitions(graph, possibleTargets, dst)
return True
def _getTargets(self, sm, graph, maxDiff):
@@ -111,7 +110,7 @@ class GraphBuilder(object):
if len(possibleTargets) == 0:
self.log.debug("Can't randomize escape, fallback to vanilla")
possibleTargets.append('Climb Bottom Left')
self.random.shuffle(possibleTargets)
random.shuffle(possibleTargets)
return possibleTargets
def getPossibleEscapeTargets(self, emptyContainer, graph, maxDiff):

View File

@@ -1,6 +1,6 @@
from ..utils.utils import randGaussBounds, getRangeDict, chooseFromRange
from ..utils import log
import logging, copy
import logging, copy, random
class Item:
__slots__ = ( 'Category', 'Class', 'Name', 'Code', 'Type', 'BeamBits', 'ItemBits', 'Id' )
@@ -335,7 +335,7 @@ class ItemManager:
itemCode = item.Code + modifier
return itemCode
def __init__(self, majorsSplit, qty, sm, nLocs, bossesItems, maxDiff, random):
def __init__(self, majorsSplit, qty, sm, nLocs, bossesItems, maxDiff):
self.qty = qty
self.sm = sm
self.majorsSplit = majorsSplit
@@ -344,7 +344,6 @@ class ItemManager:
self.maxDiff = maxDiff
self.majorClass = 'Chozo' if majorsSplit == 'Chozo' else 'Major'
self.itemPool = []
self.random = random
def newItemPool(self, addBosses=True):
self.itemPool = []
@@ -387,7 +386,7 @@ class ItemManager:
return ItemManager.Items[itemType].withClass(itemClass)
def createItemPool(self, exclude=None):
itemPoolGenerator = ItemPoolGenerator.factory(self.majorsSplit, self, self.qty, self.sm, exclude, self.nLocs, self.maxDiff, self.random)
itemPoolGenerator = ItemPoolGenerator.factory(self.majorsSplit, self, self.qty, self.sm, exclude, self.nLocs, self.maxDiff)
self.itemPool = itemPoolGenerator.getItemPool()
@staticmethod
@@ -403,20 +402,20 @@ class ItemPoolGenerator(object):
nbBosses = 9
@staticmethod
def factory(majorsSplit, itemManager, qty, sm, exclude, nLocs, maxDiff, random):
def factory(majorsSplit, itemManager, qty, sm, exclude, nLocs, maxDiff):
if majorsSplit == 'Chozo':
return ItemPoolGeneratorChozo(itemManager, qty, sm, maxDiff, random)
return ItemPoolGeneratorChozo(itemManager, qty, sm, maxDiff)
elif majorsSplit == 'Plando':
return ItemPoolGeneratorPlando(itemManager, qty, sm, exclude, nLocs, maxDiff, random)
return ItemPoolGeneratorPlando(itemManager, qty, sm, exclude, nLocs, maxDiff)
elif nLocs == ItemPoolGenerator.maxLocs:
if majorsSplit == "Scavenger":
return ItemPoolGeneratorScavenger(itemManager, qty, sm, maxDiff, random)
return ItemPoolGeneratorScavenger(itemManager, qty, sm, maxDiff)
else:
return ItemPoolGeneratorMajors(itemManager, qty, sm, maxDiff, random)
return ItemPoolGeneratorMajors(itemManager, qty, sm, maxDiff)
else:
return ItemPoolGeneratorMinimizer(itemManager, qty, sm, nLocs, maxDiff, random)
return ItemPoolGeneratorMinimizer(itemManager, qty, sm, nLocs, maxDiff)
def __init__(self, itemManager, qty, sm, maxDiff, random):
def __init__(self, itemManager, qty, sm, maxDiff):
self.itemManager = itemManager
self.qty = qty
self.sm = sm
@@ -424,13 +423,12 @@ class ItemPoolGenerator(object):
self.maxEnergy = 18 # 14E, 4R
self.maxDiff = maxDiff
self.log = log.get('ItemPool')
self.random = random
def isUltraSparseNoTanks(self):
# if low stuff botwoon is not known there is a hard energy req of one tank, even
# with both suits
lowStuffBotwoon = self.sm.knowsLowStuffBotwoon()
return self.random.random() < 0.5 and (lowStuffBotwoon.bool == True and lowStuffBotwoon.difficulty <= self.maxDiff)
return random.random() < 0.5 and (lowStuffBotwoon.bool == True and lowStuffBotwoon.difficulty <= self.maxDiff)
def calcMaxMinors(self):
pool = self.itemManager.getItemPool()
@@ -466,7 +464,7 @@ class ItemPoolGenerator(object):
rangeDict = getRangeDict(ammoQty)
self.log.debug("rangeDict: {}".format(rangeDict))
while len(self.itemManager.getItemPool()) < maxItems:
item = chooseFromRange(rangeDict, self.random)
item = chooseFromRange(rangeDict)
self.itemManager.addMinor(item)
else:
minorsTypes = ['Missile', 'Super', 'PowerBomb']
@@ -524,7 +522,7 @@ class ItemPoolGeneratorChozo(ItemPoolGenerator):
# no etank nor reserve
self.itemManager.removeItem('ETank')
self.itemManager.addItem('NoEnergy', 'Chozo')
elif self.random.random() < 0.5:
elif random.random() < 0.5:
# replace only etank with reserve
self.itemManager.removeItem('ETank')
self.itemManager.addItem('Reserve', 'Chozo')
@@ -537,9 +535,9 @@ class ItemPoolGeneratorChozo(ItemPoolGenerator):
# 4-6
# already 3E and 1R
alreadyInPool = 4
rest = randGaussBounds(self.random, 2, 5)
rest = randGaussBounds(2, 5)
if rest >= 1:
if self.random.random() < 0.5:
if random.random() < 0.5:
self.itemManager.addItem('Reserve', 'Minor')
else:
self.itemManager.addItem('ETank', 'Minor')
@@ -552,13 +550,13 @@ class ItemPoolGeneratorChozo(ItemPoolGenerator):
# 8-12
# add up to 3 Reserves or ETanks (cannot add more than 3 reserves)
for i in range(3):
if self.random.random() < 0.5:
if random.random() < 0.5:
self.itemManager.addItem('Reserve', 'Minor')
else:
self.itemManager.addItem('ETank', 'Minor')
# 7 already in the pool (3 E, 1 R, + the previous 3)
alreadyInPool = 7
rest = 1 + randGaussBounds(self.random, 4, 3.7)
rest = 1 + randGaussBounds(4, 3.7)
for i in range(rest):
self.itemManager.addItem('ETank', 'Minor')
# fill the rest with NoEnergy
@@ -583,10 +581,10 @@ class ItemPoolGeneratorChozo(ItemPoolGenerator):
return self.itemManager.getItemPool()
class ItemPoolGeneratorMajors(ItemPoolGenerator):
def __init__(self, itemManager, qty, sm, maxDiff, random):
super(ItemPoolGeneratorMajors, self).__init__(itemManager, qty, sm, maxDiff, random)
self.sparseRest = 1 + randGaussBounds(self.random,2, 5)
self.mediumRest = 3 + randGaussBounds(self.random, 4, 3.7)
def __init__(self, itemManager, qty, sm, maxDiff):
super(ItemPoolGeneratorMajors, self).__init__(itemManager, qty, sm, maxDiff)
self.sparseRest = 1 + randGaussBounds(2, 5)
self.mediumRest = 3 + randGaussBounds(4, 3.7)
self.ultraSparseNoTanks = self.isUltraSparseNoTanks()
def addNoEnergy(self):
@@ -611,7 +609,7 @@ class ItemPoolGeneratorMajors(ItemPoolGenerator):
# no energy at all
self.addNoEnergy()
else:
if self.random.random() < 0.5:
if random.random() < 0.5:
self.itemManager.addItem('ETank')
else:
self.itemManager.addItem('Reserve')
@@ -622,7 +620,7 @@ class ItemPoolGeneratorMajors(ItemPoolGenerator):
elif energyQty == 'sparse':
# 4-6
if self.random.random() < 0.5:
if random.random() < 0.5:
self.itemManager.addItem('Reserve')
else:
self.itemManager.addItem('ETank')
@@ -641,7 +639,7 @@ class ItemPoolGeneratorMajors(ItemPoolGenerator):
alreadyInPool = 2
n = getE(3)
for i in range(n):
if self.random.random() < 0.5:
if random.random() < 0.5:
self.itemManager.addItem('Reserve')
else:
self.itemManager.addItem('ETank')
@@ -678,15 +676,15 @@ class ItemPoolGeneratorMajors(ItemPoolGenerator):
return self.itemManager.getItemPool()
class ItemPoolGeneratorScavenger(ItemPoolGeneratorMajors):
def __init__(self, itemManager, qty, sm, maxDiff, random):
super(ItemPoolGeneratorScavenger, self).__init__(itemManager, qty, sm, maxDiff, random)
def __init__(self, itemManager, qty, sm, maxDiff):
super(ItemPoolGeneratorScavenger, self).__init__(itemManager, qty, sm, maxDiff)
def addNoEnergy(self):
self.itemManager.addItem('Nothing')
class ItemPoolGeneratorMinimizer(ItemPoolGeneratorMajors):
def __init__(self, itemManager, qty, sm, nLocs, maxDiff, random):
super(ItemPoolGeneratorMinimizer, self).__init__(itemManager, qty, sm, maxDiff, random)
def __init__(self, itemManager, qty, sm, nLocs, maxDiff):
super(ItemPoolGeneratorMinimizer, self).__init__(itemManager, qty, sm, maxDiff)
self.maxItems = nLocs
self.calcMaxAmmo()
nMajors = len([itemName for itemName,item in ItemManager.Items.items() if item.Class == 'Major' and item.Category != 'Energy'])
@@ -718,8 +716,8 @@ class ItemPoolGeneratorMinimizer(ItemPoolGeneratorMajors):
self.log.debug("maxEnergy: "+str(self.maxEnergy))
class ItemPoolGeneratorPlando(ItemPoolGenerator):
def __init__(self, itemManager, qty, sm, exclude, nLocs, maxDiff, random):
super(ItemPoolGeneratorPlando, self).__init__(itemManager, qty, sm, maxDiff, random)
def __init__(self, itemManager, qty, sm, exclude, nLocs, maxDiff):
super(ItemPoolGeneratorPlando, self).__init__(itemManager, qty, sm, maxDiff)
# in exclude dict:
# in alreadyPlacedItems:
# dict of 'itemType: count' of items already added in the plando.
@@ -807,7 +805,7 @@ class ItemPoolGeneratorPlando(ItemPoolGenerator):
if ammoQty:
rangeDict = getRangeDict(ammoQty)
while len(self.itemManager.getItemPool()) < maxItems and remain > 0:
item = chooseFromRange(rangeDict, self.random)
item = chooseFromRange(rangeDict)
self.itemManager.addMinor(item)
remain -= 1

View File

@@ -1,4 +1,4 @@
import sys, time
import sys, random, time
from ..utils import log
from ..logic.logic import Logic
@@ -14,7 +14,7 @@ from ..utils.doorsmanager import DoorsManager
# entry point for rando execution ("randomize" method)
class RandoExec(object):
def __init__(self, seedName, vcr, randoSettings, graphSettings, player, random):
def __init__(self, seedName, vcr, randoSettings, graphSettings, player):
self.errorMsg = ""
self.seedName = seedName
self.vcr = vcr
@@ -22,7 +22,6 @@ class RandoExec(object):
self.graphSettings = graphSettings
self.log = log.get('RandoExec')
self.player = player
self.random = random
# processes settings to :
# - create Restrictions and GraphBuilder objects
@@ -32,7 +31,7 @@ class RandoExec(object):
vcr = VCR(self.seedName, 'rando') if self.vcr == True else None
self.errorMsg = ""
split = self.randoSettings.restrictions['MajorMinor']
self.graphBuilder = GraphBuilder(self.graphSettings, self.random)
self.graphBuilder = GraphBuilder(self.graphSettings)
container = None
i = 0
attempts = 500 if self.graphSettings.areaRando or self.graphSettings.doorsColorsRando or split == 'Scavenger' else 1
@@ -44,10 +43,10 @@ class RandoExec(object):
while container is None and i < attempts and now <= endDate:
self.restrictions = Restrictions(self.randoSettings)
if self.graphSettings.doorsColorsRando == True:
DoorsManager.randomize(self.graphSettings.allowGreyDoors, self.player, self.random)
DoorsManager.randomize(self.graphSettings.allowGreyDoors, self.player)
self.areaGraph = self.graphBuilder.createGraph(self.randoSettings.maxDiff)
services = RandoServices(self.areaGraph, self.restrictions, random=self.random)
setup = RandoSetup(self.graphSettings, Logic.locations[:], services, self.player, self.random)
services = RandoServices(self.areaGraph, self.restrictions)
setup = RandoSetup(self.graphSettings, Logic.locations[:], services, self.player)
self.setup = setup
container = setup.createItemLocContainer(endDate, vcr)
if container is None:
@@ -79,7 +78,7 @@ class RandoExec(object):
n = nMaj
elif split == 'Chozo':
n = nChozo
GraphUtils.updateLocClassesStart(startAP.GraphArea, split, possibleMajLocs, preserveMajLocs, n, self.random)
GraphUtils.updateLocClassesStart(startAP.GraphArea, split, possibleMajLocs, preserveMajLocs, n)
def postProcessItemLocs(self, itemLocs, hide):
# hide some items like in dessy's
@@ -90,7 +89,7 @@ class RandoExec(object):
if (item.Category != "Nothing"
and loc.CanHidden == True
and loc.Visibility == 'Visible'):
if bool(self.random.getrandbits(1)) == True:
if bool(random.getrandbits(1)) == True:
loc.Visibility = 'Hidden'
# put nothing in unfilled locations
filledLocNames = [il.Location.Name for il in itemLocs]

View File

@@ -1,4 +1,5 @@
import copy, sys, logging, os
import copy, random, sys, logging, os
from enum import Enum, unique
from ..utils import log
from ..utils.parameters import infinity
@@ -18,13 +19,12 @@ class ComebackCheckType(Enum):
# collection of stateless services to be used mainly by fillers
class RandoServices(object):
def __init__(self, graph, restrictions, cache=None, *, random):
def __init__(self, graph, restrictions, cache=None):
self.restrictions = restrictions
self.settings = restrictions.settings
self.areaGraph = graph
self.cache = cache
self.log = log.get('RandoServices')
self.random = random
@staticmethod
def printProgress(s):
@@ -217,7 +217,7 @@ class RandoServices(object):
# choose a morph item location in that context
morphItemLoc = ItemLocation(
morph,
self.random.choice(morphLocs)
random.choice(morphLocs)
)
# acquire morph in new context and see if we can still open new locs
newAP = self.collect(ap, containerCpy, morphItemLoc)
@@ -232,7 +232,7 @@ class RandoServices(object):
if morphLocItem is None or len(itemLocDict) == 1:
# no morph, or it is the only possibility: nothing to do
return
morphLocs = self.restrictions.lateMorphCheck(container, itemLocDict[morphLocItem], self.random)
morphLocs = self.restrictions.lateMorphCheck(container, itemLocDict[morphLocItem])
if morphLocs is not None:
itemLocDict[morphLocItem] = morphLocs
else:
@@ -380,10 +380,10 @@ class RandoServices(object):
(itemLocDict, isProg) = self.getPossiblePlacements(ap, container, ComebackCheckType.NoCheck)
assert not isProg
items = list(itemLocDict.keys())
self.random.shuffle(items)
random.shuffle(items)
for item in items:
cont = copy.copy(container)
loc = self.random.choice(itemLocDict[item])
loc = random.choice(itemLocDict[item])
itemLoc1 = ItemLocation(item, loc)
self.log.debug("itemLoc1 attempt: "+getItemLocStr(itemLoc1))
newAP = self.collect(ap, cont, itemLoc1)
@@ -391,8 +391,8 @@ class RandoServices(object):
self.cache.reset()
(ild, isProg) = self.getPossiblePlacements(newAP, cont, ComebackCheckType.NoCheck)
if isProg:
item2 = self.random.choice(list(ild.keys()))
itemLoc2 = ItemLocation(item2, self.random.choice(ild[item2]))
item2 = random.choice(list(ild.keys()))
itemLoc2 = ItemLocation(item2, random.choice(ild[item2]))
self.log.debug("itemLoc2: "+getItemLocStr(itemLoc2))
return (itemLoc1, itemLoc2)
return None

View File

@@ -1,4 +1,5 @@
import sys
import sys, random
from collections import defaultdict
from ..rando.Items import ItemManager
from ..utils.utils import getRangeDict, chooseFromRange
@@ -31,11 +32,11 @@ class RandoSettings(object):
def isPlandoRando(self):
return self.PlandoOptions is not None
def getItemManager(self, smbm, nLocs, bossesItems, random):
def getItemManager(self, smbm, nLocs, bossesItems):
if not self.isPlandoRando():
return ItemManager(self.restrictions['MajorMinor'], self.qty, smbm, nLocs, bossesItems, self.maxDiff, random)
return ItemManager(self.restrictions['MajorMinor'], self.qty, smbm, nLocs, bossesItems, self.maxDiff)
else:
return ItemManager('Plando', self.qty, smbm, nLocs, bossesItems, self.maxDiff, random)
return ItemManager('Plando', self.qty, smbm, nLocs, bossesItems, self.maxDiff)
def getExcludeItems(self, locations):
if not self.isPlandoRando():
@@ -93,7 +94,7 @@ class ProgSpeedParameters(object):
self.restrictions = restrictions
self.nLocs = nLocs
def getVariableSpeed(self, random):
def getVariableSpeed(self):
ranges = getRangeDict({
'slowest':7,
'slow':20,
@@ -101,7 +102,7 @@ class ProgSpeedParameters(object):
'fast':27,
'fastest':11
})
return chooseFromRange(ranges, random)
return chooseFromRange(ranges)
def getMinorHelpProb(self, progSpeed):
if self.restrictions.split != 'Major':
@@ -133,7 +134,7 @@ class ProgSpeedParameters(object):
def isSlow(self, progSpeed):
return progSpeed == "slow" or (progSpeed == "slowest" and self.restrictions.split == "Chozo")
def getItemLimit(self, progSpeed, random):
def getItemLimit(self, progSpeed):
itemLimit = self.nLocs
if self.isSlow(progSpeed):
itemLimit = int(self.nLocs*0.209) # 21 for 105

View File

@@ -1,4 +1,4 @@
import copy
import copy, random
from ..utils import log
from ..utils.utils import randGaussBounds
@@ -16,9 +16,8 @@ from ..rom.rom_patches import RomPatches
# checks init conditions for the randomizer: processes super fun settings, graph, start location, special restrictions
# the entry point is createItemLocContainer
class RandoSetup(object):
def __init__(self, graphSettings, locations, services, player, random):
def __init__(self, graphSettings, locations, services, player):
self.sm = SMBoolManager(player, services.settings.maxDiff)
self.random = random
self.settings = services.settings
self.graphSettings = graphSettings
self.startAP = graphSettings.startAP
@@ -32,7 +31,7 @@ class RandoSetup(object):
# print("nLocs Setup: "+str(len(self.locations)))
# in minimizer we can have some missing boss locs
bossesItems = [loc.BossItemType for loc in self.locations if loc.isBoss()]
self.itemManager = self.settings.getItemManager(self.sm, len(self.locations), bossesItems, random)
self.itemManager = self.settings.getItemManager(self.sm, len(self.locations), bossesItems)
self.forbiddenItems = []
self.restrictedLocs = []
self.lastRestricted = []
@@ -166,7 +165,7 @@ class RandoSetup(object):
return True
self.log.debug("********* PRE RANDO START")
container = copy.copy(self.container)
filler = FrontFiller(self.startAP, self.areaGraph, self.restrictions, container, random=self.random)
filler = FrontFiller(self.startAP, self.areaGraph, self.restrictions, container)
condition = filler.createStepCountCondition(4)
(isStuck, itemLocations, progItems) = filler.generateItems(condition)
self.log.debug("********* PRE RANDO END")
@@ -346,9 +345,9 @@ class RandoSetup(object):
def getForbiddenItemsFromList(self, itemList):
self.log.debug('getForbiddenItemsFromList: ' + str(itemList))
remove = []
n = randGaussBounds(self.random, len(itemList))
n = randGaussBounds(len(itemList))
for i in range(n):
idx = self.random.randint(0, len(itemList) - 1)
idx = random.randint(0, len(itemList) - 1)
item = itemList.pop(idx)
if item is not None:
remove.append(item)

View File

@@ -1,4 +1,4 @@
import copy
import copy, random
from ..utils import log
from ..graph.graph_utils import getAccessPoint
from ..rando.ItemLocContainer import getLocListStr
@@ -112,7 +112,7 @@ class Restrictions(object):
return item.Class == "Minor"
# return True if we can keep morph as a possibility
def lateMorphCheck(self, container, possibleLocs, random):
def lateMorphCheck(self, container, possibleLocs):
# the closer we get to the limit the higher the chances of allowing morph
proba = random.randint(0, self.lateMorphLimit)
if self.split == 'Full':

View File

@@ -1,7 +1,7 @@
#!/usr/bin/python3
from Utils import output_path
import argparse, os.path, json, sys, shutil, copy, requests
import argparse, os.path, json, sys, shutil, random, copy, requests
from .rando.RandoSettings import RandoSettings, GraphSettings
from .rando.RandoExec import RandoExec
@@ -39,7 +39,7 @@ objectives = defaultMultiValues['objective']
tourians = defaultMultiValues['tourian']
areaRandomizations = defaultMultiValues['areaRandomization']
def randomMulti(args, param, defaultMultiValues, random):
def randomMulti(args, param, defaultMultiValues):
value = args[param]
isRandom = False
@@ -250,11 +250,10 @@ class VariaRandomizer:
parser.add_argument('--tourianList', help="list to choose from when random",
dest='tourianList', nargs='?', default=None)
def __init__(self, options, rom, player, seed, random):
def __init__(self, options, rom, player):
# parse args
self.args = copy.deepcopy(VariaRandomizer.parser.parse_args(["--logic", "varia"])) #dummy custom args to skip parsing _sys.argv while still get default values
self.player = player
self.random = random
args = self.args
args.rom = rom
# args.startLocation = to_pascal_case_with_space(options.startLocation.current_key)
@@ -324,13 +323,11 @@ class VariaRandomizer:
logger.debug("preset: {}".format(preset))
# Archipelago provides a seed for the multiworld.
self.seed = seed
# # if no seed given, choose one
# if args.seed == 0:
# self.seed = random.randrange(sys.maxsize)
# else:
# self.seed = args.seed
# if no seed given, choose one
if args.seed == 0:
self.seed = random.randrange(sys.maxsize)
else:
self.seed = args.seed
logger.debug("seed: {}".format(self.seed))
if args.raceMagic is not None:
@@ -363,12 +360,12 @@ class VariaRandomizer:
logger.debug("maxDifficulty: {}".format(self.maxDifficulty))
# handle random parameters with dynamic pool of values
(_, progSpeed) = randomMulti(args.__dict__, "progressionSpeed", speeds, random)
(_, progDiff) = randomMulti(args.__dict__, "progressionDifficulty", progDiffs, random)
(majorsSplitRandom, args.majorsSplit) = randomMulti(args.__dict__, "majorsSplit", majorsSplits, random)
(_, self.gravityBehaviour) = randomMulti(args.__dict__, "gravityBehaviour", gravityBehaviours, random)
(_, args.tourian) = randomMulti(args.__dict__, "tourian", tourians, random)
(areaRandom, args.area) = randomMulti(args.__dict__, "area", areaRandomizations, random)
(_, progSpeed) = randomMulti(args.__dict__, "progressionSpeed", speeds)
(_, progDiff) = randomMulti(args.__dict__, "progressionDifficulty", progDiffs)
(majorsSplitRandom, args.majorsSplit) = randomMulti(args.__dict__, "majorsSplit", majorsSplits)
(_, self.gravityBehaviour) = randomMulti(args.__dict__, "gravityBehaviour", gravityBehaviours)
(_, args.tourian) = randomMulti(args.__dict__, "tourian", tourians)
(areaRandom, args.area) = randomMulti(args.__dict__, "area", areaRandomizations)
areaRandomization = args.area in ['light', 'full']
lightArea = args.area == 'light'
@@ -629,7 +626,7 @@ class VariaRandomizer:
if args.objective:
if (args.objectiveRandom):
availableObjectives = [goal for goal in objectives if goal != "collect 100% items"] if "random" in args.objectiveList else args.objectiveList
self.objectivesManager.setRandom(args.nbObjective, availableObjectives, self.random)
self.objectivesManager.setRandom(args.nbObjective, availableObjectives)
else:
maxActiveGoals = Objectives.maxActiveGoals - addedObjectives
if len(args.objective) > maxActiveGoals:
@@ -663,7 +660,7 @@ class VariaRandomizer:
# print("energyQty:{}".format(energyQty))
#try:
self.randoExec = RandoExec(seedName, args.vcr, randoSettings, graphSettings, self.player, self.random)
self.randoExec = RandoExec(seedName, args.vcr, randoSettings, graphSettings, self.player)
self.container = self.randoExec.randomize()
# if we couldn't find an area layout then the escape graph is not created either
# and getDoorConnections will crash if random escape is activated.
@@ -693,7 +690,7 @@ class VariaRandomizer:
'gameend.ips', 'grey_door_animals.ips', 'low_timer.ips', 'metalimals.ips',
'phantoonimals.ips', 'ridleyimals.ips']
if args.escapeRando == False:
args.patches.append(self.random.choice(animalsPatches))
args.patches.append(random.choice(animalsPatches))
args.patches.append("Escape_Animals_Change_Event")
else:
optErrMsgs.append("Ignored animals surprise because of escape randomization")
@@ -763,9 +760,9 @@ class VariaRandomizer:
# patch local rom
# romFileName = args.rom
# shutil.copyfile(romFileName, outputFilename)
romPatcher = RomPatcher(settings=patcherSettings, magic=args.raceMagic, player=self.player, random=self.random)
romPatcher = RomPatcher(settings=patcherSettings, magic=args.raceMagic, player=self.player)
else:
romPatcher = RomPatcher(settings=patcherSettings, magic=args.raceMagic, random=self.random)
romPatcher = RomPatcher(settings=patcherSettings, magic=args.raceMagic)
if customPrePatchApply != None:
customPrePatchApply(romPatcher)

View File

@@ -49,7 +49,7 @@ class RomPatcher:
'DoorsColors': ['beam_doors_plms.ips', 'beam_doors_gfx.ips', 'red_doors.ips']
}
def __init__(self, settings=None, romFileName=None, magic=None, player=0, *, random):
def __init__(self, settings=None, romFileName=None, magic=None, player=0):
self.log = log.get('RomPatcher')
self.settings = settings
#self.romFileName = romFileName
@@ -76,7 +76,6 @@ class RomPatcher:
0x93ea: self.forceRoomCRE
}
self.player = player
self.random = random
def patchRom(self):
self.applyIPSPatches()
@@ -497,9 +496,9 @@ class RomPatcher:
self.ipsPatches = []
def writeSeed(self, seed):
r = random.Random(seed)
seedInfo = r.randint(0, 0xFFFF)
seedInfo2 = r.randint(0, 0xFFFF)
random.seed(seed)
seedInfo = random.randint(0, 0xFFFF)
seedInfo2 = random.randint(0, 0xFFFF)
self.romFile.writeWord(seedInfo, snes_to_pc(0xdfff00))
self.romFile.writeWord(seedInfo2)
@@ -1067,7 +1066,7 @@ class RomPatcher:
def writeObjectives(self, itemLocs, tourian):
objectives = Objectives.objDict[self.player]
objectives.writeGoals(self.romFile, self.random)
objectives.writeGoals(self.romFile)
objectives.writeIntroObjectives(self.romFile, tourian)
self.writeItemsMasks(itemLocs)
# hack bomb_torizo.ips to wake BT in all cases if necessary, ie chozo bots objective is on, and nothing at bombs

View File

@@ -1,3 +1,4 @@
import random
from enum import IntEnum,IntFlag
import copy
from ..logic.smbool import SMBool
@@ -122,7 +123,7 @@ class Door(object):
else:
return [color for color in colorsList if color not in self.forbiddenColors]
def randomize(self, allowGreyDoors, random):
def randomize(self, allowGreyDoors):
if self.canRandomize():
if self.canGrey and allowGreyDoors:
self.setColor(random.choice(self.filterColorList(colorsListGrey)))
@@ -346,9 +347,9 @@ class DoorsManager():
currentDoors['CrabShaftRight'].forceBlue()
@staticmethod
def randomize(allowGreyDoors, player, random):
def randomize(allowGreyDoors, player):
for door in DoorsManager.doorsDict[player].values():
door.randomize(allowGreyDoors, random)
door.randomize(allowGreyDoors)
# set both ends of toilet to the same color to avoid soft locking in area rando
toiletTop = DoorsManager.doorsDict[player]['PlasmaSparkBottom']
toiletBottom = DoorsManager.doorsDict[player]['OasisTop']

View File

@@ -1,4 +1,5 @@
import copy
import random
from ..rom.addresses import Addresses
from ..rom.rom import pc_to_snes
from ..logic.helpers import Bosses
@@ -27,7 +28,7 @@ class Synonyms(object):
]
alreadyUsed = []
@staticmethod
def getVerb(random):
def getVerb():
verb = random.choice(Synonyms.killSynonyms)
while verb in Synonyms.alreadyUsed:
verb = random.choice(Synonyms.killSynonyms)
@@ -87,10 +88,10 @@ class Goal(object):
# not all objectives require an ap (like limit objectives)
return self.clearFunc(smbm, ap)
def getText(self, random):
def getText(self):
out = "{}. ".format(self.rank)
if self.useSynonym:
out += self.text.format(Synonyms.getVerb(random))
out += self.text.format(Synonyms.getVerb())
else:
out += self.text
assert len(out) <= 28, "Goal text '{}' is too long: {}, max 28".format(out, len(out))
@@ -675,7 +676,7 @@ class Objectives(object):
return [goal.name for goal in _goals.values() if goal.available and (not removeNothing or goal.name != "nothing")]
# call from rando
def setRandom(self, nbGoals, availableGoals, random):
def setRandom(self, nbGoals, availableGoals):
while self.nbActiveGoals < nbGoals and availableGoals:
goalName = random.choice(availableGoals)
self.addGoal(goalName)
@@ -701,7 +702,7 @@ class Objectives(object):
LOG.debug("tourianRequired: {}".format(self.tourianRequired))
# call from rando
def writeGoals(self, romFile, random):
def writeGoals(self, romFile):
# write check functions
romFile.seek(Addresses.getOne('objectivesList'))
for goal in self.activeGoals:
@@ -735,7 +736,7 @@ class Objectives(object):
space = 3 if self.nbActiveGoals == 5 else 4
for i, goal in enumerate(self.activeGoals):
addr = baseAddr + i * lineLength * space
text = goal.getText(random)
text = goal.getText()
romFile.seek(addr)
for c in text:
if c not in char2tile:

View File

@@ -1,5 +1,5 @@
import io
import os, json, re
import os, json, re, random
import pathlib
import sys
from typing import Any
@@ -88,7 +88,7 @@ def normalizeRounding(n):
# gauss random in [0, r] range
# the higher the slope, the less probable extreme values are.
def randGaussBounds(random, r, slope=5):
def randGaussBounds(r, slope=5):
r = float(r)
n = normalizeRounding(random.gauss(r/2, r/slope))
if n < 0:
@@ -111,7 +111,7 @@ def getRangeDict(weightDict):
return rangeDict
def chooseFromRange(rangeDict, random):
def chooseFromRange(rangeDict):
r = random.random()
val = None
for v in sorted(rangeDict, key=rangeDict.get):

View File

@@ -3,4 +3,4 @@ from .options import StardewValleyOption, Goal, FarmType, StartingMoney, ProfitM
ArcadeMachineLocations, SpecialOrderLocations, QuestLocations, Fishsanity, Museumsanity, Monstersanity, Shipsanity, Cooksanity, Chefsanity, Craftsanity, \
Friendsanity, FriendsanityHeartSize, Booksanity, Walnutsanity, NumberOfMovementBuffs, EnabledFillerBuffs, ExcludeGingerIsland, TrapItems, \
MultipleDaySleepEnabled, MultipleDaySleepCost, ExperienceMultiplier, FriendshipMultiplier, DebrisMultiplier, QuickStart, Gifting, Mods, BundlePlando, \
StardewValleyOptions, enabled_mods, disabled_mods, all_mods
StardewValleyOptions

View File

@@ -1,6 +1,5 @@
from . import SVTestBase
from . import SVTestBase, minimal_locations_maximal_items
from .assertion import WorldAssertMixin
from .options.presets import minimal_locations_maximal_items
from .. import options
from ..mods.mod_data import ModNames

View File

@@ -1,6 +1,5 @@
from BaseClasses import MultiWorld, get_seed
from . import setup_solo_multiworld, SVTestCase, solo_multiworld
from .options.presets import allsanity_no_mods_6_x_x, get_minsanity_options
from . import setup_solo_multiworld, SVTestCase, allsanity_no_mods_6_x_x, get_minsanity_options, solo_multiworld
from .. import StardewValleyWorld
from ..items import Group, item_table
from ..options import Friendsanity, SeasonRandomization, Museumsanity, Shipsanity, Goal

View File

@@ -3,9 +3,7 @@ import unittest
from unittest import TestCase, SkipTest
from BaseClasses import MultiWorld
from . import setup_solo_multiworld
from .assertion import RuleAssertMixin
from .options.presets import allsanity_mods_6_x_x, minimal_locations_maximal_items
from . import RuleAssertMixin, setup_solo_multiworld, allsanity_mods_6_x_x, minimal_locations_maximal_items
from .. import StardewValleyWorld
from ..data.bundle_data import all_bundle_items_except_money
from ..logic.logic import StardewLogic

View File

@@ -1,6 +1,6 @@
from . import SVTestBase
from .options.presets import default_6_x_x, allsanity_no_mods_6_x_x, allsanity_mods_6_x_x_exclude_disabled, get_minsanity_options, \
minimal_locations_maximal_items, minimal_locations_maximal_items_with_island
from . import SVTestBase, allsanity_no_mods_6_x_x, \
allsanity_mods_6_x_x, minimal_locations_maximal_items, minimal_locations_maximal_items_with_island, get_minsanity_options, default_6_x_x, \
allsanity_mods_6_x_x_exclude_disabled
from .. import location_table
from ..items import Group, item_table

View File

@@ -2,10 +2,9 @@ import itertools
from BaseClasses import ItemClassification
from Options import NamedRange
from . import SVTestCase, solo_multiworld, SVTestBase
from . import SVTestCase, allsanity_no_mods_6_x_x, allsanity_mods_6_x_x, solo_multiworld, SVTestBase
from .assertion import WorldAssertMixin
from .long.option_names import all_option_choices
from .options.presets import allsanity_no_mods_6_x_x, allsanity_mods_6_x_x
from .. import items_by_group, Group, StardewValleyWorld
from ..locations import locations_by_tag, LocationTags, location_table
from ..options import ExcludeGingerIsland, ToolProgression, Goal, SeasonRandomization, TrapItems, SpecialOrderLocations, ArcadeMachineLocations

View File

@@ -11,8 +11,9 @@ from test.general import gen_steps, setup_solo_multiworld as setup_base_solo_mul
from worlds.AutoWorld import call_all
from .assertion import RuleAssertMixin
from .options.utils import fill_namespace_with_default, parse_class_option_keys, fill_dataclass_with_default
from .. import StardewValleyWorld, StardewItem
from .. import StardewValleyWorld, options, StardewItem
from ..options import StardewValleyOption
from ..options.options import enabled_mods
logger = logging.getLogger(__name__)
@@ -20,6 +21,169 @@ DEFAULT_TEST_SEED = get_seed()
logger.info(f"Default Test Seed: {DEFAULT_TEST_SEED}")
def default_6_x_x():
return {
options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.default,
options.BackpackProgression.internal_name: options.BackpackProgression.default,
options.Booksanity.internal_name: options.Booksanity.default,
options.BuildingProgression.internal_name: options.BuildingProgression.default,
options.BundlePrice.internal_name: options.BundlePrice.default,
options.BundleRandomization.internal_name: options.BundleRandomization.default,
options.Chefsanity.internal_name: options.Chefsanity.default,
options.Cooksanity.internal_name: options.Cooksanity.default,
options.Craftsanity.internal_name: options.Craftsanity.default,
options.Cropsanity.internal_name: options.Cropsanity.default,
options.ElevatorProgression.internal_name: options.ElevatorProgression.default,
options.EntranceRandomization.internal_name: options.EntranceRandomization.default,
options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.default,
options.FestivalLocations.internal_name: options.FestivalLocations.default,
options.Fishsanity.internal_name: options.Fishsanity.default,
options.Friendsanity.internal_name: options.Friendsanity.default,
options.FriendsanityHeartSize.internal_name: options.FriendsanityHeartSize.default,
options.Goal.internal_name: options.Goal.default,
options.Mods.internal_name: options.Mods.default,
options.Monstersanity.internal_name: options.Monstersanity.default,
options.Museumsanity.internal_name: options.Museumsanity.default,
options.NumberOfMovementBuffs.internal_name: options.NumberOfMovementBuffs.default,
options.EnabledFillerBuffs.internal_name: options.EnabledFillerBuffs.default,
options.QuestLocations.internal_name: options.QuestLocations.default,
options.SeasonRandomization.internal_name: options.SeasonRandomization.default,
options.Shipsanity.internal_name: options.Shipsanity.default,
options.SkillProgression.internal_name: options.SkillProgression.default,
options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.default,
options.ToolProgression.internal_name: options.ToolProgression.default,
options.TrapItems.internal_name: options.TrapItems.default,
options.Walnutsanity.internal_name: options.Walnutsanity.default
}
def allsanity_no_mods_6_x_x():
return {
options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_full_shuffling,
options.BackpackProgression.internal_name: options.BackpackProgression.option_progressive,
options.Booksanity.internal_name: options.Booksanity.option_all,
options.BuildingProgression.internal_name: options.BuildingProgression.option_progressive,
options.BundlePrice.internal_name: options.BundlePrice.option_expensive,
options.BundleRandomization.internal_name: options.BundleRandomization.option_thematic,
options.Chefsanity.internal_name: options.Chefsanity.option_all,
options.Cooksanity.internal_name: options.Cooksanity.option_all,
options.Craftsanity.internal_name: options.Craftsanity.option_all,
options.Cropsanity.internal_name: options.Cropsanity.option_enabled,
options.ElevatorProgression.internal_name: options.ElevatorProgression.option_progressive,
options.EntranceRandomization.internal_name: options.EntranceRandomization.option_disabled,
options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false,
options.FestivalLocations.internal_name: options.FestivalLocations.option_hard,
options.Fishsanity.internal_name: options.Fishsanity.option_all,
options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
options.FriendsanityHeartSize.internal_name: 1,
options.Goal.internal_name: options.Goal.option_perfection,
options.Mods.internal_name: frozenset(),
options.Monstersanity.internal_name: options.Monstersanity.option_progressive_goals,
options.Museumsanity.internal_name: options.Museumsanity.option_all,
options.EnabledFillerBuffs.internal_name: options.EnabledFillerBuffs.preset_all,
options.NumberOfMovementBuffs.internal_name: 12,
options.QuestLocations.internal_name: 56,
options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized,
options.Shipsanity.internal_name: options.Shipsanity.option_everything,
options.SkillProgression.internal_name: options.SkillProgression.option_progressive_with_masteries,
options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_qi,
options.ToolProgression.internal_name: options.ToolProgression.option_progressive,
options.TrapItems.internal_name: options.TrapItems.option_nightmare,
options.Walnutsanity.internal_name: options.Walnutsanity.preset_all
}
def allsanity_mods_6_x_x():
allsanity = allsanity_no_mods_6_x_x()
allsanity.update({options.Mods.internal_name: frozenset(options.Mods.valid_keys)})
return allsanity
def allsanity_mods_6_x_x_exclude_disabled():
allsanity = allsanity_no_mods_6_x_x()
allsanity.update({options.Mods.internal_name: frozenset(enabled_mods)})
return allsanity
def get_minsanity_options():
return {
options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_disabled,
options.BackpackProgression.internal_name: options.BackpackProgression.option_vanilla,
options.Booksanity.internal_name: options.Booksanity.option_none,
options.BuildingProgression.internal_name: options.BuildingProgression.option_vanilla,
options.BundlePrice.internal_name: options.BundlePrice.option_very_cheap,
options.BundleRandomization.internal_name: options.BundleRandomization.option_vanilla,
options.Chefsanity.internal_name: options.Chefsanity.option_none,
options.Cooksanity.internal_name: options.Cooksanity.option_none,
options.Craftsanity.internal_name: options.Craftsanity.option_none,
options.Cropsanity.internal_name: options.Cropsanity.option_disabled,
options.ElevatorProgression.internal_name: options.ElevatorProgression.option_vanilla,
options.EntranceRandomization.internal_name: options.EntranceRandomization.option_disabled,
options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true,
options.FestivalLocations.internal_name: options.FestivalLocations.option_disabled,
options.Fishsanity.internal_name: options.Fishsanity.option_none,
options.Friendsanity.internal_name: options.Friendsanity.option_none,
options.FriendsanityHeartSize.internal_name: 8,
options.Goal.internal_name: options.Goal.option_bottom_of_the_mines,
options.Mods.internal_name: frozenset(),
options.Monstersanity.internal_name: options.Monstersanity.option_none,
options.Museumsanity.internal_name: options.Museumsanity.option_none,
options.EnabledFillerBuffs.internal_name: options.EnabledFillerBuffs.preset_none,
options.NumberOfMovementBuffs.internal_name: 0,
options.QuestLocations.internal_name: -1,
options.SeasonRandomization.internal_name: options.SeasonRandomization.option_disabled,
options.Shipsanity.internal_name: options.Shipsanity.option_none,
options.SkillProgression.internal_name: options.SkillProgression.option_vanilla,
options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_vanilla,
options.ToolProgression.internal_name: options.ToolProgression.option_vanilla,
options.TrapItems.internal_name: options.TrapItems.option_no_traps,
options.Walnutsanity.internal_name: options.Walnutsanity.preset_none
}
def minimal_locations_maximal_items():
min_max_options = {
options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_disabled,
options.BackpackProgression.internal_name: options.BackpackProgression.option_vanilla,
options.Booksanity.internal_name: options.Booksanity.option_none,
options.BuildingProgression.internal_name: options.BuildingProgression.option_vanilla,
options.BundlePrice.internal_name: options.BundlePrice.option_expensive,
options.BundleRandomization.internal_name: options.BundleRandomization.option_shuffled,
options.Chefsanity.internal_name: options.Chefsanity.option_none,
options.Cooksanity.internal_name: options.Cooksanity.option_none,
options.Craftsanity.internal_name: options.Craftsanity.option_none,
options.Cropsanity.internal_name: options.Cropsanity.option_disabled,
options.ElevatorProgression.internal_name: options.ElevatorProgression.option_vanilla,
options.EntranceRandomization.internal_name: options.EntranceRandomization.option_disabled,
options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true,
options.FestivalLocations.internal_name: options.FestivalLocations.option_disabled,
options.Fishsanity.internal_name: options.Fishsanity.option_none,
options.Friendsanity.internal_name: options.Friendsanity.option_none,
options.FriendsanityHeartSize.internal_name: 8,
options.Goal.internal_name: options.Goal.option_craft_master,
options.Mods.internal_name: frozenset(),
options.Monstersanity.internal_name: options.Monstersanity.option_none,
options.Museumsanity.internal_name: options.Museumsanity.option_none,
options.EnabledFillerBuffs.internal_name: options.EnabledFillerBuffs.preset_all,
options.NumberOfMovementBuffs.internal_name: 12,
options.QuestLocations.internal_name: -1,
options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized,
options.Shipsanity.internal_name: options.Shipsanity.option_none,
options.SkillProgression.internal_name: options.SkillProgression.option_vanilla,
options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_vanilla,
options.ToolProgression.internal_name: options.ToolProgression.option_vanilla,
options.TrapItems.internal_name: options.TrapItems.option_nightmare,
options.Walnutsanity.internal_name: options.Walnutsanity.preset_none
}
return min_max_options
def minimal_locations_maximal_items_with_island():
min_max_options = minimal_locations_maximal_items()
min_max_options.update({options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false})
return min_max_options
class SVTestCase(unittest.TestCase):
# Set False to not skip some 'extra' tests
skip_base_tests: bool = True

View File

@@ -1,10 +1,8 @@
import random
from BaseClasses import get_seed, ItemClassification
from .. import SVTestBase, SVTestCase
from .. import SVTestBase, SVTestCase, allsanity_mods_6_x_x, fill_dataclass_with_default
from ..assertion import ModAssertMixin, WorldAssertMixin
from ..options.presets import allsanity_mods_6_x_x
from ..options.utils import fill_dataclass_with_default
from ... import options, items, Group, create_content
from ...mods.mod_data import ModNames
from ...options import SkillProgression, Walnutsanity

View File

@@ -1,164 +0,0 @@
from ... import options
def default_6_x_x():
return {
options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.default,
options.BackpackProgression.internal_name: options.BackpackProgression.default,
options.Booksanity.internal_name: options.Booksanity.default,
options.BuildingProgression.internal_name: options.BuildingProgression.default,
options.BundlePrice.internal_name: options.BundlePrice.default,
options.BundleRandomization.internal_name: options.BundleRandomization.default,
options.Chefsanity.internal_name: options.Chefsanity.default,
options.Cooksanity.internal_name: options.Cooksanity.default,
options.Craftsanity.internal_name: options.Craftsanity.default,
options.Cropsanity.internal_name: options.Cropsanity.default,
options.ElevatorProgression.internal_name: options.ElevatorProgression.default,
options.EntranceRandomization.internal_name: options.EntranceRandomization.default,
options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.default,
options.FestivalLocations.internal_name: options.FestivalLocations.default,
options.Fishsanity.internal_name: options.Fishsanity.default,
options.Friendsanity.internal_name: options.Friendsanity.default,
options.FriendsanityHeartSize.internal_name: options.FriendsanityHeartSize.default,
options.Goal.internal_name: options.Goal.default,
options.Mods.internal_name: options.Mods.default,
options.Monstersanity.internal_name: options.Monstersanity.default,
options.Museumsanity.internal_name: options.Museumsanity.default,
options.NumberOfMovementBuffs.internal_name: options.NumberOfMovementBuffs.default,
options.EnabledFillerBuffs.internal_name: options.EnabledFillerBuffs.default,
options.QuestLocations.internal_name: options.QuestLocations.default,
options.SeasonRandomization.internal_name: options.SeasonRandomization.default,
options.Shipsanity.internal_name: options.Shipsanity.default,
options.SkillProgression.internal_name: options.SkillProgression.default,
options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.default,
options.ToolProgression.internal_name: options.ToolProgression.default,
options.TrapItems.internal_name: options.TrapItems.default,
options.Walnutsanity.internal_name: options.Walnutsanity.default
}
def allsanity_no_mods_6_x_x():
return {
options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_full_shuffling,
options.BackpackProgression.internal_name: options.BackpackProgression.option_progressive,
options.Booksanity.internal_name: options.Booksanity.option_all,
options.BuildingProgression.internal_name: options.BuildingProgression.option_progressive,
options.BundlePrice.internal_name: options.BundlePrice.option_expensive,
options.BundleRandomization.internal_name: options.BundleRandomization.option_thematic,
options.Chefsanity.internal_name: options.Chefsanity.option_all,
options.Cooksanity.internal_name: options.Cooksanity.option_all,
options.Craftsanity.internal_name: options.Craftsanity.option_all,
options.Cropsanity.internal_name: options.Cropsanity.option_enabled,
options.ElevatorProgression.internal_name: options.ElevatorProgression.option_progressive,
options.EntranceRandomization.internal_name: options.EntranceRandomization.option_disabled,
options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false,
options.FestivalLocations.internal_name: options.FestivalLocations.option_hard,
options.Fishsanity.internal_name: options.Fishsanity.option_all,
options.Friendsanity.internal_name: options.Friendsanity.option_all_with_marriage,
options.FriendsanityHeartSize.internal_name: 1,
options.Goal.internal_name: options.Goal.option_perfection,
options.Mods.internal_name: frozenset(),
options.Monstersanity.internal_name: options.Monstersanity.option_progressive_goals,
options.Museumsanity.internal_name: options.Museumsanity.option_all,
options.EnabledFillerBuffs.internal_name: options.EnabledFillerBuffs.preset_all,
options.NumberOfMovementBuffs.internal_name: 12,
options.QuestLocations.internal_name: 56,
options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized,
options.Shipsanity.internal_name: options.Shipsanity.option_everything,
options.SkillProgression.internal_name: options.SkillProgression.option_progressive_with_masteries,
options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_board_qi,
options.ToolProgression.internal_name: options.ToolProgression.option_progressive,
options.TrapItems.internal_name: options.TrapItems.option_nightmare,
options.Walnutsanity.internal_name: options.Walnutsanity.preset_all
}
def allsanity_mods_6_x_x_exclude_disabled():
allsanity = allsanity_no_mods_6_x_x()
allsanity.update({options.Mods.internal_name: frozenset(options.enabled_mods)})
return allsanity
def allsanity_mods_6_x_x():
allsanity = allsanity_no_mods_6_x_x()
allsanity.update({options.Mods.internal_name: frozenset(options.all_mods)})
return allsanity
def get_minsanity_options():
return {
options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_disabled,
options.BackpackProgression.internal_name: options.BackpackProgression.option_vanilla,
options.Booksanity.internal_name: options.Booksanity.option_none,
options.BuildingProgression.internal_name: options.BuildingProgression.option_vanilla,
options.BundlePrice.internal_name: options.BundlePrice.option_very_cheap,
options.BundleRandomization.internal_name: options.BundleRandomization.option_vanilla,
options.Chefsanity.internal_name: options.Chefsanity.option_none,
options.Cooksanity.internal_name: options.Cooksanity.option_none,
options.Craftsanity.internal_name: options.Craftsanity.option_none,
options.Cropsanity.internal_name: options.Cropsanity.option_disabled,
options.ElevatorProgression.internal_name: options.ElevatorProgression.option_vanilla,
options.EntranceRandomization.internal_name: options.EntranceRandomization.option_disabled,
options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true,
options.FestivalLocations.internal_name: options.FestivalLocations.option_disabled,
options.Fishsanity.internal_name: options.Fishsanity.option_none,
options.Friendsanity.internal_name: options.Friendsanity.option_none,
options.FriendsanityHeartSize.internal_name: 8,
options.Goal.internal_name: options.Goal.option_bottom_of_the_mines,
options.Mods.internal_name: frozenset(),
options.Monstersanity.internal_name: options.Monstersanity.option_none,
options.Museumsanity.internal_name: options.Museumsanity.option_none,
options.EnabledFillerBuffs.internal_name: options.EnabledFillerBuffs.preset_none,
options.NumberOfMovementBuffs.internal_name: 0,
options.QuestLocations.internal_name: -1,
options.SeasonRandomization.internal_name: options.SeasonRandomization.option_disabled,
options.Shipsanity.internal_name: options.Shipsanity.option_none,
options.SkillProgression.internal_name: options.SkillProgression.option_vanilla,
options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_vanilla,
options.ToolProgression.internal_name: options.ToolProgression.option_vanilla,
options.TrapItems.internal_name: options.TrapItems.option_no_traps,
options.Walnutsanity.internal_name: options.Walnutsanity.preset_none
}
def minimal_locations_maximal_items():
min_max_options = {
options.ArcadeMachineLocations.internal_name: options.ArcadeMachineLocations.option_disabled,
options.BackpackProgression.internal_name: options.BackpackProgression.option_vanilla,
options.Booksanity.internal_name: options.Booksanity.option_none,
options.BuildingProgression.internal_name: options.BuildingProgression.option_vanilla,
options.BundlePrice.internal_name: options.BundlePrice.option_expensive,
options.BundleRandomization.internal_name: options.BundleRandomization.option_shuffled,
options.Chefsanity.internal_name: options.Chefsanity.option_none,
options.Cooksanity.internal_name: options.Cooksanity.option_none,
options.Craftsanity.internal_name: options.Craftsanity.option_none,
options.Cropsanity.internal_name: options.Cropsanity.option_disabled,
options.ElevatorProgression.internal_name: options.ElevatorProgression.option_vanilla,
options.EntranceRandomization.internal_name: options.EntranceRandomization.option_disabled,
options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_true,
options.FestivalLocations.internal_name: options.FestivalLocations.option_disabled,
options.Fishsanity.internal_name: options.Fishsanity.option_none,
options.Friendsanity.internal_name: options.Friendsanity.option_none,
options.FriendsanityHeartSize.internal_name: 8,
options.Goal.internal_name: options.Goal.option_craft_master,
options.Mods.internal_name: frozenset(),
options.Monstersanity.internal_name: options.Monstersanity.option_none,
options.Museumsanity.internal_name: options.Museumsanity.option_none,
options.EnabledFillerBuffs.internal_name: options.EnabledFillerBuffs.preset_all,
options.NumberOfMovementBuffs.internal_name: 12,
options.QuestLocations.internal_name: -1,
options.SeasonRandomization.internal_name: options.SeasonRandomization.option_randomized,
options.Shipsanity.internal_name: options.Shipsanity.option_none,
options.SkillProgression.internal_name: options.SkillProgression.option_vanilla,
options.SpecialOrderLocations.internal_name: options.SpecialOrderLocations.option_vanilla,
options.ToolProgression.internal_name: options.ToolProgression.option_vanilla,
options.TrapItems.internal_name: options.TrapItems.option_nightmare,
options.Walnutsanity.internal_name: options.Walnutsanity.preset_none
}
return min_max_options
def minimal_locations_maximal_items_with_island():
min_max_options = minimal_locations_maximal_items()
min_max_options.update({options.ExcludeGingerIsland.internal_name: options.ExcludeGingerIsland.option_false})
return min_max_options

View File

@@ -8,8 +8,7 @@ from typing import List
from BaseClasses import get_seed
from Fill import distribute_items_restrictive, balance_multiworld_progression
from worlds import AutoWorld
from .. import SVTestCase, setup_multiworld
from ..options.presets import default_6_x_x, allsanity_no_mods_6_x_x, allsanity_mods_6_x_x, minimal_locations_maximal_items
from .. import SVTestCase, minimal_locations_maximal_items, setup_multiworld, default_6_x_x, allsanity_no_mods_6_x_x, allsanity_mods_6_x_x
assert default_6_x_x
assert allsanity_no_mods_6_x_x

View File

@@ -1,5 +1,4 @@
from .. import SVTestBase
from ..options.presets import allsanity_mods_6_x_x
from .. import SVTestBase, allsanity_mods_6_x_x
from ...stardew_rule import HasProgressionPercent

View File

@@ -1,9 +1,8 @@
import argparse
import json
from .. import setup_solo_multiworld
from ..options.presets import allsanity_mods_6_x_x
from ...options import FarmType, EntranceRandomization
from ...test import setup_solo_multiworld, allsanity_mods_6_x_x
if __name__ == "__main__":
parser = argparse.ArgumentParser()

View File

@@ -1,8 +1,7 @@
import unittest
from unittest.mock import Mock
from .. import SVTestBase, fill_namespace_with_default
from ..options.presets import allsanity_mods_6_x_x
from .. import SVTestBase, allsanity_mods_6_x_x, fill_namespace_with_default
from ... import STARDEW_VALLEY, FarmType, BundleRandomization, EntranceRandomization

View File

@@ -1 +1 @@
bsdiff4>=1.2.2
bsdiff4>=1.2.2

View File

@@ -689,9 +689,7 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
atoll_statue = regions["Ruined Atoll"].connect(
connecting_region=regions["Ruined Atoll Statue"],
rule=lambda state: has_ability(prayer, state, world)
and ((has_ladder("Ladders in South Atoll", state, world)
and state.has_any((laurels, grapple), player)
and (has_sword(state, player) or state.has_any((fire_wand, gun), player)))
and (has_ladder("Ladders in South Atoll", state, world)
# shoot fuse and have the shot hit you mid-LS
or (can_ladder_storage(state, world) and state.has(fire_wand, player)
and options.ladder_storage >= LadderStorage.option_hard)))
@@ -1085,7 +1083,6 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
swamp_mid_to_cath = regions["Swamp Mid"].connect(
connecting_region=regions["Swamp to Cathedral Main Entrance Region"],
rule=lambda state: (has_ability(prayer, state, world)
and (has_sword(state, player))
and (state.has(laurels, player)
# blam yourself in the face with a wand shot off the fuse
or (can_ladder_storage(state, world) and state.has(fire_wand, player)

View File

@@ -125,8 +125,7 @@ def set_region_rules(world: "TunicWorld") -> None:
# there's some boxes in the way
and (has_melee(state, player) or state.has_any((gun, grapple, fire_wand), player)))
world.get_entrance("Ruined Atoll -> Library").access_rule = \
lambda state: (state.has_any({grapple, laurels}, player) and has_ability(prayer, state, world)
and (has_sword(state, player) or state.has_any((fire_wand, gun), player)))
lambda state: state.has_any({grapple, laurels}, player) and has_ability(prayer, state, world)
world.get_entrance("Overworld -> Quarry").access_rule = \
lambda state: (has_sword(state, player) or state.has(fire_wand, player)) \
and (state.has_any({grapple, laurels, gun}, player) or can_ladder_storage(state, world))
@@ -142,7 +141,7 @@ def set_region_rules(world: "TunicWorld") -> None:
world.get_entrance("Lower Quarry -> Rooted Ziggurat").access_rule = \
lambda state: state.has(grapple, player) and has_ability(prayer, state, world)
world.get_entrance("Swamp -> Cathedral").access_rule = \
lambda state: (state.has(laurels, player) and has_ability(prayer, state, world) and has_sword(state, player)) \
lambda state: (state.has(laurels, player) and has_ability(prayer, state, world)) \
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)
world.get_entrance("Overworld -> Spirit Arena").access_rule = \
lambda state: ((state.has(gold_hexagon, player, options.hexagon_goal.value) if options.hexagon_quest.value

View File

@@ -1,373 +0,0 @@
from collections.abc import Iterable
from typing import TYPE_CHECKING, NamedTuple, Optional
from BaseClasses import Item
from BaseClasses import ItemClassification as IC
from worlds.AutoWorld import World
if TYPE_CHECKING:
from .randomizers.Dungeons import Dungeon
def item_factory(items: str | Iterable[str], world: World) -> Item | list[Item]:
"""
Create items based on their names.
Depending on the input, this function can return a single item or a list of items.
:param items: The name or names of the items to create.
:param world: The game world.
:raises KeyError: If an unknown item name is provided.
:return: A single item or a list of items.
"""
ret: list[Item] = []
singleton = False
if isinstance(items, str):
items = [items]
singleton = True
for item in items:
if item in ITEM_TABLE:
ret.append(world.create_item(item))
else:
raise KeyError(f"Unknown item {item}")
return ret[0] if singleton else ret
class TWWItemData(NamedTuple):
"""
This class represents the data for an item in The Wind Waker.
:param type: The type of the item (e.g., "Item", "Dungeon Item").
:param classification: The item's classification (progression, useful, filler).
:param code: The unique code identifier for the item.
:param quantity: The number of this item available.
:param item_id: The ID used to represent the item in-game.
"""
type: str
classification: IC
code: Optional[int]
quantity: int
item_id: Optional[int]
class TWWItem(Item):
"""
This class represents an item in The Wind Waker.
:param name: The item's name.
:param player: The ID of the player who owns the item.
:param data: The data associated with this item.
:param classification: Optional classification to override the default.
"""
game: str = "The Wind Waker"
type: Optional[str]
dungeon: Optional["Dungeon"] = None
def __init__(self, name: str, player: int, data: TWWItemData, classification: Optional[IC] = None) -> None:
super().__init__(
name,
data.classification if classification is None else classification,
None if data.code is None else TWWItem.get_apid(data.code),
player,
)
self.type = data.type
self.item_id = data.item_id
@staticmethod
def get_apid(code: int) -> int:
"""
Compute the Archipelago ID for the given item code.
:param code: The unique code for the item.
:return: The computed Archipelago ID.
"""
base_id: int = 2322432
return base_id + code
@property
def dungeon_item(self) -> Optional[str]:
"""
Determine if the item is a dungeon item and, if so, returns its type.
:return: The type of dungeon item, or `None` if it is not a dungeon item.
"""
if self.type in ("Small Key", "Big Key", "Map", "Compass"):
return self.type
return None
ITEM_TABLE: dict[str, TWWItemData] = {
"Telescope": TWWItemData("Item", IC.useful, 0, 1, 0x20),
# "Boat's Sail": TWWItemData("Item", IC.progression, 1, 1, 0x78), # noqa: E131
"Wind Waker": TWWItemData("Item", IC.progression, 2, 1, 0x22),
"Grappling Hook": TWWItemData("Item", IC.progression, 3, 1, 0x25),
"Spoils Bag": TWWItemData("Item", IC.progression, 4, 1, 0x24),
"Boomerang": TWWItemData("Item", IC.progression, 5, 1, 0x2D),
"Deku Leaf": TWWItemData("Item", IC.progression, 6, 1, 0x34),
"Tingle Tuner": TWWItemData("Item", IC.progression, 7, 1, 0x21),
"Iron Boots": TWWItemData("Item", IC.progression, 8, 1, 0x29),
"Magic Armor": TWWItemData("Item", IC.progression, 9, 1, 0x2A),
"Bait Bag": TWWItemData("Item", IC.progression, 10, 1, 0x2C),
"Bombs": TWWItemData("Item", IC.progression, 11, 1, 0x31),
"Delivery Bag": TWWItemData("Item", IC.progression, 12, 1, 0x30),
"Hookshot": TWWItemData("Item", IC.progression, 13, 1, 0x2F),
"Skull Hammer": TWWItemData("Item", IC.progression, 14, 1, 0x33),
"Power Bracelets": TWWItemData("Item", IC.progression, 15, 1, 0x28),
"Hero's Charm": TWWItemData("Item", IC.useful, 16, 1, 0x43),
"Hurricane Spin": TWWItemData("Item", IC.useful, 17, 1, 0xAA),
"Dragon Tingle Statue": TWWItemData("Item", IC.progression, 18, 1, 0xA3),
"Forbidden Tingle Statue": TWWItemData("Item", IC.progression, 19, 1, 0xA4),
"Goddess Tingle Statue": TWWItemData("Item", IC.progression, 20, 1, 0xA5),
"Earth Tingle Statue": TWWItemData("Item", IC.progression, 21, 1, 0xA6),
"Wind Tingle Statue": TWWItemData("Item", IC.progression, 22, 1, 0xA7),
"Wind's Requiem": TWWItemData("Item", IC.progression, 23, 1, 0x6D),
"Ballad of Gales": TWWItemData("Item", IC.progression, 24, 1, 0x6E),
"Command Melody": TWWItemData("Item", IC.progression, 25, 1, 0x6F),
"Earth God's Lyric": TWWItemData("Item", IC.progression, 26, 1, 0x70),
"Wind God's Aria": TWWItemData("Item", IC.progression, 27, 1, 0x71),
"Song of Passing": TWWItemData("Item", IC.progression, 28, 1, 0x72),
"Triforce Shard 1": TWWItemData("Item", IC.progression, 29, 1, 0x61),
"Triforce Shard 2": TWWItemData("Item", IC.progression, 30, 1, 0x62),
"Triforce Shard 3": TWWItemData("Item", IC.progression, 31, 1, 0x63),
"Triforce Shard 4": TWWItemData("Item", IC.progression, 32, 1, 0x64),
"Triforce Shard 5": TWWItemData("Item", IC.progression, 33, 1, 0x65),
"Triforce Shard 6": TWWItemData("Item", IC.progression, 34, 1, 0x66),
"Triforce Shard 7": TWWItemData("Item", IC.progression, 35, 1, 0x67),
"Triforce Shard 8": TWWItemData("Item", IC.progression, 36, 1, 0x68),
"Skull Necklace": TWWItemData("Item", IC.filler, 37, 9, 0x45),
"Boko Baba Seed": TWWItemData("Item", IC.filler, 38, 1, 0x46),
"Golden Feather": TWWItemData("Item", IC.filler, 39, 9, 0x47),
"Knight's Crest": TWWItemData("Item", IC.filler, 40, 3, 0x48),
"Red Chu Jelly": TWWItemData("Item", IC.filler, 41, 1, 0x49),
"Green Chu Jelly": TWWItemData("Item", IC.filler, 42, 1, 0x4A),
"Joy Pendant": TWWItemData("Item", IC.filler, 43, 20, 0x1F),
"All-Purpose Bait": TWWItemData("Item", IC.filler, 44, 1, 0x82),
"Hyoi Pear": TWWItemData("Item", IC.filler, 45, 4, 0x83),
"Note to Mom": TWWItemData("Item", IC.progression, 46, 1, 0x99),
"Maggie's Letter": TWWItemData("Item", IC.progression, 47, 1, 0x9A),
"Moblin's Letter": TWWItemData("Item", IC.progression, 48, 1, 0x9B),
"Cabana Deed": TWWItemData("Item", IC.progression, 49, 1, 0x9C),
"Fill-Up Coupon": TWWItemData("Item", IC.useful, 50, 1, 0x9E),
"Nayru's Pearl": TWWItemData("Item", IC.progression, 51, 1, 0x69),
"Din's Pearl": TWWItemData("Item", IC.progression, 52, 1, 0x6A),
"Farore's Pearl": TWWItemData("Item", IC.progression, 53, 1, 0x6B),
"Progressive Sword": TWWItemData("Item", IC.progression, 54, 4, 0x38),
"Progressive Shield": TWWItemData("Item", IC.progression, 55, 2, 0x3B),
"Progressive Picto Box": TWWItemData("Item", IC.progression, 56, 2, 0x23),
"Progressive Bow": TWWItemData("Item", IC.progression, 57, 3, 0x27),
"Progressive Magic Meter": TWWItemData("Item", IC.progression, 58, 2, 0xB1),
"Quiver Capacity Upgrade": TWWItemData("Item", IC.progression, 59, 2, 0xAF),
"Bomb Bag Capacity Upgrade": TWWItemData("Item", IC.useful, 60, 2, 0xAD),
"Wallet Capacity Upgrade": TWWItemData("Item", IC.progression, 61, 2, 0xAB),
"Empty Bottle": TWWItemData("Item", IC.progression, 62, 4, 0x50),
"Triforce Chart 1": TWWItemData("Item", IC.progression_skip_balancing, 63, 1, 0xFE),
"Triforce Chart 2": TWWItemData("Item", IC.progression_skip_balancing, 64, 1, 0xFD),
"Triforce Chart 3": TWWItemData("Item", IC.progression_skip_balancing, 65, 1, 0xFC),
"Triforce Chart 4": TWWItemData("Item", IC.progression_skip_balancing, 66, 1, 0xFB),
"Triforce Chart 5": TWWItemData("Item", IC.progression_skip_balancing, 67, 1, 0xFA),
"Triforce Chart 6": TWWItemData("Item", IC.progression_skip_balancing, 68, 1, 0xF9),
"Triforce Chart 7": TWWItemData("Item", IC.progression_skip_balancing, 69, 1, 0xF8),
"Triforce Chart 8": TWWItemData("Item", IC.progression_skip_balancing, 70, 1, 0xF7),
"Treasure Chart 1": TWWItemData("Item", IC.progression_skip_balancing, 71, 1, 0xE7),
"Treasure Chart 2": TWWItemData("Item", IC.progression_skip_balancing, 72, 1, 0xEE),
"Treasure Chart 3": TWWItemData("Item", IC.progression_skip_balancing, 73, 1, 0xE0),
"Treasure Chart 4": TWWItemData("Item", IC.progression_skip_balancing, 74, 1, 0xE1),
"Treasure Chart 5": TWWItemData("Item", IC.progression_skip_balancing, 75, 1, 0xF2),
"Treasure Chart 6": TWWItemData("Item", IC.progression_skip_balancing, 76, 1, 0xEA),
"Treasure Chart 7": TWWItemData("Item", IC.progression_skip_balancing, 77, 1, 0xCC),
"Treasure Chart 8": TWWItemData("Item", IC.progression_skip_balancing, 78, 1, 0xD4),
"Treasure Chart 9": TWWItemData("Item", IC.progression_skip_balancing, 79, 1, 0xDA),
"Treasure Chart 10": TWWItemData("Item", IC.progression_skip_balancing, 80, 1, 0xDE),
"Treasure Chart 11": TWWItemData("Item", IC.progression_skip_balancing, 81, 1, 0xF6),
"Treasure Chart 12": TWWItemData("Item", IC.progression_skip_balancing, 82, 1, 0xE9),
"Treasure Chart 13": TWWItemData("Item", IC.progression_skip_balancing, 83, 1, 0xCF),
"Treasure Chart 14": TWWItemData("Item", IC.progression_skip_balancing, 84, 1, 0xDD),
"Treasure Chart 15": TWWItemData("Item", IC.progression_skip_balancing, 85, 1, 0xF5),
"Treasure Chart 16": TWWItemData("Item", IC.progression_skip_balancing, 86, 1, 0xE3),
"Treasure Chart 17": TWWItemData("Item", IC.progression_skip_balancing, 87, 1, 0xD7),
"Treasure Chart 18": TWWItemData("Item", IC.progression_skip_balancing, 88, 1, 0xE4),
"Treasure Chart 19": TWWItemData("Item", IC.progression_skip_balancing, 89, 1, 0xD1),
"Treasure Chart 20": TWWItemData("Item", IC.progression_skip_balancing, 90, 1, 0xF3),
"Treasure Chart 21": TWWItemData("Item", IC.progression_skip_balancing, 91, 1, 0xCE),
"Treasure Chart 22": TWWItemData("Item", IC.progression_skip_balancing, 92, 1, 0xD9),
"Treasure Chart 23": TWWItemData("Item", IC.progression_skip_balancing, 93, 1, 0xF1),
"Treasure Chart 24": TWWItemData("Item", IC.progression_skip_balancing, 94, 1, 0xEB),
"Treasure Chart 25": TWWItemData("Item", IC.progression_skip_balancing, 95, 1, 0xD6),
"Treasure Chart 26": TWWItemData("Item", IC.progression_skip_balancing, 96, 1, 0xD3),
"Treasure Chart 27": TWWItemData("Item", IC.progression_skip_balancing, 97, 1, 0xCD),
"Treasure Chart 28": TWWItemData("Item", IC.progression_skip_balancing, 98, 1, 0xE2),
"Treasure Chart 29": TWWItemData("Item", IC.progression_skip_balancing, 99, 1, 0xE6),
"Treasure Chart 30": TWWItemData("Item", IC.progression_skip_balancing, 100, 1, 0xF4),
"Treasure Chart 31": TWWItemData("Item", IC.progression_skip_balancing, 101, 1, 0xF0),
"Treasure Chart 32": TWWItemData("Item", IC.progression_skip_balancing, 102, 1, 0xD0),
"Treasure Chart 33": TWWItemData("Item", IC.progression_skip_balancing, 103, 1, 0xEF),
"Treasure Chart 34": TWWItemData("Item", IC.progression_skip_balancing, 104, 1, 0xE5),
"Treasure Chart 35": TWWItemData("Item", IC.progression_skip_balancing, 105, 1, 0xE8),
"Treasure Chart 36": TWWItemData("Item", IC.progression_skip_balancing, 106, 1, 0xD8),
"Treasure Chart 37": TWWItemData("Item", IC.progression_skip_balancing, 107, 1, 0xD5),
"Treasure Chart 38": TWWItemData("Item", IC.progression_skip_balancing, 108, 1, 0xED),
"Treasure Chart 39": TWWItemData("Item", IC.progression_skip_balancing, 109, 1, 0xEC),
"Treasure Chart 40": TWWItemData("Item", IC.progression_skip_balancing, 110, 1, 0xDF),
"Treasure Chart 41": TWWItemData("Item", IC.progression_skip_balancing, 111, 1, 0xD2),
"Tingle's Chart": TWWItemData("Item", IC.filler, 112, 1, 0xDC),
"Ghost Ship Chart": TWWItemData("Item", IC.progression, 113, 1, 0xDB),
"Octo Chart": TWWItemData("Item", IC.filler, 114, 1, 0xCA),
"Great Fairy Chart": TWWItemData("Item", IC.filler, 115, 1, 0xC9),
"Secret Cave Chart": TWWItemData("Item", IC.filler, 116, 1, 0xC6),
"Light Ring Chart": TWWItemData("Item", IC.filler, 117, 1, 0xC5),
"Platform Chart": TWWItemData("Item", IC.filler, 118, 1, 0xC4),
"Beedle's Chart": TWWItemData("Item", IC.filler, 119, 1, 0xC3),
"Submarine Chart": TWWItemData("Item", IC.filler, 120, 1, 0xC2),
"Green Rupee": TWWItemData("Item", IC.filler, 121, 1, 0x01),
"Blue Rupee": TWWItemData("Item", IC.filler, 122, 2, 0x02),
"Yellow Rupee": TWWItemData("Item", IC.filler, 123, 3, 0x03),
"Red Rupee": TWWItemData("Item", IC.filler, 124, 8, 0x04),
"Purple Rupee": TWWItemData("Item", IC.filler, 125, 10, 0x05),
"Orange Rupee": TWWItemData("Item", IC.useful, 126, 15, 0x06),
"Silver Rupee": TWWItemData("Item", IC.useful, 127, 20, 0x0F),
"Rainbow Rupee": TWWItemData("Item", IC.useful, 128, 1, 0xB8),
"Piece of Heart": TWWItemData("Item", IC.useful, 129, 44, 0x07),
"Heart Container": TWWItemData("Item", IC.useful, 130, 6, 0x08),
"DRC Big Key": TWWItemData("Big Key", IC.progression, 131, 1, 0x14),
"DRC Small Key": TWWItemData("Small Key", IC.progression, 132, 4, 0x13),
"FW Big Key": TWWItemData("Big Key", IC.progression, 133, 1, 0x40),
"FW Small Key": TWWItemData("Small Key", IC.progression, 134, 1, 0x1D),
"TotG Big Key": TWWItemData("Big Key", IC.progression, 135, 1, 0x5C),
"TotG Small Key": TWWItemData("Small Key", IC.progression, 136, 2, 0x5B),
"ET Big Key": TWWItemData("Big Key", IC.progression, 138, 1, 0x74),
"ET Small Key": TWWItemData("Small Key", IC.progression, 139, 3, 0x73),
"WT Big Key": TWWItemData("Big Key", IC.progression, 140, 1, 0x81),
"WT Small Key": TWWItemData("Small Key", IC.progression, 141, 2, 0x77),
"DRC Dungeon Map": TWWItemData("Map", IC.filler, 142, 1, 0x1B),
"DRC Compass": TWWItemData("Compass", IC.filler, 143, 1, 0x1C),
"FW Dungeon Map": TWWItemData("Map", IC.filler, 144, 1, 0x41),
"FW Compass": TWWItemData("Compass", IC.filler, 145, 1, 0x5A),
"TotG Dungeon Map": TWWItemData("Map", IC.filler, 146, 1, 0x5D),
"TotG Compass": TWWItemData("Compass", IC.filler, 147, 1, 0x5E),
"FF Dungeon Map": TWWItemData("Map", IC.filler, 148, 1, 0x5F),
"FF Compass": TWWItemData("Compass", IC.filler, 149, 1, 0x60),
"ET Dungeon Map": TWWItemData("Map", IC.filler, 150, 1, 0x75),
"ET Compass": TWWItemData("Compass", IC.filler, 151, 1, 0x76),
"WT Dungeon Map": TWWItemData("Map", IC.filler, 152, 1, 0x84),
"WT Compass": TWWItemData("Compass", IC.filler, 153, 1, 0x85),
"Victory": TWWItemData("Event", IC.progression, None, 1, None),
}
ISLAND_NUMBER_TO_CHART_NAME = {
1: "Treasure Chart 25",
2: "Treasure Chart 7",
3: "Treasure Chart 24",
4: "Triforce Chart 2",
5: "Treasure Chart 11",
6: "Triforce Chart 7",
7: "Treasure Chart 13",
8: "Treasure Chart 41",
9: "Treasure Chart 29",
10: "Treasure Chart 22",
11: "Treasure Chart 18",
12: "Treasure Chart 30",
13: "Treasure Chart 39",
14: "Treasure Chart 19",
15: "Treasure Chart 8",
16: "Treasure Chart 2",
17: "Treasure Chart 10",
18: "Treasure Chart 26",
19: "Treasure Chart 3",
20: "Treasure Chart 37",
21: "Treasure Chart 27",
22: "Treasure Chart 38",
23: "Triforce Chart 1",
24: "Treasure Chart 21",
25: "Treasure Chart 6",
26: "Treasure Chart 14",
27: "Treasure Chart 34",
28: "Treasure Chart 5",
29: "Treasure Chart 28",
30: "Treasure Chart 35",
31: "Triforce Chart 3",
32: "Triforce Chart 6",
33: "Treasure Chart 1",
34: "Treasure Chart 20",
35: "Treasure Chart 36",
36: "Treasure Chart 23",
37: "Treasure Chart 12",
38: "Treasure Chart 16",
39: "Treasure Chart 4",
40: "Treasure Chart 17",
41: "Treasure Chart 31",
42: "Triforce Chart 5",
43: "Treasure Chart 9",
44: "Triforce Chart 4",
45: "Treasure Chart 40",
46: "Triforce Chart 8",
47: "Treasure Chart 15",
48: "Treasure Chart 32",
49: "Treasure Chart 33",
}
LOOKUP_ID_TO_NAME: dict[int, str] = {
TWWItem.get_apid(data.code): item for item, data in ITEM_TABLE.items() if data.code is not None
}
item_name_groups = {
"Songs": {
"Wind's Requiem",
"Ballad of Gales",
"Command Melody",
"Earth God's Lyric",
"Wind God's Aria",
"Song of Passing",
},
"Mail": {
"Note to Mom",
"Maggie's Letter",
"Moblin's Letter",
},
"Special Charts": {
"Tingle's Chart",
"Ghost Ship Chart",
"Octo Chart",
"Great Fairy Chart",
"Secret Cave Chart",
"Light Ring Chart",
"Platform Chart",
"Beedle's Chart",
"Submarine Chart",
},
}
# generic groups, (Name, substring)
_simple_groups = {
("Tingle Statues", "Tingle Statue"),
("Shards", "Shard"),
("Pearls", "Pearl"),
("Triforce Charts", "Triforce Chart"),
("Treasure Charts", "Treasure Chart"),
("Small Keys", "Small Key"),
("Big Keys", "Big Key"),
("Rupees", "Rupee"),
("Dungeon Items", "Compass"),
("Dungeon Items", "Map"),
}
for basename, substring in _simple_groups:
if basename not in item_name_groups:
item_name_groups[basename] = set()
for itemname in ITEM_TABLE:
if substring in itemname:
item_name_groups[basename].add(itemname)

File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More