Add decoupled option, mess with plando connection stuff

This commit is contained in:
Scipio Wright
2024-08-03 14:54:19 -04:00
parent 676cebb22c
commit f45f3cfc1a
3 changed files with 96 additions and 16 deletions

View File

@@ -54,6 +54,7 @@ class SeedGroup(TypedDict):
ladder_storage: int # ls value
laurels_at_10_fairies: bool # laurels location value
entrance_layout: int # entrance layout value
decoupled: bool
plando: TunicPlandoConnections # consolidated plando connections for the seed group
@@ -113,6 +114,7 @@ class TunicWorld(World):
self.options.entrance_rando.value = passthrough["entrance_rando"]
self.options.shuffle_ladders.value = passthrough["shuffle_ladders"]
self.options.entrance_layout.value = EntranceLayout.option_standard
self.options.decoupled = passthrough["decoupled"]
self.options.laurels_location.value = LaurelsLocation.option_anywhere
self.options.combat_logic.value = passthrough["combat_logic"]
@@ -132,7 +134,8 @@ class TunicWorld(World):
ladder_storage=tunic.options.ladder_storage.value,
laurels_at_10_fairies=tunic.options.laurels_location == LaurelsLocation.option_10_fairies,
entrance_layout=tunic.options.entrance_layout.value,
plando=multiworld.plando_connections[tunic.player])
decoupled=bool(tunic.options.decoupled),
plando=tunic.options.plando_connections)
continue
# off is more restrictive
@@ -154,33 +157,41 @@ class TunicWorld(World):
elif cls.seed_groups[group]["entrance_layout"] != tunic.options.entrance_layout.value:
raise OptionError(f"TUNIC: Conflict between seed group {group}'s Entrance Layout options. "
f"Seed group cannot have both Fixed Shop and Direction Pairs enabled.")
# todo: make it break if you don't have matching portal directions with direction pairs on
if multiworld.plando_connections[tunic.player]:
# decoupled loses to coupled, I could instead make this fail but eh
if not tunic.options.decoupled:
cls.seed_groups[group]["decoupled"] = False
if tunic.options.plando_connections:
# loop through the connections in the player's yaml
for cxn in multiworld.plando_connections[tunic.player]:
for cxn in tunic.options.plando_connections:
new_cxn = True
# if they used the entrance direction, just swap it around
if cxn.direction == "entrance" and cls.seed_groups[group]["decoupled"]:
player_cxn = PlandoConnection(entrance=cxn.exit, exit=cxn.entrance, direction="exit", percentage=cxn.percentage)
else:
player_cxn = cxn
for group_cxn in cls.seed_groups[group]["plando"]:
# if neither entrance nor exit match anything in the group, add to group
if ((cxn.entrance == group_cxn.entrance and cxn.exit == group_cxn.exit)
or (cxn.exit == group_cxn.entrance and cxn.entrance == group_cxn.exit)):
if ((player_cxn.entrance == group_cxn.entrance and player_cxn.exit == group_cxn.exit)
# if decoupled is off, the entrance and exit can be swapped
or (player_cxn.exit == group_cxn.entrance and player_cxn.entrance == group_cxn.exit
and not cls.seed_groups[group]["decoupled"])):
new_cxn = False
break
# check if this pair is the same as a pair in the group already
is_mismatched = (
cxn.entrance == group_cxn.entrance and cxn.exit != group_cxn.exit
or cxn.entrance == group_cxn.exit and cxn.exit != group_cxn.entrance
or cxn.exit == group_cxn.entrance and cxn.entrance != group_cxn.exit
or cxn.exit == group_cxn.exit and cxn.entrance != group_cxn.entrance
player_cxn.entrance == group_cxn.entrance and player_cxn.exit != group_cxn.exit
or player_cxn.entrance == group_cxn.exit and player_cxn.exit != group_cxn.entrance
or player_cxn.exit == group_cxn.entrance and player_cxn.entrance != group_cxn.exit
or player_cxn.exit == group_cxn.exit and player_cxn.entrance != group_cxn.entrance
)
if is_mismatched:
raise OptionError(f"TUNIC: Conflict between seed group {group}'s plando "
f"connection {group_cxn.entrance} <-> {group_cxn.exit} and "
f"{tunic.multiworld.get_player_name(tunic.player)}'s plando "
f"connection {cxn.entrance} <-> {cxn.exit}")
f"connection {player_cxn.entrance} <-> {player_cxn.exit}")
if new_cxn:
cls.seed_groups[group]["plando"].value.append(cxn)
cls.seed_groups[group]["plando"].value.append(player_cxn)
def create_item(self, name: str, classification: ItemClassification = None) -> TunicItem:
item_data = item_table[name]
@@ -400,6 +411,7 @@ class TunicWorld(World):
"lanternless": self.options.lanternless.value,
"maskless": self.options.maskless.value,
"entrance_rando": int(bool(self.options.entrance_rando.value)),
"decoupled": self.options.decoupled.value,
"shuffle_ladders": self.options.shuffle_ladders.value,
"combat_logic": self.options.combat_logic.value,
"Hexagon Quest Prayer": self.ability_unlocks["Pages 24-25 (Prayer)"],

View File

@@ -557,3 +557,53 @@ def update_reachable_regions(connected_regions: Set[str], traversal_reqs: Dict[s
connected_regions = update_reachable_regions(connected_regions, traversal_reqs, has_laurels, logic)
return connected_regions
# which directions are opposites
direction_pairs: Dict[int, int] = {
Direction.north: Direction.south,
Direction.south: Direction.north,
Direction.east: Direction.west,
Direction.west: Direction.east,
Direction.ladder_up: Direction.ladder_down,
Direction.ladder_down: Direction.ladder_up,
Direction.floor: Direction.floor,
}
# verify that two portals are in compatible directions
def verify_direction_pair(portal1: Portal, portal2: Portal) -> bool:
if portal1.direction == direction_pairs[portal2.direction]:
return True
elif portal1.name.startswith("Shop"):
if portal2.direction in [Direction.north, Direction.east]:
return True
elif portal2.name.startswith("Shop"):
if portal1.direction in [Direction.north, Direction.east]:
return True
else:
return False
# verify that two plando'd portals are in compatible directions
def verify_plando_directions(connection: PlandoConnection) -> bool:
entrance_portal = None
exit_portal = None
for portal in portal_mapping:
if connection.entrance == portal.name:
entrance_portal = portal
if connection.exit == portal.name:
exit_portal = portal
if entrance_portal and exit_portal:
if entrance_portal.direction == direction_pairs[exit_portal.direction]:
return True
# this is two shop portals, they can never pair directions
elif not entrance_portal and not exit_portal:
return False
# if one of them is none, it's a shop, which has two possible directions
elif not entrance_portal:
if exit_portal.direction in [Direction.north, Direction.east]:
return True
elif not exit_portal:
if entrance_portal.direction in [Direction.north, Direction.east]:
return True

View File

@@ -146,6 +146,15 @@ class EntranceLayout(Choice):
default = 0
class Decoupled(Toggle):
"""
Decouple the entrances, so that when you go from one entrance to another, the return trip won't necessarily bring you back to the same place.
Note: For seed groups, if any player in a seed group does not have Decoupled enabled, no one in the seed group will have Decoupled entrances.
"""
internal_name = "decoupled"
display_name = "Decoupled Entrances"
class LaurelsLocation(Choice):
"""
Force the Hero's Laurels to be placed at a location in your world.
@@ -176,7 +185,9 @@ class TunicPlandoConnections(PlandoConnections):
Generic connection plando. Format is:
- entrance: "Entrance Name"
exit: "Exit Name"
direction: "Direction"
percentage: 100
Direction must be one of 'entrance', 'exit', or 'both', and defaults to 'both' if omitted.
Percentage is an integer from 0 to 100 which determines whether that connection will be made. Defaults to 100 if omitted.
If the Entrance Layout option is set to Standard or Fixed Shop, you can plando multiple shops.
Note that you will wrong warp if you have multiple shops in the same scene.
@@ -272,6 +283,7 @@ class TunicOptions(PerGameCommonOptions):
shuffle_ladders: ShuffleLadders
entrance_rando: EntranceRando
entrance_layout: EntranceLayout
decoupled: Decoupled
plando_connections: TunicPlandoConnections
fool_traps: FoolTraps
hexagon_quest: HexagonQuest
@@ -298,8 +310,14 @@ tunic_option_groups = [
LaurelsZips,
IceGrappling,
LadderStorage,
LadderStorageWithoutItems
])
LadderStorageWithoutItems,
]),
OptionGroup("Entrance Randomizer", [
EntranceRando,
EntranceLayout,
Decoupled,
PlandoConnections,
]),
]
tunic_option_presets: Dict[str, Dict[str, Any]] = {