diff options
Diffstat (limited to 'apworld')
| -rw-r--r-- | apworld/__init__.py | 6 | ||||
| -rw-r--r-- | apworld/options.py | 23 | ||||
| -rw-r--r-- | apworld/player_logic.py | 16 | ||||
| -rw-r--r-- | apworld/regions.py | 18 | ||||
| -rw-r--r-- | apworld/static_logic.py | 12 | ||||
| -rw-r--r-- | apworld/tracker.py | 4 |
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 | ||
| 94 | class 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 | |||
| 100 | class 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 | |||
| 94 | class EnableIcarus(Toggle): | 115 | class 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 | |||
| 5 | from .items import SYMBOL_ITEMS | 5 | from .items import SYMBOL_ITEMS |
| 6 | from typing import TYPE_CHECKING, NamedTuple | 6 | from typing import TYPE_CHECKING, NamedTuple |
| 7 | 7 | ||
| 8 | from .options import ShuffleLetters, CyanDoorBehavior, VictoryCondition | 8 | from .options import ShuffleLetters, CyanDoorBehavior, VictoryCondition, FastTravelAccess |
| 9 | 9 | ||
| 10 | if TYPE_CHECKING: | 10 | if 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 | |||
| 5 | from entrance_rando import randomize_entrances | 5 | from entrance_rando import randomize_entrances |
| 6 | from .items import Lingo2Item | 6 | from .items import Lingo2Item |
| 7 | from .locations import Lingo2Location | 7 | from .locations import Lingo2Location |
| 8 | from .options import FastTravelAccess | ||
| 8 | from .player_logic import AccessRequirements | 9 | from .player_logic import AccessRequirements |
| 9 | from .rules import make_location_lambda | 10 | from .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: |
