From b96d1c99047e2ab79ca35f56bc936ec7606cac48 Mon Sep 17 00:00:00 2001 From: Pecomare Date: Fri, 28 Mar 2025 13:53:00 +0100 Subject: [PATCH 1/6] Add Charge Boots to logic # Conflicts: # Logic.py # Simplified Ratchet & Clank 2.yaml --- .gitignore | 3 +- ItemPool.py | 7 +- Logic.py | 171 ++++++++++++++++++++++++++++++++++++++++++++----- Rac2Options.py | 6 ++ __init__.py | 2 +- 5 files changed, 169 insertions(+), 20 deletions(-) diff --git a/.gitignore b/.gitignore index e7dd66f..8ff5e5b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ __pycache__ *.aprac2 *.iso test/test_output -lib \ No newline at end of file +lib +/.idea/ diff --git a/ItemPool.py b/ItemPool.py index a95a497..2624705 100644 --- a/ItemPool.py +++ b/ItemPool.py @@ -3,13 +3,16 @@ from BaseClasses import ItemClassification, Item from .data import Items, Locations from .data.Items import CoordData, EquipmentData, ProgressiveUpgradeData, ItemData -from .Rac2Options import StartingWeapons +from .Rac2Options import StartingWeapons, Rac2Options if TYPE_CHECKING: from . import Rac2World -def get_classification(item: ItemData) -> ItemClassification: +def get_classification(item: ItemData, options: Rac2Options = None) -> ItemClassification: + if options is not None: + if options.charge_boots_in_logic and item == Items.CHARGE_BOOTS: + return ItemClassification.progression if item in Items.COORDS: return ItemClassification.progression if item in [ diff --git a/Logic.py b/Logic.py index 9cb44c0..4f315bc 100644 --- a/Logic.py +++ b/Logic.py @@ -98,6 +98,10 @@ def oozla_tractor_puzzle_pb_rule(state: CollectionState, player: int) -> bool: if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: return True + if options.charge_boots_in_logic: + return (can_charge(state, player) + or can_tractor(state, player)) + return can_tractor(state, player) @@ -118,18 +122,40 @@ def oozla_swamp_monster_ii_rule(state: CollectionState, player: int) -> bool: def maktar_photo_booth_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) + if (options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM + and options.charge_boots_in_logic): + return (can_electrolyze(state, player) + or can_heli(state, player) + or can_charge(state, player)) + if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: return (can_electrolyze(state, player) or can_heli(state, player)) + if options.charge_boots_in_logic: + return (can_electrolyze(state, player) + or can_charge(state, player)) + return can_electrolyze(state, player) def maktar_deactivate_jamming_array_rule(state: CollectionState, player: int) -> bool: + options = get_options(state, player) + + if options.charge_boots_in_logic: + return (can_charge(state, player) + or can_tractor(state, player)) + return can_tractor(state, player) def maktar_jamming_array_pb_rule(state: CollectionState, player: int) -> bool: + options = get_options(state, player) + + if options.charge_boots_in_logic: + return (can_charge(state, player) + or can_tractor(state, player)) + return can_tractor(state, player) @@ -156,13 +182,19 @@ def barlow_inventor_rule(state: CollectionState, player: int) -> bool: def barlow_overbike_race_rule(state: CollectionState, player: int) -> bool: + if not can_electrolyze(state, player): + return False + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: - return can_electrolyze(state, player) + if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + return True - return (can_improved_jump(state, player) - and can_electrolyze(state, player)) + if options.charge_boots_in_logic: + return (can_charge(state, player) + or can_improved_jump(state, player)) + + return can_improved_jump(state, player) def barlow_hound_cave_pb_rule(state: CollectionState, player: int) -> bool: @@ -175,6 +207,11 @@ def notak_top_pier_telescreen_rule(state: CollectionState, player: int) -> bool: if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: return True + if options.charge_boots_in_logic: + return (can_charge(state, player) + or (can_improved_jump(state, player) + and can_thermanate(state, player))) + return (can_improved_jump(state, player) and can_thermanate(state, player)) @@ -185,6 +222,11 @@ def notak_worker_bots_rule(state: CollectionState, player: int) -> bool: if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: return True + if options.charge_boots_in_logic: + return (can_charge(state, player) + or (can_heli(state, player) + and can_thermanate(state, player))) + return (can_heli(state, player) and can_thermanate(state, player)) @@ -195,9 +237,17 @@ def notak_timed_dynamo_rule(state: CollectionState, player: int) -> bool: if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: return True - return (can_dynamo(state, player) - and can_thermanate(state, player) - and can_improved_jump(state, player)) + if options.charge_boots_in_logic: + if not can_dynamo(state, player): + return False + + return (can_charge(state, player) + or (can_improved_jump(state, player) + and can_thermanate(state, player))) + + return (can_thermanate(state, player) + and can_improved_jump(state, player) + and can_dynamo(state, player)) def siberius_defeat_thief_rule(state: CollectionState, player: int) -> bool: @@ -219,6 +269,10 @@ def siberius_fenced_area_pb_rule(state: CollectionState, player: int) -> bool: if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: return True + if options.charge_boots_in_logic: + return (can_charge(state, player) + or can_heli(state, player)) + return can_heli(state, player) @@ -261,6 +315,11 @@ def tabora_northeast_desert_pb_rule(state: CollectionState, player: int) -> bool def tabora_canyon_glide_pillar_nt_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) + if options.charge_boots_in_logic: + return (can_heli(state, player) + and can_swingshot(state, player) + and can_charge(state, player)) + if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: return (can_heli(state, player) and can_swingshot(state, player) @@ -329,6 +388,11 @@ def dobbo_facility_glide_nt_rule(state: CollectionState, player: int) -> bool: def joba_hoverbike_race_rule(state: CollectionState, player: int) -> bool: + options = get_options(state, player) + + if options.charge_boots_in_logic: + return can_charge(state, player) + return can_swingshot(state, player) @@ -370,6 +434,13 @@ def joba_hidden_cliff_pb_rule(state: CollectionState, player: int) -> bool: if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: return True + if options.charge_boots_in_logic: + if not can_dynamo(state, player): + return False + + return (can_charge(state, player) + or can_swingshot(state, player)) + return (can_dynamo(state, player) and can_swingshot(state, player)) @@ -400,9 +471,17 @@ def todano_search_rocket_silo_rule(state: CollectionState, player: int) -> bool: if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: return True + if options.charge_boots_in_logic: + if not (can_electrolyze(state, player) + and can_infiltrate(state, player)): + return False + + return (can_charge(state, player) + or can_improved_jump(state, player)) + return (can_electrolyze(state, player) - and can_improved_jump(state, player) - and can_infiltrate(state, player)) + and can_infiltrate(state, player) + and can_improved_jump(state, player)) def todano_stuart_zurgo_trade_rule(state: CollectionState, player: int) -> bool: @@ -476,13 +555,16 @@ def boldan_find_fizzwidget_rule(state: CollectionState, player: int) -> bool: if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_HARD: return True - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: - return (can_gravity(state, player) - and can_improved_jump(state, player)) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: - return (can_swingshot(state, player) - and can_gravity(state, player)) + return can_gravity(state, player) + + if options.charge_boots_in_logic: + if not (can_levitate(state, player) + and can_gravity(state, player)): + return False + + return (can_charge(state, player) + or can_swingshot(state, player)) return (can_levitate(state, player) and can_swingshot(state, player) @@ -568,7 +650,7 @@ def aranos_omniwrench_12000_rule(state: CollectionState, player: int) -> bool: def snivelak_rescue_angelak_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >+ FIRST_PERSON_EASY: + if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: return can_swingshot(state, player) return (can_swingshot(state, player) @@ -583,6 +665,16 @@ def snivelak_dynamo_pb_rule(state: CollectionState, player: int) -> bool: if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: return can_swingshot(state, player) + if options.charge_boots_in_logic: + if not (can_swingshot(state, player) + and can_grind(state, player) + and can_gravity(state, player) + and can_dynamo(state, player)): + return False + + return (can_charge(state, player) + or can_heli(state, player)) + return (can_swingshot(state, player) and can_grind(state, player) and can_gravity(state, player) @@ -630,9 +722,17 @@ def smolg_mutant_crab_rule(state: CollectionState, player: int) -> bool: if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: if not can_levitate(state, player): return False + return (can_swingshot(state, player) or can_electrolyze(state, player)) + if options.charge_boots_in_logic: + if not can_levitate(state, player): + return False + + return (can_charge(state, player) + or can_swingshot(state, player)) + return (can_swingshot(state, player) and can_levitate(state, player)) @@ -643,9 +743,17 @@ def smolg_floating_platform_pb_rule(state: CollectionState, player: int) -> bool if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: if not can_levitate(state, player): return False + return (can_swingshot(state, player) or can_electrolyze(state, player)) + if options.charge_boots_in_logic: + if not can_levitate(state, player): + return False + + return (can_charge(state, player) + or can_swingshot(state, player)) + return (can_swingshot(state, player) and can_levitate(state, player)) @@ -661,6 +769,17 @@ def smolg_warehouse_pb_rule(state: CollectionState, player: int) -> bool: def damosel_hypnotist_rule(state: CollectionState, player: int) -> bool: + options = get_options(state, player) + + if options.charge_boots_in_logic: + if not (can_improved_jump(state, player) + and has_hypnomatic_parts(state, player)): + return False + + return (can_charge(state, player) + or (can_swingshot(state, player) + and can_thermanate(state, player))) + return (can_swingshot(state, player) and can_improved_jump(state, player) and can_thermanate(state, player) @@ -672,6 +791,16 @@ def damosel_train_rails_rule(state: CollectionState, player: int) -> bool: def damosel_frozen_mountain_pb_rule(state: CollectionState, player: int) -> bool: + options = get_options(state, player) + + if options.charge_boots_in_logic: + if not can_grind(state, player): + return False + + return (can_charge(state, player) + or (can_improved_jump(state, player) + and can_thermanate(state, player))) + return (can_swingshot(state, player) and can_improved_jump(state, player) and can_thermanate(state, player) @@ -679,6 +808,16 @@ def damosel_frozen_mountain_pb_rule(state: CollectionState, player: int) -> bool def damosel_pyramid_pb_rule(state: CollectionState, player: int) -> bool: + options = get_options(state, player) + + if options.charge_boots_in_logic: + if not (can_improved_jump(state, player) + and can_hypnotize(state, player)): + return False + + return (can_charge(state, player) + or can_swingshot(state, player)) + return (can_swingshot(state, player) and can_improved_jump(state, player) and can_hypnotize(state, player)) diff --git a/Rac2Options.py b/Rac2Options.py index ea21e4b..15c6c0d 100644 --- a/Rac2Options.py +++ b/Rac2Options.py @@ -117,6 +117,11 @@ class FirstPersonModeGlitchInLogic(Choice): default = 0 +class ChargeBootsInLogic(Toggle): + """If enabled, logic should take charge boots into account when evaluating which locations are reachable.""" + display_name = "Charge Boots In Logic" + + @dataclass class Rac2Options(PerGameCommonOptions): start_inventory_from_pool: StartInventoryPool @@ -135,3 +140,4 @@ class Rac2Options(PerGameCommonOptions): extra_spaceship_challenge_locations: ExtraSpaceshipChallengeLocations extend_weapon_progression: ExtendWeaponProgression first_person_mode_glitch_in_logic: FirstPersonModeGlitchInLogic + charge_boots_in_logic: ChargeBootsInLogic diff --git a/__init__.py b/__init__.py index 8f656aa..bf1e69a 100644 --- a/__init__.py +++ b/__init__.py @@ -95,7 +95,7 @@ def create_item(self, name: str, override: Optional[ItemClassification] = None) if override: return Rac2Item(name, override, self.item_name_to_id[name], self.player) item_data = Items.from_name(name) - return Rac2Item(name, ItemPool.get_classification(item_data), self.item_name_to_id[name], self.player) + return Rac2Item(name, ItemPool.get_classification(item_data, self.options), self.item_name_to_id[name], self.player) def create_event(self, name: str) -> "Item": return Rac2Item(name, ItemClassification.progression, None, self.player) From bf2d18dfc0d932cd413cdc559d6766f9ca79b674 Mon Sep 17 00:00:00 2001 From: Pecomare Date: Sat, 5 Apr 2025 17:41:21 +0200 Subject: [PATCH 2/6] Refactor logic into global difficulties --- ItemPool.py | 4 +- Logic.py | 737 ++++++++++++++++-------------- Rac2Options.py | 31 +- Simplified Ratchet & Clank 2.yaml | 12 +- data/Locations.py | 2 +- 5 files changed, 414 insertions(+), 372 deletions(-) diff --git a/ItemPool.py b/ItemPool.py index 2624705..26fe0b0 100644 --- a/ItemPool.py +++ b/ItemPool.py @@ -1,8 +1,10 @@ from typing import TYPE_CHECKING from BaseClasses import ItemClassification, Item + from .data import Items, Locations from .data.Items import CoordData, EquipmentData, ProgressiveUpgradeData, ItemData +from .Logic import GLITCH_LOGIC_HARD from .Rac2Options import StartingWeapons, Rac2Options if TYPE_CHECKING: @@ -11,7 +13,7 @@ def get_classification(item: ItemData, options: Rac2Options = None) -> ItemClassification: if options is not None: - if options.charge_boots_in_logic and item == Items.CHARGE_BOOTS: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD and item == Items.CHARGE_BOOTS: return ItemClassification.progression if item in Items.COORDS: return ItemClassification.progression diff --git a/Logic.py b/Logic.py index 4f315bc..66e87b8 100644 --- a/Logic.py +++ b/Logic.py @@ -74,9 +74,9 @@ def has_hypnomatic_parts(state: CollectionState, player: int) -> bool: return state.has(Items.HYPNOMATIC_PART.name, player, 3) -FIRST_PERSON_EASY = 1 -FIRST_PERSON_MEDIUM = 2 -FIRST_PERSON_HARD = 3 +GLITCH_LOGIC_MEDIUM = 1 +GLITCH_LOGIC_HARD = 2 +GLITCH_LOGIC_EXPERT = 3 def get_options(state: CollectionState, player: int) -> Rac2Options: @@ -84,34 +84,39 @@ def get_options(state: CollectionState, player: int) -> Rac2Options: def oozla_end_store_cutscene_rule(state: CollectionState, player: int) -> bool: + if can_dynamo(state, player): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - return can_dynamo(state, player) + return False def oozla_tractor_puzzle_pb_rule(state: CollectionState, player: int) -> bool: + if can_tractor(state, player): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - if options.charge_boots_in_logic: - return (can_charge(state, player) - or can_tractor(state, player)) - - return can_tractor(state, player) + return False def oozla_swamp_ruins_pb_rule(state: CollectionState, player: int) -> bool: + if can_dynamo(state, player): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - return can_dynamo(state, player) + return False def oozla_swamp_monster_ii_rule(state: CollectionState, player: int) -> bool: @@ -120,43 +125,40 @@ def oozla_swamp_monster_ii_rule(state: CollectionState, player: int) -> bool: def maktar_photo_booth_rule(state: CollectionState, player: int) -> bool: - options = get_options(state, player) - - if (options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM - and options.charge_boots_in_logic): - return (can_electrolyze(state, player) - or can_heli(state, player) - or can_charge(state, player)) + if can_electrolyze(state, player): + return True - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: - return (can_electrolyze(state, player) - or can_heli(state, player)) + options = get_options(state, player) - if options.charge_boots_in_logic: - return (can_electrolyze(state, player) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + return (can_heli(state, player) or can_charge(state, player)) - return can_electrolyze(state, player) + return False def maktar_deactivate_jamming_array_rule(state: CollectionState, player: int) -> bool: + if can_tractor(state, player): + return True + options = get_options(state, player) - if options.charge_boots_in_logic: - return (can_charge(state, player) - or can_tractor(state, player)) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + return can_charge(state, player) - return can_tractor(state, player) + return False def maktar_jamming_array_pb_rule(state: CollectionState, player: int) -> bool: + if can_tractor(state, player): + return True + options = get_options(state, player) - if options.charge_boots_in_logic: - return (can_charge(state, player) - or can_tractor(state, player)) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + return can_charge(state, player) - return can_tractor(state, player) + return False def endako_rescue_clank_rule(state: CollectionState, player: int) -> bool: @@ -173,28 +175,28 @@ def endako_crane_nt_rule(state: CollectionState, player: int) -> bool: def barlow_inventor_rule(state: CollectionState, player: int) -> bool: + if can_swingshot(state, player): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - return can_swingshot(state, player) + return False def barlow_overbike_race_rule(state: CollectionState, player: int) -> bool: - if not can_electrolyze(state, player): - return False + if (can_improved_jump(state, player) + and can_electrolyze(state, player)): + return True options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: - return True - - if options.charge_boots_in_logic: - return (can_charge(state, player) - or can_improved_jump(state, player)) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + return can_electrolyze(state, player) - return can_improved_jump(state, player) + return False def barlow_hound_cave_pb_rule(state: CollectionState, player: int) -> bool: @@ -202,52 +204,43 @@ def barlow_hound_cave_pb_rule(state: CollectionState, player: int) -> bool: def notak_top_pier_telescreen_rule(state: CollectionState, player: int) -> bool: + if (can_improved_jump(state, player) + and can_thermanate(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - if options.charge_boots_in_logic: - return (can_charge(state, player) - or (can_improved_jump(state, player) - and can_thermanate(state, player))) - - return (can_improved_jump(state, player) - and can_thermanate(state, player)) + return False def notak_worker_bots_rule(state: CollectionState, player: int) -> bool: + if (can_heli(state, player) + and can_thermanate(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - if options.charge_boots_in_logic: - return (can_charge(state, player) - or (can_heli(state, player) - and can_thermanate(state, player))) - - return (can_heli(state, player) - and can_thermanate(state, player)) + return False def notak_timed_dynamo_rule(state: CollectionState, player: int) -> bool: - options = get_options(state, player) - - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: + if (can_thermanate(state, player) + and can_improved_jump(state, player) + and can_dynamo(state, player)): return True - if options.charge_boots_in_logic: - if not can_dynamo(state, player): - return False + options = get_options(state, player) - return (can_charge(state, player) - or (can_improved_jump(state, player) - and can_thermanate(state, player))) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + return True - return (can_thermanate(state, player) - and can_improved_jump(state, player) - and can_dynamo(state, player)) + return False def siberius_defeat_thief_rule(state: CollectionState, player: int) -> bool: @@ -255,25 +248,27 @@ def siberius_defeat_thief_rule(state: CollectionState, player: int) -> bool: def siberius_flamebot_ledge_pb_rule(state: CollectionState, player: int) -> bool: + if can_tractor(state, player): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - return can_tractor(state, player) + return False def siberius_fenced_area_pb_rule(state: CollectionState, player: int) -> bool: + if can_heli(state, player): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - if options.charge_boots_in_logic: - return (can_charge(state, player) - or can_heli(state, player)) - - return can_heli(state, player) + return False def tabora_meet_angelar_rule(state: CollectionState, player: int) -> bool: @@ -282,29 +277,35 @@ def tabora_meet_angelar_rule(state: CollectionState, player: int) -> bool: def tabora_underground_mines_end_rule(state: CollectionState, player: int) -> bool: + if (can_heli(state, player) + and can_swingshot(state, player) + and can_thermanate(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return (can_heli(state, player) and can_swingshot(state, player)) - return (can_heli(state, player) - and can_swingshot(state, player) - and can_thermanate(state, player)) + return False def tabora_canyon_glide_pb_rule(state: CollectionState, player: int) -> bool: + if (can_heli(state, player) + and can_swingshot(state, player) + and can_thermanate(state, player) + and can_glide(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return (can_heli(state, player) and can_swingshot(state, player) and can_glide(state, player)) - return (can_heli(state, player) - and can_swingshot(state, player) - and can_thermanate(state, player) - and can_glide(state, player)) + return False def tabora_northeast_desert_pb_rule(state: CollectionState, player: int) -> bool: @@ -313,477 +314,515 @@ def tabora_northeast_desert_pb_rule(state: CollectionState, player: int) -> bool def tabora_canyon_glide_pillar_nt_rule(state: CollectionState, player: int) -> bool: - options = get_options(state, player) + if (can_heli(state, player) + and can_swingshot(state, player) + and can_thermanate(state, player) + and can_glide(state, player)): + return True - if options.charge_boots_in_logic: - return (can_heli(state, player) - and can_swingshot(state, player) - and can_charge(state, player)) + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return (can_heli(state, player) - and can_swingshot(state, player) - and can_glide(state, player)) + and can_swingshot(state, player)) - return (can_heli(state, player) - and can_swingshot(state, player) - and can_thermanate(state, player) - and can_glide(state, player)) + return False def dobbo_defeat_thug_leader_rule(state: CollectionState, player: int) -> bool: + if (can_swingshot(state, player) + and can_improved_jump(state, player) + and can_dynamo(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return can_swingshot(state, player) - return (can_swingshot(state, player) - and can_improved_jump(state, player) - and can_dynamo(state, player)) + return False def dobbo_facility_terminal_rule(state: CollectionState, player: int) -> bool: + if (can_swingshot(state, player) + and can_glide(state, player) + and can_dynamo(state, player) + and can_electrolyze(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - return (can_swingshot(state, player) - and can_glide(state, player) - and can_dynamo(state, player) - and can_electrolyze(state, player)) + return False def dobbo_spiderbot_room_pb_rule(state: CollectionState, player: int) -> bool: + if (can_dynamo(state, player) + and can_swingshot(state, player) + and can_spiderbot(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return can_swingshot(state, player) - return (can_swingshot(state, player) - and can_dynamo(state, player) - and can_spiderbot(state, player)) + return False def dobbo_facility_glide_pb_rule(state: CollectionState, player: int) -> bool: + if (can_swingshot(state, player) + and can_glide(state, player) + and can_dynamo(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - return (can_swingshot(state, player) - and can_glide(state, player) - and can_dynamo(state, player)) + return False def dobbo_facility_glide_nt_rule(state: CollectionState, player: int) -> bool: + if (can_swingshot(state, player) + and can_glide(state, player) + and can_dynamo(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - return (can_swingshot(state, player) - and can_glide(state, player) - and can_dynamo(state, player)) + return False def joba_hoverbike_race_rule(state: CollectionState, player: int) -> bool: + if can_swingshot(state, player): + return True + options = get_options(state, player) - if options.charge_boots_in_logic: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return can_charge(state, player) - return can_swingshot(state, player) + return False def joba_shady_salesman_rule(state: CollectionState, player: int) -> bool: + if (can_dynamo(state, player) + and can_improved_jump(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - return (can_dynamo(state, player) - and can_improved_jump(state, player)) + return False def joba_arena_battle_rule(state: CollectionState, player: int) -> bool: - options = get_options(state, player) - - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: - return can_levitate(state, player) - - return (can_dynamo(state, player) + if (can_dynamo(state, player) and can_improved_jump(state, player) - and can_levitate(state, player)) - + and can_levitate(state, player)): + return True -def joba_arena_cage_match_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return can_levitate(state, player) - return (can_dynamo(state, player) - and can_improved_jump(state, player) - and can_levitate(state, player)) + return False def joba_hidden_cliff_pb_rule(state: CollectionState, player: int) -> bool: - options = get_options(state, player) - - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: + if (can_dynamo(state, player) + and can_swingshot(state, player)): return True - if options.charge_boots_in_logic: - if not can_dynamo(state, player): - return False + options = get_options(state, player) - return (can_charge(state, player) - or can_swingshot(state, player)) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + return True - return (can_dynamo(state, player) - and can_swingshot(state, player)) + return False def joba_levitator_tower_pb_rule(state: CollectionState, player: int) -> bool: + if (can_dynamo(state, player) + and can_improved_jump(state, player) + and can_levitate(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return can_levitate(state, player) - return (can_dynamo(state, player) - and can_improved_jump(state, player) - and can_levitate(state, player)) + return False def joba_timed_dynamo_nt_rule(state: CollectionState, player: int) -> bool: + if can_dynamo(state, player): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - return can_dynamo(state, player) + return False def todano_search_rocket_silo_rule(state: CollectionState, player: int) -> bool: - options = get_options(state, player) - - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if (can_electrolyze(state, player) + and can_infiltrate(state, player) + and can_improved_jump(state, player)): return True - if options.charge_boots_in_logic: - if not (can_electrolyze(state, player) - and can_infiltrate(state, player)): - return False + options = get_options(state, player) - return (can_charge(state, player) - or can_improved_jump(state, player)) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + return True - return (can_electrolyze(state, player) - and can_infiltrate(state, player) - and can_improved_jump(state, player)) + return False def todano_stuart_zurgo_trade_rule(state: CollectionState, player: int) -> bool: + if (can_electrolyze(state, player) + and can_tractor(state, player) + and has_qwark_statuette(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_HARD: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_EXPERT: return has_qwark_statuette(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return (can_tractor(state, player) and has_qwark_statuette(state, player)) - return (can_electrolyze(state, player) - and can_tractor(state, player) - and has_qwark_statuette(state, player)) + return False def todano_facility_interior_rule(state: CollectionState, player: int) -> bool: + if (can_electrolyze(state, player) + and can_tractor(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_HARD: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_EXPERT: return True - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return can_tractor(state, player) - return (can_electrolyze(state, player) - and can_tractor(state, player)) + return False def todano_near_stuart_zurgo_pb_rule(state: CollectionState, player: int) -> bool: + if (can_electrolyze(state, player) + and can_tractor(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_HARD: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_EXPERT: return True - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return can_tractor(state, player) - return (can_electrolyze(state, player) - and can_tractor(state, player)) + return False def todano_spiderbot_conveyor_pb_rule(state: CollectionState, player: int) -> bool: + if (can_electrolyze(state, player) + and can_tractor(state, player) + and can_improved_jump(state, player) + and can_spiderbot(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return (can_tractor(state, player) - and can_improved_jump(state, player) and can_spiderbot(state, player)) - return (can_electrolyze(state, player) - and can_tractor(state, player) - and can_improved_jump(state, player) - and can_spiderbot(state, player)) + return False def todano_rocket_silo_nt_rule(state: CollectionState, player: int) -> bool: + if (can_electrolyze(state, player) + and can_infiltrate(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - return (can_electrolyze(state, player) - and can_infiltrate(state, player)) + return False def boldan_find_fizzwidget_rule(state: CollectionState, player: int) -> bool: + if (can_levitate(state, player) + and can_swingshot(state, player) + and can_gravity(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_HARD: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_EXPERT: return True - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return can_gravity(state, player) - if options.charge_boots_in_logic: - if not (can_levitate(state, player) - and can_gravity(state, player)): - return False - - return (can_charge(state, player) - or can_swingshot(state, player)) - - return (can_levitate(state, player) - and can_swingshot(state, player) - and can_gravity(state, player)) + return False def boldan_spiderbot_alley_pb_rule(state: CollectionState, player: int) -> bool: + if (can_levitate(state, player) + and can_spiderbot(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - return (can_levitate(state, player) - and can_spiderbot(state, player)) + return False def boldan_floating_platform_rule(state: CollectionState, player: int) -> bool: + if (can_levitate(state, player) + and can_gravity(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: - return can_gravity(state, player) - - return (can_levitate(state, player) - and can_gravity(state, player)) + return False def boldan_fountain_nt_rule(state: CollectionState, player: int) -> bool: + if can_levitate(state, player): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - return can_levitate(state, player) + return False def aranos_control_room_rule(state: CollectionState, player: int) -> bool: + if (can_gravity(state, player) + and can_infiltrate(state, player) + and can_levitate(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_HARD: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_EXPERT: return True - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return (can_infiltrate(state, player) and can_levitate(state, player)) - return (can_gravity(state, player) - and can_infiltrate(state, player) - and can_levitate(state, player)) + return False def aranos_plumber_rule(state: CollectionState, player: int) -> bool: + if (can_gravity(state, player) + and can_levitate(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - return (can_gravity(state, player) - and can_levitate(state, player)) + return False def aranos_under_ship_pb_rule(state: CollectionState, player: int) -> bool: + if (can_gravity(state, player) + and can_heli(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return can_heli(state, player) - return (can_gravity(state, player) - and can_heli(state, player)) + return False def aranos_omniwrench_12000_rule(state: CollectionState, player: int) -> bool: + if can_gravity(state, player): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - return can_gravity(state, player) + return False def snivelak_rescue_angelak_rule(state: CollectionState, player: int) -> bool: - options = get_options(state, player) - - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: - return can_swingshot(state, player) - - return (can_swingshot(state, player) + if (can_swingshot(state, player) and can_grind(state, player) and can_gravity(state, player) - and can_dynamo(state, player)) - + and can_dynamo(state, player)): + return True -def snivelak_dynamo_pb_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return can_swingshot(state, player) - if options.charge_boots_in_logic: - if not (can_swingshot(state, player) - and can_grind(state, player) - and can_gravity(state, player) - and can_dynamo(state, player)): - return False + return False - return (can_charge(state, player) - or can_heli(state, player)) - return (can_swingshot(state, player) +def snivelak_dynamo_pb_rule(state: CollectionState, player: int) -> bool: + if (can_swingshot(state, player) and can_grind(state, player) and can_gravity(state, player) and can_dynamo(state, player) - and can_heli(state, player)) + and can_heli(state, player)): + return True + + options = get_options(state, player) + + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + return can_swingshot(state, player) + + return False def snivelak_swingshot_tower_nt_rule(state: CollectionState, player: int) -> bool: + if (can_swingshot(state, player) + and can_heli(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return can_swingshot(state, player) - return (can_swingshot(state, player) - and can_heli(state, player)) + return False def smolg_balloon_transmission_rule(state: CollectionState, player: int) -> bool: + if (can_improved_jump(state, player) + and can_dynamo(state, player) + and can_electrolyze(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return can_electrolyze(state, player) - return (can_improved_jump(state, player) - and can_dynamo(state, player) - and can_electrolyze(state, player)) + return False def smolg_distribution_facility_end_rule(state: CollectionState, player: int) -> bool: + if (can_improved_jump(state, player) + and can_dynamo(state, player) + and can_electrolyze(state, player) + and can_grind(state, player) + and can_infiltrate(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return can_electrolyze(state, player) - return (can_improved_jump(state, player) - and can_dynamo(state, player) - and can_electrolyze(state, player) - and can_grind(state, player) - and can_infiltrate(state, player)) + return False def smolg_mutant_crab_rule(state: CollectionState, player: int) -> bool: + if (can_swingshot(state, player) + and can_levitate(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: if not can_levitate(state, player): return False return (can_swingshot(state, player) + or can_charge(state, player) or can_electrolyze(state, player)) - if options.charge_boots_in_logic: - if not can_levitate(state, player): - return False - - return (can_charge(state, player) - or can_swingshot(state, player)) - - return (can_swingshot(state, player) - and can_levitate(state, player)) + return False def smolg_floating_platform_pb_rule(state: CollectionState, player: int) -> bool: + if (can_swingshot(state, player) + and can_levitate(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: if not can_levitate(state, player): return False return (can_swingshot(state, player) + or can_charge(state, player) or can_electrolyze(state, player)) - if options.charge_boots_in_logic: - if not can_levitate(state, player): - return False - - return (can_charge(state, player) - or can_swingshot(state, player)) - - return (can_swingshot(state, player) - and can_levitate(state, player)) + return False def smolg_warehouse_pb_rule(state: CollectionState, player: int) -> bool: + if (can_dynamo(state, player) + or can_improved_jump(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - return (can_dynamo(state, player) - and can_improved_jump(state, player)) + return False def damosel_hypnotist_rule(state: CollectionState, player: int) -> bool: + if (can_swingshot(state, player) + and can_improved_jump(state, player) + and can_thermanate(state, player) + and has_hypnomatic_parts(state, player)): + return True + options = get_options(state, player) - if options.charge_boots_in_logic: - if not (can_improved_jump(state, player) - and has_hypnomatic_parts(state, player)): + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + if not has_hypnomatic_parts(state, player): return False return (can_charge(state, player) or (can_swingshot(state, player) and can_thermanate(state, player))) - return (can_swingshot(state, player) - and can_improved_jump(state, player) - and can_thermanate(state, player) - and has_hypnomatic_parts(state, player)) + return False def damosel_train_rails_rule(state: CollectionState, player: int) -> bool: @@ -791,45 +830,52 @@ def damosel_train_rails_rule(state: CollectionState, player: int) -> bool: def damosel_frozen_mountain_pb_rule(state: CollectionState, player: int) -> bool: + if (can_swingshot(state, player) + and can_improved_jump(state, player) + and can_thermanate(state, player) + and can_grind(state, player)): + return True + options = get_options(state, player) - if options.charge_boots_in_logic: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: if not can_grind(state, player): return False return (can_charge(state, player) - or (can_improved_jump(state, player) - and can_thermanate(state, player))) + or can_thermanate(state, player)) - return (can_swingshot(state, player) - and can_improved_jump(state, player) - and can_thermanate(state, player) - and can_grind(state, player)) + return False def damosel_pyramid_pb_rule(state: CollectionState, player: int) -> bool: + if (can_swingshot(state, player) + and can_improved_jump(state, player) + and can_hypnotize(state, player)): + return True + options = get_options(state, player) - if options.charge_boots_in_logic: - if not (can_improved_jump(state, player) - and can_hypnotize(state, player)): + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + if not can_hypnotize(state, player): return False return (can_charge(state, player) or can_swingshot(state, player)) - return (can_swingshot(state, player) - and can_improved_jump(state, player) - and can_hypnotize(state, player)) + return False def grelbin_find_angela_rule(state: CollectionState, player: int) -> bool: + if can_hypnotize(state, player): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: return True - return can_hypnotize(state, player) + return False def grelbin_mystic_more_moonstones_rule(state: CollectionState, player: int) -> bool: @@ -853,65 +899,64 @@ def grelbin_yeti_cave_pb_rule(state: CollectionState, player: int) -> bool: def yeedil_defeat_mutated_protopet_rule(state: CollectionState, player: int) -> bool: + if (can_hypnotize(state, player) + and can_swingshot(state, player) + and can_infiltrate(state, player) + and can_dynamo(state, player) + and can_improved_jump(state, player) + and can_electrolyze(state, player)): + return True + options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_HARD: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_EXPERT: return can_infiltrate(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: - return (can_hypnotize(state, player) - and can_swingshot(state, player) - and can_infiltrate(state, player)) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + if not can_infiltrate(state, player): + return False - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: - return (can_hypnotize(state, player) - and can_swingshot(state, player) - and can_infiltrate(state, player) - and can_dynamo(state, player) - and can_improved_jump(state, player)) + return (can_charge(state, player) + or (can_hypnotize(state, player) + and can_swingshot(state, player))) - return (can_hypnotize(state, player) - and can_swingshot(state, player) - and can_infiltrate(state, player) - and can_dynamo(state, player) - and can_improved_jump(state, player) - and can_electrolyze(state, player)) + return False def yeedil_bridge_grindrail_pb_rule(state: CollectionState, player: int) -> bool: - options = get_options(state, player) - - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: + if can_grind(state, player): return True - return can_grind(state, player) - - -def yeedil_tractor_pillar_pb_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_HARD: - return can_infiltrate(state, player) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + return True - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_MEDIUM: - return (can_hypnotize(state, player) - and can_swingshot(state, player) - and can_infiltrate(state, player)) + return False - if options.first_person_mode_glitch_in_logic >= FIRST_PERSON_EASY: - return (can_hypnotize(state, player) - and can_swingshot(state, player) - and can_infiltrate(state, player) - and can_dynamo(state, player) - and can_improved_jump(state, player) - and can_tractor(state, player) - and can_grind(state, player)) - return (can_hypnotize(state, player) +def yeedil_tractor_pillar_pb_rule(state: CollectionState, player: int) -> bool: + if (can_hypnotize(state, player) and can_swingshot(state, player) and can_infiltrate(state, player) and can_dynamo(state, player) and can_improved_jump(state, player) and can_electrolyze(state, player) and can_tractor(state, player) - and can_grind(state, player)) + and can_grind(state, player)): + return True + + options = get_options(state, player) + + if options.glitch_logic_difficulty >= GLITCH_LOGIC_EXPERT: + return can_infiltrate(state, player) + + if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + if not can_infiltrate(state, player): + return False + + return (can_charge(state, player) + or (can_hypnotize(state, player) + and can_swingshot(state, player))) + + return False \ No newline at end of file diff --git a/Rac2Options.py b/Rac2Options.py index 15c6c0d..22d613b 100644 --- a/Rac2Options.py +++ b/Rac2Options.py @@ -103,25 +103,21 @@ class ExtendWeaponProgression(Toggle): display_name = "Extended Weapon Progression" -class FirstPersonModeGlitchInLogic(Choice): - """Determines if logic should take first person mode glitches into account when evaluating which locations are - reachable. Various difficulty levels can be picked: - - Easy: simple climbs (e.g. getting the Platinum Bolt near Oozla scientist) - - Medium: harder climbs and basic lateral movement (e.g. Getting to the Notak Worker Bots without the Heli-pack nor the Thermanator) - - Hard: full navigation following walls (e.g. Getting to the Mutated Protopet only with the Infiltrator)""" - display_name = "First Person Mode Glitch In Logic" - option_disabled = 0 - option_easy = 1 - option_medium = 2 - option_hard = 3 +class GlitchLogicDifficulty(Choice): + """Determines the kind of logic that should taken into account when evaluating which locations are reachable. + Various difficulty levels can be picked: + - Beginner: Intended way of doing things, or things you can deduce from a casual playthrough + - Medium: Obscure knowledge, out-of-the-box thinking, unintuitive usage of game mechanics but no glitch involved (e.g. tight heli-pack glide, tight hyperstrike wrench jumps, simple macaroni & sideflip movement tech etc...) + - Hard: Simple to intermediate glitches allowed (e.g. most decoy glove clip-outs, most charge boots tech, most decoy proxies, pack switch, etc...) + - Expert: All known glitches allowed (e.g. hardest clip-outs, complex movement chains where keeping momentum is required, etc...)""" + display_name = "Glitch Logic Difficulty" + option_beginner = 0 + option_medium = 1 + option_hard = 2 + option_expert = 3 default = 0 -class ChargeBootsInLogic(Toggle): - """If enabled, logic should take charge boots into account when evaluating which locations are reachable.""" - display_name = "Charge Boots In Logic" - - @dataclass class Rac2Options(PerGameCommonOptions): start_inventory_from_pool: StartInventoryPool @@ -139,5 +135,4 @@ class Rac2Options(PerGameCommonOptions): weapon_xp_multiplier: WeaponExperienceMultiplier extra_spaceship_challenge_locations: ExtraSpaceshipChallengeLocations extend_weapon_progression: ExtendWeaponProgression - first_person_mode_glitch_in_logic: FirstPersonModeGlitchInLogic - charge_boots_in_logic: ChargeBootsInLogic + glitch_logic_difficulty: GlitchLogicDifficulty diff --git a/Simplified Ratchet & Clank 2.yaml b/Simplified Ratchet & Clank 2.yaml index 23787e5..70dd7f3 100644 --- a/Simplified Ratchet & Clank 2.yaml +++ b/Simplified Ratchet & Clank 2.yaml @@ -101,13 +101,13 @@ Ratchet & Clank 2: # This effectively makes all weapons that are usually restricted to NG+ available with enough grinding. extend_weapon_progression: false - # Determines if logic should take first person mode glitches into account when evaluating which locations are reachable. + # Determines the kind of logic that should taken into account when evaluating which locations are reachable. # Various difficulty levels can be picked: - # - Easy: simple climbs (e.g. getting the Platinum Bolt near Oozla scientist) - # - Medium: harder climbs and basic lateral movement (e.g. Getting to the Notak Worker Bots without the Heli-pack nor the Thermanator) - # - Hard: full navigation following walls (e.g. Getting to the Mutated Protopet only with the Infiltrator) - # Can be disabled with value disabled. - first_person_mode_glitch_in_logic: disabled + # - Beginner: Intended way of doing things, or things you can deduce from a casual playthrough + # - Medium: Obscure knowledge, out-of-the-box thinking, unintuitive usage of game mechanics but no glitch involved (e.g. tight heli-pack glide, tight hyperstrike wrench jumps, simple macaroni & sideflip movement tech etc...) + # - Hard: Simple to intermediate glitches allowed (e.g. most decoy glove clip-outs, most charge boots tech, most decoy proxies, pack switch, etc...) + # - Expert: All known glitches allowed (e.g. hardest clip-outs, complex movement chains where keeping momentum is required, etc...) + glitch_logic_difficulty: beginner # Item & Location Options local_items: diff --git a/data/Locations.py b/data/Locations.py index ba21fe6..0296027 100644 --- a/data/Locations.py +++ b/data/Locations.py @@ -119,7 +119,7 @@ class LocationData(NamedTuple): JOBA_FIRST_HOVERBIKE_RACE = LocationData(110, "Joba: First Hoverbike Race - Charge Boots", joba_hoverbike_race_rule) JOBA_SHADY_SALESMAN = LocationData(111, "Joba: Shady Salesman - Levitator", joba_shady_salesman_rule) JOBA_ARENA_BATTLE = LocationData(112, "Joba: Arena Battle - Gravity Boots", joba_arena_battle_rule) -JOBA_ARENA_CAGE_MATCH = LocationData(113, "Joba: Arena Cage Match - Infiltrator", joba_arena_cage_match_rule) +JOBA_ARENA_CAGE_MATCH = LocationData(113, "Joba: Arena Cage Match - Infiltrator", joba_arena_battle_rule) JOBA_HIDDEN_CLIFF_PB = LocationData(114, "Joba: Hidden Cliff - Platinum Bolt", joba_hidden_cliff_pb_rule) JOBA_LEVITATOR_TOWER_PB = LocationData(115, "Joba: Levitator Tower - Platinum Bolt", joba_levitator_tower_pb_rule) JOBA_HOVERBIKE_RACE_SHORTCUT_NT = LocationData(116, "Joba: Hoverbike Race Shortcut - Nanotech Boost", joba_hoverbike_race_rule) From 3ac67831855e58f3062bfd727f4f5f139d871b2e Mon Sep 17 00:00:00 2001 From: Pecomare Date: Sun, 25 May 2025 18:53:54 +0200 Subject: [PATCH 3/6] Remove Region requirements for Tabora and Aranos --- Regions.py | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/Regions.py b/Regions.py index 6e2dd1d..7a71811 100644 --- a/Regions.py +++ b/Regions.py @@ -24,23 +24,7 @@ def create_regions(world: 'Rac2World'): if planet_data.locations: def generate_planet_access_rule(planet: PlanetData) -> typing.Callable[[CollectionState], bool]: def planet_access_rule(state: CollectionState): - # Connect with special case access rules - if planet == Planets.TABORA: - return ( - state.has(Items.coord_for_planet(planet.number).name, world.player) - and can_heli(state, world.player) - and can_swingshot(state, world.player) - ) - if planet == Planets.ARANOS_PRISON: - return ( - state.has(Items.coord_for_planet(planet.number).name, world.player) - and state.has_all([ - Items.GRAVITY_BOOTS.name, Items.LEVITATOR.name, Items.INFILTRATOR.name], world.player - ) - ) - # Connect with general case access rule - else: - return state.has(Items.coord_for_planet(planet.number).name, world.player) + return state.has(Items.coord_for_planet(planet.number).name, world.player) return planet_access_rule region = Region(planet_data.name, world.player, world.multiworld) From bf38b07bf8da53558b48a44f2181398da537c332 Mon Sep 17 00:00:00 2001 From: Pecomare Date: Sun, 25 May 2025 18:59:56 +0200 Subject: [PATCH 4/6] Remove unused imports from Logic.py --- Regions.py | 1 - 1 file changed, 1 deletion(-) diff --git a/Regions.py b/Regions.py index 7a71811..bdd8dc7 100644 --- a/Regions.py +++ b/Regions.py @@ -1,7 +1,6 @@ import typing from BaseClasses import CollectionState, Region, Location -from .Logic import can_heli, can_swingshot from .data import Planets from .data import Items from .data.Planets import PlanetData From f9ed44e50ce9309c957864397b837cc765d58b39 Mon Sep 17 00:00:00 2001 From: Pecomare Date: Sat, 27 Dec 2025 21:39:45 +0100 Subject: [PATCH 5/6] Remove FPM from logic & Add other movement tech --- Logic.py | 588 +++++++++++++++++++++++----------------------- data/Locations.py | 4 +- 2 files changed, 295 insertions(+), 297 deletions(-) diff --git a/Logic.py b/Logic.py index 66e87b8..94ebe63 100644 --- a/Logic.py +++ b/Logic.py @@ -27,6 +27,10 @@ def can_heli(state: CollectionState, player: int) -> bool: return state.has(Items.HELI_PACK.name, player) +def can_thruster(state: CollectionState, player: int) -> bool: + return state.has(Items.THRUSTER_PACK.name, player) + + def can_grind(state: CollectionState, player: int) -> bool: return state.has(Items.GRIND_BOOTS.name, player) @@ -66,6 +70,15 @@ def can_spiderbot(state: CollectionState, player: int) -> bool: return state.has(Items.SPIDERBOT_GLOVE.name, player) +def can_clip(state: CollectionState, player: int) -> bool: + return state.has_any([Items.DECOY_GLOVE.name, + Items.MEGA_DECOY_GLOVE.name, + Items.MINITURRET_GLOVE.name, + Items.MEGATURRET_GLOVE.name, + Items.MEGA_MEGATURRET_GLOVE.name, + Items.ULTRA_MEGATURRET_GLOVE.name], player) + + def has_qwark_statuette(state: CollectionState, player: int) -> bool: return state.has(Items.QWARK_STATUETTE.name, player) @@ -90,6 +103,7 @@ def oozla_end_store_cutscene_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + # clip-out -> activate checkpoint -> death abuse return True return False @@ -101,7 +115,8 @@ def oozla_tractor_puzzle_pb_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # tight hyperstrike return True return False @@ -114,6 +129,7 @@ def oozla_swamp_ruins_pb_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + # same as end store cutscene return True return False @@ -131,8 +147,10 @@ def maktar_photo_booth_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return (can_heli(state, player) - or can_charge(state, player)) + # wrench jump or pack switch + return (can_charge(state, player) + or (can_heli(state, player) + and can_thruster(state, player))) return False @@ -144,6 +162,7 @@ def maktar_deactivate_jamming_array_rule(state: CollectionState, player: int) -> options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + # wrench jump return can_charge(state, player) return False @@ -156,6 +175,7 @@ def maktar_jamming_array_pb_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + # same as deactivate jamming array return can_charge(state, player) return False @@ -170,22 +190,23 @@ def endako_crane_pb_rule(state: CollectionState, player: int) -> bool: def endako_crane_nt_rule(state: CollectionState, player: int) -> bool: - return (can_electrolyze(state, player) - and can_infiltrate(state, player)) - - -def barlow_inventor_rule(state: CollectionState, player: int) -> bool: - if can_swingshot(state, player): + if (can_electrolyze(state, player) + and can_infiltrate(state, player)): return True options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True + # decoy or turret clip + return can_clip(state, player) return False +def barlow_inventor_rule(state: CollectionState, player: int) -> bool: + return can_swingshot(state, player) + + def barlow_overbike_race_rule(state: CollectionState, player: int) -> bool: if (can_improved_jump(state, player) and can_electrolyze(state, player)): @@ -194,6 +215,8 @@ def barlow_overbike_race_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + # tight hyperstrike + # not sure if tiptoes are doable all the way return can_electrolyze(state, player) return False @@ -210,7 +233,8 @@ def notak_top_pier_telescreen_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # wall jump in the thermanator room return True return False @@ -224,7 +248,9 @@ def notak_worker_bots_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True + # clip through the purple barrier and thermanate at the right time + return (can_clip(state, player) + and can_thermanate(state, player)) return False @@ -238,23 +264,33 @@ def notak_timed_dynamo_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True + # wall jump in the thermanator room and decoy or turret clip + return can_clip(state, player) + + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # wall jump in the thermanator room + return can_dynamo(state, player) return False def siberius_defeat_thief_rule(state: CollectionState, player: int) -> bool: + # TODO is wrench jump possible ? return can_swingshot(state, player) def siberius_flamebot_ledge_pb_rule(state: CollectionState, player: int) -> bool: - if can_tractor(state, player): + if (can_tractor(state, player) + and can_improved_jump(state, player)): return True options = get_options(state, player) - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # ledge grab with high-jump + # or sideflip hyperstrike with tractor beam + return (can_improved_jump(state, player) + or can_tractor(state, player)) return False @@ -266,14 +302,30 @@ def siberius_fenced_area_pb_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True + # wrench jump + # or clip + return (can_charge(state, player) + or can_clip(state, player)) return False -def tabora_meet_angelar_rule(state: CollectionState, player: int) -> bool: - return (can_heli(state, player) - and can_swingshot(state, player)) +def tabora_meet_angela_rule(state: CollectionState, player: int) -> bool: + if (can_heli(state, player) + and can_swingshot(state, player)): + return True + + options = get_options(state, player) + + if options.glitch_logic_difficulty >= GLITCH_LOGIC_EXPERT: + # conserve swing momentum -> slope interception -> tiptoes + return can_swingshot(state, player) + + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # hold charge boots + return can_charge(state, player) + + return False def tabora_underground_mines_end_rule(state: CollectionState, player: int) -> bool: @@ -284,36 +336,44 @@ def tabora_underground_mines_end_rule(state: CollectionState, player: int) -> bo options = get_options(state, player) - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return (can_heli(state, player) - and can_swingshot(state, player)) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_EXPERT: + # same as meet angela + thermanator + return (can_swingshot(state, player) + and can_thermanate(state, player)) + + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # same as meet angela + thermanator + return (can_charge(state, player) + and can_thermanate(state, player)) return False -def tabora_canyon_glide_pb_rule(state: CollectionState, player: int) -> bool: +def tabora_underground_mines_pb_rule(state: CollectionState, player: int) -> bool: if (can_heli(state, player) and can_swingshot(state, player) - and can_thermanate(state, player) - and can_glide(state, player)): + and can_thermanate(state, player)): return True options = get_options(state, player) - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return (can_heli(state, player) - and can_swingshot(state, player) - and can_glide(state, player)) - - return False + if options.glitch_logic_difficulty >= GLITCH_LOGIC_EXPERT: + # same as meet angela + thermanator + return (can_swingshot(state, player) + and can_thermanate(state, player)) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # long jump above water and high-jump with heli to ledge grab + # or thermanator and sideflip hyperstrike + return ((can_heli(state, player) + and can_swingshot(state, player)) + or (can_charge(state, player) + and can_thermanate(state, player))) -def tabora_northeast_desert_pb_rule(state: CollectionState, player: int) -> bool: - return (can_heli(state, player) - and can_swingshot(state, player)) + return False -def tabora_canyon_glide_pillar_nt_rule(state: CollectionState, player: int) -> bool: +def tabora_canyon_glide_pb_rule(state: CollectionState, player: int) -> bool: if (can_heli(state, player) and can_swingshot(state, player) and can_thermanate(state, player) @@ -322,13 +382,29 @@ def tabora_canyon_glide_pillar_nt_rule(state: CollectionState, player: int) -> b options = get_options(state, player) - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return (can_heli(state, player) - and can_swingshot(state, player)) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_EXPERT: + # same as meet angela + thermanator + glider + return (can_swingshot(state, player) + and can_thermanate(state, player) + and can_glide(state, player)) + + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # same as meet angela + thermanator + glider + return (can_charge(state, player) + and can_thermanate(state, player) + and can_glide(state, player)) return False +def tabora_northeast_desert_pb_rule(state: CollectionState, player: int) -> bool: + return tabora_meet_angela_rule(state, player) + + +def tabora_canyon_glide_pillar_nt_rule(state: CollectionState, player: int) -> bool: + return tabora_canyon_glide_pb_rule(state, player) + + def dobbo_defeat_thug_leader_rule(state: CollectionState, player: int) -> bool: if (can_swingshot(state, player) and can_improved_jump(state, player) @@ -338,24 +414,18 @@ def dobbo_defeat_thug_leader_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return can_swingshot(state, player) + # wrench jump above dynamo platforms + return (can_swingshot(state, player) + and can_charge(state, player)) return False def dobbo_facility_terminal_rule(state: CollectionState, player: int) -> bool: - if (can_swingshot(state, player) + return (can_swingshot(state, player) and can_glide(state, player) and can_dynamo(state, player) - and can_electrolyze(state, player)): - return True - - options = get_options(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True - - return False + and can_electrolyze(state, player)) def dobbo_spiderbot_room_pb_rule(state: CollectionState, player: int) -> bool: @@ -367,37 +437,26 @@ def dobbo_spiderbot_room_pb_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return can_swingshot(state, player) + # wrench jump above dynamo platforms and turret or decoy clip + return (can_swingshot(state, player) + and (can_dynamo(state, player) + or can_charge(state, player)) + and (can_spiderbot(state, player) + or can_clip(state, player))) return False def dobbo_facility_glide_pb_rule(state: CollectionState, player: int) -> bool: - if (can_swingshot(state, player) + return (can_swingshot(state, player) and can_glide(state, player) - and can_dynamo(state, player)): - return True - - options = get_options(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True - - return False + and can_dynamo(state, player)) def dobbo_facility_glide_nt_rule(state: CollectionState, player: int) -> bool: - if (can_swingshot(state, player) + return (can_swingshot(state, player) and can_glide(state, player) - and can_dynamo(state, player)): - return True - - options = get_options(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True - - return False + and can_dynamo(state, player)) def joba_hoverbike_race_rule(state: CollectionState, player: int) -> bool: @@ -407,6 +466,7 @@ def joba_hoverbike_race_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + # wrench jump return can_charge(state, player) return False @@ -419,8 +479,9 @@ def joba_shady_salesman_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # tight double jumps in dynamo platform section + return can_dynamo(state, player) return False @@ -433,8 +494,10 @@ def joba_arena_battle_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return can_levitate(state, player) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # same as shady salesman + levitator + return (can_dynamo(state, player) + and can_levitate(state, player)) return False @@ -447,7 +510,9 @@ def joba_hidden_cliff_pb_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True + # wrench jump + return (can_dynamo(state, player) + and can_charge(state, player)) return False @@ -460,22 +525,16 @@ def joba_levitator_tower_pb_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return can_levitate(state, player) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # same as shady salesman + levitator + return (can_dynamo(state, player) + and can_levitate(state, player)) return False def joba_timed_dynamo_nt_rule(state: CollectionState, player: int) -> bool: - if can_dynamo(state, player): - return True - - options = get_options(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True - - return False + return can_dynamo(state, player) def todano_search_rocket_silo_rule(state: CollectionState, player: int) -> bool: @@ -487,59 +546,32 @@ def todano_search_rocket_silo_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True + # clip through electrolyzer barrier and tight hyperstrike + return (can_electrolyze(state, player) + and can_clip(state, player)) + + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # tight hyperstrike + return (can_electrolyze(state, player) + and can_infiltrate(state, player)) return False def todano_stuart_zurgo_trade_rule(state: CollectionState, player: int) -> bool: - if (can_electrolyze(state, player) + return (can_electrolyze(state, player) and can_tractor(state, player) - and has_qwark_statuette(state, player)): - return True - - options = get_options(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_EXPERT: - return has_qwark_statuette(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return (can_tractor(state, player) - and has_qwark_statuette(state, player)) - - return False + and has_qwark_statuette(state, player)) def todano_facility_interior_rule(state: CollectionState, player: int) -> bool: - if (can_electrolyze(state, player) - and can_tractor(state, player)): - return True - - options = get_options(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_EXPERT: - return True - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return can_tractor(state, player) - - return False + return (can_electrolyze(state, player) + and can_tractor(state, player)) def todano_near_stuart_zurgo_pb_rule(state: CollectionState, player: int) -> bool: - if (can_electrolyze(state, player) - and can_tractor(state, player)): - return True - - options = get_options(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_EXPERT: - return True - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return can_tractor(state, player) - - return False + return (can_electrolyze(state, player) + and can_tractor(state, player)) def todano_spiderbot_conveyor_pb_rule(state: CollectionState, player: int) -> bool: @@ -552,109 +584,53 @@ def todano_spiderbot_conveyor_pb_rule(state: CollectionState, player: int) -> bo options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return (can_tractor(state, player) + # sideflip hyperstrike + return (can_electrolyze(state, player) + and can_tractor(state, player) and can_spiderbot(state, player)) return False def todano_rocket_silo_nt_rule(state: CollectionState, player: int) -> bool: - if (can_electrolyze(state, player) - and can_infiltrate(state, player)): - return True - - options = get_options(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True - - return False + return (can_electrolyze(state, player) + and can_infiltrate(state, player)) def boldan_find_fizzwidget_rule(state: CollectionState, player: int) -> bool: - if (can_levitate(state, player) + # TODO can we sideflip hyperstrike to skip the levitator ? + return (can_levitate(state, player) and can_swingshot(state, player) - and can_gravity(state, player)): - return True - - options = get_options(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_EXPERT: - return True - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return can_gravity(state, player) - - return False + and can_gravity(state, player)) def boldan_spiderbot_alley_pb_rule(state: CollectionState, player: int) -> bool: - if (can_levitate(state, player) - and can_spiderbot(state, player)): - return True - - options = get_options(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True - - return False + # TODO can we sideflip hyperstrike to skip the levitator ? can we clip ? + return (can_levitate(state, player) + and can_spiderbot(state, player)) def boldan_floating_platform_rule(state: CollectionState, player: int) -> bool: - if (can_levitate(state, player) - and can_gravity(state, player)): - return True - - options = get_options(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True - - return False + # TODO can we sideflip hyperstrike to skip the levitator ? + return (can_levitate(state, player) + and can_gravity(state, player)) def boldan_fountain_nt_rule(state: CollectionState, player: int) -> bool: - if can_levitate(state, player): - return True - - options = get_options(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True - - return False + # TODO can we sideflip hyperstrike to skip the levitator ? + return can_levitate(state, player) def aranos_control_room_rule(state: CollectionState, player: int) -> bool: - if (can_gravity(state, player) + # TODO can we clip through infiltrator ? + return (can_gravity(state, player) and can_infiltrate(state, player) - and can_levitate(state, player)): - return True - - options = get_options(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_EXPERT: - return True - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return (can_infiltrate(state, player) - and can_levitate(state, player)) - - return False + and can_levitate(state, player)) def aranos_plumber_rule(state: CollectionState, player: int) -> bool: - if (can_gravity(state, player) - and can_levitate(state, player)): - return True - - options = get_options(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True - - return False + return (can_gravity(state, player) + and can_levitate(state, player)) def aranos_under_ship_pb_rule(state: CollectionState, player: int) -> bool: @@ -664,40 +640,28 @@ def aranos_under_ship_pb_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return can_heli(state, player) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # charge and hold at the right angle + return (can_gravity(state, player) + and can_charge(state, player)) return False def aranos_omniwrench_12000_rule(state: CollectionState, player: int) -> bool: - if can_gravity(state, player): - return True - - options = get_options(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True - - return False + return can_gravity(state, player) def snivelak_rescue_angelak_rule(state: CollectionState, player: int) -> bool: - if (can_swingshot(state, player) + # TODO are grind boots skippable ? + return (can_swingshot(state, player) and can_grind(state, player) and can_gravity(state, player) - and can_dynamo(state, player)): - return True - - options = get_options(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return can_swingshot(state, player) - - return False + and can_dynamo(state, player)) def snivelak_dynamo_pb_rule(state: CollectionState, player: int) -> bool: + # TODO are grind boots skippable ? if (can_swingshot(state, player) and can_grind(state, player) and can_gravity(state, player) @@ -707,13 +671,19 @@ def snivelak_dynamo_pb_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return can_swingshot(state, player) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # charge over lava pit + return (can_swingshot(state, player) + and can_grind(state, player) + and can_gravity(state, player) + and can_dynamo(state, player) + and can_charge(state, player)) return False def snivelak_swingshot_tower_nt_rule(state: CollectionState, player: int) -> bool: + # TODO momentum conservation ? if (can_swingshot(state, player) and can_heli(state, player)): return True @@ -734,8 +704,10 @@ def smolg_balloon_transmission_rule(state: CollectionState, player: int) -> bool options = get_options(state, player) - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return can_electrolyze(state, player) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # double jumps + return (can_dynamo(state, player) + and can_electrolyze(state, player)) return False @@ -751,7 +723,17 @@ def smolg_distribution_facility_end_rule(state: CollectionState, player: int) -> options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return can_electrolyze(state, player) + # double jumps and walk on grind section + return (can_dynamo(state, player) + and can_electrolyze(state, player) + and can_infiltrate(state, player)) + + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # double jumps + return (can_dynamo(state, player) + and can_electrolyze(state, player) + and can_grind(state, player) + and can_infiltrate(state, player)) return False @@ -764,43 +746,27 @@ def smolg_mutant_crab_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - if not can_levitate(state, player): - return False - - return (can_swingshot(state, player) - or can_charge(state, player) - or can_electrolyze(state, player)) + # wrench jump + return (can_levitate(state, player) + and can_charge(state, player)) return False def smolg_floating_platform_pb_rule(state: CollectionState, player: int) -> bool: - if (can_swingshot(state, player) - and can_levitate(state, player)): - return True - - options = get_options(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - if not can_levitate(state, player): - return False - - return (can_swingshot(state, player) - or can_charge(state, player) - or can_electrolyze(state, player)) - - return False + return smolg_mutant_crab_rule(state, player) def smolg_warehouse_pb_rule(state: CollectionState, player: int) -> bool: if (can_dynamo(state, player) - or can_improved_jump(state, player)): + and can_improved_jump(state, player)): return True options = get_options(state, player) - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # wall jump and tight double jump + return can_dynamo(state, player) return False @@ -815,6 +781,8 @@ def damosel_hypnotist_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: + # charge over pit and wrench jump + # or sideflip hyperstrike on thermanated water if not has_hypnomatic_parts(state, player): return False @@ -839,11 +807,17 @@ def damosel_frozen_mountain_pb_rule(state: CollectionState, player: int) -> bool options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - if not can_grind(state, player): - return False + # charge over the pit and double jumps + return ((can_charge(state, player) + or can_swingshot(state, player)) + and can_thermanate(state, player) + and can_grind(state, player)) - return (can_charge(state, player) - or can_thermanate(state, player)) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # double jumps + return (can_swingshot(state, player) + and can_thermanate(state, player) + and can_grind(state, player)) return False @@ -860,32 +834,31 @@ def damosel_pyramid_pb_rule(state: CollectionState, player: int) -> bool: if not can_hypnotize(state, player): return False + # charge over the pit and double jumps return (can_charge(state, player) or can_swingshot(state, player)) + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # double jumps + return (can_swingshot(state, player) + and can_hypnotize(state, player)) + return False def grelbin_find_angela_rule(state: CollectionState, player: int) -> bool: - if can_hypnotize(state, player): - return True - - options = get_options(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True - - return False + # TODO sideflip hyperstrike ? + return can_hypnotize(state, player) def grelbin_mystic_more_moonstones_rule(state: CollectionState, player: int) -> bool: + # TODO clip through infiltrator gate ? return (can_glide(state, player) and can_infiltrate(state, player)) def grelbin_ice_plains_pb_rule(state: CollectionState, player: int) -> bool: - return (can_glide(state, player) - and can_infiltrate(state, player)) + return grelbin_mystic_more_moonstones_rule(state, player) def grelbin_underwater_tunnel_pb_rule(state: CollectionState, player: int) -> bool: @@ -893,12 +866,18 @@ def grelbin_underwater_tunnel_pb_rule(state: CollectionState, player: int) -> bo def grelbin_yeti_cave_pb_rule(state: CollectionState, player: int) -> bool: + # TODO clip through infiltrator gate ? return (can_glide(state, player) and can_infiltrate(state, player) and can_hypnotize(state, player)) +def yeedil_bridge_grindrail_pb_rule(state: CollectionState, player: int) -> bool: + return can_grind(state, player) + + def yeedil_defeat_mutated_protopet_rule(state: CollectionState, player: int) -> bool: + # TODO can we clip through infiltrator ? if (can_hypnotize(state, player) and can_swingshot(state, player) and can_infiltrate(state, player) @@ -910,32 +889,33 @@ def yeedil_defeat_mutated_protopet_rule(state: CollectionState, player: int) -> options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_EXPERT: - return can_infiltrate(state, player) - - if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - if not can_infiltrate(state, player): - return False - + # charge cancels + wrench jumps + clip + hyperstrike return (can_charge(state, player) - or (can_hypnotize(state, player) - and can_swingshot(state, player))) - - return False - - -def yeedil_bridge_grindrail_pb_rule(state: CollectionState, player: int) -> bool: - if can_grind(state, player): - return True - - options = get_options(state, player) + and state.has(Items.HOVERBOMB_GUN.name, player) + and can_clip(state, player) + and can_electrolyze(state, player)) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - return True + # clip + hyperstrike + return (can_hypnotize(state, player) + and can_swingshot(state, player) + and can_clip(state, player) + and can_dynamo(state, player) + and can_electrolyze(state, player)) + + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # hyperstrike + return (can_hypnotize(state, player) + and can_swingshot(state, player) + and can_infiltrate(state, player) + and can_dynamo(state, player) + and can_electrolyze(state, player)) return False def yeedil_tractor_pillar_pb_rule(state: CollectionState, player: int) -> bool: + # TODO can we clip through infiltrator and tractor ? if (can_hypnotize(state, player) and can_swingshot(state, player) and can_infiltrate(state, player) @@ -949,14 +929,32 @@ def yeedil_tractor_pillar_pb_rule(state: CollectionState, player: int) -> bool: options = get_options(state, player) if options.glitch_logic_difficulty >= GLITCH_LOGIC_EXPERT: - return can_infiltrate(state, player) + # charge cancels + wrench jumps + clip + hyperstrike + return (can_charge(state, player) + and state.has(Items.HOVERBOMB_GUN.name, player) + and can_clip(state, player) + and can_electrolyze(state, player) + and can_tractor(state, player) + and can_grind(state, player)) if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: - if not can_infiltrate(state, player): - return False - - return (can_charge(state, player) - or (can_hypnotize(state, player) - and can_swingshot(state, player))) + # clip + hyperstrike + return (can_hypnotize(state, player) + and can_swingshot(state, player) + and can_clip(state, player) + and can_dynamo(state, player) + and can_electrolyze(state, player) + and can_tractor(state, player) + and can_grind(state, player)) + + if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: + # hyperstrike + return (can_hypnotize(state, player) + and can_swingshot(state, player) + and can_infiltrate(state, player) + and can_dynamo(state, player) + and can_electrolyze(state, player) + and can_tractor(state, player) + and can_grind(state, player)) return False \ No newline at end of file diff --git a/data/Locations.py b/data/Locations.py index 0296027..e551248 100644 --- a/data/Locations.py +++ b/data/Locations.py @@ -78,9 +78,9 @@ class LocationData(NamedTuple): SIBERIUS_FENCED_AREA_PB = LocationData(72, "Siberius: Fenced Area - Platinum Bolt", siberius_fenced_area_pb_rule) """ Tabora """ -TABORA_MEET_ANGELA = LocationData(80, "Tabora: Meet Angela", tabora_meet_angelar_rule) +TABORA_MEET_ANGELA = LocationData(80, "Tabora: Meet Angela", tabora_meet_angela_rule) TABORA_UNDERGROUND_MINES_END = LocationData(81, "Tabora: Underground Mines - Glider", tabora_underground_mines_end_rule) -TABORA_UNDERGROUND_MINES_PB = LocationData(82, "Tabora: Underground Mines - Platinum Bolt", tabora_underground_mines_end_rule) +TABORA_UNDERGROUND_MINES_PB = LocationData(82, "Tabora: Underground Mines - Platinum Bolt", tabora_underground_mines_pb_rule) TABORA_CANYON_GLIDE_PB = LocationData(83, "Tabora: Canyon Glide - Platinum Bolt", tabora_canyon_glide_pb_rule) TABORA_NORTHEAST_DESERT_PB = LocationData(84, "Tabora: Northeast Desert - Platinum Bolt", tabora_northeast_desert_pb_rule) TABORA_CANYON_GLIDE_PILLAR_NT = LocationData(85, "Tabora: Canyon Glide Pillar - Nanotech Boost", tabora_canyon_glide_pillar_nt_rule) From 226d7f471d00b343555b2ba4ceddff2c91edb0a8 Mon Sep 17 00:00:00 2001 From: Pecomare Date: Sat, 27 Dec 2025 21:54:02 +0100 Subject: [PATCH 6/6] Put back thermanator in Notak logic --- Logic.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Logic.py b/Logic.py index 94ebe63..237b439 100644 --- a/Logic.py +++ b/Logic.py @@ -235,7 +235,7 @@ def notak_top_pier_telescreen_rule(state: CollectionState, player: int) -> bool: if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: # wall jump in the thermanator room - return True + return can_thermanate(state, player) return False @@ -265,11 +265,13 @@ def notak_timed_dynamo_rule(state: CollectionState, player: int) -> bool: if options.glitch_logic_difficulty >= GLITCH_LOGIC_HARD: # wall jump in the thermanator room and decoy or turret clip - return can_clip(state, player) + return (can_thermanate(state, player) + and can_clip(state, player)) if options.glitch_logic_difficulty >= GLITCH_LOGIC_MEDIUM: # wall jump in the thermanator room - return can_dynamo(state, player) + return (can_thermanate(state, player) + and can_dynamo(state, player)) return False