Fixed some bugs + added documentation + added a few features (#87)
* Refactorings + minor logic fix * Fixed unnececerly recalculation of item_name_groups * Enabled other itemId's so that they can be send to client when desired * Marked the loss of location 1337158 * Updated network graph * First draft tinmespinner documentation * Moved personal items to slot_data rather than location scouts * Disabled Remote Items * Updated docs * Fixed port override
This commit is contained in:
@@ -81,7 +81,7 @@ class World(metaclass=AutoWorldRegister):
|
||||
# increment this every time something in your world's names/id mappings changes.
|
||||
# While this is set to 0 in *any* AutoWorld, the entire DataPackage is considered in testing mode and will be
|
||||
# retrieved by clients on every connection.
|
||||
data_version = 1
|
||||
data_version: int = 1
|
||||
|
||||
hint_blacklist: Set[str] = frozenset() # any names that should not be hintable
|
||||
|
||||
@@ -100,7 +100,7 @@ class World(metaclass=AutoWorldRegister):
|
||||
forced_auto_forfeit: bool = False
|
||||
|
||||
# Hide World Type from various views. Does not remove functionality.
|
||||
hidden = False
|
||||
hidden: bool = False
|
||||
|
||||
# autoset on creation:
|
||||
world: MultiWorld
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Dict, Tuple, NamedTuple
|
||||
from typing import Dict, Set, Tuple, NamedTuple
|
||||
|
||||
class ItemData(NamedTuple):
|
||||
category: str
|
||||
@@ -9,50 +9,50 @@ class ItemData(NamedTuple):
|
||||
# A lot of items arent normally dropped by the randomizer as they are mostly enemy drops, but they can be enabled if desired
|
||||
item_table: Dict[str, ItemData] = {
|
||||
'Eternal Crown': ItemData('Equipment', 1337000),
|
||||
#'Security Visor': ItemData('Equipment', 1337001),
|
||||
#'Engineer Goggles': ItemData('Equipment', 1337002),
|
||||
#'Leather Helmet': ItemData('Equipment', 1337003),
|
||||
#'Copper Helmet': ItemData('Equipment', 1337004),
|
||||
'Security Visor': ItemData('Equipment', 1337001, 0),
|
||||
'Engineer Goggles': ItemData('Equipment', 1337002, 0),
|
||||
'Leather Helmet': ItemData('Equipment', 1337003, 0),
|
||||
'Copper Helmet': ItemData('Equipment', 1337004, 0),
|
||||
'Pointy Hat': ItemData('Equipment', 1337005),
|
||||
#'Dragoon Helmet': ItemData('Equipment', 1337006),
|
||||
'Dragoon Helmet': ItemData('Equipment', 1337006, 0),
|
||||
'Buckle Hat': ItemData('Equipment', 1337007),
|
||||
#'Advisor Hat': ItemData('Equipment', 1337008),
|
||||
'Advisor Hat': ItemData('Equipment', 1337008, 0),
|
||||
'Librarian Hat': ItemData('Equipment', 1337009),
|
||||
#'Combat Helmet': ItemData('Equipment', 1337010),
|
||||
'Combat Helmet': ItemData('Equipment', 1337010, 0),
|
||||
'Captain\'s Cap': ItemData('Equipment', 1337011),
|
||||
'Lab Glasses': ItemData('Equipment', 1337012),
|
||||
'Empire Crown': ItemData('Equipment', 1337013),
|
||||
'Viletian Crown': ItemData('Equipment', 1337014),
|
||||
#'Sunglasses': ItemData('Equipment', 1337015),
|
||||
'Sunglasses': ItemData('Equipment', 1337015, 0),
|
||||
'Old Coat': ItemData('Equipment', 1337016),
|
||||
#'Trendy Jacket': ItemData('Equipment', 1337017),
|
||||
#'Security Vest': ItemData('Equipment', 1337018),
|
||||
#'Leather Jerkin': ItemData('Equipment', 1337019),
|
||||
#'Copper Breastplate': ItemData('Equipment', 1337020),
|
||||
'Trendy Jacket': ItemData('Equipment', 1337017, 0),
|
||||
'Security Vest': ItemData('Equipment', 1337018, 0),
|
||||
'Leather Jerkin': ItemData('Equipment', 1337019, 0),
|
||||
'Copper Breastplate': ItemData('Equipment', 1337020, 0),
|
||||
'Traveler\'s Cloak': ItemData('Equipment', 1337021),
|
||||
#'Dragoon Armor': ItemData('Equipment', 1337022),
|
||||
'Dragoon Armor': ItemData('Equipment', 1337022, 0),
|
||||
'Midnight Cloak': ItemData('Equipment', 1337023),
|
||||
#'Advisor Robe': ItemData('Equipment', 1337024),
|
||||
'Advisor Robe': ItemData('Equipment', 1337024, 0),
|
||||
'Librarian Robe': ItemData('Equipment', 1337025),
|
||||
#'Military Armor': ItemData('Equipment', 1337026),
|
||||
'Military Armor': ItemData('Equipment', 1337026, 0),
|
||||
'Captain\'s Uniform': ItemData('Equipment', 1337027),
|
||||
'Lab Coat': ItemData('Equipment', 1337028),
|
||||
'Empress Robe': ItemData('Equipment', 1337029),
|
||||
'Princess Dress': ItemData('Equipment', 1337030),
|
||||
'Eternal Coat': ItemData('Equipment', 1337031),
|
||||
#'Synthetic Plume': ItemData('Equipment', 1337032),
|
||||
#'Cheveur Plume': ItemData('Equipment', 1337033),
|
||||
'Synthetic Plume': ItemData('Equipment', 1337032, 0),
|
||||
'Cheveur Plume': ItemData('Equipment', 1337033, 0),
|
||||
'Metal Wristband': ItemData('Equipment', 1337034),
|
||||
#'Nymph Hairband': ItemData('Equipment', 1337035),
|
||||
#'Mother o\' Pearl': ItemData('Equipment', 1337036),
|
||||
'Nymph Hairband': ItemData('Equipment', 1337035, 0),
|
||||
'Mother o\' Pearl': ItemData('Equipment', 1337036, 0),
|
||||
'Bird Statue': ItemData('Equipment', 1337037),
|
||||
#'Chaos Stole': ItemData('Equipment', 1337038),
|
||||
'Chaos Stole': ItemData('Equipment', 1337038, 0),
|
||||
'Pendulum': ItemData('Equipment', 1337039),
|
||||
#'Chaos Horn': ItemData('Equipment', 1337040),
|
||||
'Chaos Horn': ItemData('Equipment', 1337040, 0),
|
||||
'Filigree Clasp': ItemData('Equipment', 1337041),
|
||||
#'Azure Stole': ItemData('Equipment', 1337042),
|
||||
'Azure Stole': ItemData('Equipment', 1337042, 0),
|
||||
'Ancient Coin': ItemData('Equipment', 1337043),
|
||||
#'Shiny Rock': ItemData('Equipment', 1337044),
|
||||
'Shiny Rock': ItemData('Equipment', 1337044, 0),
|
||||
'Galaxy Earrings': ItemData('Equipment', 1337045),
|
||||
'Selen\'s Bangle': ItemData('Equipment', 1337046),
|
||||
'Glass Pumpkin': ItemData('Equipment', 1337047),
|
||||
@@ -76,45 +76,45 @@ item_table: Dict[str, ItemData] = {
|
||||
'Antidote': ItemData('UseItem', 1337065, 0),
|
||||
'Chaos Rose': ItemData('UseItem', 1337066, 0),
|
||||
'Warp Shard': ItemData('UseItem', 1337067),
|
||||
#'Dream Wisp': ItemData('UseItem', 1337068),
|
||||
#'PlaceHolderItem1': ItemData('UseItem', 1337069),
|
||||
#'Lachiemi Sun': ItemData('UseItem', 1337070),
|
||||
'Dream Wisp': ItemData('UseItem', 1337068, 0),
|
||||
'PlaceHolderItem1': ItemData('UseItem', 1337069, 0),
|
||||
'Lachiemi Sun': ItemData('UseItem', 1337070, 0),
|
||||
'Jerky': ItemData('UseItem', 1337071),
|
||||
#'Biscuit': ItemData('UseItem', 1337072),
|
||||
#'Fried Cheveur': ItemData('UseItem', 1337073),
|
||||
#'Sautéed Wyvern Tail': ItemData('UseItem', 1337074),
|
||||
#'Unagi Roll': ItemData('UseItem', 1337075),
|
||||
#'Cheveur au Vin': ItemData('UseItem', 1337076),
|
||||
#'Royal Casserole': ItemData('UseItem', 1337077),
|
||||
'Biscuit': ItemData('UseItem', 1337072, 0),
|
||||
'Fried Cheveur': ItemData('UseItem', 1337073, 0),
|
||||
'Sautéed Wyvern Tail': ItemData('UseItem', 1337074, 0),
|
||||
'Unagi Roll': ItemData('UseItem', 1337075, 0),
|
||||
'Cheveur au Vin': ItemData('UseItem', 1337076, 0),
|
||||
'Royal Casserole': ItemData('UseItem', 1337077, 0),
|
||||
'Spaghetti': ItemData('UseItem', 1337078),
|
||||
#'Plump Maggot': ItemData('UseItem', 1337079),
|
||||
#'Orange Juice': ItemData('UseItem', 1337080),
|
||||
'Plump Maggot': ItemData('UseItem', 1337079, 0),
|
||||
'Orange Juice': ItemData('UseItem', 1337080, 0),
|
||||
'Filigree Tea': ItemData('UseItem', 1337081),
|
||||
#'Empress Cake': ItemData('UseItem', 1337082),
|
||||
#'Rotten Tail': ItemData('UseItem', 1337083),
|
||||
#'Alchemy Tools': ItemData('UseItem', 1337084),
|
||||
'Empress Cake': ItemData('UseItem', 1337082, 0),
|
||||
'Rotten Tail': ItemData('UseItem', 1337083, 0),
|
||||
'Alchemy Tools': ItemData('UseItem', 1337084, 0),
|
||||
'Galaxy Stone': ItemData('UseItem', 1337085),
|
||||
#1337086 Used interally
|
||||
#'Essence Crystal': ItemData('UseItem', 1337087),
|
||||
#'Gold Ring': ItemData('UseItem', 1337088),
|
||||
#'Gold Necklace': ItemData('UseItem', 1337089),
|
||||
# 1337086 Used interally
|
||||
'Essence Crystal': ItemData('UseItem', 1337087, 0),
|
||||
'Gold Ring': ItemData('UseItem', 1337088, 0),
|
||||
'Gold Necklace': ItemData('UseItem', 1337089, 0),
|
||||
'Herb': ItemData('UseItem', 1337090),
|
||||
#'Mushroom': ItemData('UseItem', 1337091),
|
||||
#'Plasma Crystal': ItemData('UseItem', 1337092),
|
||||
'Mushroom': ItemData('UseItem', 1337091, 0),
|
||||
'Plasma Crystal': ItemData('UseItem', 1337092, 0),
|
||||
'Plasma IV Bag': ItemData('UseItem', 1337093),
|
||||
#'Cheveur Drumstick': ItemData('UseItem', 1337094),
|
||||
#'Wyvern Tail': ItemData('UseItem', 1337095),
|
||||
#'Eel Meat': ItemData('UseItem', 1337096),
|
||||
#'Cheveux Breast': ItemData('UseItem', 1337097),
|
||||
'Cheveur Drumstick': ItemData('UseItem', 1337094, 0),
|
||||
'Wyvern Tail': ItemData('UseItem', 1337095, 0),
|
||||
'Eel Meat': ItemData('UseItem', 1337096, 0),
|
||||
'Cheveux Breast': ItemData('UseItem', 1337097, 0),
|
||||
'Food Synthesizer': ItemData('UseItem', 1337098),
|
||||
#'Cheveux Feather': ItemData('UseItem', 1337099),
|
||||
#'Siren Ink': ItemData('UseItem', 1337100),
|
||||
#'Plasma Core': ItemData('UseItem', 1337101),
|
||||
#'Silver Ore': ItemData('UseItem', 1337102),
|
||||
#'Historical Documents': ItemData('UseItem', 1337103),
|
||||
#'MapReveal 0': ItemData('UseItem', 1337104),
|
||||
#'MapReveal 1': ItemData('UseItem', 1337105),
|
||||
#'MapReveal 2': ItemData('UseItem', 1337106),
|
||||
'Cheveux Feather': ItemData('UseItem', 1337099, 0),
|
||||
'Siren Ink': ItemData('UseItem', 1337100, 0),
|
||||
'Plasma Core': ItemData('UseItem', 1337101, 0),
|
||||
'Silver Ore': ItemData('UseItem', 1337102, 0),
|
||||
'Historical Documents': ItemData('UseItem', 1337103, 0),
|
||||
'MapReveal 0': ItemData('UseItem', 1337104, 0),
|
||||
'MapReveal 1': ItemData('UseItem', 1337105, 0),
|
||||
'MapReveal 2': ItemData('UseItem', 1337106, 0),
|
||||
'Timespinner Wheel': ItemData('Relic', 1337107, progression=True),
|
||||
'Timespinner Spindle': ItemData('Relic', 1337108, progression=True),
|
||||
'Timespinner Gear 1': ItemData('Relic', 1337109, progression=True),
|
||||
@@ -193,7 +193,7 @@ item_table: Dict[str, ItemData] = {
|
||||
'Max Sand': ItemData('Stat', 1337249, 14)
|
||||
}
|
||||
|
||||
starter_melee_weapons: Tuple[str] = (
|
||||
starter_melee_weapons: Tuple[str, ...] = (
|
||||
'Blue Orb',
|
||||
'Blade Orb',
|
||||
'Fire Orb',
|
||||
@@ -211,7 +211,7 @@ starter_melee_weapons: Tuple[str] = (
|
||||
'Radiant Orb'
|
||||
)
|
||||
|
||||
starter_spells: Tuple[str] = (
|
||||
starter_spells: Tuple[str, ...] = (
|
||||
'Colossal Blade',
|
||||
'Infernal Flames',
|
||||
'Plasma Geyser',
|
||||
@@ -229,7 +229,7 @@ starter_spells: Tuple[str] = (
|
||||
)
|
||||
|
||||
# weighted
|
||||
starter_progression_items: Tuple[str] = (
|
||||
starter_progression_items: Tuple[str, ...] = (
|
||||
'Talaria Attachment',
|
||||
'Talaria Attachment',
|
||||
'Succubus Hairpin',
|
||||
@@ -241,7 +241,7 @@ starter_progression_items: Tuple[str] = (
|
||||
'Lightwall'
|
||||
)
|
||||
|
||||
filler_items: Tuple[str] = (
|
||||
filler_items: Tuple[str, ...] = (
|
||||
'Potion',
|
||||
'Ether',
|
||||
'Hi-Potion',
|
||||
@@ -254,4 +254,12 @@ filler_items: Tuple[str] = (
|
||||
'Mind Refresh ULTRA',
|
||||
'Antidote',
|
||||
'Chaos Rose'
|
||||
)
|
||||
)
|
||||
|
||||
def get_item_names_per_category() -> Dict[str, Set[str]]:
|
||||
categories: Dict[str, Set[str]] = {}
|
||||
|
||||
for name, data in item_table.items():
|
||||
categories.setdefault(data.category, set()).add(name)
|
||||
|
||||
return categories
|
||||
@@ -4,14 +4,12 @@ from .Options import is_option_enabled
|
||||
|
||||
EventId: Optional[int] = None
|
||||
|
||||
|
||||
class LocationData(NamedTuple):
|
||||
region: str
|
||||
name: str
|
||||
code: Optional[int]
|
||||
rule: Callable = lambda state: True
|
||||
|
||||
|
||||
def get_locations(world: Optional[MultiWorld], player: Optional[int]):
|
||||
location_table: Tuple[LocationData, ...] = (
|
||||
# PresentItemLocations
|
||||
@@ -200,6 +198,7 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]):
|
||||
# DownloadTerminals
|
||||
LocationData('Libary', 'Library terminal 1', 1337157, lambda state: state.has('Tablet', player)),
|
||||
LocationData('Libary', 'Library terminal 2', 1337156, lambda state: state.has('Tablet', player)),
|
||||
# 1337158 Is Lost in time
|
||||
LocationData('Libary', 'Library terminal 3', 1337159, lambda state: state.has('Tablet', player)),
|
||||
LocationData('Libary', 'V terminal 1', 1337160, lambda state: state.has_all(['Tablet', 'Library Keycard V'], player)),
|
||||
LocationData('Libary', 'V terminal 2', 1337161, lambda state: state.has_all(['Tablet', 'Library Keycard V'], player)),
|
||||
@@ -218,6 +217,7 @@ def get_locations(world: Optional[MultiWorld], player: Optional[int]):
|
||||
return ( *location_table, *downloadable_items )
|
||||
else:
|
||||
return location_table
|
||||
|
||||
|
||||
starter_progression_locations: Tuple[str, ...] = (
|
||||
'Starter chest 2',
|
||||
|
||||
@@ -19,7 +19,7 @@ class DownloadableItems(Toggle):
|
||||
display_name = "Downloadable items"
|
||||
|
||||
class FacebookMode(Toggle):
|
||||
"With the tablet you will be able to download items at terminals"
|
||||
"Requires Oculus Rift(ng) to spot the weakspots in walls and floors"
|
||||
display_name = "Facebook mode"
|
||||
|
||||
class StartWithMeyef(Toggle):
|
||||
|
||||
@@ -3,7 +3,7 @@ from BaseClasses import MultiWorld
|
||||
from .Options import is_option_enabled
|
||||
|
||||
def get_pyramid_keys_unlock(world: MultiWorld, player: int) -> str:
|
||||
present_teleportation_gates: Tuple[str] = (
|
||||
present_teleportation_gates: Tuple[str, ...] = (
|
||||
"GateKittyBoss",
|
||||
"GateLeftLibrary",
|
||||
"GateMilitairyGate",
|
||||
@@ -12,7 +12,7 @@ def get_pyramid_keys_unlock(world: MultiWorld, player: int) -> str:
|
||||
"GateLakeDesolation"
|
||||
)
|
||||
|
||||
past_teleportation_gates: Tuple[str] = (
|
||||
past_teleportation_gates: Tuple[str, ...] = (
|
||||
"GateLakeSirineRight",
|
||||
"GateAccessToPast",
|
||||
"GateCastleRamparts",
|
||||
|
||||
@@ -3,46 +3,46 @@ from BaseClasses import MultiWorld, Region, Entrance, Location, RegionType
|
||||
from .Options import is_option_enabled
|
||||
from .Locations import LocationData
|
||||
|
||||
def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData], pyramid_keys_unlock: str):
|
||||
def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData, ...], location_cache: List[Location], pyramid_keys_unlock: str):
|
||||
locations_per_region = get_locations_per_region(locations)
|
||||
|
||||
world.regions += [
|
||||
create_region(world, player, locations_per_region, 'Menu'),
|
||||
create_region(world, player, locations_per_region, 'Tutorial'),
|
||||
create_region(world, player, locations_per_region, 'Lake desolation'),
|
||||
create_region(world, player, locations_per_region, 'Upper lake desolation'),
|
||||
create_region(world, player, locations_per_region, 'Lower lake desolation'),
|
||||
create_region(world, player, locations_per_region, 'Libary'),
|
||||
create_region(world, player, locations_per_region, 'Libary top'),
|
||||
create_region(world, player, locations_per_region, 'Varndagroth tower left'),
|
||||
create_region(world, player, locations_per_region, 'Varndagroth tower right (upper)'),
|
||||
create_region(world, player, locations_per_region, 'Varndagroth tower right (lower)'),
|
||||
create_region(world, player, locations_per_region, 'Varndagroth tower right (elevator)'),
|
||||
create_region(world, player, locations_per_region, 'Sealed Caves (Sirens)'),
|
||||
create_region(world, player, locations_per_region, 'Militairy Fortress'),
|
||||
create_region(world, player, locations_per_region, 'The lab'),
|
||||
create_region(world, player, locations_per_region, 'The lab (power off)'),
|
||||
create_region(world, player, locations_per_region, 'The lab (upper)'),
|
||||
create_region(world, player, locations_per_region, 'Emperors tower'),
|
||||
create_region(world, player, locations_per_region, 'Skeleton Shaft'),
|
||||
create_region(world, player, locations_per_region, 'Sealed Caves (upper)'),
|
||||
create_region(world, player, locations_per_region, 'Sealed Caves (Xarion)'),
|
||||
create_region(world, player, locations_per_region, 'Refugee Camp'),
|
||||
create_region(world, player, locations_per_region, 'Forest'),
|
||||
create_region(world, player, locations_per_region, 'Left Side forest Caves'),
|
||||
create_region(world, player, locations_per_region, 'Upper Lake Sirine'),
|
||||
create_region(world, player, locations_per_region, 'Lower Lake Sirine'),
|
||||
create_region(world, player, locations_per_region, 'Caves of Banishment (upper)'),
|
||||
create_region(world, player, locations_per_region, 'Caves of Banishment (Maw)'),
|
||||
create_region(world, player, locations_per_region, 'Caves of Banishment (Sirens)'),
|
||||
create_region(world, player, locations_per_region, 'Caste Ramparts'),
|
||||
create_region(world, player, locations_per_region, 'Caste Keep'),
|
||||
create_region(world, player, locations_per_region, 'Royal towers (lower)'),
|
||||
create_region(world, player, locations_per_region, 'Royal towers'),
|
||||
create_region(world, player, locations_per_region, 'Royal towers (upper)'),
|
||||
create_region(world, player, locations_per_region, 'Ancient Pyramid (left)'),
|
||||
create_region(world, player, locations_per_region, 'Ancient Pyramid (right)'),
|
||||
create_region(world, player, locations_per_region, 'Space time continuum')
|
||||
create_region(world, player, locations_per_region, location_cache, 'Menu'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Tutorial'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Lake desolation'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Upper lake desolation'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Lower lake desolation'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Libary'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Libary top'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Varndagroth tower left'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Varndagroth tower right (upper)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Varndagroth tower right (lower)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Varndagroth tower right (elevator)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Sealed Caves (Sirens)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Militairy Fortress'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'The lab'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'The lab (power off)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'The lab (upper)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Emperors tower'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Skeleton Shaft'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Sealed Caves (upper)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Sealed Caves (Xarion)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Refugee Camp'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Forest'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Left Side forest Caves'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Upper Lake Sirine'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Lower Lake Sirine'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Caves of Banishment (upper)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Caves of Banishment (Maw)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Caves of Banishment (Sirens)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Caste Ramparts'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Caste Keep'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Royal towers (lower)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Royal towers'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Royal towers (upper)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Ancient Pyramid (left)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Ancient Pyramid (right)'),
|
||||
create_region(world, player, locations_per_region, location_cache, 'Space time continuum')
|
||||
]
|
||||
|
||||
connectStartingRegion(world, player)
|
||||
@@ -149,7 +149,7 @@ def create_regions(world: MultiWorld, player: int, locations: Tuple[LocationData
|
||||
connect(world, player, names, 'Space time continuum', 'Caves of Banishment (Maw)', lambda state: pyramid_keys_unlock == "GateMaw")
|
||||
connect(world, player, names, 'Space time continuum', 'Caves of Banishment (upper)', lambda state: pyramid_keys_unlock == "GateCavesOfBanishment")
|
||||
|
||||
def create_location(player: int, name: str, id: Optional[int], region: Region, rule: Callable) -> Location:
|
||||
def create_location(player: int, name: str, id: Optional[int], region: Region, rule: Callable, location_cache: List[Location]) -> Location:
|
||||
location = Location(player, name, id, region)
|
||||
location.access_rule = rule
|
||||
|
||||
@@ -157,19 +157,23 @@ def create_location(player: int, name: str, id: Optional[int], region: Region, r
|
||||
location.event = True
|
||||
location.locked = True
|
||||
|
||||
location_cache.append(location)
|
||||
|
||||
return location
|
||||
|
||||
def create_region(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]], name: str) -> Region:
|
||||
|
||||
def create_region(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]], location_cache: List[Location], name: str) -> Region:
|
||||
region = Region(name, RegionType.Generic, name, player)
|
||||
region.world = world
|
||||
|
||||
if name in locations_per_region:
|
||||
for location_data in locations_per_region[name]:
|
||||
location = create_location(player, location_data.name, location_data.code, region, location_data.rule)
|
||||
location = create_location(player, location_data.name, location_data.code, region, location_data.rule, location_cache)
|
||||
region.locations.append(location)
|
||||
|
||||
return region
|
||||
|
||||
|
||||
def connectStartingRegion(world: MultiWorld, player: int):
|
||||
menu = world.get_region('Menu', player)
|
||||
tutorial = world.get_region('Tutorial', player)
|
||||
@@ -192,6 +196,7 @@ def connectStartingRegion(world: MultiWorld, player: int):
|
||||
teleport_back_to_start.connect(starting_region)
|
||||
space_time_continuum.exits.append(teleport_back_to_start)
|
||||
|
||||
|
||||
def connect(world: MultiWorld, player: int, used_names : Dict[str, int], source: str, target: str, rule: Optional[Callable] = None):
|
||||
sourceRegion = world.get_region(source, player)
|
||||
targetRegion = world.get_region(target, player)
|
||||
@@ -211,10 +216,11 @@ def connect(world: MultiWorld, player: int, used_names : Dict[str, int], source:
|
||||
sourceRegion.exits.append(connection)
|
||||
connection.connect(targetRegion)
|
||||
|
||||
def get_locations_per_region(locations: Tuple[LocationData]) -> Dict[str, List[LocationData]]:
|
||||
|
||||
def get_locations_per_region(locations: Tuple[LocationData, ...]) -> Dict[str, List[LocationData]]:
|
||||
per_region: Dict[str, List[LocationData]] = {}
|
||||
|
||||
for location in locations:
|
||||
per_region[location.region] = [ location ] if location.region not in per_region else per_region[location.region] + [ location ]
|
||||
per_region.setdefault(location.region, []).append(location)
|
||||
|
||||
return per_region
|
||||
@@ -1,52 +1,60 @@
|
||||
from typing import Dict, List, Set
|
||||
from BaseClasses import Item, MultiWorld
|
||||
from BaseClasses import Item, MultiWorld, Location
|
||||
from ..AutoWorld import World
|
||||
from .LogicMixin import TimespinnerLogic
|
||||
from .Items import item_table, starter_melee_weapons, starter_spells, starter_progression_items, filler_items
|
||||
from .Items import get_item_names_per_category, item_table, starter_melee_weapons, starter_spells, starter_progression_items, filler_items
|
||||
from .Locations import get_locations, starter_progression_locations, EventId
|
||||
from .Regions import create_regions
|
||||
from .Options import is_option_enabled, timespinner_options
|
||||
from .PyramidKeys import get_pyramid_keys_unlock
|
||||
|
||||
|
||||
class TimespinnerWorld(World):
|
||||
"""
|
||||
Timespinner is a beautiful metroidvania inspired by classic 90s action-platformers.
|
||||
Travel back in time to change fate itself. Join timekeeper Lunais on her quest for revenge against the empire that killed her family.
|
||||
"""
|
||||
|
||||
options = timespinner_options
|
||||
game = "Timespinner"
|
||||
topology_present = True
|
||||
data_version = 1
|
||||
hidden = True
|
||||
remote_items = False
|
||||
data_version = 2
|
||||
|
||||
item_name_to_id = {name: data.code for name, data in item_table.items()}
|
||||
location_name_to_id = {location.name: location.code for location in get_locations(None, None)}
|
||||
item_name_groups = get_item_names_per_category()
|
||||
|
||||
locked_locations: Dict[int, List[str]] = {}
|
||||
pyramid_keys_unlock: Dict[int, str] = {}
|
||||
location_cache: Dict[int, List[Location]] = {}
|
||||
|
||||
def generate_early(self):
|
||||
self.locked_locations[self.player] = []
|
||||
self.location_cache[self.player] = []
|
||||
self.pyramid_keys_unlock[self.player] = get_pyramid_keys_unlock(self.world, self.player)
|
||||
|
||||
self.item_name_groups = get_item_name_groups()
|
||||
|
||||
def create_regions(self):
|
||||
create_regions(self.world, self.player, get_locations(self.world, self.player),
|
||||
self.pyramid_keys_unlock[self.player])
|
||||
create_regions(self.world, self.player, get_locations(self.world, self.player),
|
||||
self.location_cache[self.player], self.pyramid_keys_unlock[self.player])
|
||||
|
||||
|
||||
def create_item(self, name: str) -> Item:
|
||||
return create_item(name, self.player)
|
||||
|
||||
|
||||
def set_rules(self):
|
||||
setup_events(self.world, self.player, self.locked_locations[self.player])
|
||||
|
||||
self.world.completion_condition[self.player] = lambda state: state.has('Killed Nightmare', self.player)
|
||||
|
||||
|
||||
def generate_basic(self):
|
||||
excluded_items = get_excluded_items_based_on_options(self.world, self.player)
|
||||
|
||||
assign_starter_items(self.world, self.player, excluded_items, self.locked_locations[self.player])
|
||||
|
||||
if not is_option_enabled(self.world, self.player, "QuickSeed") or \
|
||||
not is_option_enabled(self.world, self.player, "Inverted"):
|
||||
if not is_option_enabled(self.world, self.player, "QuickSeed") and not is_option_enabled(self.world, self.player, "Inverted"):
|
||||
place_first_progression_item(self.world, self.player, excluded_items, self.locked_locations[self.player])
|
||||
|
||||
pool = get_item_pool(self.world, self.player, excluded_items)
|
||||
@@ -55,17 +63,18 @@ class TimespinnerWorld(World):
|
||||
|
||||
self.world.itempool += pool
|
||||
|
||||
|
||||
def fill_slot_data(self) -> Dict:
|
||||
slot_data = {}
|
||||
|
||||
for option_name in timespinner_options:
|
||||
option = getattr(self.world, option_name)[self.player]
|
||||
slot_data[option_name] = int(option.value)
|
||||
slot_data[option_name] = is_option_enabled(self.world, self.player, option_name)
|
||||
|
||||
slot_data["StinkyMaw"] = 1
|
||||
slot_data["ProgressiveVerticalMovement"] = 0
|
||||
slot_data["ProgressiveKeycards"] = 0
|
||||
slot_data["StinkyMaw"] = True
|
||||
slot_data["ProgressiveVerticalMovement"] = False
|
||||
slot_data["ProgressiveKeycards"] = False
|
||||
slot_data["PyramidKeysGate"] = self.pyramid_keys_unlock[self.player]
|
||||
slot_data["PersonalItems"] = get_personal_items(self.player, self.location_cache[self.player])
|
||||
|
||||
return slot_data
|
||||
|
||||
@@ -106,7 +115,7 @@ def assign_starter_items(world: MultiWorld, player: int, excluded_items: List[st
|
||||
|
||||
|
||||
def get_item_pool(world: MultiWorld, player: int, excluded_items: List[str]) -> List[Item]:
|
||||
pool = []
|
||||
pool: List[Item] = []
|
||||
|
||||
for name, data in item_table.items():
|
||||
if not name in excluded_items:
|
||||
@@ -159,10 +168,11 @@ def setup_events(world: MultiWorld, player: int, locked_locations: List[str]):
|
||||
location.place_locked_item(item)
|
||||
|
||||
|
||||
def get_item_name_groups() -> Dict[str, Set[str]]:
|
||||
groups: Dict[str, Set[str]] = {}
|
||||
def get_personal_items(player: int, locations: List[Location]) -> Dict[int, int]:
|
||||
personal_items: Dict[int, int] = {}
|
||||
|
||||
for name, data in item_table.items():
|
||||
groups.setdefault(data.category, set()).add(name)
|
||||
|
||||
return groups
|
||||
for location in locations:
|
||||
if location.address and location.item and location.item.code and location.item.player == player:
|
||||
personal_items[location.address] = location.item.code
|
||||
|
||||
return personal_items
|
||||
Reference in New Issue
Block a user