about summary refs log tree commit diff stats
path: root/apworld
diff options
context:
space:
mode:
Diffstat (limited to 'apworld')
-rw-r--r--apworld/__init__.py6
-rw-r--r--apworld/options.py23
-rw-r--r--apworld/player_logic.py16
-rw-r--r--apworld/regions.py18
-rw-r--r--apworld/static_logic.py12
-rw-r--r--apworld/tracker.py4
6 files changed, 78 insertions, 1 deletions
diff --git a/apworld/__init__.py b/apworld/__init__.py index 5bad63e..42350bc 100644 --- a/apworld/__init__.py +++ b/apworld/__init__.py
@@ -71,6 +71,10 @@ class Lingo2World(World):
71 self.port_pairings = {} 71 self.port_pairings = {}
72 72
73 def create_regions(self): 73 def create_regions(self):
74 if hasattr(self.multiworld, "re_gen_passthrough") and "Lingo 2" in self.multiworld.re_gen_passthrough:
75 self.player_logic.rte_mapping = [self.world.static_logic.map_id_by_name[map_name]
76 for map_name in self.multiworld.re_gen_passthrough["Lingo 2"]["rte"]]
77
74 create_regions(self) 78 create_regions(self)
75 79
76 def connect_entrances(self): 80 def connect_entrances(self):
@@ -140,6 +144,7 @@ class Lingo2World(World):
140 "enable_gift_maps", 144 "enable_gift_maps",
141 "enable_icarus", 145 "enable_icarus",
142 "endings_requirement", 146 "endings_requirement",
147 "fast_travel_access",
143 "keyholder_sanity", 148 "keyholder_sanity",
144 "masteries_requirement", 149 "masteries_requirement",
145 "shuffle_control_center_colors", 150 "shuffle_control_center_colors",
@@ -155,6 +160,7 @@ class Lingo2World(World):
155 160
156 slot_data: dict[str, object] = { 161 slot_data: dict[str, object] = {
157 **self.options.as_dict(*slot_options), 162 **self.options.as_dict(*slot_options),
163 "rte": [self.static_logic.objects.maps[map_id].name for map_id in self.player_logic.rte_mapping],
158 "version": self.static_logic.get_data_version(), 164 "version": self.static_logic.get_data_version(),
159 } 165 }
160 166
diff --git a/apworld/options.py b/apworld/options.py index 5661351..063af21 100644 --- a/apworld/options.py +++ b/apworld/options.py
@@ -91,6 +91,27 @@ class CyanDoorBehavior(Choice):
91 option_item = 2 91 option_item = 2
92 92
93 93
94class ShuffleFastTravel(Toggle):
95 """If enabled, the list of maps you can fast travel to is randomized, except for The Entry, which is always
96 accessible."""
97 display_name = "Shuffle Fast Travel"
98
99
100class FastTravelAccess(Choice):
101 """
102 Controls how the fast travel buttons on the pause menu work.
103
104 - **Vanilla**: You can only fast travel to maps once you have been to them and stepped foot in the general area that
105 the warp would place you. This option means that fast travel has no impact on logic.
106 - **Unlocked**: All five fast travel maps will be available from the start.
107 - **Items**: Only The Entry is available from the start. The other fast travel buttons are locked behind items.
108 """
109 display_name = "Fast Travel Access"
110 option_vanilla = 0
111 option_unlocked = 1
112 option_items = 2
113
114
94class EnableIcarus(Toggle): 115class EnableIcarus(Toggle):
95 """ 116 """
96 Controls whether Icarus is randomized. If disabled, which is the default, no locations or items will be created for 117 Controls whether Icarus is randomized. If disabled, which is the default, no locations or items will be created for
@@ -234,6 +255,8 @@ class Lingo2Options(PerGameCommonOptions):
234 shuffle_worldports: ShuffleWorldports 255 shuffle_worldports: ShuffleWorldports
235 keyholder_sanity: KeyholderSanity 256 keyholder_sanity: KeyholderSanity
236 cyan_door_behavior: CyanDoorBehavior 257 cyan_door_behavior: CyanDoorBehavior
258 shuffle_fast_travel: ShuffleFastTravel
259 fast_travel_access: FastTravelAccess
237 enable_icarus: EnableIcarus 260 enable_icarus: EnableIcarus
238 enable_gift_maps: EnableGiftMaps 261 enable_gift_maps: EnableGiftMaps
239 daedalus_only: DaedalusOnly 262 daedalus_only: DaedalusOnly
diff --git a/apworld/player_logic.py b/apworld/player_logic.py index b946296..a02856e 100644 --- a/apworld/player_logic.py +++ b/apworld/player_logic.py
@@ -5,7 +5,7 @@ from .generated import data_pb2 as data_pb2
5from .items import SYMBOL_ITEMS 5from .items import SYMBOL_ITEMS
6from typing import TYPE_CHECKING, NamedTuple 6from typing import TYPE_CHECKING, NamedTuple
7 7
8from .options import ShuffleLetters, CyanDoorBehavior, VictoryCondition 8from .options import ShuffleLetters, CyanDoorBehavior, VictoryCondition, FastTravelAccess
9 9
10if TYPE_CHECKING: 10if TYPE_CHECKING:
11 from . import Lingo2World 11 from . import Lingo2World
@@ -221,6 +221,7 @@ class Lingo2PlayerLogic:
221 221
222 double_letter_amount: dict[str, int] 222 double_letter_amount: dict[str, int]
223 goal_room_id: int 223 goal_room_id: int
224 rte_mapping: list[int]
224 225
225 def __init__(self, world: "Lingo2World"): 226 def __init__(self, world: "Lingo2World"):
226 self.world = world 227 self.world = world
@@ -304,6 +305,19 @@ class Lingo2PlayerLogic:
304 if "The Fuzzy" in world.options.enable_gift_maps.value: 305 if "The Fuzzy" in world.options.enable_gift_maps.value:
305 self.real_items.append("Numbers") 306 self.real_items.append("Numbers")
306 307
308 if world.options.shuffle_fast_travel:
309 travelable_maps = [map_id for map_id in self.shuffled_maps
310 if world.static_logic.objects.maps[map_id].HasField("rte_room")]
311 self.rte_mapping = world.random.sample(travelable_maps, 4)
312 else:
313 canonical_rtes = ["the_plaza", "the_gallery", "daedalus", "control_center"]
314 self.rte_mapping = [world.static_logic.map_id_by_name[map_name] for map_name in canonical_rtes
315 if world.static_logic.map_id_by_name[map_name] in self.shuffled_maps]
316
317 if world.options.fast_travel_access == FastTravelAccess.option_items:
318 for rte_map in self.rte_mapping:
319 self.real_items.append(world.static_logic.get_map_rte_item_name(rte_map))
320
307 if self.world.options.shuffle_doors: 321 if self.world.options.shuffle_doors:
308 for progressive in world.static_logic.objects.progressives: 322 for progressive in world.static_logic.objects.progressives:
309 for i in range(0, len(progressive.doors)): 323 for i in range(0, len(progressive.doors)):
diff --git a/apworld/regions.py b/apworld/regions.py index 2f9b571..500139f 100644 --- a/apworld/regions.py +++ b/apworld/regions.py
@@ -5,6 +5,7 @@ from BaseClasses import Region, ItemClassification, Entrance
5from entrance_rando import randomize_entrances 5from entrance_rando import randomize_entrances
6from .items import Lingo2Item 6from .items import Lingo2Item
7from .locations import Lingo2Location 7from .locations import Lingo2Location
8from .options import FastTravelAccess
8from .player_logic import AccessRequirements 9from .player_logic import AccessRequirements
9from .rules import make_location_lambda 10from .rules import make_location_lambda
10 11
@@ -153,6 +154,23 @@ def create_regions(world: "Lingo2World"):
153 for region in reqs.get_referenced_rooms(): 154 for region in reqs.get_referenced_rooms():
154 world.multiworld.register_indirect_condition(regions[region], connection) 155 world.multiworld.register_indirect_condition(regions[region], connection)
155 156
157 if world.options.fast_travel_access != FastTravelAccess.option_vanilla:
158 for rte_map_id in world.player_logic.rte_mapping:
159 rte_map = world.static_logic.objects.maps[rte_map_id]
160 to_region = world.static_logic.get_room_region_name(rte_map.rte_room)
161
162 if to_region not in regions:
163 continue
164
165 connection_name = f"Return to {to_region}"
166
167 reqs = AccessRequirements()
168
169 if world.options.fast_travel_access == FastTravelAccess.option_items:
170 reqs.items.add(world.static_logic.get_map_rte_item_name(rte_map_id))
171
172 regions["Menu"].connect(regions[to_region], connection_name, make_location_lambda(reqs, world, None))
173
156 world.multiworld.regions += regions.values() 174 world.multiworld.regions += regions.values()
157 175
158 176
diff --git a/apworld/static_logic.py b/apworld/static_logic.py index 715178e..672ae5a 100644 --- a/apworld/static_logic.py +++ b/apworld/static_logic.py
@@ -18,6 +18,8 @@ class Lingo2StaticLogic:
18 door_id_by_ap_id: dict[int, int] 18 door_id_by_ap_id: dict[int, int]
19 port_id_by_ap_id: dict[int, int] 19 port_id_by_ap_id: dict[int, int]
20 20
21 map_id_by_name: dict[str, int]
22
21 def __init__(self): 23 def __init__(self):
22 self.item_id_to_name = {} 24 self.item_id_to_name = {}
23 self.location_id_to_name = {} 25 self.location_id_to_name = {}
@@ -79,6 +81,10 @@ class Lingo2StaticLogic:
79 for trap_name in ANTI_COLLECTABLE_TRAPS: 81 for trap_name in ANTI_COLLECTABLE_TRAPS:
80 self.item_id_to_name[self.objects.special_ids[trap_name]] = trap_name 82 self.item_id_to_name[self.objects.special_ids[trap_name]] = trap_name
81 83
84 for game_map in self.objects.maps:
85 if game_map.HasField("rte_room"):
86 self.item_id_to_name[game_map.rte_ap_id] = self.get_map_rte_item_name(game_map.id)
87
82 self.item_name_to_id = {name: ap_id for ap_id, name in self.item_id_to_name.items()} 88 self.item_name_to_id = {name: ap_id for ap_id, name in self.item_id_to_name.items()}
83 self.location_name_to_id = {name: ap_id for ap_id, name in self.location_id_to_name.items()} 89 self.location_name_to_id = {name: ap_id for ap_id, name in self.location_id_to_name.items()}
84 90
@@ -90,6 +96,8 @@ class Lingo2StaticLogic:
90 self.door_id_by_ap_id = {door.ap_id: door.id for door in self.objects.doors if door.HasField("ap_id")} 96 self.door_id_by_ap_id = {door.ap_id: door.id for door in self.objects.doors if door.HasField("ap_id")}
91 self.port_id_by_ap_id = {port.ap_id: port.id for port in self.objects.ports if port.HasField("ap_id")} 97 self.port_id_by_ap_id = {port.ap_id: port.id for port in self.objects.ports if port.HasField("ap_id")}
92 98
99 self.map_id_by_name = {game_map.name: game_map.id for game_map in self.objects.maps}
100
93 def get_door_item_name(self, door: data_pb2.Door) -> str: 101 def get_door_item_name(self, door: data_pb2.Door) -> str:
94 return f"{self.get_map_object_map_name(door)} - {door.name}" 102 return f"{self.get_map_object_map_name(door)} - {door.name}"
95 103
@@ -177,6 +185,10 @@ class Lingo2StaticLogic:
177 def get_room_object_map_id(self, obj) -> int: 185 def get_room_object_map_id(self, obj) -> int:
178 return self.objects.rooms[obj.room_id].map_id 186 return self.objects.rooms[obj.room_id].map_id
179 187
188 def get_map_rte_item_name(self, map_id: int) -> str:
189 game_map = self.objects.maps[map_id]
190 return f"Return to {game_map.display_name}"
191
180 def get_data_version(self) -> list[int]: 192 def get_data_version(self) -> list[int]:
181 version = self.objects.version 193 version = self.objects.version
182 return [version.major, version.minor, version.patch] 194 return [version.major, version.minor, version.patch]
diff --git a/apworld/tracker.py b/apworld/tracker.py index a84c3f8..3e1cafb 100644 --- a/apworld/tracker.py +++ b/apworld/tracker.py
@@ -44,6 +44,10 @@ class Tracker:
44 for k, t in Lingo2Options.type_hints.items()}) 44 for k, t in Lingo2Options.type_hints.items()})
45 45
46 self.world.generate_early() 46 self.world.generate_early()
47
48 self.world.player_logic.rte_mapping = [self.world.static_logic.map_id_by_name[map_name]
49 for map_name in slot_data.get("rte", [])]
50
47 self.world.create_regions() 51 self.world.create_regions()
48 52
49 if self.world.options.shuffle_worldports: 53 if self.world.options.shuffle_worldports: