mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-30 01:53:22 -07:00
Finishing out hooking the new rules into the code
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user