Added Timespinner support (#77)
AP side for 0.1.8 inclusion, Client and Documentation outstanding.
This commit is contained in:
parent
7ac9bd8591
commit
822e8941ed
|
@ -0,0 +1,257 @@
|
|||
from typing import Dict, Tuple, NamedTuple
|
||||
|
||||
class ItemData(NamedTuple):
|
||||
category: str
|
||||
code: int
|
||||
count: int = 1
|
||||
progression: bool = False
|
||||
|
||||
# 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),
|
||||
'Pointy Hat': ItemData('Equipment', 1337005),
|
||||
#'Dragoon Helmet': ItemData('Equipment', 1337006),
|
||||
'Buckle Hat': ItemData('Equipment', 1337007),
|
||||
#'Advisor Hat': ItemData('Equipment', 1337008),
|
||||
'Librarian Hat': ItemData('Equipment', 1337009),
|
||||
#'Combat Helmet': ItemData('Equipment', 1337010),
|
||||
'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),
|
||||
'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),
|
||||
'Traveler\'s Cloak': ItemData('Equipment', 1337021),
|
||||
#'Dragoon Armor': ItemData('Equipment', 1337022),
|
||||
'Midnight Cloak': ItemData('Equipment', 1337023),
|
||||
#'Advisor Robe': ItemData('Equipment', 1337024),
|
||||
'Librarian Robe': ItemData('Equipment', 1337025),
|
||||
#'Military Armor': ItemData('Equipment', 1337026),
|
||||
'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),
|
||||
'Metal Wristband': ItemData('Equipment', 1337034),
|
||||
#'Nymph Hairband': ItemData('Equipment', 1337035),
|
||||
#'Mother o\' Pearl': ItemData('Equipment', 1337036),
|
||||
'Bird Statue': ItemData('Equipment', 1337037),
|
||||
#'Chaos Stole': ItemData('Equipment', 1337038),
|
||||
'Pendulum': ItemData('Equipment', 1337039),
|
||||
#'Chaos Horn': ItemData('Equipment', 1337040),
|
||||
'Filigree Clasp': ItemData('Equipment', 1337041),
|
||||
#'Azure Stole': ItemData('Equipment', 1337042),
|
||||
'Ancient Coin': ItemData('Equipment', 1337043),
|
||||
#'Shiny Rock': ItemData('Equipment', 1337044),
|
||||
'Galaxy Earrings': ItemData('Equipment', 1337045),
|
||||
'Selen\'s Bangle': ItemData('Equipment', 1337046),
|
||||
'Glass Pumpkin': ItemData('Equipment', 1337047),
|
||||
'Gilded Egg': ItemData('Equipment', 1337048),
|
||||
'Meyef': ItemData('Familiar', 1337049),
|
||||
'Griffin': ItemData('Familiar', 1337050),
|
||||
'Merchant Crow': ItemData('Familiar', 1337051),
|
||||
'Kobo': ItemData('Familiar', 1337052),
|
||||
'Sprite': ItemData('Familiar', 1337053),
|
||||
'Demon': ItemData('Familiar', 1337054),
|
||||
'Potion': ItemData('UseItem', 1337055, 0),
|
||||
'Ether': ItemData('UseItem', 1337056, 0),
|
||||
'Sand Vial': ItemData('UseItem', 1337057, 0),
|
||||
'Hi-Potion': ItemData('UseItem', 1337058, 0),
|
||||
'Hi-Ether': ItemData('UseItem', 1337059, 0),
|
||||
'Sand Bottle': ItemData('UseItem', 1337060, 0),
|
||||
'Berry Pick-Mi-Up': ItemData('UseItem', 1337061, 0),
|
||||
'Berry Pick-Mi-Up+': ItemData('UseItem', 1337062, 0),
|
||||
'Mind Refresh': ItemData('UseItem', 1337063, 0),
|
||||
'Mind Refresh ULTRA': ItemData('UseItem', 1337064, 0),
|
||||
'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),
|
||||
'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),
|
||||
'Spaghetti': ItemData('UseItem', 1337078),
|
||||
#'Plump Maggot': ItemData('UseItem', 1337079),
|
||||
#'Orange Juice': ItemData('UseItem', 1337080),
|
||||
'Filigree Tea': ItemData('UseItem', 1337081),
|
||||
#'Empress Cake': ItemData('UseItem', 1337082),
|
||||
#'Rotten Tail': ItemData('UseItem', 1337083),
|
||||
#'Alchemy Tools': ItemData('UseItem', 1337084),
|
||||
'Galaxy Stone': ItemData('UseItem', 1337085),
|
||||
#1337086 Used interally
|
||||
#'Essence Crystal': ItemData('UseItem', 1337087),
|
||||
#'Gold Ring': ItemData('UseItem', 1337088),
|
||||
#'Gold Necklace': ItemData('UseItem', 1337089),
|
||||
'Herb': ItemData('UseItem', 1337090),
|
||||
#'Mushroom': ItemData('UseItem', 1337091),
|
||||
#'Plasma Crystal': ItemData('UseItem', 1337092),
|
||||
'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),
|
||||
'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),
|
||||
'Timespinner Wheel': ItemData('Relic', 1337107, progression=True),
|
||||
'Timespinner Spindle': ItemData('Relic', 1337108, progression=True),
|
||||
'Timespinner Gear 1': ItemData('Relic', 1337109, progression=True),
|
||||
'Timespinner Gear 2': ItemData('Relic', 1337110, progression=True),
|
||||
'Timespinner Gear 3': ItemData('Relic', 1337111, progression=True),
|
||||
'Twin Pyramid Key': ItemData('Relic', 1337112, progression=True),
|
||||
'Celestial Sash': ItemData('Relic', 1337113, progression=True),
|
||||
'Succubus Hairpin': ItemData('Relic', 1337114, progression=True),
|
||||
'Talaria Attachment': ItemData('Relic', 1337115, progression=True),
|
||||
'Water Mask': ItemData('Relic', 1337116, progression=True),
|
||||
'Gas Mask': ItemData('Relic', 1337117, progression=True),
|
||||
'Soul Scanner': ItemData('Relic', 1337118),
|
||||
'Security Keycard A': ItemData('Relic', 1337119, progression=True),
|
||||
'Security Keycard B': ItemData('Relic', 1337120, progression=True),
|
||||
'Security Keycard C': ItemData('Relic', 1337121, progression=True),
|
||||
'Security Keycard D': ItemData('Relic', 1337122, progression=True),
|
||||
'Library Keycard V': ItemData('Relic', 1337123, progression=True),
|
||||
'Tablet': ItemData('Relic', 1337124, progression=True),
|
||||
'Elevator Keycard': ItemData('Relic', 1337125, progression=True),
|
||||
'Jewelry Box': ItemData('Relic', 1337126),
|
||||
'Goddess Brooch': ItemData('Relic', 1337127),
|
||||
'Wyrm Brooch': ItemData('Relic', 1337128),
|
||||
'Greed Brooch': ItemData('Relic', 1337129),
|
||||
'Eternal Brooch': ItemData('Relic', 1337130),
|
||||
'Blue Orb': ItemData('Orb Melee', 1337131),
|
||||
'Blade Orb': ItemData('Orb Melee', 1337132),
|
||||
'Fire Orb': ItemData('Orb Melee', 1337133, progression=True),
|
||||
'Plasma Orb': ItemData('Orb Melee', 1337134, progression=True),
|
||||
'Iron Orb': ItemData('Orb Melee', 1337135),
|
||||
'Ice Orb': ItemData('Orb Melee', 1337136),
|
||||
'Wind Orb': ItemData('Orb Melee', 1337137),
|
||||
'Gun Orb': ItemData('Orb Melee', 1337138),
|
||||
'Umbra Orb': ItemData('Orb Melee', 1337139),
|
||||
'Empire Orb': ItemData('Orb Melee', 1337140),
|
||||
'Eye Orb': ItemData('Orb Melee', 1337141),
|
||||
'Blood Orb': ItemData('Orb Melee', 1337142),
|
||||
'Forbidden Tome': ItemData('Orb Melee', 1337143),
|
||||
'Shattered Orb': ItemData('Orb Melee', 1337144),
|
||||
'Nether Orb': ItemData('Orb Melee', 1337145),
|
||||
'Radiant Orb': ItemData('Orb Melee', 1337146),
|
||||
'Aura Blast': ItemData('Orb Spell', 1337147),
|
||||
'Colossal Blade': ItemData('Orb Spell', 1337148),
|
||||
'Infernal Flames': ItemData('Orb Spell', 1337149, progression=True),
|
||||
'Plasma Geyser': ItemData('Orb Spell', 1337150, progression=True),
|
||||
'Colossal Hammer': ItemData('Orb Spell', 1337151),
|
||||
'Frozen Spires': ItemData('Orb Spell', 1337152),
|
||||
'Storm Eye': ItemData('Orb Spell', 1337153),
|
||||
'Arm Cannon': ItemData('Orb Spell', 1337154),
|
||||
'Dark Flames': ItemData('Orb Spell', 1337155),
|
||||
'Aura Serpent': ItemData('Orb Spell', 1337156),
|
||||
'Chaos Blades': ItemData('Orb Spell', 1337157),
|
||||
'Crimson Vortex': ItemData('Orb Spell', 1337158),
|
||||
'Djinn Inferno': ItemData('Orb Spell', 1337159, progression=True),
|
||||
'Bombardment': ItemData('Orb Spell', 1337160),
|
||||
'Corruption': ItemData('Orb Spell', 1337161),
|
||||
'Lightwall': ItemData('Orb Spell', 1337162, progression=True),
|
||||
'Bleak Ring': ItemData('Orb Passive', 1337163),
|
||||
'Scythe Ring': ItemData('Orb Passive', 1337164),
|
||||
'Pyro Ring': ItemData('Orb Passive', 1337165, progression=True),
|
||||
'Royal Ring': ItemData('Orb Passive', 1337166, progression=True),
|
||||
'Shield Ring': ItemData('Orb Passive', 1337167),
|
||||
'Icicle Ring': ItemData('Orb Passive', 1337168),
|
||||
'Tailwind Ring': ItemData('Orb Passive', 1337169),
|
||||
'Economizer Ring': ItemData('Orb Passive', 1337170),
|
||||
'Dusk Ring': ItemData('Orb Passive', 1337171),
|
||||
'Star of Lachiem': ItemData('Orb Passive', 1337172),
|
||||
'Oculus Ring': ItemData('Orb Passive', 1337173, progression=True),
|
||||
'Sanguine Ring': ItemData('Orb Passive', 1337174),
|
||||
'Sun Ring': ItemData('Orb Passive', 1337175),
|
||||
'Silence Ring': ItemData('Orb Passive', 1337176),
|
||||
'Shadow Seal': ItemData('Orb Passive', 1337177),
|
||||
'Hope Ring': ItemData('Orb Passive', 1337178),
|
||||
'Max HP': ItemData('Stat', 1337179, 12),
|
||||
'Max Aura': ItemData('Stat', 1337180, 13),
|
||||
# 1337181 - 1337248 Reserved
|
||||
'Max Sand': ItemData('Stat', 1337249, 14)
|
||||
}
|
||||
|
||||
starter_melee_weapons: Tuple[str] = (
|
||||
'Blue Orb',
|
||||
'Blade Orb',
|
||||
'Fire Orb',
|
||||
'Iron Orb',
|
||||
'Ice Orb',
|
||||
'Wind Orb',
|
||||
'Gun Orb',
|
||||
'Umbra Orb',
|
||||
'Empire Orb',
|
||||
'Eye Orb',
|
||||
'Blood Orb',
|
||||
'Forbidden Tome',
|
||||
'Shattered Orb',
|
||||
'Nether Orb',
|
||||
'Radiant Orb'
|
||||
)
|
||||
|
||||
starter_spells: Tuple[str] = (
|
||||
'Colossal Blade',
|
||||
'Infernal Flames',
|
||||
'Plasma Geyser',
|
||||
'Colossal Hammer',
|
||||
'Frozen Spires',
|
||||
'Storm Eye',
|
||||
'Arm Cannon',
|
||||
'Dark Flames',
|
||||
'Aura Serpent',
|
||||
'Chaos Blades',
|
||||
'Crimson Vortex',
|
||||
'Djinn Inferno',
|
||||
'Bombardment',
|
||||
'Corruption'
|
||||
)
|
||||
|
||||
# weighted
|
||||
starter_progression_items: Tuple[str] = (
|
||||
'Talaria Attachment',
|
||||
'Talaria Attachment',
|
||||
'Succubus Hairpin',
|
||||
'Succubus Hairpin',
|
||||
'Timespinner Wheel',
|
||||
'Timespinner Wheel',
|
||||
'Twin Pyramid Key',
|
||||
'Celestial Sash',
|
||||
'Lightwall'
|
||||
)
|
||||
|
||||
filler_items: Tuple[str] = (
|
||||
'Potion',
|
||||
'Ether',
|
||||
'Hi-Potion',
|
||||
'Hi-Ether',
|
||||
'Sand Vial',
|
||||
'Sand Bottle',
|
||||
'Berry Pick-Mi-Up',
|
||||
'Berry Pick-Mi-Up+',
|
||||
'Mind Refresh',
|
||||
'Mind Refresh ULTRA',
|
||||
'Antidote',
|
||||
'Chaos Rose'
|
||||
)
|
|
@ -0,0 +1,225 @@
|
|||
from typing import Tuple, Optional, Callable, NamedTuple
|
||||
from BaseClasses import MultiWorld
|
||||
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: MultiWorld, player: int):
|
||||
location_table: Tuple[LocationData] = (
|
||||
# PresentItemLocations
|
||||
LocationData('Tutorial', 'Yo Momma 1', 1337000),
|
||||
LocationData('Tutorial', 'Yo Momma 2', 1337001),
|
||||
LocationData('Lake desolation', 'Starter chest 2', 1337002),
|
||||
LocationData('Lake desolation', 'Starter chest 3', 1337003),
|
||||
LocationData('Lake desolation', 'Starter chest 1', 1337004),
|
||||
LocationData('Lake desolation', 'Timespinner Wheel room', 1337005),
|
||||
LocationData('Upper lake desolation', 'Forget me not chest', 1337006),
|
||||
LocationData('Lower lake desolation', 'Chicken chest', 1337007, lambda state: state._timespinner_has_timestop(world, player)),
|
||||
LocationData('Lower lake desolation', 'Not so secret room', 1337008, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Lower lake desolation', 'Tank chest', 1337009, lambda state: state._timespinner_has_timestop(world, player)),
|
||||
LocationData('Upper lake desolation', 'Oxygen recovery room', 1337010),
|
||||
LocationData('Upper lake desolation', 'Lake secret', 1337011, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Upper lake desolation', 'Double jump cave floor', 1337012, lambda state: state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('Upper lake desolation', 'Double jump cave platform', 1337013),
|
||||
LocationData('Upper lake desolation', 'Fire-Locked sparrow chest', 1337014),
|
||||
LocationData('Upper lake desolation', 'Crash site pedestal', 1337015),
|
||||
LocationData('Upper lake desolation', 'Crash site chest 1', 1337016, lambda state: state.has_all(['Killed Maw', 'Gas Mask'], player)),
|
||||
LocationData('Upper lake desolation', 'Crash site chest 2', 1337017, lambda state: state.has_all(['Killed Maw', 'Gas Mask'], player)),
|
||||
LocationData('Upper lake desolation', 'Kitty Boss', 1337018),
|
||||
LocationData('Libary', 'Basement', 1337019),
|
||||
LocationData('Libary', 'Consolation', 1337020),
|
||||
LocationData('Libary', 'Librarian', 1337021),
|
||||
LocationData('Libary', 'Reading nook chest', 1337022),
|
||||
LocationData('Libary', 'Storage room chest 1', 1337023, lambda state: state._timespinner_has_keycard_D(world, player)),
|
||||
LocationData('Libary', 'Storage room chest 2', 1337024, lambda state: state._timespinner_has_keycard_D(world, player)),
|
||||
LocationData('Libary', 'Storage room chest 3', 1337025, lambda state: state._timespinner_has_keycard_D(world, player)),
|
||||
LocationData('Libary top', 'Backer room chest 5', 1337026),
|
||||
LocationData('Libary top', 'Backer room chest 4', 1337027),
|
||||
LocationData('Libary top', 'Backer room chest 3', 1337028),
|
||||
LocationData('Libary top', 'Backer room chest 2', 1337029),
|
||||
LocationData('Libary top', 'Backer room chest 1', 1337030),
|
||||
LocationData('Varndagroth tower left', 'Elevator Key not required', 1337031),
|
||||
LocationData('Varndagroth tower left', 'Ye olde Timespinner', 1337032),
|
||||
LocationData('Varndagroth tower left', 'C Keycard chest', 1337033, lambda state: state._timespinner_has_keycard_C(world, player)),
|
||||
LocationData('Varndagroth tower left', 'Left air vents secret', 1337034, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Varndagroth tower left', 'Left elevator chest', 1337035, lambda state: state.has('Elevator Keycard', player)),
|
||||
LocationData('Varndagroth tower right (upper)', 'Spider heck room', 1337036),
|
||||
LocationData('Varndagroth tower right (elevator)', 'Right elevator chest', 1337037),
|
||||
LocationData('Varndagroth tower right (upper)', 'Elevator card chest', 1337038, lambda state: state.has('Elevator Keycard', player) or state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('Varndagroth tower right (upper)', 'Air vents left', 1337039, lambda state: state.has('Elevator Keycard', player) or state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('Varndagroth tower right (upper)', 'Air Vents right', 1337040, lambda state: state.has('Elevator Keycard', player) or state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('Varndagroth tower right (lower)', 'Right side bottom floor', 1337041),
|
||||
LocationData('Varndagroth tower right (elevator)', 'Varndagroth', 1337042, lambda state: state._timespinner_has_keycard_C(world, player)),
|
||||
LocationData('Varndagroth tower right (elevator)', 'Varndagroth Spider hell', 1337043, lambda state: state._timespinner_has_keycard_A(world, player)),
|
||||
LocationData('Skeleton Shaft', 'Skeleton', 1337044),
|
||||
LocationData('Sealed Caves (Xarion)', 'Shroom jump room', 1337045, lambda state: state._timespinner_has_timestop(world, player)),
|
||||
LocationData('Sealed Caves (Xarion)', 'Double shroom room', 1337046),
|
||||
LocationData('Sealed Caves (Xarion)', 'Mini jackpot room', 1337047, lambda state: state._timespinner_has_forwarddash_doublejump(world, player)),
|
||||
LocationData('Sealed Caves (Xarion)', 'Below mini jackpot room', 1337048),
|
||||
LocationData('Sealed Caves (Xarion)', 'Sealed cave secret room', 1337049, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Sealed Caves (Xarion)', 'Below Sealed cave secret', 1337050),
|
||||
LocationData('Sealed Caves (Xarion)', 'Last chance before Xarion', 1337051, lambda state: state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('Sealed Caves (Xarion)', 'Xarion', 1337052),
|
||||
LocationData('Sealed Caves (Sirens)', 'Solo siren chest', 1337053, lambda state: state.has('Water Mask', player)),
|
||||
LocationData('Sealed Caves (Sirens)', 'Big siren room right', 1337054, lambda state: state.has('Water Mask', player)),
|
||||
LocationData('Sealed Caves (Sirens)', 'Big siren Room left', 1337055, lambda state: state.has('Water Mask', player)),
|
||||
LocationData('Sealed Caves (Sirens)', 'Room after sirens chest 2', 1337056),
|
||||
LocationData('Sealed Caves (Sirens)', 'Room after sirens chest 1', 1337057),
|
||||
LocationData('Militairy Fortress', 'Militairy Bomber chest', 1337058, lambda state: state.has('Timespinner Wheel', player) and state._timespinner_has_doublejump_of_npc(world, player)),
|
||||
LocationData('Militairy Fortress', 'Close combat room', 1337059),
|
||||
LocationData('Militairy Fortress', 'Bridge full of soldiers', 1337060),
|
||||
LocationData('Militairy Fortress', 'Giantess Room', 1337061),
|
||||
LocationData('Militairy Fortress', 'Bridge with Giantess', 1337062),
|
||||
LocationData('Militairy Fortress', 'Military B door chest 2', 1337063, lambda state: state._timespinner_has_doublejump(world, player) and state._timespinner_has_keycard_B(world, player)),
|
||||
LocationData('Militairy Fortress', 'Military B door chest 1', 1337064, lambda state: state._timespinner_has_doublejump(world, player) and state._timespinner_has_keycard_B(world, player)),
|
||||
LocationData('Militairy Fortress', 'Military pedestal', 1337065, lambda state: state._timespinner_has_doublejump(world, player) and (state._timespinner_has_doublejump_of_npc(world, player) or state._timespinner_has_forwarddash_doublejump(world, player))),
|
||||
LocationData('The lab', 'Coffee Break chest', 1337066),
|
||||
LocationData('The lab', 'Lower trash right', 1337067, lambda state: state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('The lab', 'Lower trash left', 1337068, lambda state: state._timespinner_has_upwarddash(world, player)),
|
||||
LocationData('The lab', 'Single turret room', 1337069, lambda state: state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('The lab (power off)', 'Trash jump room', 1337070),
|
||||
LocationData('The lab (power off)', 'Dynamo Works', 1337071),
|
||||
LocationData('The lab (upper)', 'Blob mom', 1337072),
|
||||
LocationData('The lab (power off)', 'Experiment #13', 1337073),
|
||||
LocationData('The lab (upper)', 'Download and chest room', 1337074),
|
||||
LocationData('The lab (upper)', 'Lab secret', 1337075, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('The lab (power off)', 'Lab Spider hell', 1337076, lambda state: state._timespinner_has_keycard_A(world, player)),
|
||||
LocationData('Emperors tower', 'Bottom', 1337077),
|
||||
LocationData('Emperors tower', 'After Courtyard Floor Secret', 1337078, lambda state: state._timespinner_has_upwarddash(world, player) and state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Emperors tower', 'After Courtyard Chest', 1337079, lambda state: state._timespinner_has_upwarddash(world, player)),
|
||||
LocationData('Emperors tower', 'Galactic Sage Room', 1337080),
|
||||
LocationData('Emperors tower', 'Bottom of Right Tower', 1337081),
|
||||
LocationData('Emperors tower', 'Wayyyy up there', 1337082),
|
||||
LocationData('Emperors tower', 'Left tower balcony', 1337083),
|
||||
LocationData('Emperors tower', 'Dad\'s Chambers chest', 1337084),
|
||||
LocationData('Emperors tower', 'Dad\'s Chambers pedestal', 1337085),
|
||||
|
||||
# PastItemLocations
|
||||
LocationData('Refugee Camp', 'Neliste\'s Bra', 1337086),
|
||||
LocationData('Refugee Camp', 'Refugee camp storage chest 3', 1337087),
|
||||
LocationData('Refugee Camp', 'Refugee camp storage chest 2', 1337088),
|
||||
LocationData('Refugee Camp', 'Refugee camp storage chest 1', 1337089),
|
||||
LocationData('Forest', 'Refugee camp roof', 1337090),
|
||||
LocationData('Forest', 'Bat jump chest', 1337091, lambda state: state._timespinner_has_doublejump_of_npc(world, player) or state._timespinner_has_forwarddash_doublejump(world, player)),
|
||||
LocationData('Forest', 'Green platform secret', 1337092, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Forest', 'Rats guarded chest', 1337093),
|
||||
LocationData('Forest', 'Waterfall chest 1', 1337094, lambda state: state.has('Water Mask', player)),
|
||||
LocationData('Forest', 'Waterfall chest 2', 1337095, lambda state: state.has('Water Mask', player)),
|
||||
LocationData('Forest', 'Batcave', 1337096),
|
||||
LocationData('Forest', 'Bridge Chest', 1337097),
|
||||
LocationData('Left Side forest Caves', 'Solitary bat room', 1337098),
|
||||
LocationData('Upper Lake Sirine', 'Rat nest', 1337099),
|
||||
LocationData('Upper Lake Sirine', 'Double jump cave platform (past)', 1337100, lambda state: state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('Upper Lake Sirine', 'Double jump cave floor (past)', 1337101),
|
||||
LocationData('Upper Lake Sirine', 'West lake serene cave secret', 1337102, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Upper Lake Sirine', 'Chest behind vines', 1337103),
|
||||
LocationData('Upper Lake Sirine', 'Pyramid keys room', 1337104),
|
||||
LocationData('Lower Lake Sirine', 'Deep dive', 1337105),
|
||||
LocationData('Lower Lake Sirine', 'Under the eels', 1337106),
|
||||
LocationData('Lower Lake Sirine', 'Water spikes room', 1337107),
|
||||
LocationData('Lower Lake Sirine', 'Underwater secret', 1337108, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Lower Lake Sirine', 'T chest', 1337109),
|
||||
LocationData('Lower Lake Sirine', 'Past the eels', 1337110),
|
||||
LocationData('Lower Lake Sirine', 'Underwater pedestal', 1337111),
|
||||
LocationData('Caves of Banishment (upper)', 'Mushroom double jump', 1337112, lambda state: state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('Caves of Banishment (upper)', 'Caves of banishment secret room', 1337113),
|
||||
LocationData('Caves of Banishment (upper)', 'Below caves of banishment secret', 1337114),
|
||||
LocationData('Caves of Banishment (upper)', 'Single shroom room', 1337115),
|
||||
LocationData('Caves of Banishment (upper)', 'Jackpot room chest 1', 1337116, lambda state: state._timespinner_has_forwarddash_doublejump(world, player)),
|
||||
LocationData('Caves of Banishment (upper)', 'Jackpot room chest 2', 1337117, lambda state: state._timespinner_has_forwarddash_doublejump(world, player)),
|
||||
LocationData('Caves of Banishment (upper)', 'Jackpot room chest 3', 1337118, lambda state: state._timespinner_has_forwarddash_doublejump(world, player)),
|
||||
LocationData('Caves of Banishment (upper)', 'Jackpot room chest 4', 1337119, lambda state: state._timespinner_has_forwarddash_doublejump(world, player)),
|
||||
LocationData('Caves of Banishment (upper)', 'Banishment pedestal', 1337120),
|
||||
LocationData('Caves of Banishment (Maw)', 'Last chance before Maw', 1337121, lambda state: state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('Caves of Banishment (Maw)', 'Killed Maw', EventId, lambda state: state.has('Gas Mask', player)),
|
||||
LocationData('Caves of Banishment (Maw)', 'Mineshaft', 1337122, lambda state: state.has('Gas Mask', player)),
|
||||
LocationData('Caves of Banishment (Sirens)', 'Wyvern room', 1337123),
|
||||
LocationData('Caves of Banishment (Sirens)', 'Above water sirens', 1337124),
|
||||
LocationData('Caves of Banishment (Sirens)', 'Underwater sirens left', 1337125, lambda state: state.has('Water Mask', player)),
|
||||
LocationData('Caves of Banishment (Sirens)', 'Underwater sirens right', 1337126, lambda state: state.has('Water Mask', player)),
|
||||
LocationData('Caves of Banishment (Sirens)', 'Water hook', 1337127),
|
||||
LocationData('Caste Ramparts', 'Caste Bomber chest', 1337128, lambda state: state._timespinner_has_multiple_small_jumps_of_npc(world, player)),
|
||||
LocationData('Caste Ramparts', 'Freeze the engineer', 1337129, lambda state: state.has('Talaria Attachment', player) or state._timespinner_has_timestop(world, player)),
|
||||
LocationData('Caste Ramparts', 'Giantess guarded room', 1337130),
|
||||
LocationData('Caste Ramparts', 'Knight and archer guarded room', 1337131),
|
||||
LocationData('Caste Ramparts', 'Castle pedestal', 1337132),
|
||||
LocationData('Caste Keep', 'Basement secret pedestal', 1337133, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Caste Keep', 'Break the wall', 1337134),
|
||||
LocationData('Royal towers (lower)', 'Yas queen room', 1337135, lambda state: state._timespinner_has_pink(world, player)),
|
||||
LocationData('Caste Keep', 'Basement hammer', 1337136),
|
||||
LocationData('Caste Keep', 'Omelette chest', 1337137),
|
||||
LocationData('Caste Keep', 'Just an egg', 1337138),
|
||||
LocationData('Caste Keep', 'Out of the way', 1337139),
|
||||
LocationData('Caste Keep', 'Killed Twins', EventId, lambda state: state._timespinner_has_timestop(world, player)),
|
||||
LocationData('Caste Keep', 'Twins', 1337140, lambda state: state._timespinner_has_timestop(world, player)),
|
||||
LocationData('Caste Keep', 'Royal guard tiny room', 1337141, lambda state: state._timespinner_has_doublejump(world, player)),
|
||||
LocationData('Royal towers (lower)', 'Royal tower floor secret', 1337142, lambda state: state._timespinner_has_doublejump(world, player) and state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Royal towers', 'Above the gap', 1337143),
|
||||
LocationData('Royal towers', 'Under the ice mage', 1337144),
|
||||
LocationData('Royal towers (upper)', 'Next to easy struggle juggle room', 1337145),
|
||||
LocationData('Royal towers (upper)', 'Easy struggle juggle', 1337146, lambda state: state._timespinner_has_doublejump_of_npc(world, player)),
|
||||
LocationData('Royal towers (upper)', 'Hard struggle juggle', 1337147, lambda state: state._timespinner_has_doublejump_of_npc(world, player)),
|
||||
LocationData('Royal towers (upper)', 'No sturggle required', 1337148, lambda state: state._timespinner_has_doublejump_of_npc(world, player)),
|
||||
LocationData('Royal towers', 'Right tower freebie', 1337149),
|
||||
LocationData('Royal towers (upper)', 'Above the cide mage', 1337150),
|
||||
LocationData('Royal towers (upper)', 'Royal guard big room', 1337151),
|
||||
LocationData('Royal towers (upper)', 'Before Aelana', 1337152),
|
||||
LocationData('Royal towers (upper)', 'Killed Aelana', EventId),
|
||||
LocationData('Royal towers (upper)', 'Statue room', 1337153, lambda state: state._timespinner_has_upwarddash(world, player)),
|
||||
LocationData('Royal towers (upper)', 'Aelana\'s pedestal', 1337154),
|
||||
LocationData('Royal towers (upper)', 'After Aelana', 1337155),
|
||||
|
||||
# 1337157 - 1337170 Downloads
|
||||
|
||||
# 1337171 - 1337238 Reserved
|
||||
|
||||
# PyramidItemLocations
|
||||
#LocationData('Temporal Gyre', 'Transition chest 1', 1337239),
|
||||
#LocationData('Temporal Gyre', 'Transition chest 2', 1337240),
|
||||
#LocationData('Temporal Gyre', 'Transition chest 3', 1337241),
|
||||
#LocationData('Temporal Gyre', 'Ravenlord pre fight', 1337242),
|
||||
#LocationData('Temporal Gyre', 'Ravenlord post fight', 1337243),
|
||||
#LocationData('Temporal Gyre', 'Ifrid pre fight', 1337244),
|
||||
#LocationData('Temporal Gyre', 'Ifrid post fight', 1337245),
|
||||
LocationData('Ancient Pyramid (left)', 'Why not it\'s right there', 1337246),
|
||||
LocationData('Ancient Pyramid (left)', 'Conviction guarded room', 1337247),
|
||||
LocationData('Ancient Pyramid (right)', 'Pit secret room', 1337248, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Ancient Pyramid (right)', 'Regret chest', 1337249, lambda state: state._timespinner_can_break_walls(world, player)),
|
||||
LocationData('Ancient Pyramid (right)', 'Killed Nightmare', EventId)
|
||||
)
|
||||
|
||||
downloadable_items: Tuple[LocationData] = (
|
||||
# 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)),
|
||||
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)),
|
||||
LocationData('Libary', 'V terminal 3', 1337162, lambda state: state.has_all(['Tablet', 'Library Keycard V'], player)),
|
||||
LocationData('Libary top', 'Backer room terminal', 1337163, lambda state: state.has('Tablet', player)),
|
||||
LocationData('Varndagroth tower right (elevator)', 'Medbay', 1337164, lambda state: state.has('Tablet', player) and state._timespinner_has_keycard_B(world, player)),
|
||||
LocationData('The lab (upper)', 'Chest and download terminal', 1337165, lambda state: state.has('Tablet', player)),
|
||||
LocationData('The lab (power off)', 'Lab terminal middle', 1337166, lambda state: state.has('Tablet', player)),
|
||||
LocationData('The lab (power off)', 'Sentry platform terminal', 1337167, lambda state: state.has('Tablet', player)),
|
||||
LocationData('The lab', 'Experiment 13 terminal', 1337168, lambda state: state.has('Tablet', player)),
|
||||
LocationData('The lab', 'Lab terminal left', 1337169, lambda state: state.has('Tablet', player)),
|
||||
LocationData('The lab (power off)', 'Lab terminal right', 1337170, lambda state: state.has('Tablet', player))
|
||||
)
|
||||
|
||||
if not world or is_option_enabled(world, player, "DownloadableItems"):
|
||||
return ( *location_table, *downloadable_items )
|
||||
else:
|
||||
return location_table
|
||||
|
||||
starter_progression_locations: Tuple[str] = (
|
||||
'Starter chest 2',
|
||||
'Starter chest 3',
|
||||
'Starter chest 1',
|
||||
'Timespinner Wheel room'
|
||||
)
|
|
@ -0,0 +1,58 @@
|
|||
from BaseClasses import MultiWorld
|
||||
from ..AutoWorld import LogicMixin
|
||||
from .Options import is_option_enabled
|
||||
|
||||
class TimespinnerLogic(LogicMixin):
|
||||
def _timespinner_has_timestop(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any(['Timespinner Wheel', 'Succubus Hairpin', 'Lightwall', 'Celestial Sash'], player)
|
||||
|
||||
def _timespinner_has_doublejump(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any(['Succubus Hairpin', 'Lightwall', 'Celestial Sash'], player)
|
||||
|
||||
def _timespinner_has_forwarddash_doublejump(self, world: MultiWorld, player: int) -> bool:
|
||||
return self._timespinner_has_upwarddash(world, player) or (self.has('Talaria Attachment', player) and self._timespinner_has_doublejump(world, player))
|
||||
|
||||
def _timespinner_has_doublejump_of_npc(self, world: MultiWorld, player: int) -> bool:
|
||||
return self._timespinner_has_upwarddash(world, player) or (self.has('Timespinner Wheel', player) and self._timespinner_has_doublejump(world, player))
|
||||
|
||||
def _timespinner_has_multiple_small_jumps_of_npc(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has('Timespinner Wheel', player) or self._timespinner_has_upwarddash(world, player)
|
||||
|
||||
def _timespinner_has_upwarddash(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any(['Lightwall', 'Celestial Sash'], player)
|
||||
|
||||
def _timespinner_has_fire(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any(['Fire Orb', 'Infernal Flames', 'Pyro Ring', 'Djinn Inferno'], player)
|
||||
|
||||
def _timespinner_has_pink(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_any(['Plasma Orb', 'Plasma Geyser', 'Royal Ring'], player)
|
||||
|
||||
def _timespinner_has_keycard_A(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has('Security Keycard A', player)
|
||||
|
||||
def _timespinner_has_keycard_B(self, world: MultiWorld, player: int) -> bool:
|
||||
if is_option_enabled(world, player, "SpecificKeycards"):
|
||||
return self.has('Security Keycard B', player)
|
||||
else:
|
||||
return self.has_any(['Security Keycard A', 'Security Keycard B'], player)
|
||||
|
||||
def _timespinner_has_keycard_C(self, world: MultiWorld, player: int) -> bool:
|
||||
if is_option_enabled(world, player, "SpecificKeycards"):
|
||||
return self.has('Security Keycard C', player)
|
||||
else:
|
||||
return self.has_any(['Security Keycard A', 'Security Keycard B', 'Security Keycard C'], player)
|
||||
|
||||
def _timespinner_has_keycard_D(self, world: MultiWorld, player: int) -> bool:
|
||||
if is_option_enabled(world, player, "SpecificKeycards"):
|
||||
return self.has('Security Keycard D', player)
|
||||
else:
|
||||
return self.has_any(['Security Keycard A', 'Security Keycard B', 'Security Keycard C', 'Security Keycard D'], player)
|
||||
|
||||
def _timespinner_can_break_walls(self, world: MultiWorld, player: int) -> bool:
|
||||
if is_option_enabled(world, player, "FacebookMode"):
|
||||
return self.has('Oculus Ring', player)
|
||||
else:
|
||||
return True
|
||||
|
||||
def _timespinner_can_kill_all_3_bosses(self, world: MultiWorld, player: int) -> bool:
|
||||
return self.has_all(['Killed Maw', 'Killed Twins', 'Killed Aelana'], player)
|
|
@ -0,0 +1,65 @@
|
|||
from typing import Dict
|
||||
from BaseClasses import MultiWorld
|
||||
from Options import Toggle
|
||||
|
||||
class StartWithJewelryBox(Toggle):
|
||||
"Start with Jewelry Box unlocked"
|
||||
display_name = "Start with Jewelry Box"
|
||||
|
||||
#class ProgressiveVerticalMovement(Toggle):
|
||||
# "Always find vertical movement in the following order Succubus Hairpin -> Light Wall -> Celestial Sash"
|
||||
# display_name = "Progressive vertical movement"
|
||||
|
||||
#class ProgressiveKeycards(Toggle):
|
||||
# "Always find Security Keycard's in the following order D -> C -> B -> A"
|
||||
# display_name = "Progressive keycards"
|
||||
|
||||
class DownloadableItems(Toggle):
|
||||
"With the tablet you will be able to download items at terminals"
|
||||
display_name = "Downloadable items"
|
||||
|
||||
class FacebookMode(Toggle):
|
||||
"With the tablet you will be able to download items at terminals"
|
||||
display_name = "Facebook mode"
|
||||
|
||||
class StartWithMeyef(Toggle):
|
||||
"Start with Meyef, ideal for when you want to play multiplayer."
|
||||
display_name = "Start with Meyef"
|
||||
|
||||
class QuickSeed(Toggle):
|
||||
"Start with Talaria Attachment, Nyoom!"
|
||||
display_name = "Quick seed"
|
||||
|
||||
class SpecificKeycards(Toggle):
|
||||
"Keycards can only open corresponding doors"
|
||||
display_name = "Specific Keycards"
|
||||
|
||||
class Inverted(Toggle):
|
||||
"Start in the past"
|
||||
display_name = "Inverted"
|
||||
|
||||
#class StinkyMaw(Toggle):
|
||||
# "Require gassmask for Maw"
|
||||
# display_name = "Stinky Maw"
|
||||
|
||||
# Some options that are available in the timespinner randomizer arent currently implemented
|
||||
timespinner_options: Dict[str, Toggle] = {
|
||||
"StartWithJewelryBox": StartWithJewelryBox,
|
||||
#"ProgressiveVerticalMovement": ProgressiveVerticalMovement,
|
||||
#"ProgressiveKeycards": ProgressiveKeycards,
|
||||
"DownloadableItems": DownloadableItems,
|
||||
"FacebookMode": FacebookMode,
|
||||
"StartWithMeyef": StartWithMeyef,
|
||||
"QuickSeed": QuickSeed,
|
||||
"SpecificKeycards": SpecificKeycards,
|
||||
"Inverted": Inverted,
|
||||
#"StinkyMaw": StinkyMaw
|
||||
}
|
||||
|
||||
def is_option_enabled(world: MultiWorld, player: int, name: str) -> bool:
|
||||
option = getattr(world, name, None)
|
||||
|
||||
if option == None:
|
||||
return False
|
||||
|
||||
return int(option[player].value) > 0
|
|
@ -0,0 +1,30 @@
|
|||
from typing import Tuple
|
||||
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] = (
|
||||
"GateKittyBoss",
|
||||
"GateLeftLibrary",
|
||||
"GateMilitairyGate",
|
||||
"GateSealedCaves",
|
||||
"GateSealedSirensCave",
|
||||
"GateLakeDesolation"
|
||||
)
|
||||
|
||||
past_teleportation_gates: Tuple[str] = (
|
||||
"GateLakeSirineRight",
|
||||
"GateAccessToPast",
|
||||
"GateCastleRamparts",
|
||||
"GateCastleKeep",
|
||||
"GateRoyalTowers",
|
||||
"GateMaw",
|
||||
"GateCavesOfBanishment"
|
||||
)
|
||||
|
||||
if is_option_enabled(world, player, "Inverted"):
|
||||
gates = present_teleportation_gates
|
||||
else:
|
||||
gates = (*past_teleportation_gates, *present_teleportation_gates)
|
||||
|
||||
return world.random.choice(gates)
|
|
@ -0,0 +1,220 @@
|
|||
from typing import List, Dict, Tuple, Optional, Callable
|
||||
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):
|
||||
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')
|
||||
]
|
||||
|
||||
connectStartingRegion(world, player)
|
||||
|
||||
names = {}
|
||||
|
||||
connect(world, player, names, 'Lake desolation', 'Lower lake desolation', lambda state: state._timespinner_has_timestop(world, player or state.has('Talaria Attachment', player)))
|
||||
connect(world, player, names, 'Lake desolation', 'Upper lake desolation', lambda state: state._timespinner_has_fire(world, player) and state.can_reach('Upper Lake Sirine', 'Region', player))
|
||||
connect(world, player, names, 'Lake desolation', 'Skeleton Shaft', lambda state: state._timespinner_has_doublejump(world, player))
|
||||
connect(world, player, names, 'Lake desolation', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Upper lake desolation', 'Lake desolation')
|
||||
connect(world, player, names, 'Upper lake desolation', 'Lower lake desolation')
|
||||
connect(world, player, names, 'Lower lake desolation', 'Lake desolation')
|
||||
connect(world, player, names, 'Lower lake desolation', 'Libary')
|
||||
connect(world, player, names, 'Lower lake desolation', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Libary', 'Lower lake desolation')
|
||||
connect(world, player, names, 'Libary', 'Libary top', lambda state: state._timespinner_has_doublejump(world, player) or state.has('Talaria Attachment', player))
|
||||
connect(world, player, names, 'Libary', 'Varndagroth tower left', lambda state: state._timespinner_has_keycard_C(world, player))
|
||||
connect(world, player, names, 'Libary', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Libary top', 'Libary')
|
||||
connect(world, player, names, 'Varndagroth tower left', 'Libary')
|
||||
connect(world, player, names, 'Varndagroth tower left', 'Varndagroth tower right (upper)', lambda state: state._timespinner_has_keycard_C(world, player))
|
||||
connect(world, player, names, 'Varndagroth tower left', 'Varndagroth tower right (lower)', lambda state: state._timespinner_has_keycard_B(world, player))
|
||||
connect(world, player, names, 'Varndagroth tower left', 'Sealed Caves (Sirens)', lambda state: state._timespinner_has_keycard_B(world, player) and state.has('Elevator Keycard', player))
|
||||
connect(world, player, names, 'Varndagroth tower left', 'Refugee Camp', lambda state: state.has('Timespinner Wheel', player) and state.has('Timespinner Spindle', player))
|
||||
connect(world, player, names, 'Varndagroth tower right (upper)', 'Varndagroth tower left')
|
||||
connect(world, player, names, 'Varndagroth tower right (upper)', 'Varndagroth tower right (elevator)', lambda state: state.has('Elevator Keycard', player))
|
||||
connect(world, player, names, 'Varndagroth tower right (elevator)', 'Varndagroth tower right (upper)')
|
||||
connect(world, player, names, 'Varndagroth tower right (elevator)', 'Varndagroth tower right (lower)')
|
||||
connect(world, player, names, 'Varndagroth tower right (lower)', 'Varndagroth tower left')
|
||||
connect(world, player, names, 'Varndagroth tower right (lower)', 'Varndagroth tower right (elevator)', lambda state: state.has('Elevator Keycard', player))
|
||||
connect(world, player, names, 'Varndagroth tower right (lower)', 'Sealed Caves (Sirens)', lambda state: state._timespinner_has_keycard_B(world, player) and state.has('Elevator Keycard', player))
|
||||
connect(world, player, names, 'Varndagroth tower right (lower)', 'Militairy Fortress', lambda state: state._timespinner_can_kill_all_3_bosses(world, player))
|
||||
connect(world, player, names, 'Varndagroth tower right (lower)', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Sealed Caves (Sirens)', 'Varndagroth tower left', lambda state: state.has('Elevator Keycard', player))
|
||||
connect(world, player, names, 'Sealed Caves (Sirens)', 'Varndagroth tower right (lower)', lambda state: state.has('Elevator Keycard', player))
|
||||
connect(world, player, names, 'Sealed Caves (Sirens)', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Militairy Fortress', 'Varndagroth tower right (lower)', lambda state: state._timespinner_can_kill_all_3_bosses(world, player))
|
||||
connect(world, player, names, 'Militairy Fortress', 'The lab', lambda state: state._timespinner_has_keycard_B(world, player) and state._timespinner_has_doublejump(world, player))
|
||||
connect(world, player, names, 'The lab', 'Militairy Fortress')
|
||||
connect(world, player, names, 'The lab', 'The lab (power off)', lambda state: state._timespinner_has_doublejump_of_npc(world, player))
|
||||
connect(world, player, names, 'The lab (power off)', 'The lab')
|
||||
connect(world, player, names, 'The lab (power off)', 'The lab (upper)', lambda state: state._timespinner_has_forwarddash_doublejump(world, player))
|
||||
connect(world, player, names, 'The lab (upper)', 'The lab (power off)')
|
||||
connect(world, player, names, 'The lab (upper)', 'Emperors tower', lambda state: state._timespinner_has_forwarddash_doublejump(world, player))
|
||||
connect(world, player, names, 'The lab (upper)', 'Ancient Pyramid (left)', lambda state: state.has_all(['Timespinner Wheel', 'Timespinner Spindle', 'Timespinner Gear 1', 'Timespinner Gear 2', 'Timespinner Gear 3'], player))
|
||||
connect(world, player, names, 'Emperors tower', 'The lab (upper)')
|
||||
connect(world, player, names, 'Skeleton Shaft', 'Lake desolation')
|
||||
connect(world, player, names, 'Skeleton Shaft', 'Sealed Caves (upper)', lambda state: state._timespinner_has_keycard_A(world, player))
|
||||
connect(world, player, names, 'Skeleton Shaft', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Sealed Caves (upper)', 'Skeleton Shaft')
|
||||
connect(world, player, names, 'Sealed Caves (upper)', 'Sealed Caves (Xarion)', lambda state: state.has('Twin Pyramid Key', player) or state._timespinner_has_forwarddash_doublejump(world, player))
|
||||
connect(world, player, names, 'Sealed Caves (Xarion)', 'Sealed Caves (upper)', lambda state: state._timespinner_has_forwarddash_doublejump(world, player))
|
||||
connect(world, player, names, 'Sealed Caves (Xarion)', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Refugee Camp', 'Forest')
|
||||
connect(world, player, names, 'Refugee Camp', 'Libary', lambda state: is_option_enabled(world, player, "Inverted"))
|
||||
connect(world, player, names, 'Refugee Camp', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Forest', 'Refugee Camp')
|
||||
connect(world, player, names, 'Forest', 'Left Side forest Caves', lambda state: state.has('Talaria Attachment', player) or state._timespinner_has_timestop(world, player))
|
||||
connect(world, player, names, 'Forest', 'Caves of Banishment (Sirens)')
|
||||
connect(world, player, names, 'Forest', 'Caste Ramparts')
|
||||
connect(world, player, names, 'Left Side forest Caves', 'Forest')
|
||||
connect(world, player, names, 'Left Side forest Caves', 'Upper Lake Sirine', lambda state: state._timespinner_has_timestop(world, player))
|
||||
connect(world, player, names, 'Left Side forest Caves', 'Lower Lake Sirine', lambda state: state.has('Water Mask', player))
|
||||
connect(world, player, names, 'Left Side forest Caves', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Upper Lake Sirine', 'Left Side forest Caves')
|
||||
connect(world, player, names, 'Upper Lake Sirine', 'Lower Lake Sirine', lambda state: state.has('Water Mask', player))
|
||||
connect(world, player, names, 'Lower Lake Sirine', 'Upper Lake Sirine')
|
||||
connect(world, player, names, 'Lower Lake Sirine', 'Left Side forest Caves')
|
||||
connect(world, player, names, 'Lower Lake Sirine', 'Caves of Banishment (upper)')
|
||||
connect(world, player, names, 'Caves of Banishment (upper)', 'Upper Lake Sirine', lambda state: state.has('Water Mask', player))
|
||||
connect(world, player, names, 'Caves of Banishment (upper)', 'Caves of Banishment (Maw)', lambda state: state.has('Twin Pyramid Key', player) or state._timespinner_has_forwarddash_doublejump(world, player))
|
||||
connect(world, player, names, 'Caves of Banishment (upper)', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Caves of Banishment (Maw)', 'Caves of Banishment (upper)', lambda state: state._timespinner_has_forwarddash_doublejump(world, player))
|
||||
connect(world, player, names, 'Caves of Banishment (Maw)', 'Caves of Banishment (Sirens)', lambda state: state.has('Gas Mask', player))
|
||||
connect(world, player, names, 'Caves of Banishment (Maw)', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Caves of Banishment (Sirens)', 'Forest')
|
||||
connect(world, player, names, 'Caste Ramparts', 'Forest')
|
||||
connect(world, player, names, 'Caste Ramparts', 'Caste Keep')
|
||||
connect(world, player, names, 'Caste Ramparts', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Caste Keep', 'Caste Ramparts')
|
||||
connect(world, player, names, 'Caste Keep', 'Royal towers (lower)', lambda state: state._timespinner_has_doublejump(world, player))
|
||||
connect(world, player, names, 'Caste Keep', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Royal towers (lower)', 'Caste Keep')
|
||||
connect(world, player, names, 'Royal towers (lower)', 'Royal towers', lambda state: state.has('Timespinner Wheel', player) or state._timespinner_has_forwarddash_doublejump(world, player))
|
||||
connect(world, player, names, 'Royal towers (lower)', 'Space time continuum', lambda state: state.has('Twin Pyramid Key', player))
|
||||
connect(world, player, names, 'Royal towers', 'Royal towers (lower)')
|
||||
connect(world, player, names, 'Royal towers', 'Royal towers (upper)', lambda state: state._timespinner_has_doublejump(world, player))
|
||||
connect(world, player, names, 'Royal towers (upper)', 'Royal towers')
|
||||
connect(world, player, names, 'Ancient Pyramid (left)', 'The lab (upper)')
|
||||
connect(world, player, names, 'Ancient Pyramid (left)', 'Ancient Pyramid (right)', lambda state: state._timespinner_has_upwarddash(world, player))
|
||||
connect(world, player, names, 'Ancient Pyramid (right)', 'Ancient Pyramid (left)', lambda state: state._timespinner_has_upwarddash(world, player))
|
||||
connect(world, player, names, 'Space time continuum', 'Lake desolation', lambda state: pyramid_keys_unlock == "GateLakeDesolation")
|
||||
connect(world, player, names, 'Space time continuum', 'Lower lake desolation', lambda state: pyramid_keys_unlock == "GateKittyBoss")
|
||||
connect(world, player, names, 'Space time continuum', 'Libary', lambda state: pyramid_keys_unlock == "GateLeftLibrary")
|
||||
connect(world, player, names, 'Space time continuum', 'Varndagroth tower right (lower)', lambda state: pyramid_keys_unlock == "GateMilitairyGate")
|
||||
connect(world, player, names, 'Space time continuum', 'Skeleton Shaft', lambda state: pyramid_keys_unlock == "GateSealedCaves")
|
||||
connect(world, player, names, 'Space time continuum', 'Sealed Caves (Sirens)', lambda state: pyramid_keys_unlock == "GateSealedSirensCave")
|
||||
connect(world, player, names, 'Space time continuum', 'Left Side forest Caves', lambda state: pyramid_keys_unlock == "GateLakeSirineRight")
|
||||
connect(world, player, names, 'Space time continuum', 'Refugee Camp', lambda state: pyramid_keys_unlock == "GateAccessToPast")
|
||||
connect(world, player, names, 'Space time continuum', 'Caste Ramparts', lambda state: pyramid_keys_unlock == "GateCastleRamparts")
|
||||
connect(world, player, names, 'Space time continuum', 'Caste Keep', lambda state: pyramid_keys_unlock == "GateCastleKeep")
|
||||
connect(world, player, names, 'Space time continuum', 'Royal towers (lower)', lambda state: pyramid_keys_unlock == "GateRoyalTowers")
|
||||
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:
|
||||
location = Location(player, name, id, region)
|
||||
location.access_rule = rule
|
||||
|
||||
if id is None:
|
||||
location.event = True
|
||||
location.locked = True
|
||||
|
||||
return location
|
||||
|
||||
def create_region(world: MultiWorld, player: int, locations_per_region: Dict[str, List[LocationData]], 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)
|
||||
region.locations.append(location)
|
||||
|
||||
return region
|
||||
|
||||
def connectStartingRegion(world: MultiWorld, player: int):
|
||||
menu = world.get_region('Menu', player)
|
||||
tutorial = world.get_region('Tutorial', player)
|
||||
space_time_continuum = world.get_region('Space time continuum', player)
|
||||
|
||||
if is_option_enabled(world, player, "Inverted"):
|
||||
starting_region = world.get_region('Refugee Camp', player)
|
||||
else:
|
||||
starting_region = world.get_region('Lake desolation', player)
|
||||
|
||||
menu_to_tutorial = Entrance(player, 'Tutorial', menu)
|
||||
menu_to_tutorial.connect(tutorial)
|
||||
menu.exits.append(menu_to_tutorial)
|
||||
|
||||
tutorial_to_start = Entrance(player, 'Start Game', tutorial)
|
||||
tutorial_to_start.connect(starting_region)
|
||||
tutorial.exits.append(tutorial_to_start)
|
||||
|
||||
teleport_back_to_start = Entrance(player, 'Teleport back to start', space_time_continuum)
|
||||
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)
|
||||
|
||||
if target not in used_names:
|
||||
used_names[target] = 1
|
||||
name = target
|
||||
else:
|
||||
used_names[target] += 1
|
||||
name = target + (' ' * used_names[target])
|
||||
|
||||
connection = Entrance(player, name, sourceRegion)
|
||||
|
||||
if rule:
|
||||
connection.access_rule = rule
|
||||
|
||||
sourceRegion.exits.append(connection)
|
||||
connection.connect(targetRegion)
|
||||
|
||||
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 ]
|
||||
|
||||
return per_region
|
|
@ -0,0 +1,154 @@
|
|||
from typing import Dict, List
|
||||
from BaseClasses import Item, MultiWorld
|
||||
from ..AutoWorld import World
|
||||
from .LogicMixin import TimespinnerLogic
|
||||
from .Items import 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):
|
||||
options = timespinner_options
|
||||
game = "Timespinner"
|
||||
topology_present = True
|
||||
data_version = 1
|
||||
|
||||
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)}
|
||||
|
||||
locked_locations: Dict[int, List[str]] = {}
|
||||
pyramid_keys_unlock: Dict[int, str] = {}
|
||||
|
||||
def generate_early(self):
|
||||
self.locked_locations[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])
|
||||
|
||||
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"):
|
||||
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)
|
||||
|
||||
fill_item_pool_with_dummy_items(self.world, self.player, self.locked_locations[self.player], pool)
|
||||
|
||||
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["StinkyMaw"] = 1
|
||||
slot_data["ProgressiveVerticalMovement"] = 0
|
||||
slot_data["ProgressiveKeycards"] = 0
|
||||
slot_data["PyramidKeysGate"] = self.pyramid_keys_unlock[self.player]
|
||||
|
||||
return slot_data
|
||||
|
||||
def create_item(name: str, player: int) -> Item:
|
||||
data = item_table[name]
|
||||
return Item(name, data.progression, data.code, player)
|
||||
|
||||
def get_excluded_items_based_on_options(world: MultiWorld, player: int) -> List[str]:
|
||||
excluded_items = []
|
||||
|
||||
if is_option_enabled(world, player, "StartWithJewelryBox"):
|
||||
excluded_items.append('Jewelry Box')
|
||||
if is_option_enabled(world, player, "StartWithMeyef"):
|
||||
excluded_items.append('Meyef')
|
||||
if is_option_enabled(world, player, "QuickSeed"):
|
||||
excluded_items.append('Talaria Attachment')
|
||||
|
||||
return excluded_items
|
||||
|
||||
def assign_starter_items(world: MultiWorld, player: int, excluded_items: List[str], locked_locations: List[str]):
|
||||
melee_weapon = world.random.choice(starter_melee_weapons)
|
||||
spell = world.random.choice(starter_spells)
|
||||
|
||||
excluded_items.append(melee_weapon)
|
||||
excluded_items.append(spell)
|
||||
|
||||
melee_weapon_item = create_item(melee_weapon, player)
|
||||
spell_item = create_item(spell, player)
|
||||
|
||||
world.get_location('Yo Momma 1', player).place_locked_item(melee_weapon_item)
|
||||
world.get_location('Yo Momma 2', player).place_locked_item(spell_item)
|
||||
|
||||
locked_locations.append('Yo Momma 1')
|
||||
locked_locations.append('Yo Momma 2')
|
||||
|
||||
def get_item_pool(world: MultiWorld, player: int, excluded_items: List[str]) -> List[Item]:
|
||||
pool = []
|
||||
|
||||
for name, data in item_table.items():
|
||||
if not name in excluded_items:
|
||||
for _ in range(data.count):
|
||||
item = update_progressive_state_based_flags(world, player, name, create_item(name, player))
|
||||
pool.append(item)
|
||||
|
||||
return pool
|
||||
|
||||
def fill_item_pool_with_dummy_items(world: MultiWorld, player: int, locked_locations: List[str], pool: List[Item]):
|
||||
for _ in range(len(get_locations(world, player)) - len(locked_locations) - len(pool)):
|
||||
item = create_item(world.random.choice(filler_items), player)
|
||||
pool.append(item)
|
||||
|
||||
def place_first_progression_item(world: MultiWorld, player: int, excluded_items: List[str], locked_locations: List[str]):
|
||||
progression_item = world.random.choice(starter_progression_items)
|
||||
location = world.random.choice(starter_progression_locations)
|
||||
|
||||
excluded_items.append(progression_item)
|
||||
locked_locations.append(location)
|
||||
|
||||
item = create_item(progression_item, player)
|
||||
|
||||
world.get_location(location, player).place_locked_item(item)
|
||||
|
||||
def update_progressive_state_based_flags(world: MultiWorld, player: int, name: str, data: Item) -> Item:
|
||||
if not data.advancement:
|
||||
return data
|
||||
|
||||
if (name == 'Tablet' or name == 'Library Keycard V') and not is_option_enabled(world, player, "DownloadableItems"):
|
||||
data.advancement = False
|
||||
if name == 'Oculus Ring' and not is_option_enabled(world, player, "FacebookMode"):
|
||||
data.advancement = False
|
||||
|
||||
return data
|
||||
|
||||
def setup_events(world: MultiWorld, player: int, locked_locations: List[str]):
|
||||
for location in get_locations(world, player):
|
||||
if location.code == EventId:
|
||||
location = world.get_location(location.name, player)
|
||||
item = Item(location.name, True, EventId, player)
|
||||
|
||||
locked_locations.append(location.name)
|
||||
|
||||
location.place_locked_item(item)
|
||||
|
||||
def get_item_name_groups() -> Dict[str, List[str]]:
|
||||
groups: Dict[str, List[str]] = {}
|
||||
|
||||
for name, data in item_table.items():
|
||||
groups[data.category] = [ name ] if data.category not in groups else groups[data.category] + [ name ]
|
||||
|
||||
return groups
|
Loading…
Reference in New Issue