mirror of
https://github.com/MarioSpore/Grinch-AP.git
synced 2025-10-21 20:21:32 -06:00
TUNIC: Logic Rules Redux (#3544)
* Clean these functions up, get the hell out of here 5 parameter function * Clean up a bunch of rules that no longer need to be multi-lined since the functions are shorter * Clean up some range functions * Update to use world instead of player like Vi recommended * Fix merge conflict * Create new options * Slightly revise ls rule * Update options.py * Update options.py * Add tedious option for ls * Update laurels zips description * Create new options * Slightly revise ls rule * Update options.py * Update options.py * Add tedious option for ls * Update laurels zips description * Creating structures to redo ladder storage rules * Put together overworld ladder groups, remove tedious * Write up the rules for the regular rules * Update slot data and UT stuff * Put new ice grapple stuff in er rules * Ice grapple hard to get to fountain cross room * More ladder data * Wrote majority of overworld ladder rules * Finish the ladder storage rules * Update notes * Add note * Add well rail to the rules * More rules * Comment out logically irrelevant entrances * Update with laurels_zip helper * Add parameter to has_ice_grapple_logic for difficulty * Add new parameter to has_ice_grapple_logic * Move ice grapple chest to lower forest in ER/ladders * Fix rule * Finishing out hooking the new rules into the code * Fix bugs * Add more hard ice grapples * Fix more bugs * Shops my beloved * Change victory condition back * Remove debug stuff * Update plando connections description * Fix extremely rare bug * Add well front -> back hard ladder storages * Note in ls rules about knocking yourself down with bombs being out of logic * Add atoll fuse with wand + hard ls * Add some nonsense that boils down to activating the fuse in overworld * Further update LS description * Fix missing logic on bridge switch chest in upper zig * Revise upper zig rule change to account for ER * Fix merge conflict * Fix formatting, fix rule for heir access after merge * Add the shop sword logic stuff in * Remove todo that was already done * Fill out a to-do with some cursed nonsense * Fix event in wrong region * Fix missing cathedral -> elevator connection * Fix missing cathedral -> elevator connection * Add ER exception to cathedral -> elevator * Fix secret gathering place issue * Fix incorrect ls rule * Move 3 locations to Quarry Back since they're easily accessible from the back * Also update non-er region * Remove redundant parentheses * Add new test for a weird edge case in ER * Slight option description updates * Use has_ladder in spots where it wasn't used for some reason, add a comment * Fix unit test for ER * Update per exempt's suggestion * Add back LogicRules as an invisible option, to not break old yamls * Remove unused elevation from portal class * Update ladder storage without items description * Remove shop_scene stuff since it's no longer relevant in the mod by the time this version comes out * Remove shop scene stuff from game info since it's no longer relevant in the mod by the time this comes out * Update portal list to match main * god I love github merging things * Remove note * Add ice grapple hard path from upper overworld to temple rafters entrance * Actually that should be medium * Remove outdated note * Add ice grapple hard for swamp mid to the ledge * Add missing laurels zip in swamp * Some fixes to the ladder storage data while reviewing it * Add unit test for weird edge case * Backport outlet region system to fix ls bug * Fix incorrect ls, add todo * Add missing swamp ladder storage connections * Add swamp zip to er data * Add swamp zip to er rules * Add hard ice grapple for forest grave path main to upper * Add ice grapple logic for all bomb walls except the east quarry one * Add ice grapple logic for frog stairs eye to mouth without the ladder * Add hard ice grapple for overworld to the stairs to west garden * Add the ice grapple boss quick kills to medium ice grappling * Add the reverse connection for the ice grapple kill on Garden Knight * Add atoll house ice grapple push, and add west garden ice grapple entry to the regular rules
This commit is contained in:
@@ -3,7 +3,7 @@ from typing import Dict, TYPE_CHECKING
|
||||
|
||||
from worlds.generic.Rules import set_rule, forbid_item, add_rule
|
||||
from BaseClasses import CollectionState
|
||||
from .options import TunicOptions
|
||||
from .options import TunicOptions, LadderStorage, IceGrappling
|
||||
if TYPE_CHECKING:
|
||||
from . import TunicWorld
|
||||
|
||||
@@ -27,10 +27,10 @@ green_hexagon = "Green Questagon"
|
||||
blue_hexagon = "Blue Questagon"
|
||||
gold_hexagon = "Gold Questagon"
|
||||
|
||||
# "Quarry - [East] Bombable Wall" is excluded from this list since it has slightly different rules
|
||||
bomb_walls = ["East Forest - Bombable Wall", "Eastern Vault Fortress - [East Wing] Bombable Wall",
|
||||
"Overworld - [Central] Bombable Wall", "Overworld - [Southwest] Bombable Wall Near Fountain",
|
||||
"Quarry - [West] Upper Area Bombable Wall", "Quarry - [East] Bombable Wall",
|
||||
"Ruined Atoll - [Northwest] Bombable Wall"]
|
||||
"Quarry - [West] Upper Area Bombable Wall", "Ruined Atoll - [Northwest] Bombable Wall"]
|
||||
|
||||
|
||||
def randomize_ability_unlocks(random: Random, options: TunicOptions) -> Dict[str, int]:
|
||||
@@ -64,32 +64,33 @@ def has_sword(state: CollectionState, player: int) -> bool:
|
||||
return state.has("Sword", player) or state.has("Sword Upgrade", player, 2)
|
||||
|
||||
|
||||
def has_ice_grapple_logic(long_range: bool, state: CollectionState, world: "TunicWorld") -> bool:
|
||||
player = world.player
|
||||
if not world.options.logic_rules:
|
||||
def laurels_zip(state: CollectionState, world: "TunicWorld") -> bool:
|
||||
return world.options.laurels_zips and state.has(laurels, world.player)
|
||||
|
||||
|
||||
def has_ice_grapple_logic(long_range: bool, difficulty: IceGrappling, state: CollectionState, world: "TunicWorld") -> bool:
|
||||
if world.options.ice_grappling < difficulty:
|
||||
return False
|
||||
if not long_range:
|
||||
return state.has_all({ice_dagger, grapple}, player)
|
||||
return state.has_all({ice_dagger, grapple}, world.player)
|
||||
else:
|
||||
return state.has_all({ice_dagger, fire_wand, grapple}, player) and has_ability(icebolt, state, world)
|
||||
return state.has_all({ice_dagger, fire_wand, grapple}, world.player) and has_ability(icebolt, state, world)
|
||||
|
||||
|
||||
def can_ladder_storage(state: CollectionState, world: "TunicWorld") -> bool:
|
||||
return world.options.logic_rules == "unrestricted" and has_stick(state, world.player)
|
||||
if not world.options.ladder_storage:
|
||||
return False
|
||||
if world.options.ladder_storage_without_items:
|
||||
return True
|
||||
return has_stick(state, world.player) or state.has(grapple, world.player)
|
||||
|
||||
|
||||
def has_mask(state: CollectionState, world: "TunicWorld") -> bool:
|
||||
if world.options.maskless:
|
||||
return True
|
||||
else:
|
||||
return state.has(mask, world.player)
|
||||
return world.options.maskless or state.has(mask, world.player)
|
||||
|
||||
|
||||
def has_lantern(state: CollectionState, world: "TunicWorld") -> bool:
|
||||
if world.options.lanternless:
|
||||
return True
|
||||
else:
|
||||
return state.has(lantern, world.player)
|
||||
return world.options.lanternless or state.has(lantern, world.player)
|
||||
|
||||
|
||||
def set_region_rules(world: "TunicWorld") -> None:
|
||||
@@ -102,12 +103,14 @@ def set_region_rules(world: "TunicWorld") -> None:
|
||||
lambda state: has_stick(state, player) or state.has(fire_wand, player)
|
||||
world.get_entrance("Overworld -> Dark Tomb").access_rule = \
|
||||
lambda state: has_lantern(state, world)
|
||||
# laurels in, ladder storage in through the furnace, or ice grapple down the belltower
|
||||
world.get_entrance("Overworld -> West Garden").access_rule = \
|
||||
lambda state: state.has(laurels, player) \
|
||||
or can_ladder_storage(state, world)
|
||||
lambda state: (state.has(laurels, player)
|
||||
or can_ladder_storage(state, world)
|
||||
or has_ice_grapple_logic(False, IceGrappling.option_hard, state, world))
|
||||
world.get_entrance("Overworld -> Eastern Vault Fortress").access_rule = \
|
||||
lambda state: state.has(laurels, player) \
|
||||
or has_ice_grapple_logic(True, state, world) \
|
||||
or has_ice_grapple_logic(True, IceGrappling.option_easy, state, world) \
|
||||
or can_ladder_storage(state, world)
|
||||
# using laurels or ls to get in is covered by the -> Eastern Vault Fortress rules
|
||||
world.get_entrance("Overworld -> Beneath the Vault").access_rule = \
|
||||
@@ -124,8 +127,8 @@ def set_region_rules(world: "TunicWorld") -> None:
|
||||
world.get_entrance("Lower Quarry -> Rooted Ziggurat").access_rule = \
|
||||
lambda state: state.has(grapple, player) and has_ability(prayer, state, world)
|
||||
world.get_entrance("Swamp -> Cathedral").access_rule = \
|
||||
lambda state: state.has(laurels, player) and has_ability(prayer, state, world) \
|
||||
or has_ice_grapple_logic(False, state, world)
|
||||
lambda state: (state.has(laurels, player) and has_ability(prayer, state, world)) \
|
||||
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)
|
||||
world.get_entrance("Overworld -> Spirit Arena").access_rule = \
|
||||
lambda state: ((state.has(gold_hexagon, player, options.hexagon_goal.value) if options.hexagon_quest.value
|
||||
else state.has_all({red_hexagon, green_hexagon, blue_hexagon}, player)
|
||||
@@ -133,10 +136,18 @@ def set_region_rules(world: "TunicWorld") -> None:
|
||||
and has_ability(prayer, state, world) and has_sword(state, player)
|
||||
and state.has_any({lantern, laurels}, player))
|
||||
|
||||
world.get_region("Quarry").connect(world.get_region("Rooted Ziggurat"),
|
||||
rule=lambda state: has_ice_grapple_logic(True, IceGrappling.option_hard, state, world)
|
||||
and has_ability(prayer, state, world))
|
||||
|
||||
if options.ladder_storage >= LadderStorage.option_medium:
|
||||
# ls at any ladder in a safe spot in quarry to get to the monastery rope entrance
|
||||
world.get_region("Quarry Back").connect(world.get_region("Monastery"),
|
||||
rule=lambda state: can_ladder_storage(state, world))
|
||||
|
||||
|
||||
def set_location_rules(world: "TunicWorld") -> None:
|
||||
player = world.player
|
||||
options = world.options
|
||||
|
||||
forbid_item(world.get_location("Secret Gathering Place - 20 Fairy Reward"), fairies, player)
|
||||
|
||||
@@ -147,11 +158,13 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||
lambda state: has_ability(prayer, state, world)
|
||||
or state.has(laurels, player)
|
||||
or can_ladder_storage(state, world)
|
||||
or (has_ice_grapple_logic(True, state, world) and has_lantern(state, world)))
|
||||
or (has_ice_grapple_logic(True, IceGrappling.option_easy, state, world)
|
||||
and has_lantern(state, world)))
|
||||
set_rule(world.get_location("Fortress Courtyard - Page Near Cave"),
|
||||
lambda state: has_ability(prayer, state, world) or state.has(laurels, player)
|
||||
or can_ladder_storage(state, world)
|
||||
or (has_ice_grapple_logic(True, state, world) and has_lantern(state, world)))
|
||||
or (has_ice_grapple_logic(True, IceGrappling.option_easy, state, world)
|
||||
and has_lantern(state, world)))
|
||||
set_rule(world.get_location("East Forest - Dancing Fox Spirit Holy Cross"),
|
||||
lambda state: has_ability(holy_cross, state, world))
|
||||
set_rule(world.get_location("Forest Grave Path - Holy Cross Code by Grave"),
|
||||
@@ -186,17 +199,17 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||
lambda state: state.has(laurels, player))
|
||||
set_rule(world.get_location("Old House - Normal Chest"),
|
||||
lambda state: state.has(house_key, player)
|
||||
or has_ice_grapple_logic(False, state, world)
|
||||
or (state.has(laurels, player) and options.logic_rules))
|
||||
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)
|
||||
or laurels_zip(state, world))
|
||||
set_rule(world.get_location("Old House - Holy Cross Chest"),
|
||||
lambda state: has_ability(holy_cross, state, world) and (
|
||||
state.has(house_key, player)
|
||||
or has_ice_grapple_logic(False, state, world)
|
||||
or (state.has(laurels, player) and options.logic_rules)))
|
||||
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)
|
||||
or laurels_zip(state, world)))
|
||||
set_rule(world.get_location("Old House - Shield Pickup"),
|
||||
lambda state: state.has(house_key, player)
|
||||
or has_ice_grapple_logic(False, state, world)
|
||||
or (state.has(laurels, player) and options.logic_rules))
|
||||
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)
|
||||
or laurels_zip(state, world))
|
||||
set_rule(world.get_location("Overworld - [Northwest] Page on Pillar by Dark Tomb"),
|
||||
lambda state: state.has(laurels, player))
|
||||
set_rule(world.get_location("Overworld - [Southwest] From West Garden"),
|
||||
@@ -206,7 +219,7 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||
or (has_lantern(state, world) and has_sword(state, player))
|
||||
or can_ladder_storage(state, world))
|
||||
set_rule(world.get_location("Overworld - [Northwest] Chest Beneath Quarry Gate"),
|
||||
lambda state: state.has_any({grapple, laurels}, player) or options.logic_rules)
|
||||
lambda state: state.has_any({grapple, laurels}, player))
|
||||
set_rule(world.get_location("Overworld - [East] Grapple Chest"),
|
||||
lambda state: state.has(grapple, player))
|
||||
set_rule(world.get_location("Special Shop - Secret Page Pickup"),
|
||||
@@ -215,11 +228,11 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||
lambda state: has_ability(holy_cross, state, world)
|
||||
and (state.has(laurels, player) or (has_lantern(state, world) and (has_sword(state, player)
|
||||
or state.has(fire_wand, player)))
|
||||
or has_ice_grapple_logic(False, state, world)))
|
||||
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)))
|
||||
set_rule(world.get_location("Sealed Temple - Page Pickup"),
|
||||
lambda state: state.has(laurels, player)
|
||||
or (has_lantern(state, world) and (has_sword(state, player) or state.has(fire_wand, player)))
|
||||
or has_ice_grapple_logic(False, state, world))
|
||||
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world))
|
||||
set_rule(world.get_location("West Furnace - Lantern Pickup"),
|
||||
lambda state: has_stick(state, player) or state.has_any({fire_wand, laurels}, player))
|
||||
|
||||
@@ -254,7 +267,7 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||
lambda state: state.has(laurels, player) and has_ability(holy_cross, state, world))
|
||||
set_rule(world.get_location("West Garden - [East Lowlands] Page Behind Ice Dagger House"),
|
||||
lambda state: (state.has(laurels, player) and has_ability(prayer, state, world))
|
||||
or has_ice_grapple_logic(True, state, world))
|
||||
or has_ice_grapple_logic(True, IceGrappling.option_easy, state, world))
|
||||
set_rule(world.get_location("West Garden - [Central Lowlands] Below Left Walkway"),
|
||||
lambda state: state.has(laurels, player))
|
||||
set_rule(world.get_location("West Garden - [Central Highlands] After Garden Knight"),
|
||||
@@ -265,12 +278,15 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||
# Ruined Atoll
|
||||
set_rule(world.get_location("Ruined Atoll - [West] Near Kevin Block"),
|
||||
lambda state: state.has(laurels, player))
|
||||
# ice grapple push a crab through the door
|
||||
set_rule(world.get_location("Ruined Atoll - [East] Locked Room Lower Chest"),
|
||||
lambda state: state.has(laurels, player) or state.has(key, player, 2))
|
||||
lambda state: state.has(laurels, player) or state.has(key, player, 2)
|
||||
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world))
|
||||
set_rule(world.get_location("Ruined Atoll - [East] Locked Room Upper Chest"),
|
||||
lambda state: state.has(laurels, player) or state.has(key, player, 2))
|
||||
lambda state: state.has(laurels, player) or state.has(key, player, 2)
|
||||
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world))
|
||||
set_rule(world.get_location("Librarian - Hexagon Green"),
|
||||
lambda state: has_sword(state, player) or options.logic_rules)
|
||||
lambda state: has_sword(state, player))
|
||||
|
||||
# Frog's Domain
|
||||
set_rule(world.get_location("Frog's Domain - Side Room Grapple Secret"),
|
||||
@@ -285,10 +301,12 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||
lambda state: state.has(laurels, player))
|
||||
set_rule(world.get_location("Fortress Arena - Siege Engine/Vault Key Pickup"),
|
||||
lambda state: has_sword(state, player)
|
||||
and (has_ability(prayer, state, world) or has_ice_grapple_logic(False, state, world)))
|
||||
and (has_ability(prayer, state, world)
|
||||
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)))
|
||||
set_rule(world.get_location("Fortress Arena - Hexagon Red"),
|
||||
lambda state: state.has(vault_key, player)
|
||||
and (has_ability(prayer, state, world) or has_ice_grapple_logic(False, state, world)))
|
||||
and (has_ability(prayer, state, world)
|
||||
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)))
|
||||
|
||||
# Beneath the Vault
|
||||
set_rule(world.get_location("Beneath the Fortress - Bridge"),
|
||||
@@ -301,14 +319,14 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||
lambda state: state.has(laurels, player))
|
||||
set_rule(world.get_location("Rooted Ziggurat Upper - Near Bridge Switch"),
|
||||
lambda state: has_sword(state, player) or state.has_all({fire_wand, laurels}, player))
|
||||
# nmg - kill boss scav with orb + firecracker, or similar
|
||||
set_rule(world.get_location("Rooted Ziggurat Lower - Hexagon Blue"),
|
||||
lambda state: has_sword(state, player) or (state.has(grapple, player) and options.logic_rules))
|
||||
lambda state: has_sword(state, player))
|
||||
|
||||
# Swamp
|
||||
set_rule(world.get_location("Cathedral Gauntlet - Gauntlet Reward"),
|
||||
lambda state: (state.has(fire_wand, player) and has_sword(state, player))
|
||||
and (state.has(laurels, player) or has_ice_grapple_logic(False, state, world)))
|
||||
and (state.has(laurels, player)
|
||||
or has_ice_grapple_logic(False, IceGrappling.option_medium, state, world)))
|
||||
set_rule(world.get_location("Swamp - [Entrance] Above Entryway"),
|
||||
lambda state: state.has(laurels, player))
|
||||
set_rule(world.get_location("Swamp - [South Graveyard] Upper Walkway Dash Chest"),
|
||||
@@ -335,8 +353,16 @@ def set_location_rules(world: "TunicWorld") -> None:
|
||||
# Bombable Walls
|
||||
for location_name in bomb_walls:
|
||||
# has_sword is there because you can buy bombs in the shop
|
||||
set_rule(world.get_location(location_name), lambda state: state.has(gun, player) or has_sword(state, player))
|
||||
set_rule(world.get_location(location_name),
|
||||
lambda state: state.has(gun, player)
|
||||
or has_sword(state, player)
|
||||
or has_ice_grapple_logic(False, IceGrappling.option_hard, state, world))
|
||||
add_rule(world.get_location("Cube Cave - Holy Cross Chest"),
|
||||
lambda state: state.has(gun, player)
|
||||
or has_sword(state, player)
|
||||
or has_ice_grapple_logic(False, IceGrappling.option_hard, state, world))
|
||||
# can't ice grapple to this one, not enough space
|
||||
set_rule(world.get_location("Quarry - [East] Bombable Wall"),
|
||||
lambda state: state.has(gun, player) or has_sword(state, player))
|
||||
|
||||
# Shop
|
||||
|
||||
Reference in New Issue
Block a user