Skip to content

Commit

Permalink
Timespinner: New options from TS Rando v1.25 + Logic fix (Archipelago…
Browse files Browse the repository at this point in the history
…MW#2090)

Co-authored-by: black-sliver <[email protected]>
  • Loading branch information
2 people authored and Jouramie committed Feb 28, 2024
1 parent 8934201 commit 09547b2
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 74 deletions.
30 changes: 15 additions & 15 deletions worlds/timespinner/Locations.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import List, Tuple, Optional, Callable, NamedTuple
from typing import List, Optional, Callable, NamedTuple
from BaseClasses import MultiWorld, CollectionState
from .Options import is_option_enabled
from .PreCalculatedWeights import PreCalculatedWeights
Expand All @@ -11,11 +11,11 @@ class LocationData(NamedTuple):
region: str
name: str
code: Optional[int]
rule: Callable[[CollectionState], bool] = lambda state: True
rule: Optional[Callable[[CollectionState], bool]] = None


def get_location_datas(world: Optional[MultiWorld], player: Optional[int],
precalculated_weights: PreCalculatedWeights) -> Tuple[LocationData, ...]:
precalculated_weights: PreCalculatedWeights) -> List[LocationData]:

flooded: PreCalculatedWeights = precalculated_weights
logic = TimespinnerLogic(world, player, precalculated_weights)
Expand Down Expand Up @@ -88,9 +88,9 @@ def get_location_datas(world: Optional[MultiWorld], player: Optional[int],
LocationData('Military Fortress (hangar)', 'Military Fortress: Soldiers bridge', 1337060),
LocationData('Military Fortress (hangar)', 'Military Fortress: Giantess room', 1337061),
LocationData('Military Fortress (hangar)', 'Military Fortress: Giantess bridge', 1337062),
LocationData('Military Fortress (hangar)', 'Military Fortress: B door chest 2', 1337063, lambda state: logic.has_doublejump(state) and logic.has_keycard_B(state)),
LocationData('Military Fortress (hangar)', 'Military Fortress: B door chest 1', 1337064, lambda state: logic.has_doublejump(state) and logic.has_keycard_B(state)),
LocationData('Military Fortress (hangar)', 'Military Fortress: Pedestal', 1337065, lambda state: logic.has_doublejump_of_npc(state) or logic.has_forwarddash_doublejump(state)),
LocationData('Military Fortress (hangar)', 'Military Fortress: B door chest 2', 1337063, lambda state: logic.has_keycard_B(state) and (state.has('Water Mask', player) if flooded.flood_lab else logic.has_doublejump(state))),
LocationData('Military Fortress (hangar)', 'Military Fortress: B door chest 1', 1337064, lambda state: logic.has_keycard_B(state) and (state.has('Water Mask', player) if flooded.flood_lab else logic.has_doublejump(state))),
LocationData('Military Fortress (hangar)', 'Military Fortress: Pedestal', 1337065, lambda state: state.has('Water Mask', player) if flooded.flood_lab else (logic.has_doublejump_of_npc(state) or logic.has_forwarddash_doublejump(state))),
LocationData('The lab', 'Lab: Coffee break', 1337066),
LocationData('The lab', 'Lab: Lower trash right', 1337067, logic.has_doublejump),
LocationData('The lab', 'Lab: Lower trash left', 1337068, logic.has_upwarddash),
Expand Down Expand Up @@ -139,17 +139,17 @@ def get_location_datas(world: Optional[MultiWorld], player: Optional[int],
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Under the eels', 1337106),
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Water spikes room', 1337107),
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Underwater secret', 1337108, logic.can_break_walls),
LocationData('Lower Lake Serene', 'Lake Serene (Lower): T chest', 1337109, lambda state: not flooded.dry_lake_serene or logic.has_doublejump_of_npc(state)),
LocationData('Lower Lake Serene', 'Lake Serene (Lower): T chest', 1337109, lambda state: flooded.flood_lake_serene or logic.has_doublejump_of_npc(state)),
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Past the eels', 1337110),
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Underwater pedestal', 1337111, lambda state: not flooded.dry_lake_serene or logic.has_doublejump(state)),
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Shroom jump room', 1337112, lambda state: not flooded.flood_maw or logic.has_doublejump(state)),
LocationData('Lower Lake Serene', 'Lake Serene (Lower): Underwater pedestal', 1337111, lambda state: flooded.flood_lake_serene or logic.has_doublejump(state)),
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Shroom jump room', 1337112, lambda state: flooded.flood_maw or logic.has_doublejump(state)),
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Secret room', 1337113, lambda state: logic.can_break_walls(state) and (not flooded.flood_maw or state.has('Water Mask', player))),
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Bottom left room', 1337114, lambda state: not flooded.flood_maw or state.has('Water Mask', player)),
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Single shroom room', 1337115),
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Jackpot room chest 1', 1337116, lambda state: logic.has_forwarddash_doublejump(state) or flooded.flood_maw),
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Jackpot room chest 2', 1337117, lambda state: logic.has_forwarddash_doublejump(state) or flooded.flood_maw),
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Jackpot room chest 3', 1337118, lambda state: logic.has_forwarddash_doublejump(state) or flooded.flood_maw),
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Jackpot room chest 4', 1337119, lambda state: logic.has_forwarddash_doublejump(state) or flooded.flood_maw),
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Jackpot room chest 1', 1337116, lambda state: flooded.flood_maw or logic.has_forwarddash_doublejump(state)),
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Jackpot room chest 2', 1337117, lambda state: flooded.flood_maw or logic.has_forwarddash_doublejump(state)),
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Jackpot room chest 3', 1337118, lambda state: flooded.flood_maw or logic.has_forwarddash_doublejump(state)),
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Jackpot room chest 4', 1337119, lambda state: flooded.flood_maw or logic.has_forwarddash_doublejump(state)),
LocationData('Caves of Banishment (upper)', 'Caves of Banishment (Maw): Pedestal', 1337120, lambda state: not flooded.flood_maw or state.has('Water Mask', player)),
LocationData('Caves of Banishment (Maw)', 'Caves of Banishment (Maw): Last chance before Maw', 1337121, lambda state: state.has('Water Mask', player) if flooded.flood_maw else logic.has_doublejump(state)),
LocationData('Caves of Banishment (Maw)', 'Caves of Banishment (Maw): Plasma Crystal', 1337173, lambda state: state.has_any({'Gas Mask', 'Talaria Attachment'}, player) and (not flooded.flood_maw or state.has('Water Mask', player))),
Expand Down Expand Up @@ -197,7 +197,7 @@ def get_location_datas(world: Optional[MultiWorld], player: Optional[int],
LocationData('Ancient Pyramid (entrance)', 'Ancient Pyramid: Why not it\'s right there', 1337246),
LocationData('Ancient Pyramid (left)', 'Ancient Pyramid: Conviction guarded room', 1337247),
LocationData('Ancient Pyramid (left)', 'Ancient Pyramid: Pit secret room', 1337248, lambda state: logic.can_break_walls(state) and (not flooded.flood_pyramid_shaft or state.has('Water Mask', player))),
LocationData('Ancient Pyramid (left)', 'Ancient Pyramid: Regret chest', 1337249, lambda state: logic.can_break_walls(state) and (not flooded.flood_pyramid_shaft or state.has('Water Mask', player))),
LocationData('Ancient Pyramid (left)', 'Ancient Pyramid: Regret chest', 1337249, lambda state: logic.can_break_walls(state) and (state.has('Water Mask', player) if flooded.flood_pyramid_shaft else logic.has_doublejump(state))),
LocationData('Ancient Pyramid (right)', 'Ancient Pyramid: Nightmare Door chest', 1337236, lambda state: not flooded.flood_pyramid_back or state.has('Water Mask', player)),
LocationData('Ancient Pyramid (right)', 'Killed Nightmare', EventId, lambda state: state.has_all({'Timespinner Wheel', 'Timespinner Spindle', 'Timespinner Gear 1', 'Timespinner Gear 2', 'Timespinner Gear 3'}, player) and (not flooded.flood_pyramid_back or state.has('Water Mask', player)))
]
Expand Down Expand Up @@ -271,4 +271,4 @@ def get_location_datas(world: Optional[MultiWorld], player: Optional[int],
LocationData('Ifrit\'s Lair', 'Ifrit: Post fight (chest)', 1337245),
)

return tuple(location_table)
return location_table
35 changes: 27 additions & 8 deletions worlds/timespinner/Options.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,23 @@ class LoreChecks(Toggle):
display_name = "Lore Checks"


class BossRando(Toggle):
"Shuffles the positions of all bosses."
class BossRando(Choice):
"Wheter all boss locations are shuffled, and if their damage/hp should be scaled."
display_name = "Boss Randomization"
option_off = 0
option_scaled = 1
option_unscaled = 2
alias_true = 1


class BossScaling(DefaultOnToggle):
"When Boss Rando is enabled, scales the bosses' HP, XP, and ATK to the stats of the location they replace (Recommended)"
display_name = "Scale Random Boss Stats"
class EnemyRando(Choice):
"Wheter enemies will be randomized, and if their damage/hp should be scaled."
display_name = "Enemy Randomization"
option_off = 0
option_scaled = 1
option_unscaled = 2
option_ryshia = 3
alias_true = 1


class DamageRando(Choice):
Expand Down Expand Up @@ -336,6 +345,7 @@ def rising_tide_option(location: str, with_save_point_option: bool = False) -> D
class RisingTidesOverrides(OptionDict):
"""Odds for specific areas to be flooded or drained, only has effect when RisingTides is on.
Areas that are not specified will roll with the default 33% chance of getting flooded or drained"""
display_name = "Rising Tides Overrides"
schema = Schema({
**rising_tide_option("Xarion"),
**rising_tide_option("Maw"),
Expand All @@ -345,9 +355,10 @@ class RisingTidesOverrides(OptionDict):
**rising_tide_option("CastleBasement", with_save_point_option=True),
**rising_tide_option("CastleCourtyard"),
**rising_tide_option("LakeDesolation"),
**rising_tide_option("LakeSerene")
**rising_tide_option("LakeSerene"),
**rising_tide_option("LakeSereneBridge"),
**rising_tide_option("Lab"),
})
display_name = "Rising Tides Overrides"
default = {
"Xarion": { "Dry": 67, "Flooded": 33 },
"Maw": { "Dry": 67, "Flooded": 33 },
Expand All @@ -358,6 +369,8 @@ class RisingTidesOverrides(OptionDict):
"CastleCourtyard": { "Dry": 67, "Flooded": 33 },
"LakeDesolation": { "Dry": 67, "Flooded": 33 },
"LakeSerene": { "Dry": 33, "Flooded": 67 },
"LakeSereneBridge": { "Dry": 67, "Flooded": 33 },
"Lab": { "Dry": 67, "Flooded": 33 },
}


Expand All @@ -383,6 +396,11 @@ class Traps(OptionList):
default = [ "Meteor Sparrow Trap", "Poison Trap", "Chaos Trap", "Neurotoxin Trap", "Bee Trap" ]


class PresentAccessWithWheelAndSpindle(Toggle):
"""When inverted, allows using the refugee camp warp when both the Timespinner Wheel and Spindle is acquired."""
display_name = "Past Wheel & Spindle Warp"


# Some options that are available in the timespinner randomizer arent currently implemented
timespinner_options: Dict[str, Option] = {
"StartWithJewelryBox": StartWithJewelryBox,
Expand All @@ -396,7 +414,7 @@ class Traps(OptionList):
"Cantoran": Cantoran,
"LoreChecks": LoreChecks,
"BossRando": BossRando,
"BossScaling": BossScaling,
"EnemyRando": EnemyRando,
"DamageRando": DamageRando,
"DamageRandoOverrides": DamageRandoOverrides,
"HpCap": HpCap,
Expand All @@ -419,6 +437,7 @@ class Traps(OptionList):
"UnchainedKeys": UnchainedKeys,
"TrapChance": TrapChance,
"Traps": Traps,
"PresentAccessWithWheelAndSpindle": PresentAccessWithWheelAndSpindle,
"DeathLink": DeathLink,
}

Expand Down
32 changes: 20 additions & 12 deletions worlds/timespinner/PreCalculatedWeights.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Tuple, Dict, Union
from typing import Tuple, Dict, Union, List
from BaseClasses import MultiWorld
from .Options import timespinner_options, is_option_enabled, get_option_value

Expand All @@ -17,7 +17,9 @@ class PreCalculatedWeights:
flood_moat: bool
flood_courtyard: bool
flood_lake_desolation: bool
dry_lake_serene: bool
flood_lake_serene: bool
flood_lake_serene_bridge: bool
flood_lab: bool

def __init__(self, world: MultiWorld, player: int):
if world and is_option_enabled(world, player, "RisingTides"):
Expand All @@ -32,8 +34,9 @@ def __init__(self, world: MultiWorld, player: int):
self.flood_moat, _ = self.roll_flood_setting(world, player, weights_overrrides, "CastleMoat")
self.flood_courtyard, _ = self.roll_flood_setting(world, player, weights_overrrides, "CastleCourtyard")
self.flood_lake_desolation, _ = self.roll_flood_setting(world, player, weights_overrrides, "LakeDesolation")
flood_lake_serene, _ = self.roll_flood_setting(world, player, weights_overrrides, "LakeSerene")
self.dry_lake_serene = not flood_lake_serene
self.flood_lake_serene, _ = self.roll_flood_setting(world, player, weights_overrrides, "LakeSerene")
self.flood_lake_serene_bridge, _ = self.roll_flood_setting(world, player, weights_overrrides, "LakeSereneBridge")
self.flood_lab, _ = self.roll_flood_setting(world, player, weights_overrrides, "Lab")
else:
self.flood_basement = False
self.flood_basement_high = False
Expand All @@ -44,30 +47,32 @@ def __init__(self, world: MultiWorld, player: int):
self.flood_moat = False
self.flood_courtyard = False
self.flood_lake_desolation = False
self.dry_lake_serene = False
self.flood_lake_serene = True
self.flood_lake_serene_bridge = False
self.flood_lab = False

self.pyramid_keys_unlock, self.present_key_unlock, self.past_key_unlock, self.time_key_unlock = \
self.get_pyramid_keys_unlocks(world, player, self.flood_maw)
self.get_pyramid_keys_unlocks(world, player, self.flood_maw, self.flood_xarion)

@staticmethod
def get_pyramid_keys_unlocks(world: MultiWorld, player: int, is_maw_flooded: bool) -> Tuple[str, str, str, str]:
present_teleportation_gates: Tuple[str, ...] = (
def get_pyramid_keys_unlocks(world: MultiWorld, player: int, is_maw_flooded: bool, is_xarion_flooded: bool) -> Tuple[str, str, str, str]:
present_teleportation_gates: List[str] = [
"GateKittyBoss",
"GateLeftLibrary",
"GateMilitaryGate",
"GateSealedCaves",
"GateSealedSirensCave",
"GateLakeDesolation"
)
]

past_teleportation_gates: Tuple[str, ...] = (
past_teleportation_gates: List[str] = [
"GateLakeSereneRight",
"GateAccessToPast",
"GateCastleRamparts",
"GateCastleKeep",
"GateRoyalTowers",
"GateCavesOfBanishment"
)
]

ancient_pyramid_teleportation_gates: Tuple[str, ...] = (
"GateGyre",
Expand All @@ -84,7 +89,10 @@ def get_pyramid_keys_unlocks(world: MultiWorld, player: int, is_maw_flooded: boo
)

if not is_maw_flooded:
past_teleportation_gates += ("GateMaw", )
past_teleportation_gates.append("GateMaw")

if not is_xarion_flooded:
present_teleportation_gates.append("GateXarion")

if is_option_enabled(world, player, "Inverted"):
all_gates: Tuple[str, ...] = present_teleportation_gates
Expand Down
Loading

0 comments on commit 09547b2

Please sign in to comment.