Finishing out hooking the new rules into the code

This commit is contained in:
Scipio Wright
2024-06-14 19:41:23 -04:00
parent f78be22a16
commit 985c02de38
6 changed files with 85 additions and 76 deletions

View File

@@ -8,7 +8,8 @@ from .er_rules import set_er_location_rules
from .regions import tunic_regions
from .er_scripts import create_er_regions
from .er_data import portal_mapping
from .options import TunicOptions, EntranceRando, tunic_option_groups, tunic_option_presets, TunicPlandoConnections
from .options import (TunicOptions, EntranceRando, tunic_option_groups, tunic_option_presets, TunicPlandoConnections,
LaurelsLocation)
from worlds.AutoWorld import WebWorld, World
from Options import PlandoConnection
from decimal import Decimal, ROUND_HALF_UP
@@ -40,10 +41,12 @@ class TunicLocation(Location):
class SeedGroup(TypedDict):
logic_rules: int # logic rules value
laurels_zips: bool # laurels_zips value
ice_grappling: int # ice_grappling value
ladder_storage: int # ls value
laurels_at_10_fairies: bool # laurels location value
fixed_shop: bool # fixed shop value
plando: TunicPlandoConnections # consolidated of plando connections for the seed group
plando: TunicPlandoConnections # consolidated plando connections for the seed group
class TunicWorld(World):
@@ -112,19 +115,28 @@ class TunicWorld(World):
group = tunic.options.entrance_rando.value
# if this is the first world in the group, set the rules equal to its rules
if group not in cls.seed_groups:
cls.seed_groups[group] = SeedGroup(logic_rules=tunic.options.logic_rules.value,
laurels_at_10_fairies=tunic.options.laurels_location == 3,
fixed_shop=bool(tunic.options.fixed_shop),
plando=multiworld.plando_connections[tunic.player])
cls.seed_groups[group] = \
SeedGroup(laurels_zips=bool(tunic.options.laurels_zips),
ice_grappling=tunic.options.ice_grappling.value,
ladder_storage=tunic.options.ladder_storage.value,
laurels_at_10_fairies=tunic.options.laurels_location == LaurelsLocation.option_10_fairies,
fixed_shop=bool(tunic.options.fixed_shop),
plando=multiworld.plando_connections[tunic.player])
continue
# off is more restrictive
if not tunic.options.laurels_zips:
cls.seed_groups[group]["laurels_zips"] = False
# lower value is more restrictive
if tunic.options.logic_rules.value < cls.seed_groups[group]["logic_rules"]:
cls.seed_groups[group]["logic_rules"] = tunic.options.logic_rules.value
if tunic.options.ice_grappling < cls.seed_groups[group]["ice_grappling"]:
cls.seed_groups[group]["ice_grappling"] = tunic.options.ice_grappling.value
# lower value is more restrictive
if tunic.options.ladder_storage.value < cls.seed_groups[group]["ladder_storage"]:
cls.seed_groups[group]["ladder_storage"] = tunic.options.ladder_storage.value
# laurels at 10 fairies changes logic for secret gathering place placement
if tunic.options.laurels_location == 3:
cls.seed_groups[group]["laurels_at_10_fairies"] = True
# fewer shops, one at windmill
# more restrictive, overrides the option for others in the same group, which is better than failing imo
if tunic.options.fixed_shop:
cls.seed_groups[group]["fixed_shop"] = True

View File

