mirror of
https://github.com/ArchipelagoMW/Archipelago.git
synced 2026-03-23 08:33:23 -07:00
The Messenger: Universal Tracker support (#5344)
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import logging
|
||||
from typing import Any, ClassVar, TextIO
|
||||
|
||||
from BaseClasses import CollectionState, Entrance, EntranceType, Item, ItemClassification, MultiWorld, Tutorial
|
||||
from BaseClasses import CollectionState, Entrance, EntranceType, Item, ItemClassification, MultiWorld, Tutorial, \
|
||||
PlandoOptions
|
||||
from Options import Accessibility
|
||||
from Utils import output_path
|
||||
from settings import FilePath, Group
|
||||
@@ -18,6 +19,7 @@ from .rules import MessengerHardRules, MessengerOOBRules, MessengerRules
|
||||
from .shop import FIGURINES, PROG_SHOP_ITEMS, SHOP_ITEMS, USEFUL_SHOP_ITEMS, shuffle_shop_prices
|
||||
from .subclasses import MessengerItem, MessengerRegion, MessengerShopLocation
|
||||
from .transitions import disconnect_entrances, shuffle_transitions
|
||||
from .universal_tracker import reverse_portal_exits_into_portal_plando, reverse_transitions_into_plando_connections
|
||||
|
||||
components.append(
|
||||
Component(
|
||||
@@ -151,6 +153,10 @@ class MessengerWorld(World):
|
||||
reachable_locs: bool = False
|
||||
filler: dict[str, int]
|
||||
|
||||
@staticmethod
|
||||
def interpret_slot_data(slot_data: dict[str, Any]) -> dict[str, Any]:
|
||||
return slot_data
|
||||
|
||||
def generate_early(self) -> None:
|
||||
if self.options.goal == Goal.option_power_seal_hunt:
|
||||
self.total_seals = self.options.total_seals.value
|
||||
@@ -188,6 +194,11 @@ class MessengerWorld(World):
|
||||
self.spoiler_portal_mapping = {}
|
||||
self.transitions = []
|
||||
|
||||
if hasattr(self.multiworld, "re_gen_passthrough"):
|
||||
slot_data = self.multiworld.re_gen_passthrough.get(self.game)
|
||||
if slot_data:
|
||||
self.starting_portals = slot_data["starting_portals"]
|
||||
|
||||
def create_regions(self) -> None:
|
||||
# MessengerRegion adds itself to the multiworld
|
||||
# create simple regions
|
||||
@@ -279,6 +290,16 @@ class MessengerWorld(World):
|
||||
def connect_entrances(self) -> None:
|
||||
if self.options.shuffle_transitions:
|
||||
disconnect_entrances(self)
|
||||
keep_entrance_logic = False
|
||||
|
||||
if hasattr(self.multiworld, "re_gen_passthrough"):
|
||||
slot_data = self.multiworld.re_gen_passthrough.get(self.game)
|
||||
if slot_data:
|
||||
self.multiworld.plando_options |= PlandoOptions.connections
|
||||
self.options.portal_plando.value = reverse_portal_exits_into_portal_plando(slot_data["portal_exits"])
|
||||
self.options.plando_connections.value = reverse_transitions_into_plando_connections(slot_data["transitions"])
|
||||
keep_entrance_logic = True
|
||||
|
||||
add_closed_portal_reqs(self)
|
||||
# i need portal shuffle to happen after rules exist so i can validate it
|
||||
attempts = 20
|
||||
@@ -295,7 +316,7 @@ class MessengerWorld(World):
|
||||
raise RuntimeError("Unable to generate valid portal output.")
|
||||
|
||||
if self.options.shuffle_transitions:
|
||||
shuffle_transitions(self)
|
||||
shuffle_transitions(self, keep_entrance_logic)
|
||||
|
||||
def write_spoiler_header(self, spoiler_handle: TextIO) -> None:
|
||||
if self.options.available_portals < 6:
|
||||
@@ -463,7 +484,7 @@ class MessengerWorld(World):
|
||||
"loc_data": {loc.address: {loc.item.name: [loc.item.code, loc.item.flags]}
|
||||
for loc in multiworld.get_filled_locations() if loc.address},
|
||||
}
|
||||
|
||||
|
||||
output = orjson.dumps(data, option=orjson.OPT_NON_STR_KEYS)
|
||||
with open(out_path, "wb") as f:
|
||||
f.write(output)
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
from functools import cached_property
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from BaseClasses import CollectionState, Entrance, EntranceType, Item, ItemClassification, Location, Region
|
||||
from entrance_rando import ERPlacementState
|
||||
from BaseClasses import CollectionState, Item, ItemClassification, Location, Region
|
||||
from .regions import LOCATIONS, MEGA_SHARDS
|
||||
from .shop import FIGURINES, SHOP_ITEMS
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from BaseClasses import Entrance, Region
|
||||
from BaseClasses import Region, CollectionRule
|
||||
from entrance_rando import EntranceType, randomize_entrances
|
||||
from .connections import RANDOMIZED_CONNECTIONS, TRANSITIONS
|
||||
from .options import ShuffleTransitions, TransitionPlando
|
||||
@@ -26,7 +26,6 @@ def disconnect_entrances(world: "MessengerWorld") -> None:
|
||||
entrance.randomization_type = er_type
|
||||
mock_entrance.randomization_type = er_type
|
||||
|
||||
|
||||
for parent, child in RANDOMIZED_CONNECTIONS.items():
|
||||
if child == "Corrupted Future":
|
||||
entrance = world.get_entrance("Artificer's Portal")
|
||||
@@ -36,8 +35,9 @@ def disconnect_entrances(world: "MessengerWorld") -> None:
|
||||
entrance = world.get_entrance(f"{parent} -> {child}")
|
||||
disconnect_entrance()
|
||||
|
||||
def connect_plando(world: "MessengerWorld", plando_connections: TransitionPlando) -> None:
|
||||
def remove_dangling_exit(region: Region) -> None:
|
||||
|
||||
def connect_plando(world: "MessengerWorld", plando_connections: TransitionPlando, keep_logic: bool = False) -> None:
|
||||
def remove_dangling_exit(region: Region) -> CollectionRule:
|
||||
# find the disconnected exit and remove references to it
|
||||
for _exit in region.exits:
|
||||
if not _exit.connected_region:
|
||||
@@ -45,6 +45,7 @@ def connect_plando(world: "MessengerWorld", plando_connections: TransitionPlando
|
||||
else:
|
||||
raise ValueError(f"Unable to find randomized transition for {plando_connection}")
|
||||
region.exits.remove(_exit)
|
||||
return _exit.access_rule
|
||||
|
||||
def remove_dangling_entrance(region: Region) -> None:
|
||||
# find the disconnected entrance and remove references to it
|
||||
@@ -65,30 +66,35 @@ def connect_plando(world: "MessengerWorld", plando_connections: TransitionPlando
|
||||
else:
|
||||
dangling_exit = world.get_entrance("Artificer's Challenge")
|
||||
reg1.exits.remove(dangling_exit)
|
||||
access_rule = dangling_exit.access_rule
|
||||
else:
|
||||
reg1 = world.get_region(plando_connection.entrance)
|
||||
remove_dangling_exit(reg1)
|
||||
|
||||
access_rule = remove_dangling_exit(reg1)
|
||||
|
||||
reg2 = world.get_region(plando_connection.exit)
|
||||
remove_dangling_entrance(reg2)
|
||||
# connect the regions
|
||||
reg1.connect(reg2)
|
||||
new_exit1 = reg1.connect(reg2)
|
||||
if keep_logic:
|
||||
new_exit1.access_rule = access_rule
|
||||
|
||||
# pretend the user set the plando direction as "both" regardless of what they actually put on coupled
|
||||
if ((world.options.shuffle_transitions == ShuffleTransitions.option_coupled
|
||||
or plando_connection.direction == "both")
|
||||
and plando_connection.exit in RANDOMIZED_CONNECTIONS):
|
||||
remove_dangling_exit(reg2)
|
||||
access_rule = remove_dangling_exit(reg2)
|
||||
remove_dangling_entrance(reg1)
|
||||
reg2.connect(reg1)
|
||||
new_exit2 = reg2.connect(reg1)
|
||||
if keep_logic:
|
||||
new_exit2.access_rule = access_rule
|
||||
|
||||
|
||||
def shuffle_transitions(world: "MessengerWorld") -> None:
|
||||
def shuffle_transitions(world: "MessengerWorld", keep_logic: bool = False) -> None:
|
||||
coupled = world.options.shuffle_transitions == ShuffleTransitions.option_coupled
|
||||
|
||||
plando = world.options.plando_connections
|
||||
if plando:
|
||||
connect_plando(world, plando)
|
||||
connect_plando(world, plando, keep_logic)
|
||||
|
||||
result = randomize_entrances(world, coupled, {0: [0]})
|
||||
|
||||
|
||||
41
worlds/messenger/universal_tracker.py
Normal file
41
worlds/messenger/universal_tracker.py
Normal file
@@ -0,0 +1,41 @@
|
||||
from Options import PlandoConnection
|
||||
from .connections import RANDOMIZED_CONNECTIONS
|
||||
from .portals import REGION_ORDER, SHOP_POINTS, CHECKPOINTS
|
||||
from .transitions import TRANSITIONS
|
||||
|
||||
REVERSED_RANDOMIZED_CONNECTIONS = {v: k for k, v in RANDOMIZED_CONNECTIONS.items()}
|
||||
|
||||
|
||||
def find_spot(portal_key: int) -> str:
|
||||
"""finds the spot associated with the portal key"""
|
||||
parent = REGION_ORDER[portal_key // 100]
|
||||
if portal_key % 100 == 0:
|
||||
return f"{parent} Portal"
|
||||
if portal_key % 100 // 10 == 1:
|
||||
return SHOP_POINTS[parent][portal_key % 10]
|
||||
return CHECKPOINTS[parent][portal_key % 10]
|
||||
|
||||
|
||||
def reverse_portal_exits_into_portal_plando(portal_exits: list[int]) -> list[PlandoConnection]:
|
||||
return [
|
||||
PlandoConnection("Autumn Hills", find_spot(portal_exits[0]), "both"),
|
||||
PlandoConnection("Riviere Turquoise", find_spot(portal_exits[1]), "both"),
|
||||
PlandoConnection("Howling Grotto", find_spot(portal_exits[2]), "both"),
|
||||
PlandoConnection("Sunken Shrine", find_spot(portal_exits[3]), "both"),
|
||||
PlandoConnection("Searing Crags", find_spot(portal_exits[4]), "both"),
|
||||
PlandoConnection("Glacial Peak", find_spot(portal_exits[5]), "both"),
|
||||
]
|
||||
|
||||
|
||||
def reverse_transitions_into_plando_connections(transitions: list[list[int]]) -> list[PlandoConnection]:
|
||||
plando_connections = []
|
||||
|
||||
for connection in [
|
||||
PlandoConnection(REVERSED_RANDOMIZED_CONNECTIONS[TRANSITIONS[transition[0]]], TRANSITIONS[transition[1]], "both")
|
||||
for transition in transitions
|
||||
]:
|
||||
if connection.exit in {con.entrance for con in plando_connections}:
|
||||
continue
|
||||
plando_connections.append(connection)
|
||||
|
||||
return plando_connections
|
||||
Reference in New Issue
Block a user