Timespinner: Adds Lantern Check flags, Missing Traps (#5188)

* Timespinner: Add Torch Flags

* Add comment of all torch locations

* Add gyre and dark forest lanterns

* Add Ancient Pyramid

* Don't make cube default progression

* Add Emperors Tower

* Add lake desolation, forest

* Add lab

* Add library, varndagroth

* Add hangar

* Add ramparts

* Add Xarion

* Add castle keep

* Add royal towers

* Add lake serene

* Add remaining checks

* Add missing region

* Fix region names

* Fix location id

* Add traps to settings

* Add restriction to elevator keycard torch

* Set new traps to have quantity 0 by default

* Scythe is now useful due to torch shredding

* Add additional lantern

* Un-disable missing lantern

* Include location ids in tracker

* Remove additional space

* Fix paren

* Add missing lantern

* Remove tablet requirement for torches

* Update filler V card

* Fix brackets

* Address feedback
This commit is contained in:
Colin
2025-09-10 07:27:13 -07:00
committed by GitHub
parent 9aa0bf7245
commit 78b529fc23
7 changed files with 844 additions and 261 deletions

View File

@@ -174,7 +174,7 @@ item_table: Dict[str, ItemData] = {
'Corruption': ItemData('Orb Spell', 1337161),
'Lightwall': ItemData('Orb Spell', 1337162, progression=True),
'Bleak Ring': ItemData('Orb Passive', 1337163, useful=True),
'Scythe Ring': ItemData('Orb Passive', 1337164),
'Scythe Ring': ItemData('Orb Passive', 1337164, useful=True),
'Pyro Ring': ItemData('Orb Passive', 1337165, progression=True),
'Royal Ring': ItemData('Orb Passive', 1337166, progression=True),
'Shield Ring': ItemData('Orb Passive', 1337167),
@@ -208,9 +208,11 @@ item_table: Dict[str, ItemData] = {
'Lab Access Research': ItemData('Lab Access', 1337196, progression=True),
'Lab Access Dynamo': ItemData('Lab Access', 1337197, progression=True),
'Drawbridge Key': ItemData('Key', 1337198, progression=True),
# 1337199 Reserved
'Cube of Bodie': ItemData('Relic', 1337199, progression=True),
'Spider Trap': ItemData('Trap', 1337200, 0, trap=True),
# 1337201 - 1337248 Reserved
'Lights Out Trap': ItemData('Trap', 1337201, 0, trap=True),
'Palm Punch Trap': ItemData('Trap', 1337202, 0, trap=True),
# 1337203 - 1337248 Reserved
'Max Sand': ItemData('Stat', 1337249, 14)
}

File diff suppressed because it is too large Load Diff

View File

@@ -24,6 +24,7 @@ class TimespinnerLogic:
self.flag_eye_spy = bool(options and options.eye_spy)
self.flag_unchained_keys = bool(options and options.unchained_keys)
self.flag_prism_break = bool(options and options.prism_break)
self.flag_find_the_flame = bool(options and options.find_the_flame)
if precalculated_weights:
if self.flag_unchained_keys:
@@ -93,6 +94,12 @@ class TimespinnerLogic:
else:
return True
def can_break_lanterns(self, state: CollectionState) -> bool:
if self.flag_find_the_flame:
return state.has('Cube of Bodie', self.player)
else:
return True
def can_kill_all_3_bosses(self, state: CollectionState) -> bool:
if self.flag_prism_break:
return state.has_all({'Laser Access M', 'Laser Access I', 'Laser Access A'}, self.player)

View File

@@ -367,8 +367,8 @@ class TrapChance(Range):
class Traps(OptionList):
"""List of traps that may be in the item pool to find"""
display_name = "Traps Types"
valid_keys = { "Meteor Sparrow Trap", "Poison Trap", "Chaos Trap", "Neurotoxin Trap", "Bee Trap", "Throw Stun Trap", "Spider Trap" }
default = [ "Meteor Sparrow Trap", "Poison Trap", "Chaos Trap", "Neurotoxin Trap", "Bee Trap", "Throw Stun Trap", "Spider Trap" ]
valid_keys = { "Meteor Sparrow Trap", "Poison Trap", "Chaos Trap", "Neurotoxin Trap", "Bee Trap", "Throw Stun Trap", "Spider Trap", "Lights Out Trap", "Palm Punch Trap" }
default = [ "Meteor Sparrow Trap", "Poison Trap", "Chaos Trap", "Neurotoxin Trap", "Bee Trap", "Throw Stun Trap", "Spider Trap", "Lights Out Trap", "Palm Punch Trap" ]
class PresentAccessWithWheelAndSpindle(Toggle):
"""When inverted, allows using the refugee camp warp when both the Timespinner Wheel and Spindle is acquired."""
@@ -399,6 +399,14 @@ class RoyalRoadblock(Toggle):
"""The Royal Towers entrance door requires a royal orb (Plasma Orb, Plasma Geyser, or Royal Ring) to enter."""
display_name = "Royal Roadblock"
class PureTorcher(Toggle):
"""All lanterns contain checks. (Except tutorial)"""
display_name = "Pure Torcher"
class FindTheFlame(Toggle):
"""Lanterns in 'Pure Torcher' will not break without new item 'Cube of Bodie'."""
display_name = "Find the Flame"
@dataclass
class TimespinnerOptions(PerGameCommonOptions, DeathLinkMixin):
start_with_jewelry_box: StartWithJewelryBox
@@ -441,6 +449,8 @@ class TimespinnerOptions(PerGameCommonOptions, DeathLinkMixin):
pyramid_start: PyramidStart
gate_keep: GateKeep
royal_roadblock: RoyalRoadblock
pure_torcher: PureTorcher
find_the_flame: FindTheFlame
trap_chance: TrapChance
traps: Traps

View File

@@ -28,9 +28,11 @@ def create_regions_and_locations(world: MultiWorld, player: int, options: Timesp
create_region(world, player, locations_per_region, 'Sealed Caves (Sirens)'),
create_region(world, player, locations_per_region, 'Military Fortress'),
create_region(world, player, locations_per_region, 'Military Fortress (hangar)'),
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, 'Lab Entrance'),
create_region(world, player, locations_per_region, 'Main Lab'),
create_region(world, player, locations_per_region, 'Lab Research'),
create_region(world, player, locations_per_region, 'The lab (upper)'),
create_region(world, player, locations_per_region, 'Emperors tower (courtyard)'),
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 (Xarion)'),
@@ -41,6 +43,7 @@ def create_regions_and_locations(world: MultiWorld, player: int, options: Timesp
create_region(world, player, locations_per_region, 'Lower Lake Serene'),
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 (Flooded)'),
create_region(world, player, locations_per_region, 'Caves of Banishment (Sirens)'),
create_region(world, player, locations_per_region, 'Castle Ramparts'),
create_region(world, player, locations_per_region, 'Castle Keep'),
@@ -109,16 +112,19 @@ def create_regions_and_locations(world: MultiWorld, player: int, options: Timesp
connect(world, player, 'Military Fortress', 'Temporal Gyre', lambda state: state.has('Timespinner Wheel', player) and logic.can_kill_all_3_bosses(state))
connect(world, player, 'Military Fortress', 'Military Fortress (hangar)', logic.has_doublejump)
connect(world, player, 'Military Fortress (hangar)', 'Military Fortress')
connect(world, player, 'Military Fortress (hangar)', 'The lab', lambda state: logic.has_keycard_B(state) and (state.has('Water Mask', player) if flooded.flood_lab else logic.has_doublejump(state)))
connect(world, player, 'Military Fortress (hangar)', 'Lab Entrance', lambda state: state.has('Water Mask', player) if flooded.flood_lab else logic.has_doublejump(state))
connect(world, player, 'Lab Entrance', 'Main Lab', lambda state: logic.has_keycard_B(state))
connect(world, player, 'Main Lab', 'Lab Entrance')
connect(world, player, 'Lab Entrance', 'Military Fortress (hangar)')
connect(world, player, 'Temporal Gyre', 'Military Fortress')
connect(world, player, 'The lab', 'Military Fortress')
connect(world, player, 'The lab', 'The lab (power off)', lambda state: options.lock_key_amadeus or logic.has_doublejump_of_npc(state))
connect(world, player, 'The lab (power off)', 'The lab', lambda state: not flooded.flood_lab or state.has('Water Mask', player))
connect(world, player, 'The lab (power off)', 'The lab (upper)', lambda state: logic.has_forwarddash_doublejump(state) and ((not options.lock_key_amadeus) or state.has('Lab Access Genza', player)))
connect(world, player, 'The lab (upper)', 'The lab (power off)', lambda state: options.lock_key_amadeus and state.has('Lab Access Genza', player))
connect(world, player, 'The lab (upper)', 'Emperors tower', logic.has_forwarddash_doublejump)
connect(world, player, 'Main Lab', 'Lab Research', lambda state: state.has('Lab Access Research', player) if options.lock_key_amadeus else logic.has_doublejump_of_npc(state))
connect(world, player, 'Main Lab', 'The lab (upper)', lambda state: logic.has_forwarddash_doublejump(state) and ((not options.lock_key_amadeus) or state.has('Lab Access Genza', player)))
connect(world, player, 'The lab (upper)', 'Main Lab', lambda state: options.lock_key_amadeus and state.has('Lab Access Genza', player))
connect(world, player, 'The lab (upper)', 'Emperors tower (courtyard)', logic.has_forwarddash_doublejump)
connect(world, player, 'The lab (upper)', 'Ancient Pyramid (entrance)', lambda state: state.has_all({'Timespinner Wheel', 'Timespinner Spindle', 'Timespinner Gear 1', 'Timespinner Gear 2', 'Timespinner Gear 3'}, player))
connect(world, player, 'Emperors tower', 'The lab (upper)')
connect(world, player, 'Emperors tower (courtyard)', 'The lab (upper)')
connect(world, player, 'Emperors tower (courtyard)', 'Emperors tower', logic.has_doublejump)
connect(world, player, 'Emperors tower', 'Emperors tower (courtyard)')
connect(world, player, 'Skeleton Shaft', 'Lake desolation')
connect(world, player, 'Skeleton Shaft', 'Sealed Caves (Xarion)', logic.has_keycard_A)
connect(world, player, 'Skeleton Shaft', 'Space time continuum', logic.has_teleport)
@@ -145,6 +151,7 @@ def create_regions_and_locations(world: MultiWorld, player: int, options: Timesp
connect(world, player, 'Caves of Banishment (upper)', 'Space time continuum', logic.has_teleport)
connect(world, player, 'Caves of Banishment (Maw)', 'Caves of Banishment (upper)', lambda state: logic.has_doublejump(state) if not flooded.flood_maw else state.has('Water Mask', player))
connect(world, player, 'Caves of Banishment (Maw)', 'Caves of Banishment (Sirens)', lambda state: state.has_any({'Gas Mask', 'Talaria Attachment'}, player) )
connect(world, player, 'Caves of Banishment (Maw)', 'Caves of Banishment (Flooded)', lambda state: flooded.flood_maw or state.has('Water Mask', player))
connect(world, player, 'Caves of Banishment (Maw)', 'Space time continuum', logic.has_teleport)
connect(world, player, 'Caves of Banishment (Sirens)', 'Forest')
connect(world, player, 'Castle Ramparts', 'Forest')

View File

@@ -132,6 +132,8 @@ class TimespinnerWorld(World):
"PyramidStart": self.options.pyramid_start.value,
"GateKeep": self.options.gate_keep.value,
"RoyalRoadblock": self.options.royal_roadblock.value,
"PureTorcher": self.options.pure_torcher.value,
"FindTheFlame": self.options.find_the_flame.value,
"Traps": self.options.traps.value,
"DeathLink": self.options.death_link.value,
"StinkyMaw": True,
@@ -298,7 +300,9 @@ class TimespinnerWorld(World):
if not item.advancement:
return item
if (name == 'Tablet' or name == 'Library Keycard V') and not self.options.downloadable_items:
if name == 'Tablet' and not self.options.downloadable_items:
item.classification = ItemClassification.filler
elif name == 'Library Keycard V' and not (self.options.downloadable_items or self.options.pure_torcher):
item.classification = ItemClassification.filler
elif name == 'Oculus Ring' and not self.options.eye_spy:
item.classification = ItemClassification.filler
@@ -315,6 +319,8 @@ class TimespinnerWorld(World):
item.classification = ItemClassification.filler
elif name == "Drawbridge Key" and not self.options.gate_keep:
item.classification = ItemClassification.filler
elif name == "Cube of Bodie" and not self.options.find_the_flame:
item.classification = ItemClassification.filler
return item
@@ -361,6 +367,9 @@ class TimespinnerWorld(World):
if not self.options.gate_keep:
excluded_items.add('Drawbridge Key')
if not self.options.find_the_flame:
excluded_items.add('Cube of Bodie')
for item in self.multiworld.precollected_items[self.player]:
if item.name not in self.item_name_groups['UseItem']:
excluded_items.add(item.name)