@@ -783,8 +783,8 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
[],
"After Ruined Passage":
[["IG1"], ["LS1"]],
# "Overworld":
# [],
# "Overworld":
# [],
"Overworld at Patrol Cave":
[],
"Overworld above Patrol Cave":
@@ -799,8 +799,8 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
"Overworld Belltower": {
"Overworld Belltower at Bell":
[],
# "Overworld":
# [],
# "Overworld":
# [],
"Overworld to West Garden Upper":
[],
},
@@ -817,8 +817,8 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
# [],
# },
"Overworld Beach": {
# "Overworld":
# [],
# "Overworld":
# [],
"Overworld West Garden Laurels Entry":
[["Hyperdash"], ["LS1"]],
"Overworld to Atoll Upper":
@@ -831,20 +831,20 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
[["Hyperdash"]],
},
"Overworld to Atoll Upper": {
# "Overworld":
# [],
# "Overworld":
# [],
"Overworld Beach":
[],
},
"Overworld Tunnel Turret": {
# "Overworld":
# [],
# "Overworld":
# [],
"Overworld Beach":
[],
},
"Overworld Well Ladder": {
# "Overworld":
# [],
# "Overworld":
# [],
},
"Overworld at Patrol Cave": {
"East Overworld":
@@ -853,8 +853,8 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
[],
},
"Overworld above Patrol Cave": {
# "Overworld":
# [],
# "Overworld":
# [],
"East Overworld":
[],
"Upper Overworld":
@@ -876,32 +876,32 @@ traversal_requirements: Dict[str, Dict[str, List[List[str]]]] = {
[],
},
"Overworld above Quarry Entrance": {
# "Overworld":
# [],
# "Overworld":
# [],
"Upper Overworld":
[],
},
"Overworld Quarry Entry": {
"Overworld after Envoy":
[],
# "Overworld":
# [["IG1"]],
# "Overworld":
# [["IG1"]],
},
"Overworld after Envoy": {
# "Overworld":
# [],
# "Overworld":
# [],
"Overworld Quarry Entry":
[],
},
"After Ruined Passage": {
# "Overworld":
# [],
# "Overworld":
# [],
"Above Ruined Passage":
[],
},
"Above Ruined Passage": {
# "Overworld":
# [],
# "Overworld":
# [],
"After Ruined Passage":
[],
"East Overworld":

View File

@@ -1104,7 +1104,6 @@ def set_er_region_rules(world: "TunicWorld", regions: Dict[str, Region], portal_
def set_er_location_rules(world: "TunicWorld") -> None:
player = world.player
multiworld = world.multiworld
options = world.options
forbid_item(multiworld.get_location("Secret Gathering Place - 20 Fairy Reward", player), fairies, player)

View File

@@ -1,4 +1,4 @@
from typing import Dict, List, Set, TYPE_CHECKING
from typing import Dict, List, Set, Tuple, TYPE_CHECKING
from BaseClasses import Region, ItemClassification, Item, Location
from .locations import location_table
from .er_data import Portal, tunic_er_regions, portal_mapping, traversal_requirements, DeadEnd
@@ -132,7 +132,7 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
two_plus: List[Portal] = []
player_name = world.multiworld.get_player_name(world.player)
portal_map = portal_mapping.copy()
logic_rules = world.options.logic_rules.value
laurels_zips = world.options.laurels_zips.value
ice_grappling = world.options.ice_grappling.value
ladder_storage = world.options.ladder_storage.value
fixed_shop = world.options.fixed_shop
@@ -144,10 +144,14 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
# if it's not one of the EntranceRando options, it's a custom seed
if world.options.entrance_rando.value not in EntranceRando.options.values():
seed_group = world.seed_groups[world.options.entrance_rando.value]
logic_rules = seed_group["logic_rules"]
laurels_zips = seed_group["laurels_zips"]
ice_grappling = seed_group["ice_grappling"]
ladder_storage = seed_group["ladder_storage"]
fixed_shop = seed_group["fixed_shop"]
laurels_location = "10_fairies" if seed_group["laurels_at_10_fairies"] is True else False
logic_tricks: Tuple[bool, int, int] = (laurels_zips, ice_grappling, ladder_storage)
# marking that you don't immediately have laurels
if laurels_location == "10_fairies" and not hasattr(world.multiworld, "re_gen_passthrough"):
has_laurels = False
@@ -198,7 +202,7 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
# make better start region stuff when/if implementing random start
start_region = "Overworld"
connected_regions.add(start_region)
connected_regions = update_reachable_regions(connected_regions, traversal_reqs, has_laurels, logic_rules)
connected_regions = update_reachable_regions(connected_regions, traversal_reqs, has_laurels, logic_tricks)
if world.options.entrance_rando.value in EntranceRando.options.values():
plando_connections = world.options.plando_connections.value
@@ -230,9 +234,11 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
for region_name, region_info in tunic_er_regions.items():
if not region_info.dead_end:
non_dead_end_regions.add(region_name)
elif region_info.dead_end == 2 and logic_rules:
# if ice grappling to places is in logic, both places stop being dead ends
elif region_info.dead_end == DeadEnd.restricted and ice_grappling:
non_dead_end_regions.add(region_name)
elif region_info.dead_end == 3:
# secret gathering place and zig skip get weird, special handling
elif region_info.dead_end == DeadEnd.special:
if (region_name == "Secret Gathering Place" and laurels_location == "10_fairies") \
or (region_name == "Zig Skip Exit" and fixed_shop):
non_dead_end_regions.add(region_name)
@@ -333,7 +339,7 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
portal_pairs[portal1] = portal2
# if we have plando connections, our connected regions may change somewhat
connected_regions = update_reachable_regions(connected_regions, traversal_reqs, has_laurels, logic_rules)
connected_regions = update_reachable_regions(connected_regions, traversal_reqs, has_laurels, logic_tricks)
if fixed_shop and not hasattr(world.multiworld, "re_gen_passthrough"):
portal1 = None
@@ -395,7 +401,7 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
if waterfall_plando:
cr = connected_regions.copy()
cr.add(portal.region)
if "Secret Gathering Place" not in update_reachable_regions(cr, traversal_reqs, has_laurels, logic_rules):
if "Secret Gathering Place" not in update_reachable_regions(cr, traversal_reqs, has_laurels, logic_tricks):
continue
elif portal.region != "Secret Gathering Place":
continue
@@ -407,7 +413,7 @@ def pair_portals(world: "TunicWorld") -> Dict[Portal, Portal]:
# once we have both portals, connect them and add the new region(s) to connected_regions
if check_success == 2:
connected_regions = update_reachable_regions(connected_regions, traversal_reqs, has_laurels, logic_rules)
connected_regions = update_reachable_regions(connected_regions, traversal_reqs, has_laurels, logic_tricks)
if "Secret Gathering Place" in connected_regions:
has_laurels = True
portal_pairs[portal1] = portal2
@@ -468,7 +474,8 @@ def create_randomized_entrances(portal_pairs: Dict[Portal, Portal], regions: Dic
def update_reachable_regions(connected_regions: Set[str], traversal_reqs: Dict[str, Dict[str, List[List[str]]]],
has_laurels: bool, logic: int) -> Set[str]:
has_laurels: bool, logic: Tuple[bool, int, int]) -> Set[str]:
zips, ice_grapples, ls = logic
# starting count, so we can run it again if this changes
region_count = len(connected_regions)
for origin, destinations in traversal_reqs.items():
@@ -487,11 +494,15 @@ def update_reachable_regions(connected_regions: Set[str], traversal_reqs: Dict[s
if req == "Hyperdash":
if not has_laurels:
break
elif req == "NMG":
if not logic:
elif req == "Zip":
if not zips:
break
elif req == "UR":
if logic < 2:
# if req is higher than logic option, then it breaks since it's not a valid connection
elif req.startswith("IG"):
if int(req[-1]) > ice_grapples:
break
elif req.startswith("LS"):
if int(req[-1]) > ls:
break
elif req not in connected_regions:
break

View File

@@ -1,7 +1,7 @@
from dataclasses import dataclass
from typing import Dict, Any
from Options import (DefaultOnToggle, Toggle, StartInventoryPool, Choice, Range, TextChoice, PlandoConnections,
PerGameCommonOptions, OptionGroup)
PerGameCommonOptions, OptionGroup, Removed)
from .er_data import portal_mapping
@@ -39,22 +39,6 @@ class AbilityShuffling(Toggle):
display_name = "Shuffle Abilities"
class LogicRules(Choice):
"""
Deprecated, and will be removed in a later version.
If you have this set to NMG, it will set Laurels Zips on and Ice Grappling to medium.
If you have this set to Unrestricted, it will set Laurels Zips on, Ice Grappling to hard, and Ladder Storage to medium.
"""
internal_name = "logic_rules"
display_name = "Logic Rules"
option_restricted = 0
option_no_major_glitches = 1
alias_nmg = 1
option_unrestricted = 2
alias_ur = 2
default = 0
class Lanternless(Toggle):
"""
Choose whether you require the Lantern for dark areas.
@@ -189,8 +173,9 @@ class IceGrappling(Choice):
Choose whether grappling frozen enemies is in logic.
Easy includes ice grappling enemies that are in range without luring them. May include clips through terrain.
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.
Hard includes luring or grappling enemies to get to where you want to go.
The Medium and Hard options will give the player the Torch to return to the Overworld checkpoint to avoid softlocks.
Note: You will still be expected to ice grapple to the slime in East Forest even with this option off.
"""
internal_name = "ice_grappling"
display_name = "Ice Grapple Logic"
@@ -241,7 +226,6 @@ class TunicOptions(PerGameCommonOptions):
shuffle_ladders: ShuffleLadders
entrance_rando: EntranceRando
fixed_shop: FixedShop
logic_rules: LogicRules
fool_traps: FoolTraps
hexagon_quest: HexagonQuest
hexagon_goal: HexagonGoal
@@ -254,11 +238,12 @@ class TunicOptions(PerGameCommonOptions):
ice_grappling: IceGrappling
ladder_storage: LadderStorage
ladder_storage_without_items: LadderStorageWithoutItems
logic_rules: Removed
tunic_option_groups = [
OptionGroup("Logic Options", [
LogicRules,
Lanternless,
Maskless,
LaurelsZips,
@@ -283,6 +268,7 @@ tunic_option_presets: Dict[str, Dict[str, Any]] = {
"ability_shuffling": True,
"entrance_rando": True,
"fool_traps": "onslaught",
"laurels_zips": True,
"ice_grappling": "hard",
"ladder_storage": "hard",
"ladder_storage_without_items": True,

View File

@@ -65,7 +65,7 @@ def laurels_zip(state: CollectionState, world: "TunicWorld") -> bool:
# todo: find and put proper typing on ice_grapple
def has_ice_grapple_logic(long_range: bool, difficulty, state: CollectionState, world: "TunicWorld") -> bool:
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:
@@ -149,7 +149,6 @@ def set_region_rules(world: "TunicWorld") -> None:
def set_location_rules(world: "TunicWorld") -> None:
multiworld = world.multiworld
player = world.player
options = world.options
forbid_item(multiworld.get_location("Secret Gathering Place - 20 Fairy Reward", player), fairies, player)
@@ -160,11 +159,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, IceGrappling.option_easy, 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(multiworld.get_location("Fortress Courtyard - Page Near Cave", player),
lambda state: has_ability(prayer, state, world) or state.has(laurels, player)
or can_ladder_storage(state, world)
or (has_ice_grapple_logic(True, IceGrappling.option_easy, 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(multiworld.get_location("East Forest - Dancing Fox Spirit Holy Cross", player),
lambda state: has_ability(holy_cross, state, world))
set_rule(multiworld.get_location("Forest Grave Path - Holy Cross Code by Grave", player),
@@ -317,7 +318,7 @@ def set_location_rules(world: "TunicWorld") -> None:
# nmg - kill boss scav with orb + firecracker, or similar
set_rule(multiworld.get_location("Rooted Ziggurat Lower - Hexagon Blue", player),
lambda state: has_sword(state, player) or (state.has(grapple, player) and options.logic_rules))
lambda state: has_sword(state, player))
# Swamp
set_rule(multiworld.get_location("Cathedral Gauntlet - Gauntlet Reward", player),