Files
dockipelago/worlds/dsr/__init__.py
Jonathan Tinney 7971961166
Some checks failed
Analyze modified files / flake8 (push) Failing after 2m28s
Build / build-win (push) Has been cancelled
Build / build-ubuntu2204 (push) Has been cancelled
ctest / Test C++ ubuntu-latest (push) Has been cancelled
ctest / Test C++ windows-latest (push) Has been cancelled
Analyze modified files / mypy (push) Has been cancelled
Build and Publish Docker Images / Push Docker image to Docker Hub (push) Successful in 5m4s
Native Code Static Analysis / scan-build (push) Failing after 5m2s
type check / pyright (push) Successful in 1m7s
unittests / Test Python 3.11.2 ubuntu-latest (push) Failing after 16m23s
unittests / Test Python 3.12 ubuntu-latest (push) Failing after 28m19s
unittests / Test Python 3.13 ubuntu-latest (push) Failing after 14m49s
unittests / Test hosting with 3.13 on ubuntu-latest (push) Successful in 5m0s
unittests / Test Python 3.13 macos-latest (push) Has been cancelled
unittests / Test Python 3.11 windows-latest (push) Has been cancelled
unittests / Test Python 3.13 windows-latest (push) Has been cancelled
add schedule I, sonic 1/frontiers/heroes, spirit island
2026-04-02 23:46:36 -07:00

942 lines
62 KiB
Python

