Write up the rules for the regular rules

This commit is contained in:
Scipio Wright
2024-06-12 19:22:03 -04:00
parent f721926deb
commit 10825f8a08
6 changed files with 87 additions and 60 deletions

View File

@@ -1,4 +1,4 @@
from typing import Dict, NamedTuple, List
from typing import Dict, NamedTuple, List, Optional
from enum import IntEnum
@@ -7,6 +7,7 @@ class Portal(NamedTuple):
region: str # AP region
destination: str # vanilla destination scene
tag: str # vanilla tag
elevation: Optional[int] = None # elevation of the entrance, for ladder storage rule generation
def scene(self) -> str: # the actual scene name in Tunic
return tunic_er_regions[self.region].game_scene
@@ -1536,37 +1537,3 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
[],
},
}
# groups for ladders at the same elevation, for use in determing whether you can ls to entrances in diff rulesets
overworld_ladder_groups: Dict[str, List[str]] = {
# lowest elevation, in-line with swamp lower, rotating lights, atoll lower, west garden lower
"Group 1": ["Ladders in Overworld Town", "Ladder to Ruined Atoll", "Ladder to Swamp"],
# in-line with furnace from beach, swamp upper entrance
"Group 2": ["Ladders near Weathervane", "Ladders in Overworld Town", "Ladder to Swamp"],
# in-line with west garden upper, ruined passage
"Group 3": ["Ladders near Weathervane", "Ladders to West Bell"],
# in-line with old house door, chest above ruined passage
"Group 4": ["Ladders near Weathervane", "Ladder to Quarry", "Ladders to West Bell", "Ladders in Overworld Town"],
# skip top of top ladder next to weathervane level, does not provide logical access to anything
# in-line with quarry
"Group 5": ["Ladders near Dark Tomb", "Ladder to Quarry", "Ladders to West Bell", "Ladders in Overworld Town",
"Ladders in Well"],
# in-line with patrol cave, east forest, fortress, and stairs towards special shop
"Group 6": ["Ladders near Overworld Checkpoint", "Ladders near Patrol Cave"],
# skip top of belltower and middle of dark tomb ladders, does not grant access to anything
# in-line with temple rafters entrance, can get you to patrol cave ladders via knocking out of ls
"Group 7": ["Ladders near Patrol Cave", "Ladder near Temple Rafters"],
# in-line with the chest above dark tomb, gets you up the mountain stairs
"Group 8": ["Ladders near Patrol Cave", "Ladder near Temple Rafters", "Ladders near Dark Tomb"],
}
# ladders accessible within different regions, only those that are relevant
region_ladders: Dict[str, List[str]] = {
"Overworld": ["Ladders near Weathervane", "Ladders near Overworld Checkpoint", "Ladders near Dark Tomb",
"Ladders in Overworld Town", "Ladder to Swamp", "Ladders in Well"],
"Overworld Beach": ["Ladder to Ruined Atoll"],
"Overworld at Patrol Cave": ["Ladders near Patrol Cave"],
"Overworld Quarry Entry": ["Ladder to Quarry"],
"Overworld Belltower": ["Ladders to West Bell"],
}

View File

@@ -0,0 +1,36 @@
from typing import Dict, List
# groups for ladders at the same elevation, for use in determing whether you can ls to entrances in diff rulesets
overworld_ladder_groups: Dict[str, List[str]] = {
# lowest elevation, in-line with swamp lower, rotating lights, atoll lower, west garden lower
"Group 1": ["Ladders in Overworld Town", "Ladder to Ruined Atoll", "Ladder to Swamp"],
# in-line with furnace from beach, swamp upper entrance
"Group 2": ["Ladders near Weathervane", "Ladders in Overworld Town", "Ladder to Swamp"],
# in-line with west garden upper, ruined passage
"Group 3": ["Ladders near Weathervane", "Ladders to West Bell"],
# in-line with old house door, chest above ruined passage
"Group 4": ["Ladders near Weathervane", "Ladder to Quarry", "Ladders to West Bell", "Ladders in Overworld Town"],
# skip top of top ladder next to weathervane level, does not provide logical access to anything
# in-line with quarry
"Group 5": ["Ladders near Dark Tomb", "Ladder to Quarry", "Ladders to West Bell", "Ladders in Overworld Town",
"Ladders in Well"],
# in-line with patrol cave, east forest, fortress, and stairs towards special shop
"Group 6": ["Ladders near Overworld Checkpoint", "Ladders near Patrol Cave"],
# skip top of belltower and middle of dark tomb ladders, does not grant access to anything
# in-line with temple rafters entrance, can get you to patrol cave ladders via knocking out of ls
"Group 7": ["Ladders near Patrol Cave", "Ladder near Temple Rafters"],
# in-line with the chest above dark tomb, gets you up the mountain stairs
"Group 8": ["Ladders near Patrol Cave", "Ladder near Temple Rafters", "Ladders near Dark Tomb"],
}
# ladders accessible within different regions of overworld, only those that are relevant
# other scenes will just have them hardcoded since this type of structure is not necessary there
region_ladders: Dict[str, List[str]] = {
"Overworld": ["Ladders near Weathervane", "Ladders near Overworld Checkpoint", "Ladders near Dark Tomb",
"Ladders in Overworld Town", "Ladder to Swamp", "Ladders in Well"],
"Overworld Beach": ["Ladder to Ruined Atoll"],
"Overworld at Patrol Cave": ["Ladders near Patrol Cave"],
"Overworld Quarry Entry": ["Ladder to Quarry"],
"Overworld Belltower": ["Ladders to West Bell"],
}