# world/dsr/__init__.py
from typing import Dict, Set, List, ClassVar, TextIO, Any
from BaseClasses import MultiWorld, Region, Item, Entrance, Tutorial, ItemClassification
from Options import Toggle, OptionError
from worlds.AutoWorld import World, WebWorld
from worlds.generic.Rules import set_rule, add_rule, add_item_rule
from .Items import DSRItem, DSRItemCategory, item_dictionary, key_item_names, item_descriptions, BuildRequiredItemPool, BuildGuaranteedItemPool, UpgradeEquipment
from .Locations import DSRLocation, DSRLocationCategory, location_tables, location_dictionary, location_skip_categories, location_locked_categories
from .Groups import location_name_groups, item_name_groups
from .Options import DSROption, option_groups, LogicToAccessCatacombs
from settings import Group, FilePath
class DSRWeb(WebWorld):
bug_report_page = ""
theme = "stone"
setup_en = Tutorial(
"Multiworld Setup Guide",
"A guide to setting up the Archipelago Dark Souls Remastered randomizer on your computer.",
"English",
"setup_en.md",
"setup/en",
["noka, ArsonAssassin"]
)
option_groups = option_groups
tutorials = [setup_en]
class DSRSettings(Group):
class UTPoptrackerPath(FilePath):
"""Path to the user's DSR Poptracker Pack."""
description= "DSR Poptracker Pack zip file"
required = False
ut_poptracker_path: UTPoptrackerPath | str = UTPoptrackerPath()
class DSRWorld(World):
"""
Dark Souls is a game where you die.
"""
game: str = "Dark Souls Remastered"
options_dataclass = DSROption
options: DSROption
topology_present: bool = True
web = DSRWeb()
data_version = 0
base_id = 11110000
enabled_location_categories: Set[DSRLocationCategory]
required_client_version = (0, 5, 1)
item_name_to_id = DSRItem.get_name_to_id()
location_name_to_id = DSRLocation.get_name_to_id()
item_name_groups = item_name_groups
item_descriptions = item_descriptions
location_name_groups = location_name_groups
settings: ClassVar[DSRSettings]
# Start UT (Universal Tracker) support
# UT map import from poptracker support
tracker_world: ClassVar = {
"map_page_maps" : "maps/maps.json",
"map_page_locations" : "locations/locations.json",
"external_pack_key" : "ut_poptracker_path"
}
# Tell UT we don't need a yaml
ut_can_gen_without_yaml = True
# Define function for it to get the options
@staticmethod
def interpret_slot_data(slot_data: dict[str, Any]) -> dict[str, Any]:
# Trigger a regen in UT
return slot_data
# End UT support
gc = 0 # good create
bc = 0 # "bad" create (ignored item)
bw = 0 # bonfire warp
def __init__(self, multiworld: MultiWorld, player: int):
super().__init__(multiworld, player)
self.locked_items = []
self.locked_locations = []
self.main_path_locations = []
self.enabled_location_categories = set()
self.all_excluded_locations = set()
def generate_early(self):
# Start UT yamlless support
re_gen_passthrough = getattr(self.multiworld, "re_gen_passthrough", {})
if re_gen_passthrough and self.game in re_gen_passthrough:
# Get the passed through slot data from the real generation
slot_data: dict[str, Any] = re_gen_passthrough[self.game]
slot_options: dict[str, Any] = slot_data.get("options", {})
# Set all your options here instead of getting them from the yaml
for key, value in slot_options.items():
opt: Optional[Option] = getattr(self.options, key, None)
if opt is not None:
# You can also set .value directly but that won't work if you have OptionSets
setattr(self.options, key, opt.from_any(value))
# End UT yamlless support
# if upgrade level max < min, reverse them
if self.options.upgraded_weapons_percentage.value > 0 and self.options.upgraded_weapons_max_level.value < self.options.upgraded_weapons_min_level.value:
(self.options.upgraded_weapons_min_level, self.options.upgraded_weapons_max_level) = (self.options.upgraded_weapons_max_level, self.options.upgraded_weapons_min_level)
# If % > 0 but no allowed infusion types, default to normal
if self.options.upgraded_weapons_percentage.value > 0 and len(self.options.upgraded_weapons_allowed_infusions.value) == 0:
self.options.upgraded_weapons_allowed_infusions.value = ['Normal']
self.enabled_location_categories.add(DSRLocationCategory.EVENT)
self.enabled_location_categories.add(DSRLocationCategory.BOSS)
self.enabled_location_categories.add(DSRLocationCategory.ITEM_LOT)
self.enabled_location_categories.add(DSRLocationCategory.BONFIRE_WARP)
# self.enabled_location_categories.add(DSRLocationCategory.DOOR)
if (self.options.fogwall_sanity.value == True):
self.enabled_location_categories.add(DSRLocationCategory.FOG_WALL)
if (self.options.boss_fogwall_sanity.value == True):
self.enabled_location_categories.add(DSRLocationCategory.BOSS_FOG_WALL)
self.all_excluded_locations.update(self.options.exclude_locations.value)
def create_regions(self):
# Create Regions
regions: Dict[str, Region] = {}
regions["Menu"] = self.create_region("Menu", [])
our_regions = [
"Undead Asylum Cell",
"Undead Asylum Cell Door",
"Northern Undead Asylum - F2 East Door",
"Northern Undead Asylum",
"Northern Undead Asylum - After Fog",
"Northern Undead Asylum - After F2 East Door",
"Northern Undead Asylum - Big Pilgrim Door",
"Firelink Shrine",
"Upper Undead Burg - Before Fog",
"Upper Undead Burg - Fog",
"Upper Undead Burg",
"Upper Undead Burg - Pine Resin Chest",
"Upper Undead Burg - Taurus Demon",
"Upper Undead Burg - Hellkite Bridge",
"Undead Parish - Before Fog",
"Undead Parish - Fog",
"Undead Parish",
"Undead Parish - Bell Gargoyles",
"Firelink Shrine - After Undead Parish Elevator",
"Northern Undead Asylum Second Visit",
"Northern Undead Asylum Second Visit - F2 West Door",
"Northern Undead Asylum Second Visit - Behind F2 West Door",
"Northern Undead Asylum Second Visit - Snuggly Trades",
"Undead Burg Basement Door",
"Lower Undead Burg",
"Lower Undead Burg - After Residence Key",
"Lower Undead Burg - Capra Demon",
"Lower Undead Burg - After Capra Demon",
"Watchtower Basement",
"Depths",
"Depths - After Sewer Chamber Key",
"Depths - Gaping Dragon",
"Depths - After Gaping Dragon",
"Depths to Blighttown Door",
"Upper Blighttown Depths Side",
"Upper Blighttown VotD Side",
"Lower Blighttown - Fog",
"Lower Blighttown",
"Lower Blighttown - Quelaag",
"Lower Blighttown - After Quelaag",
"Valley of the Drakes",
"Valley of the Drakes - After Defeating Four Kings",
"Door between Upper New Londo and Valley of the Drakes",
"Darkroot Basin",
"Darkroot Garden - Before Fog",
"Darkroot Garden",
"Darkroot Garden - Behind Artorias Door",
"Darkroot Garden - Moonlight Butterfly",
"Darkroot Garden - After Moonlight Butterfly",
"The Great Hollow",
"Ash Lake",
"Sen's Fortress",
"Sen's Fortress - After First Fog",
"Sen's Fortress - After Second Fog",
"Sen's Fortress - After Cage Key",
"Sen's Fortress - Iron Golem",
"Sen's Fortress - After Iron Golem",
"Anor Londo",
"Anor Londo - After First Fog",
"Anor Londo - After Second Fog",
"Anor Londo - Ornstein and Smough",
"Anor Londo - After Ornstein and Smough",
"Anor Londo - Gwyndolin",
"Anor Londo - After Gwyndolin",
"Painted World of Ariamis",
"Painted World of Ariamis - After Fog",
"Painted World of Ariamis - After Annex Key",
"Painted World of Ariamis - Crossbreed Priscilla",
"Upper New Londo Ruins",
"Upper New Londo Ruins - After Fog",
"New Londo Ruins Door to the Seal",
"Lower New Londo Ruins",
"The Abyss",
"The Abyss - After Four Kings",
"The Duke's Archives",
"The Duke's Archives - After First Seath Encounter",
"The Duke's Archives - After Archive Tower Cell Key",
"The Duke's Archives - After Archive Prison Extra Key",
"The Duke's Archives - Out of Cell",
"The Duke's Archives - After Archive Tower Giant Door Key",
"The Duke's Archives - Courtyard",
"The Duke's Archives - Giant Cell",
"Crystal Cave",
"Crystal Cave - After Seath",
"The Duke's Archives - First Arena after Seath's Death",
"Demon Ruins - Early",
"Demon Ruins - Ceaseless Discharge",
"Demon Ruins",
"Demon Ruins - Demon Firesage",
"Demon Ruins - After Demon Firesage",
"Demon Ruins - Centipede Demon",
"Demon Ruins Shortcut",
"Lost Izalith",
"Lost Izalith - Bed of Chaos",
"The Catacombs",
"The Catacombs - Door 1",
"The Catacombs - After Door 1",
"The Catacombs - Pinwheel",
"The Catacombs - After Pinwheel",
"Tomb of the Giants",
"Tomb of the Giants - After White Fog",
"Tomb of the Giants - Behind Golden Fog Wall",
"Tomb of the Giants - Nito",
"Tomb of the Giants - After Nito",
"Firelink Altar",
"Kiln of the First Flame",
"Kiln of the First Flame - Gwyn",
"Sanctuary Garden",
"Sanctuary Garden - Sanctuary Guardian",
"Oolacile Sanctuary",
"Royal Wood",
"Royal Wood - Artorias",
"Royal Wood - After Hawkeye Gough",
"Oolacile Township",
"Oolacile Township - Behind Light-Dispelled Walls",
"Oolacile Township - After Crest Key",
"Chasm of the Abyss",
"Chasm of the Abyss - Manus",
]
regions.update({region_name: self.create_region(region_name, location_tables[region_name]) for region_name in our_regions})
print("DSR: created " + str(self.gc) + " real and "+ str(self.bc) + " fake locations")
# Connect Regions
def create_connection(from_region: str, to_region: str):
connection = Entrance(self.player, f"{from_region} -> {to_region}", regions[from_region])
regions[from_region].exits.append(connection)
connection.connect(regions[to_region])
#print(f"Connecting {from_region} to {to_region} Using entrance: " + connection.name)
def create_connection_2way(from_region: str, to_region: str):
create_connection(from_region, to_region)
create_connection(to_region, from_region)
create_connection("Menu", "Undead Asylum Cell")
create_connection("Undead Asylum Cell", "Undead Asylum Cell Door")
create_connection("Undead Asylum Cell Door", "Northern Undead Asylum")
create_connection("Northern Undead Asylum", "Northern Undead Asylum - After Fog")
create_connection("Northern Undead Asylum - After Fog", "Northern Undead Asylum - F2 East Door")
create_connection("Northern Undead Asylum - F2 East Door", "Northern Undead Asylum - After F2 East Door")
create_connection("Northern Undead Asylum - After F2 East Door", "Northern Undead Asylum - Big Pilgrim Door")
create_connection("Northern Undead Asylum - Big Pilgrim Door", "Firelink Shrine")
create_connection("Firelink Shrine", "Upper Undead Burg - Before Fog")
create_connection("Firelink Shrine", "The Catacombs")
create_connection("Firelink Shrine", "Upper New Londo Ruins")
create_connection("Firelink Shrine - After Undead Parish Elevator", "Northern Undead Asylum Second Visit")
create_connection("Firelink Shrine", "Firelink Altar")
create_connection("Firelink Altar", "Kiln of the First Flame")
create_connection("Kiln of the First Flame", "Kiln of the First Flame - Gwyn")
create_connection("Northern Undead Asylum Second Visit", "Northern Undead Asylum Second Visit - F2 West Door")
create_connection("Northern Undead Asylum Second Visit - F2 West Door", "Northern Undead Asylum Second Visit - Behind F2 West Door")
create_connection_2way("Upper Undead Burg - Before Fog", "Upper Undead Burg - Fog")
create_connection_2way("Upper Undead Burg - Fog", "Upper Undead Burg")
create_connection("Upper Undead Burg - Hellkite Bridge", "Undead Burg Basement Door")
create_connection("Upper Undead Burg", "Upper Undead Burg - Taurus Demon")
create_connection("Upper Undead Burg - Taurus Demon", "Upper Undead Burg - Hellkite Bridge")
create_connection_2way("Upper Undead Burg - Hellkite Bridge", "Undead Parish - Before Fog")
create_connection("Upper Undead Burg", "Upper Undead Burg - Pine Resin Chest")
create_connection_2way("Upper Undead Burg", "Watchtower Basement")
create_connection_2way("Darkroot Basin", "Watchtower Basement")
create_connection_2way("Undead Parish - Before Fog", "Undead Parish - Fog")
create_connection_2way("Undead Parish - Fog", "Undead Parish")
create_connection("Undead Parish", "Undead Parish - Before Fog") # Lever->Gate, or drop near fog
create_connection("Undead Parish", "Undead Parish - Bell Gargoyles")
create_connection("Undead Parish", "Firelink Shrine - After Undead Parish Elevator")
create_connection_2way("Undead Parish", "Darkroot Garden - Before Fog")
create_connection("Undead Parish", "Sen's Fortress")
create_connection_2way("Darkroot Garden - Before Fog", "Darkroot Basin")
create_connection("Darkroot Garden - Before Fog", "Darkroot Garden - Behind Artorias Door")
create_connection("Darkroot Garden - Before Fog", "Darkroot Garden")
create_connection("Darkroot Garden", "Darkroot Garden - Moonlight Butterfly")
create_connection("Darkroot Garden - Moonlight Butterfly", "Darkroot Garden - After Moonlight Butterfly")
create_connection("Undead Burg Basement Door", "Lower Undead Burg")
create_connection("Lower Undead Burg", "Depths")
create_connection("Lower Undead Burg", "Lower Undead Burg - After Residence Key")
create_connection("Lower Undead Burg", "Lower Undead Burg - Capra Demon")
create_connection("Lower Undead Burg - Capra Demon", "Lower Undead Burg - After Capra Demon")
create_connection("Upper New Londo Ruins", "Upper New Londo Ruins - After Fog")
create_connection("Upper New Londo Ruins - After Fog", "New Londo Ruins Door to the Seal")
create_connection("New Londo Ruins Door to the Seal", "Lower New Londo Ruins")
create_connection_2way("Upper New Londo Ruins", "Door between Upper New Londo and Valley of the Drakes")
create_connection_2way("Door between Upper New Londo and Valley of the Drakes", "Valley of the Drakes")
create_connection("Lower New Londo Ruins", "Valley of the Drakes")
create_connection("Depths", "Depths - After Sewer Chamber Key")
create_connection("Depths", "Depths to Blighttown Door")
create_connection("Depths", "Depths - Gaping Dragon")
create_connection("Depths - Gaping Dragon", "Depths - After Gaping Dragon")
create_connection_2way("Valley of the Drakes", "Upper Blighttown VotD Side")
create_connection_2way("Valley of the Drakes", "Darkroot Basin")
create_connection("Valley of the Drakes", "Valley of the Drakes - After Defeating Four Kings")
create_connection("Depths to Blighttown Door", "Upper Blighttown Depths Side")
create_connection("Upper Blighttown Depths Side", "Depths to Blighttown Door")
create_connection("Upper Blighttown Depths Side", "Lower Blighttown") # Able to fall to bypass fog, by Whip
create_connection_2way("Upper Blighttown Depths Side", "Lower Blighttown - Fog")
create_connection_2way("Lower Blighttown - Fog", "Lower Blighttown")
create_connection_2way("Lower Blighttown", "Upper Blighttown VotD Side")
create_connection("Lower Blighttown", "Demon Ruins - Early")
create_connection("Lower Blighttown", "The Great Hollow")
create_connection("Lower Blighttown", "Lower Blighttown - Quelaag")
create_connection("Lower Blighttown - Quelaag", "Lower Blighttown - After Quelaag")
create_connection("The Great Hollow", "Ash Lake")
create_connection("Sen's Fortress", "Sen's Fortress - After First Fog")
create_connection("Sen's Fortress - After First Fog", "Sen's Fortress - After Second Fog")
create_connection("Sen's Fortress - After Second Fog", "Sen's Fortress - Iron Golem")
create_connection("Sen's Fortress - Iron Golem", "Sen's Fortress - After Iron Golem")
create_connection("Sen's Fortress - After First Fog", "Sen's Fortress - After Cage Key")
create_connection("Sen's Fortress - After Iron Golem", "Anor Londo")
create_connection("Anor Londo", "Anor Londo - After First Fog")
create_connection("Anor Londo - After First Fog", "Anor Londo - After Second Fog")
create_connection("Anor Londo - After Second Fog", "Anor Londo - Ornstein and Smough")
create_connection("Anor Londo - Ornstein and Smough", "Anor Londo - After Ornstein and Smough")
create_connection("Anor Londo - After Ornstein and Smough", "Anor Londo - Gwyndolin")
create_connection("Anor Londo - Gwyndolin", "Anor Londo - After Gwyndolin")
create_connection("Anor Londo", "The Duke's Archives")
create_connection("Anor Londo - After First Fog", "Painted World of Ariamis")
create_connection("Painted World of Ariamis", "Painted World of Ariamis - After Fog")
create_connection("Painted World of Ariamis - After Fog", "Painted World of Ariamis - After Annex Key")
create_connection("Painted World of Ariamis - After Fog", "Painted World of Ariamis - Crossbreed Priscilla")
create_connection("The Duke's Archives", "The Duke's Archives - After First Seath Encounter")
create_connection("The Duke's Archives - After First Seath Encounter", "The Duke's Archives - After Archive Tower Cell Key")
create_connection("The Duke's Archives - After First Seath Encounter", "The Duke's Archives - After Archive Prison Extra Key")
create_connection("The Duke's Archives - After Archive Prison Extra Key", "The Duke's Archives - Out of Cell")
create_connection("The Duke's Archives - After Archive Tower Cell Key", "The Duke's Archives - Out of Cell")
create_connection("The Duke's Archives - Out of Cell", "The Duke's Archives - After Archive Tower Giant Door Key")
create_connection("The Duke's Archives - Out of Cell", "The Duke's Archives - Giant Cell")
create_connection("The Duke's Archives - After Archive Tower Giant Door Key", "The Duke's Archives - Courtyard")
create_connection("The Duke's Archives - Courtyard", "Crystal Cave")
create_connection("Crystal Cave", "Crystal Cave - After Seath")
create_connection("Crystal Cave", "The Duke's Archives - First Arena after Seath's Death")
create_connection("The Catacombs", "The Catacombs - Door 1")
create_connection("The Catacombs - Door 1", "The Catacombs - After Door 1")
create_connection("The Catacombs - After Door 1", "The Catacombs - Pinwheel")
create_connection("The Catacombs - Pinwheel", "The Catacombs - After Pinwheel")
create_connection("The Catacombs - After Pinwheel", "Tomb of the Giants")
create_connection("Tomb of the Giants", "Tomb of the Giants - After White Fog")
create_connection("Tomb of the Giants - After White Fog", "Tomb of the Giants - Behind Golden Fog Wall")
create_connection("Tomb of the Giants - Behind Golden Fog Wall", "Tomb of the Giants - Nito")
create_connection("Tomb of the Giants - Nito", "Tomb of the Giants - After Nito")
create_connection("Lower New Londo Ruins", "The Abyss")
create_connection("The Abyss", "The Abyss - After Four Kings")
create_connection("The Abyss - After Four Kings", "Firelink Altar")
create_connection("Demon Ruins - Early", "Demon Ruins")
create_connection("Demon Ruins - Early", "Demon Ruins - Ceaseless Discharge")
create_connection("Demon Ruins", "Demon Ruins - Demon Firesage")
create_connection("Demon Ruins - Demon Firesage", "Demon Ruins - After Demon Firesage")
create_connection("Demon Ruins - After Demon Firesage", "Demon Ruins - Centipede Demon")
create_connection("Demon Ruins - Centipede Demon", "Lost Izalith")
create_connection("Demon Ruins - After Demon Firesage", "Demon Ruins Shortcut")
create_connection("Lost Izalith", "Demon Ruins Shortcut")
create_connection("Lost Izalith", "Lost Izalith - Bed of Chaos")
# DLC Entrances
create_connection("Darkroot Basin", "Sanctuary Garden")
create_connection("Sanctuary Garden", "Sanctuary Garden - Sanctuary Guardian")
create_connection("Sanctuary Garden - Sanctuary Guardian", "Oolacile Sanctuary")
create_connection("Oolacile Sanctuary", "Royal Wood")
create_connection("Royal Wood", "Royal Wood - Artorias")
create_connection("Royal Wood", "Oolacile Township")
create_connection("Oolacile Township", "Oolacile Township - After Crest Key")
create_connection("Oolacile Township", "Oolacile Township - Behind Light-Dispelled Walls")
create_connection("Oolacile Township - After Crest Key", "Royal Wood - After Hawkeye Gough")
create_connection("Oolacile Township", "Chasm of the Abyss")
create_connection("Chasm of the Abyss", "Chasm of the Abyss - Manus")
# end of entrances
# For each region, add the associated locations retrieved from the corresponding location_table
def create_region(self, region_name, location_table) -> Region:
new_region = Region(region_name, self.player, self.multiworld)
#print("location table size: " + str(len(location_table)))
for location in location_table:
#print("Creating location: " + location.name)
if (location.category in self.enabled_location_categories and
location.category not in location_skip_categories # [DSRLocationCategory.EVENT, DSRLocationCategory.DOOR]:
and location.category not in location_locked_categories
and not (self.options.excluded_location_behavior == "do_not_randomize" and location.name in self.all_excluded_locations)):
self.gc = self.gc + 1
default_item = location.default_item
if (location.category in [DSRLocationCategory.FOG_WALL, DSRLocationCategory.BOSS_FOG_WALL]):
default_item = "Fogwall Filler"
# print("Adding location: " + location.name + " with default item " + location.default_item)
new_location = DSRLocation(
self.player,
location.name,
location.category,
default_item,
self.location_name_to_id[location.name],
new_region
)
elif (location.category in self.enabled_location_categories and
location.category in location_locked_categories): # DSRLocationCategory.BONFIRE_WARP
self.bw = self.bw + 1
default_item = location.default_item
# Place bonfire warp locations statically
event_item = self.create_item(default_item)
new_location = DSRLocation(
self.player,
location.name,
location.category,
default_item,
self.location_name_to_id[location.name],
new_region
)
new_location.place_locked_item(event_item)
else:
self.bc = self.bc + 1
default_item = location.default_item
if (location.category in [DSRLocationCategory.FOG_WALL, DSRLocationCategory.BOSS_FOG_WALL,
DSRLocationCategory.DOOR]):
default_item = "Nothing"
# print("Placing event: " + default_item + " in location: " + location.name)
# Replace non-randomized progression items with events
event_item = self.create_item(default_item)
# if event_item.classification != ItemClassification.progression:
# continue
# print("Adding Location: " + location.name + " as an event with default item " + default_item)
new_location = DSRLocation(
self.player,
location.name,
location.category,
default_item,
None,
new_region
)
event_item.code = None
new_location.place_locked_item(event_item)
new_region.locations.append(new_location)
# print("created " + str(len(new_region.locations)) + " locations")
self.multiworld.regions.append(new_region)
#print("adding region: " + region_name)
return new_region
def create_items(self):
skip_itemlocs: List[DSRItem, Location] = []
skipitempool: List[DSRItem] = []
itempool: List[DSRItem] = []
itempoolSize = 0
# print("Creating items")
for location in self.multiworld.get_locations(self.player):
item_data = item_dictionary[location.default_item_name]
if (item_data.category in [DSRItemCategory.SKIP]
or location.category in location_skip_categories
or location.category in location_locked_categories): # [DSRLocationCategory.EVENT]:
# print("Adding skip item: " + location.default_item_name + " for location: " + location.name)
skip_itemlocs.append((self.create_item(location.default_item_name), location))
skipitempool.append(self.create_item(location.default_item_name))
elif location.category in self.enabled_location_categories:
if self.options.excluded_location_behavior == "do_not_randomize" and location.name in self.all_excluded_locations:
# print("Adding skip item: " + location.default_item_name + " for location: " + location.name)
skip_itemlocs.append((self.create_item(location.default_item_name), location))
skipitempool.append(self.create_item(location.default_item_name))
else:
#print("Adding item: " + location.default_item_name)
itempoolSize += 1
itempool.append(self.create_item(location.default_item_name))
# print("Requesting itempool size: " + str(itempoolSize))
# foo = BuildItemPool(itempoolSize, self.options, self)
# print("Created item pool size: " + str(len(foo)))
# Add any Key + useful items
rip = BuildRequiredItemPool(self, itempoolSize)
crip = [self.create_item(item.name) for item in rip]
disabled_items = [self.create_item(loc.default_item) for loc in location_dictionary.values() if loc.category not in self.enabled_location_categories]
StillRequiredPool = [item for item in crip if item not in itempool and item not in skipitempool and item not in disabled_items]
guaranteedpool = BuildGuaranteedItemPool(self)
filler_items = [item for item in itempool if item_dictionary[item.name].category in [DSRItemCategory.FILLER]]
junk_items = [item for item in itempool if item.name in item_name_groups["Junk"]]
removable_items = filler_items + junk_items
# print("marked " + str(len(removable_items)) + " items as removable")
# print("marked " + str(len(filler_items)) + " items as filler")
# print("marked " + str(len(junk_items)) + " items as non filler")
# for item in junk_items:
# print("junk:" + item.name)
# print("itempool size " + str(len(itempool)) + "itempoolsize=" + str(itempoolSize))
# print("skip_itemlocs size " + str(len(skip_itemlocs)))
# print("rip size " + str(len(rip)))
# print("StillRequiredPool size " + str(len(StillRequiredPool)))
# print("disabled items " + str(len(disabled_items)))
# for item in disabled_items:
# print("disabled:" + item.name)
# for item in StillRequiredPool:
# print("StillRequiredPool item: " + str(item))
# for item in skipitempool:
# print("skip item: " + str(item))
limited_pool = [item for item in StillRequiredPool if item_dictionary[item.name].category not in [DSRItemCategory.FOGWALL, DSRItemCategory.BOSSFOGWALL]]
for item in limited_pool:
print("non-fogwall required item: " + str(item))
# Replace "Soul of a Lost Undead" if needed
if len(StillRequiredPool) + len(guaranteedpool) > len(removable_items):
print("Adding " + str(len([item for item in itempool if item.name == 'Soul of a Lost Undead'])) +" Souls of a Lost Undead to removable items")
removable_items += [item for item in itempool if item.name == 'Soul of a Lost Undead']
print("now " + str(len(removable_items)) + " are removable")
# Replace "Large Soul of a Lost Undead" if needed
if len(StillRequiredPool) > len(removable_items):
print("Adding " + str(len([item for item in itempool if item.name == 'Large Soul of a Lost Undead'])) +" Large Souls of a Lost Undead to removable items")
removable_items += [item for item in itempool if item.name == 'Large Soul of a Lost Undead']
print("now " + str(len(removable_items)) + " are removable")
for item in removable_items:
if len(StillRequiredPool) > 0:
# print("removable item: " + item.name)
itempool.remove(item)
itempool.append(self.create_item(StillRequiredPool.pop().name))
elif len(guaranteedpool) > 0:
itempool.remove(item)
itempool.append(self.create_item(guaranteedpool.pop().name))
else:
break
filler_items = [item for item in itempool if item_dictionary[item.name].category in [DSRItemCategory.FILLER]]
junk_items = [item for item in itempool if item.name in item_name_groups["Junk"]]
removable_items = filler_items + junk_items
filler_items = [item for item in itempool if item_dictionary[item.name].category in [DSRItemCategory.FILLER]]
junk_items = [item for item in itempool if item.name in item_name_groups["Junk"]]
removable_items = filler_items + junk_items
# print("leftover removable items: " + str(len(removable_items)))
# print("leftover filler items: " + str(len(filler_items)))
for item in removable_items:
# print("removable item: " + item.name)
itempool.remove(item)
itempool.append(self.create_item("Soul of a Proud Knight"))
# Add regular items to itempool
self.multiworld.itempool += itempool
# Handle SKIP items separately
for skip_item_loc in skip_itemlocs:
location = skip_item_loc[1]
location.place_locked_item(skip_item_loc[0])
#self.multiworld.itempool.append(skip_item)
#print("Placing skip item: " + skip_item.name + " in location: " + location.name)
#print("Final Item pool: ")
#for item in self.multiworld.itempool:
#print(item.name)
def create_item(self, name: str) -> Item:
useful_categories = {
DSRItemCategory.EMBER,
}
data = self.item_name_to_id[name]
if name in key_item_names or item_dictionary[name].category in [DSRItemCategory.EVENT, DSRItemCategory.KEY_ITEM, DSRItemCategory.FOGWALL, DSRItemCategory.BOSSFOGWALL]:
item_classification = ItemClassification.progression
elif item_dictionary[name].category in useful_categories:
item_classification = ItemClassification.useful
else:
item_classification = ItemClassification.filler
return DSRItem(name, item_classification, data, self.player)
def get_filler_item_name(self) -> str:
return "1000 Souls"
def set_rules(self) -> None:
#print("Setting rules")
for region in self.multiworld.get_regions(self.player):
for location in region.locations:
set_rule(location, lambda state: True)
self.multiworld.completion_condition[self.player] = lambda state: state.has("Gwyn, Lord of Cinder Defeated", self.player)
set_rule(self.multiworld.get_entrance("Undead Asylum Cell -> Undead Asylum Cell Door", self.player), lambda state: state.has("Dungeon Cell Key", self.player))
#set_rule(self.multiworld.get_entrance("Undead Asylum Cell Door -> Northern Undead Asylum", self.player), lambda state: state.has("Dungeon Cell Key", self.player))
set_rule(self.multiworld.get_entrance("Northern Undead Asylum - After Fog -> Northern Undead Asylum - F2 East Door", self.player), lambda state: state.has("Undead Asylum F2 East Key", self.player))
set_rule(self.multiworld.get_entrance("Northern Undead Asylum - After F2 East Door -> Northern Undead Asylum - Big Pilgrim Door", self.player), lambda state: state.has("Big Pilgrim's Key", self.player))
set_rule(self.multiworld.get_entrance("Upper Undead Burg - Hellkite Bridge -> Undead Burg Basement Door", self.player), lambda state: state.has ("Basement Key", self.player))
set_rule(self.multiworld.get_entrance("Upper Undead Burg - Taurus Demon -> Upper Undead Burg - Hellkite Bridge", self.player), lambda state: state.has("Taurus Demon Defeated", self.player))
set_rule(self.multiworld.get_entrance("Upper Undead Burg -> Upper Undead Burg - Pine Resin Chest", self.player), lambda state: state.has("Master Key", self.player) or state.has("Residence Key", self.player))
set_rule(self.multiworld.get_entrance("Upper Undead Burg -> Watchtower Basement", self.player), lambda state: state.has("Master Key", self.player) or state.has("Watchtower Basement Key", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Pendant -> Souvenir of Reprisal", self.player), lambda state: state.has("Pendant", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Rubbish -> Titanite Chunk", self.player), lambda state: state.has("Rubbish", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Sunlight Medal -> White Titanite Chunk", self.player), lambda state: state.has("Sunlight Medal", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Bloodred Moss Clump -> Twinkling Titanite", self.player), lambda state: state.has("Bloodred Moss Clump", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Purple Moss Clump -> Twinkling Titanite", self.player), lambda state: state.has("Purple Moss Clump", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Blooming Purple Moss Clump -> Twinkling Titanite x2", self.player), lambda state: state.has("Blooming Purple Moss Clump", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Cracked Red Eye Orb -> Purging Stone x2", self.player), lambda state: state.has("Cracked Red Eye Orb", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Humanity -> Ring of Sacrifice", self.player), lambda state: state.has("Humanity", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Twin Humanities -> Rare Ring of Sacrifice", self.player), lambda state: state.has("Twin Humanities", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Dung Pie -> Demon Titanite", self.player), lambda state: state.has("Dung Pie", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Pyromancy Flame -> Red Titanite Chunk", self.player), lambda state: state.has("Pyromancy Flame", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Pyromancy Flame (Ascended) -> Red Titanite Slab", self.player), lambda state: state.has("Pyromancy Flame (Ascended)", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Egg Vermifuge -> Dragon Scale", self.player), lambda state: state.has("Egg Vermifuge", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Sunlight Maggot -> Old Witch's Ring", self.player), lambda state: state.has("Sunlight Maggot", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Sack -> Demon's Great Hammer", self.player), lambda state: state.has("Sack", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Skull Lantern -> Ring of Fog", self.player), lambda state: state.has("Skull Lantern", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Ring of the Sun Princess -> Divine Blessing x2", self.player), lambda state: state.has("Ring of the Sun Princess", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Xanthous Crown -> Ring of Favor and Protection", self.player), lambda state: state.has("Xanthous Crown", self.player))
# set_rule(self.multiworld.get_location("Snuggly: Soul of Manus -> Sorcery: Pursuers", self.player), lambda state: state.has("Soul of Manus", self.player))
set_rule(self.multiworld.get_entrance("Darkroot Basin -> Watchtower Basement", self.player), lambda state: state.has("Master Key", self.player) or state.has("Watchtower Basement Key", self.player))
set_rule(self.multiworld.get_entrance("Northern Undead Asylum Second Visit -> Northern Undead Asylum Second Visit - F2 West Door", self.player), lambda state: state.has("Undead Asylum F2 West Key", self.player))
set_rule(self.multiworld.get_entrance("Darkroot Garden - Before Fog -> Darkroot Garden - Behind Artorias Door", self.player), lambda state: state.has("Crest of Artorias", self.player))
# Else no rule - player can access without problem
set_rule(self.multiworld.get_entrance("Darkroot Garden - Moonlight Butterfly -> Darkroot Garden - After Moonlight Butterfly", self.player), lambda state: state.has("Moonlight Butterfly Defeated", self.player))
set_rule(self.multiworld.get_entrance("Lower Undead Burg -> Depths", self.player), lambda state: state.has("Key to Depths", self.player))
set_rule(self.multiworld.get_entrance("Lower Undead Burg -> Lower Undead Burg - After Residence Key", self.player), lambda state: state.has("Residence Key", self.player))
set_rule(self.multiworld.get_entrance("Lower Undead Burg - Capra Demon -> Lower Undead Burg - After Capra Demon", self.player), lambda state: state.has("Capra Demon Defeated", self.player))
set_rule(self.multiworld.get_entrance("Upper New Londo Ruins -> Door between Upper New Londo and Valley of the Drakes", self.player), lambda state: state.has("Key to New Londo Ruins", self.player) or state.has("Master Key", self.player))
set_rule(self.multiworld.get_entrance("Valley of the Drakes -> Door between Upper New Londo and Valley of the Drakes", self.player), lambda state: state.has("Key to New Londo Ruins", self.player) or state.has("Master Key", self.player))
set_rule(self.multiworld.get_entrance("Depths -> Depths - After Sewer Chamber Key", self.player), lambda state: state.has("Sewer Chamber Key", self.player))
set_rule(self.multiworld.get_entrance("Depths - Gaping Dragon -> Depths - After Gaping Dragon", self.player), lambda state: state.has("Gaping Dragon Defeated", self.player))
set_rule(self.multiworld.get_entrance("Depths -> Depths to Blighttown Door", self.player), lambda state: state.has("Blighttown Key", self.player))
set_rule(self.multiworld.get_entrance("Upper Blighttown Depths Side -> Depths to Blighttown Door", self.player), lambda state: state.has("Depths -> Blighttown opened", self.player))
set_rule(self.multiworld.get_entrance("Lower Blighttown - Quelaag -> Lower Blighttown - After Quelaag", self.player), lambda state: state.has("Chaos Witch Quelaag Defeated", self.player))
set_rule(self.multiworld.get_entrance("Lower Blighttown -> Demon Ruins - Early", self.player), lambda state: state.has("Chaos Witch Quelaag Defeated", self.player))
set_rule(self.multiworld.get_location("UP: Bell of Awakening #1 rung", self.player), lambda state: state.has("Bell Gargoyles Defeated", self.player))
# set_rule(self.multiworld.get_location("BT: Bell of Awakening #2 rung", self.player), lambda state: state.has("Chaos Witch Quelaag Defeated", self.player))
set_rule(self.multiworld.get_entrance("Undead Parish -> Sen's Fortress", self.player), lambda state: state.has("Bell of Awakening #1", self.player) and state.has("Bell of Awakening #2", self.player))
set_rule(self.multiworld.get_entrance("Sen's Fortress - After First Fog -> Sen's Fortress - After Cage Key", self.player), lambda state: state.has("Master Key", self.player) or state.has("Cage Key", self.player))
set_rule(self.multiworld.get_entrance("Sen's Fortress - Iron Golem -> Sen's Fortress - After Iron Golem", self.player), lambda state: state.has("Iron Golem Defeated", self.player))
set_rule(self.multiworld.get_entrance("Anor Londo -> The Duke's Archives", self.player), lambda state: state.has("Lordvessel Placed", self.player))
set_rule(self.multiworld.get_entrance("Anor Londo - Ornstein and Smough -> Anor Londo - After Ornstein and Smough", self.player), lambda state: state.has("Ornstein and Smough Defeated", self.player))
set_rule(self.multiworld.get_location("NL: Key to the Seal", self.player), lambda state: state.has("Lordvessel", self.player))
set_rule(self.multiworld.get_entrance("Upper New Londo Ruins - After Fog -> New Londo Ruins Door to the Seal", self.player), lambda state: state.has("Key to the Seal", self.player))
set_rule(self.multiworld.get_entrance("Valley of the Drakes -> Valley of the Drakes - After Defeating Four Kings", self.player), lambda state: state.has("Four Kings Defeated", self.player))
set_rule(self.multiworld.get_entrance("The Duke's Archives - After First Seath Encounter -> The Duke's Archives - After Archive Tower Cell Key", self.player), lambda state: state.has("Archive Tower Cell Key", self.player))
set_rule(self.multiworld.get_entrance("The Duke's Archives - After First Seath Encounter -> The Duke's Archives - After Archive Prison Extra Key", self.player), lambda state: state.has("Archive Prison Extra Key", self.player))
set_rule(self.multiworld.get_entrance("The Duke's Archives - Out of Cell -> The Duke's Archives - After Archive Tower Giant Door Key", self.player), lambda state: state.has("Archive Tower Giant Door Key", self.player))
set_rule(self.multiworld.get_entrance("The Duke's Archives - Out of Cell -> The Duke's Archives - Giant Cell", self.player), lambda state: state.has("Archive Tower Giant Cell Key", self.player))
set_rule(self.multiworld.get_location("DA: Broken Pendant", self.player), lambda state: state.has("Dusk Rescued", self.player))
set_rule(self.multiworld.get_entrance("Crystal Cave -> Crystal Cave - After Seath", self.player), lambda state: state.has("Seath the Scaleless Defeated", self.player))
set_rule(self.multiworld.get_entrance("Crystal Cave -> The Duke's Archives - First Arena after Seath's Death", self.player), lambda state: state.has("Seath the Scaleless Defeated", self.player))
set_rule(self.multiworld.get_entrance("Anor Londo - After First Fog -> Painted World of Ariamis", self.player), lambda state: state.has("Peculiar Doll", self.player))
set_rule(self.multiworld.get_entrance("Painted World of Ariamis - After Fog -> Painted World of Ariamis - After Annex Key", self.player), lambda state: state.has("Annex Key", self.player))
set_rule(self.multiworld.get_entrance("Lower New Londo Ruins -> The Abyss", self.player), lambda state: state.has("Covenant of Artorias", self.player) and ((self.options.boss_fogwall_sanity.value == False) or state.has ("Boss Fog Wall Key - Four Kings", self.player)))
set_rule(self.multiworld.get_entrance("The Abyss -> The Abyss - After Four Kings", self.player), lambda state: state.has("Four Kings Defeated", self.player))
set_rule(self.multiworld.get_entrance("Demon Ruins -> Demon Ruins - Demon Firesage", self.player), lambda state: state.has("Lordvessel Placed", self.player) and ((self.options.boss_fogwall_sanity.value == False) or state.has ("Boss Fog Wall Key - Demon Firesage", self.player)))
set_rule(self.multiworld.get_entrance("Demon Ruins - Early -> Demon Ruins", self.player), lambda state: state.has("Ceaseless Discharge Defeated", self.player))
set_rule(self.multiworld.get_entrance("Lost Izalith -> Demon Ruins Shortcut", self.player), lambda state: state.has("Bed of Chaos Defeated", self.player))
# some demon ruins checks require Orange Charred Ring
set_rule(self.multiworld.get_location("DR: Large Soul of a Proud Knight - First Jump over the Lava", self.player), lambda state: state.has("Orange Charred Ring", self.player))
set_rule(self.multiworld.get_location("DR: Chaos Flame Ember", self.player), lambda state: state.has("Orange Charred Ring", self.player))
set_rule(self.multiworld.get_entrance("Demon Ruins - After Demon Firesage -> Demon Ruins Shortcut", self.player), lambda state: state.has("Demon Ruins Shortcut opened", self.player))
set_rule(self.multiworld.get_entrance("Demon Ruins - Centipede Demon -> Lost Izalith", self.player), lambda state: state.has("Orange Charred Ring", self.player) and state.has("Centipede Demon Defeated", self.player))
set_rule(self.multiworld.get_entrance("The Catacombs - Pinwheel -> The Catacombs - After Pinwheel", self.player), lambda state: state.has("Pinwheel Defeated", self.player))
set_rule(self.multiworld.get_entrance("The Catacombs - After Pinwheel -> Tomb of the Giants", self.player), lambda state: state.has("Skull Lantern", self.player))
set_rule(self.multiworld.get_entrance("Tomb of the Giants - After White Fog -> Tomb of the Giants - Behind Golden Fog Wall", self.player), lambda state: state.has("Lordvessel Placed", self.player))
set_rule(self.multiworld.get_entrance("Tomb of the Giants - Nito -> Tomb of the Giants - After Nito", self.player), lambda state: state.has("Gravelord Nito Defeated", self.player))
# Frampt entrance to Altar
set_rule(self.multiworld.get_entrance("Firelink Shrine -> Firelink Altar", self.player),
lambda state: state.has("Bell of Awakening #1", self.player) and state.has("Bell of Awakening #2", self.player))
# Kaathe entrance to Altar - no rule needed
# Altar Lordvessel Placed event requires the lordvessel
set_rule(self.multiworld.get_location("FA: Lordvessel Placed", self.player), lambda state: state.has("Lordvessel", self.player))
# Altar to Kiln
set_rule(self.multiworld.get_entrance("Firelink Altar -> Kiln of the First Flame", self.player),
lambda state: state.has("Lord Soul (Bed of Chaos)", self.player) and state.has("Lord Soul (Nito)", self.player) and state.has("Bequeathed Lord Soul Shard (Four Kings)", self.player) and state.has("Bequeathed Lord Soul Shard (Seath)", self.player)
and state.has("Lordvessel", self.player))
# DLC areas
set_rule(self.multiworld.get_entrance("Darkroot Basin -> Sanctuary Garden", self.player), lambda state: state.has("Broken Pendant", self.player))
set_rule(self.multiworld.get_entrance("Sanctuary Garden - Sanctuary Guardian -> Oolacile Sanctuary", self.player), lambda state: state.has("Sanctuary Guardian Defeated", self.player))
set_rule(self.multiworld.get_entrance("Royal Wood -> Oolacile Township", self.player), lambda state: state.has("Artorias the Abysswalker Defeated", self.player))
set_rule(self.multiworld.get_entrance("Oolacile Township -> Oolacile Township - After Crest Key", self.player), lambda state: state.has("Crest Key", self.player))
set_rule(self.multiworld.get_entrance("Oolacile Township -> Oolacile Township - Behind Light-Dispelled Walls", self.player), lambda state: state.has("Skull Lantern", self.player))
# artificial logic - don't require jumping around BT fog wall without a "real" way to return
set_rule(self.multiworld.get_entrance("Upper Blighttown Depths Side -> Lower Blighttown", self.player), lambda state: state.has("Lordvessel", self.player) or (self.options.can_warp_without_lordvessel == True))
# artificial logic
if (self.options.fogwall_sanity == False and self.options.boss_fogwall_sanity == False):
set_rule(self.multiworld.get_entrance("Upper New Londo Ruins - After Fog -> New Londo Ruins Door to the Seal", self.player), lambda state: state.has("Ornstein and Smough Defeated", self.player) and state.has("Key to the Seal", self.player))
set_rule(self.multiworld.get_entrance("Lower Blighttown -> The Great Hollow", self.player), lambda state: state.has("Lordvessel", self.player))
# fogwall rules
def add_fog_rule(fogwall_item: str, from_region: str, to_region: str):
set_rule(self.multiworld.get_entrance(f"{from_region} -> {to_region}", self.player),
lambda state: (self.options.fogwall_sanity.value == False) or state.has (fogwall_item, self.player))
#early - removed
# set_rule(self.multiworld.get_entrance("Northern Undead Asylum -> Northern Undead Asylum - After Fog", self.player), lambda state: (self.options.fogwall_sanity.value == False) or (self.options.fogwall_sanity_include_ua.value == False) or state.has ("Fog Wall Key - Northern Undead Asylum", self.player))
#normal
add_fog_rule("Fog Wall Key - Undead Burg", "Upper Undead Burg - Before Fog", "Upper Undead Burg - Fog")
add_fog_rule("Fog Wall Key - Undead Burg", "Upper Undead Burg", "Upper Undead Burg - Fog")
add_fog_rule("Fog Wall Key - Undead Parish", "Undead Parish - Before Fog", "Undead Parish - Fog")
add_fog_rule("Fog Wall Key - Undead Parish", "Undead Parish", "Undead Parish - Fog")
add_fog_rule("Fog Wall Key - Darkroot Garden", "Darkroot Garden - Before Fog", "Darkroot Garden")
# Depths fog doesn't affect entrance logic, but is itself only accessible with the fog item
set_rule(self.multiworld.get_location("DE: Fog Wall - Depths Rat Room", self.player), lambda state: (self.options.fogwall_sanity.value == False) or state.has ("Fog Wall Key - Depths Rat Room", self.player))
add_fog_rule("Fog Wall Key - Lower Blighttown Entrance", "Upper Blighttown Depths Side", "Lower Blighttown - Fog")
add_fog_rule("Fog Wall Key - Lower Blighttown Entrance", "Lower Blighttown", "Lower Blighttown - Fog")
add_fog_rule("Fog Wall Key - Ash Lake Entrance", "The Great Hollow", "Ash Lake")
add_fog_rule("Fog Wall Key - Sen's Fortress #1 (Outside Stairs)", "Sen's Fortress", "Sen's Fortress - After First Fog")
add_fog_rule("Fog Wall Key - Sen's Fortress #2 (Upper Entrance)", "Sen's Fortress - After First Fog", "Sen's Fortress - After Second Fog")
add_fog_rule("Fog Wall Key - Anor Londo #1 (Rafters)", "Anor Londo", "Anor Londo - After First Fog")
add_fog_rule("Fog Wall Key - Anor Londo #2 (Archers)", "Anor Londo - After First Fog", "Anor Londo - After Second Fog")
add_fog_rule("Fog Wall Key - Duke's Archives Courtyard Entrance", "The Duke's Archives - After Archive Tower Giant Door Key", "The Duke's Archives - Courtyard")
# Catacombs fog does not affect entrance logic, but is itself only accessible with the fog item
set_rule(self.multiworld.get_location("TC: Fog Wall - Catacombs", self.player), lambda state: (self.options.fogwall_sanity.value == False) or state.has ("Fog Wall Key - Catacombs", self.player))
add_fog_rule("Fog Wall Key - Tomb of the Giants", "Tomb of the Giants", "Tomb of the Giants - After White Fog")
add_fog_rule("Fog Wall Key - New Londo (Upper)", "Upper New Londo Ruins", "Upper New Londo Ruins - After Fog")
# Lower new londo fog does not affect entrance logic, but is itself only accessible with the fog item
set_rule(self.multiworld.get_location("NL: Fog Wall - New Londo (Lower)", self.player), lambda state: (self.options.fogwall_sanity.value == False) or state.has ("Fog Wall Key - New Londo (Lower)", self.player))
add_fog_rule("Fog Wall Key - Painted World", "Painted World of Ariamis", "Painted World of Ariamis - After Fog")
#bosses
def add_boss_fog_rule(fogwall_item: str, from_region: str, to_region: str):
set_rule(self.multiworld.get_entrance(f"{from_region} -> {to_region}", self.player),
lambda state: (self.options.boss_fogwall_sanity.value == False) or state.has (fogwall_item, self.player))
add_boss_fog_rule("Boss Fog Wall Key - Taurus Demon", "Upper Undead Burg", "Upper Undead Burg - Taurus Demon")
add_boss_fog_rule("Boss Fog Wall Key - Capra Demon", "Lower Undead Burg", "Lower Undead Burg - Capra Demon")
add_boss_fog_rule("Boss Fog Wall Key - Bell Gargoyles", "Undead Parish", "Undead Parish - Bell Gargoyles")
add_boss_fog_rule("Boss Fog Wall Key - Moonlight Butterfly", "Darkroot Garden", "Darkroot Garden - Moonlight Butterfly")
add_boss_fog_rule("Boss Fog Wall Key - Gaping Dragon", "Depths", "Depths - Gaping Dragon")
add_boss_fog_rule("Boss Fog Wall Key - Quelaag", "Lower Blighttown", "Lower Blighttown - Quelaag")
add_boss_fog_rule("Boss Fog Wall Key - Iron Golem", "Sen's Fortress - After Second Fog", "Sen's Fortress - Iron Golem")
add_boss_fog_rule("Boss Fog Wall Key - Ornstein and Smough", "Anor Londo - After Second Fog", "Anor Londo - Ornstein and Smough")
add_boss_fog_rule("Boss Fog Wall Key - Gwyndolin", "Anor Londo - After Ornstein and Smough", "Anor Londo - Gwyndolin")
add_boss_fog_rule("Boss Fog Wall Key - Seath First Encounter", "The Duke's Archives", "The Duke's Archives - After First Seath Encounter")
add_boss_fog_rule("Boss Fog Wall Key - Pinwheel", "The Catacombs - After Door 1", "The Catacombs - Pinwheel")
add_boss_fog_rule("Boss Fog Wall Key - Nito", "Tomb of the Giants - Behind Golden Fog Wall", "Tomb of the Giants - Nito")
# 4 kings defined above (because it also needs covenant of the abyss)
add_boss_fog_rule("Boss Fog Wall Key - Ceaseless Discharge", "Demon Ruins - Early", "Demon Ruins - Ceaseless Discharge")
# Demon Firesage boss fog is earlier, because of golden fog (lordvessel) requirement
add_boss_fog_rule("Boss Fog Wall Key - Centipede Demon", "Demon Ruins - After Demon Firesage", "Demon Ruins - Centipede Demon")
add_boss_fog_rule("Boss Fog Wall Key - Bed of Chaos", "Lost Izalith", "Lost Izalith - Bed of Chaos")
add_boss_fog_rule("Boss Fog Wall Key - Crossbreed Priscilla", "Painted World of Ariamis - After Fog", "Painted World of Ariamis - Crossbreed Priscilla")
add_boss_fog_rule("Boss Fog Wall Key - Gwyn", "Kiln of the First Flame", "Kiln of the First Flame - Gwyn")
# dlc bosses
add_boss_fog_rule("Boss Fog Wall Key - Sanctuary Guardian", "Sanctuary Garden", "Sanctuary Garden - Sanctuary Guardian")
add_boss_fog_rule("Boss Fog Wall Key - Artorias", "Royal Wood", "Royal Wood - Artorias")
add_boss_fog_rule("Boss Fog Wall Key - Manus", "Chasm of the Abyss", "Chasm of the Abyss - Manus")
# end of fog wall logic
# Begin yaml options for "logic"
# Catacombs rule yaml option
if (self.options.logic_to_access_catacombs != LogicToAccessCatacombs.option_no_logic):
match self.options.logic_to_access_catacombs:
case LogicToAccessCatacombs.option_undead_merchant:
temp_condition = lambda state: state.has("Undead Merchant Access", self.player)
case LogicToAccessCatacombs.option_andre:
temp_condition = lambda state: state.has("Andre Access", self.player)
case LogicToAccessCatacombs.option_andre_or_undead_merchant:
temp_condition = lambda state: state.has("Andre Access", self.player) or state.has("Undead Merchant Access", self.player)
case LogicToAccessCatacombs.option_ornstein_and_smough:
temp_condition = lambda state: state.has("Ornstein and Smough Defeated", self.player)
case _: # default to andre or undead_merchant
temp_condition = lambda state: state.has("Andre Access", self.player) or state.has("Undead Merchant Access", self.player)
set_rule(self.multiworld.get_entrance("Firelink Shrine -> The Catacombs", self.player), temp_condition)
# End yaml options for "logic"
# for debugging purposes, you may want to visualize the layout of your world. Uncomment the following code to
# write a PlantUML diagram to the file "my_world.puml" that can help you see whether your regions and locations
# are connected and placed as desired
# from Utils import visualize_regions
# visualize_regions(self.multiworld.get_region("Menu", self.player), "my_world.puml")
def fill_slot_data(self) -> Dict[str, object]:
slot_data: Dict[str, object] = {}
name_to_dsr_code = {item.name: item.dsr_code for item in item_dictionary.values()}
# Create the mandatory lists to generate the player's output file
items_id = []
items_names = []
items_upgrades = []
items_address = []
for location in self.multiworld.get_filled_locations():
if location.item.player == self.player:
#we are the receiver of the item
items_id.append(location.item.code)
items_names.append(location.item.name)
upgrade = UpgradeEquipment(location.item.code, self.options, self)
items_upgrades.append(upgrade)
items_address.append(f'{location.player}:{location.address}')
slot_data = {
"options": {
"can_warp_without_lordvessel": self.options.can_warp_without_lordvessel.value,
"guaranteed_items": self.options.guaranteed_items.value,
"fogwall_sanity": self.options.fogwall_sanity.value,
"boss_fogwall_sanity": self.options.boss_fogwall_sanity.value,
"logic_to_access_catacombs": self.options.logic_to_access_catacombs.current_key,
"randomize_starting_loadouts": self.options.randomize_starting_loadouts.value,
"randomize_starting_gifts": self.options.randomize_starting_gifts.value,
"require_one_handed_starting_weapons": self.options.require_one_handed_starting_weapons.value,
"extra_starting_weapon_for_melee_classes": self.options.extra_starting_weapon_for_melee_classes.value,
"extra_starting_shield_for_all_classes": self.options.extra_starting_shield_for_all_classes.value,
"starting_sorcery": self.options.starting_sorcery.value,
"starting_miracle": self.options.starting_miracle.value,
"starting_pyromancy": self.options.starting_pyromancy.value,
"no_weapon_requirements": self.options.no_weapon_requirements.value,
"no_spell_stat_requirements": self.options.no_spell_stat_requirements.value,
"no_miracle_covenant_requirements": self.options.no_miracle_covenant_requirements.value,
"upgraded_weapons_percentage": self.options.upgraded_weapons_percentage.value,
"upgraded_weapons_allowed_infusions": self.options.upgraded_weapons_allowed_infusions.value,
"upgraded_weapons_adjusted_levels": self.options.upgraded_weapons_adjusted_levels.value,
"upgraded_weapons_min_level": self.options.upgraded_weapons_min_level.value,
"upgraded_weapons_max_level": self.options.upgraded_weapons_max_level.value,
"enable_deathlink": self.options.enable_deathlink.value,
},
"seed": self.multiworld.seed_name, # to verify the server's multiworld
"slot": self.multiworld.player_name[self.player], # to connect to server
"base_id": self.base_id, # to merge location and items lists
"itemsId": items_id,
"itemsUpgrades": items_upgrades,
"itemsAddress": items_address,
"apworld_api_version" : "0.1.0.0" # Manually set our apworld api level, for detecting compatibility with client
}
self.items_id = items_id
self.items_names = items_names
self.items_upgrades = items_upgrades
self.items_address = items_address
return slot_data
def write_spoiler(self, spoiler_handle: TextIO) -> None:
wrote_items = False
if (len(self.items_upgrades) > 0):
spoiler_handle.write(f"\nDSR weapon upgrades for {self.multiworld.player_name[self.player]}:\n")
for i in range(len(self.items_upgrades)):
if self.items_upgrades[i] == None or self.items_upgrades[i] == "":
continue
spoiler_handle.write(f"\nitem {self.items_names[i]} at loc {self.items_address[i]} upgraded to {self.items_upgrades[i]}.")
wrote_items = True
if not wrote_items:
spoiler_handle.write("\nNo items upgraded")
spoiler_handle.write("\n") # Spacing