View File

@@ -205,7 +205,7 @@ location_table: Dict[str, TunicLocationData] = {
"Fountain Cross Door - Page Pickup": TunicLocationData("Overworld Holy Cross", "Fountain Cross Room", location_group="Holy Cross"),
"Secret Gathering Place - Holy Cross Chest": TunicLocationData("Overworld Holy Cross", "Secret Gathering Place", location_group="Holy Cross"),
"Top of the Mountain - Page At The Peak": TunicLocationData("Overworld Holy Cross", "Top of the Mountain", location_group="Holy Cross"),
"Monastery - Monastery Chest": TunicLocationData("Quarry", "Monastery Back"),
"Monastery - Monastery Chest": TunicLocationData("Monastery", "Monastery Back"),
"Quarry - [Back Entrance] Bushes Holy Cross": TunicLocationData("Quarry Back", "Quarry Back", location_group="Holy Cross"),
"Quarry - [Back Entrance] Chest": TunicLocationData("Quarry Back", "Quarry Back"),
"Quarry - [Central] Near Shortcut Ladder": TunicLocationData("Quarry", "Quarry"),
@@ -224,7 +224,7 @@ location_table: Dict[str, TunicLocationData] = {
"Quarry - [Central] Above Ladder Dash Chest": TunicLocationData("Quarry", "Quarry Monastery Entry"),
"Quarry - [West] Upper Area Bombable Wall": TunicLocationData("Quarry Back", "Quarry Back"),
"Quarry - [East] Bombable Wall": TunicLocationData("Quarry", "Quarry"),
"Hero's Grave - Ash Relic": TunicLocationData("Quarry", "Hero Relic - Quarry"),
"Hero's Grave - Ash Relic": TunicLocationData("Monastery", "Hero Relic - Quarry"),
"Quarry - [West] Shooting Range Secret Path": TunicLocationData("Lower Quarry", "Lower Quarry"),
"Quarry - [West] Near Shooting Range": TunicLocationData("Lower Quarry", "Lower Quarry"),
"Quarry - [West] Below Shooting Range": TunicLocationData("Lower Quarry", "Lower Quarry"),

View File

@@ -188,7 +188,7 @@ class IceGrappling(Choice):
"""
Choose whether grappling frozen enemies is in logic.
Easy includes ice grappling enemies that are in range without luring them.
Medium includes using ice grapples to push enemies through doors or off ledges. Also includes bringing an enemy over to the Temple Door to grapple through it.
Medium includes using ice grapples to push enemies through doors or off ledges without luring them. Also includes bringing an enemy over to the Temple Door to grapple through it.
Hard includes luring or grappling enemies to get to where you want to go. Hard difficulty will give the player the Torch item to return to the Overworld checkpoint to avoid softlocks.
Note: You will still be expected to ice grapple to the slime in East Forest.
"""

View File

@@ -16,7 +16,8 @@ tunic_regions: Dict[str, Set[str]] = {
"Eastern Vault Fortress": {"Beneath the Vault"},
"Beneath the Vault": {"Eastern Vault Fortress"},
"Quarry Back": {"Quarry"},
"Quarry": {"Lower Quarry"},
"Quarry": {"Monastery", "Lower Quarry"},
"Monastery": set(),
"Lower Quarry": {"Rooted Ziggurat"},
"Rooted Ziggurat": set(),
"Swamp": {"Cathedral"},

View File

@@ -3,7 +3,7 @@ from typing import Dict, TYPE_CHECKING
from worlds.generic.Rules import set_rule, forbid_item
from BaseClasses import CollectionState
from .options import TunicOptions
from .options import TunicOptions, LadderStorage, IceGrappling
if TYPE_CHECKING:
from . import TunicWorld
@@ -59,17 +59,20 @@ def has_sword(state: CollectionState, player: int) -> bool:
def has_ice_grapple_logic(long_range: bool, state: CollectionState, world: "TunicWorld") -> bool:
player = world.player
if not world.options.logic_rules:
if not world.options.ice_grappling:
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:
@@ -119,13 +122,23 @@ def set_region_rules(world: "TunicWorld") -> None:
multiworld.get_entrance("Lower Quarry -> Rooted Ziggurat", player).access_rule = \
lambda state: state.has(grapple, player) and has_ability(prayer, state, world)
multiworld.get_entrance("Swamp -> Cathedral", player).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, state, world) and options.ice_grappling >= IceGrappling.option_medium)
multiworld.get_entrance("Overworld -> Spirit Arena", player).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)) and \
has_ability(prayer, state, world) and has_sword(state, player) and state.has_any({lantern, laurels}, player)
if options.ice_grappling == IceGrappling.option_hard:
world.get_region("Quarry").connect(world.get_region("Rooted Ziggurat"),
rule=lambda state: has_ice_grapple_logic(True, 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:
multiworld = world.multiworld
@@ -180,17 +193,20 @@ def set_location_rules(world: "TunicWorld") -> None:
lambda state: state.has(laurels, player))
set_rule(multiworld.get_location("Old House - Normal Chest", player),
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, state, world)
and options.ice_grappling >= IceGrappling.option_medium)
or (state.has(laurels, player) and options.laurels_zips))
set_rule(multiworld.get_location("Old House - Holy Cross Chest", player),
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, state, world)
and options.ice_grappling >= IceGrappling.option_medium)
or (state.has(laurels, player) and options.laurels_zips)))
set_rule(multiworld.get_location("Old House - Shield Pickup", player),
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, state, world)
and options.ice_grappling >= IceGrappling.option_medium)
or (state.has(laurels, player) and options.laurels_zips))
set_rule(multiworld.get_location("Overworld - [Northwest] Page on Pillar by Dark Tomb", player),
lambda state: state.has(laurels, player))
set_rule(multiworld.get_location("Overworld - [Southwest] From West Garden", player),
@@ -200,7 +216,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(multiworld.get_location("Overworld - [Northwest] Chest Beneath Quarry Gate", player),
lambda state: state.has_any({grapple, laurels}, player) or options.logic_rules)
lambda state: state.has_any({grapple, laurels}, player))
set_rule(multiworld.get_location("Overworld - [East] Grapple Chest", player),
lambda state: state.has(grapple, player))
set_rule(multiworld.get_location("Special Shop - Secret Page Pickup", player),
@@ -209,11 +225,13 @@ 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, state, world)
and options.ice_grappling >= IceGrappling.option_medium)))
set_rule(multiworld.get_location("Sealed Temple - Page Pickup", player),
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, state, world)
and options.ice_grappling >= IceGrappling.option_medium))
set_rule(multiworld.get_location("West Furnace - Lantern Pickup", player),
lambda state: has_stick(state, player) or state.has_any({fire_wand, laurels}, player))
@@ -264,7 +282,7 @@ def set_location_rules(world: "TunicWorld") -> None:
set_rule(multiworld.get_location("Ruined Atoll - [East] Locked Room Upper Chest", player),
lambda state: state.has(laurels, player) or state.has(key, player, 2))
set_rule(multiworld.get_location("Librarian - Hexagon Green", player),
lambda state: has_sword(state, player) or options.logic_rules)
lambda state: has_sword(state, player))
# Frog's Domain
set_rule(multiworld.get_location("Frog's Domain - Side Room Grapple Secret", player),
@@ -279,10 +297,14 @@ def set_location_rules(world: "TunicWorld") -> None:
lambda state: state.has(laurels, player))
set_rule(multiworld.get_location("Fortress Arena - Siege Engine/Vault Key Pickup", player),
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, state, world)
and options.ice_grappling >= IceGrappling.option_medium)))
set_rule(multiworld.get_location("Fortress Arena - Hexagon Red", player),
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, state, world)
and options.ice_grappling >= IceGrappling.option_medium)))
# Beneath the Vault
set_rule(multiworld.get_location("Beneath the Fortress - Bridge", player),
@@ -301,7 +323,8 @@ def set_location_rules(world: "TunicWorld") -> None:
# Swamp
set_rule(multiworld.get_location("Cathedral Gauntlet - Gauntlet Reward", player),
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, state, world)
and options.ice_grappling >= IceGrappling.option_medium)))
set_rule(multiworld.get_location("Swamp - [Entrance] Above Entryway", player),
lambda state: state.has(laurels, player))
set_rule(multiworld.get_location("Swamp - [South Graveyard] Upper Walkway Dash Chest", player),