diff options
Diffstat (limited to 'apworld')
| -rw-r--r-- | apworld/__init__.py | 11 | ||||
| -rw-r--r-- | apworld/client/gamedata.gd | 4 | ||||
| -rw-r--r-- | apworld/client/manager.gd | 4 | ||||
| -rw-r--r-- | apworld/context.py | 31 | ||||
| -rw-r--r-- | apworld/static_logic.py | 6 | ||||
| -rw-r--r-- | apworld/tracker.py | 5 | 
6 files changed, 51 insertions, 10 deletions
| diff --git a/apworld/__init__.py b/apworld/__init__.py index f5774c6..3d2f075 100644 --- a/apworld/__init__.py +++ b/apworld/__init__.py | |||
| @@ -77,7 +77,10 @@ class Lingo2World(World): | |||
| 77 | if self.options.shuffle_worldports: | 77 | if self.options.shuffle_worldports: | 
| 78 | if hasattr(self.multiworld, "re_gen_passthrough") and "Lingo 2" in self.multiworld.re_gen_passthrough: | 78 | if hasattr(self.multiworld, "re_gen_passthrough") and "Lingo 2" in self.multiworld.re_gen_passthrough: | 
| 79 | slot_value = self.multiworld.re_gen_passthrough["Lingo 2"]["port_pairings"] | 79 | slot_value = self.multiworld.re_gen_passthrough["Lingo 2"]["port_pairings"] | 
| 80 | self.port_pairings = {int(fp): int(tp) for fp, tp in slot_value.items()} | 80 | self.port_pairings = { | 
| 81 | self.static_logic.port_id_by_ap_id[int(fp)]: self.static_logic.port_id_by_ap_id[int(tp)] | ||
| 82 | for fp, tp in slot_value.items() | ||
| 83 | } | ||
| 81 | 84 | ||
| 82 | connect_ports_from_ut(self.port_pairings, self) | 85 | connect_ports_from_ut(self.port_pairings, self) | 
| 83 | else: | 86 | else: | 
| @@ -152,7 +155,11 @@ class Lingo2World(World): | |||
| 152 | } | 155 | } | 
| 153 | 156 | ||
| 154 | if self.options.shuffle_worldports: | 157 | if self.options.shuffle_worldports: | 
| 155 | slot_data["port_pairings"] = self.port_pairings | 158 | def get_port_ap_id(port_id): | 
| 159 | return self.static_logic.objects.ports[port_id].ap_id | ||
| 160 | |||
| 161 | slot_data["port_pairings"] = {get_port_ap_id(from_id): get_port_ap_id(to_id) | ||
| 162 | for from_id, to_id in self.port_pairings.items()} | ||
| 156 | 163 | ||
| 157 | return slot_data | 164 | return slot_data | 
| 158 | 165 | ||
| diff --git a/apworld/client/gamedata.gd b/apworld/client/gamedata.gd index 3a35125..d7e3136 100644 --- a/apworld/client/gamedata.gd +++ b/apworld/client/gamedata.gd | |||
| @@ -15,6 +15,7 @@ var symbol_item_ids = [] | |||
| 15 | var anti_trap_ids = {} | 15 | var anti_trap_ids = {} | 
| 16 | var location_name_by_id = {} | 16 | var location_name_by_id = {} | 
| 17 | var ending_display_name_by_name = {} | 17 | var ending_display_name_by_name = {} | 
| 18 | var port_id_by_ap_id = {} | ||
| 18 | 19 | ||
| 19 | var kSYMBOL_ITEMS | 20 | var kSYMBOL_ITEMS | 
| 20 | 21 | ||
| @@ -99,6 +100,9 @@ func load(data_bytes): | |||
| 99 | var map_data = port_id_by_map_node_path[map.get_name()] | 100 | var map_data = port_id_by_map_node_path[map.get_name()] | 
| 100 | map_data[port.get_path()] = port.get_id() | 101 | map_data[port.get_path()] = port.get_id() | 
| 101 | 102 | ||
| 103 | if port.has_ap_id(): | ||
| 104 | port_id_by_ap_id[port.get_ap_id()] = port.get_id() | ||
| 105 | |||
| 102 | for progressive in objects.get_progressives(): | 106 | for progressive in objects.get_progressives(): | 
| 103 | progressive_id_by_ap_id[progressive.get_ap_id()] = progressive.get_id() | 107 | progressive_id_by_ap_id[progressive.get_ap_id()] = progressive.get_id() | 
| 104 | 108 | ||
| diff --git a/apworld/client/manager.gd b/apworld/client/manager.gd index aa07559..727d17a 100644 --- a/apworld/client/manager.gd +++ b/apworld/client/manager.gd | |||
| @@ -472,7 +472,9 @@ func _client_connected(slot_data): | |||
| 472 | var raw_pp = slot_data.get("port_pairings") | 472 | var raw_pp = slot_data.get("port_pairings") | 
| 473 | 473 | ||
| 474 | for p1 in raw_pp.keys(): | 474 | for p1 in raw_pp.keys(): | 
| 475 | port_pairings[int(p1)] = int(raw_pp[p1]) | 475 | port_pairings[gamedata.port_id_by_ap_id[int(p1)]] = gamedata.port_id_by_ap_id[int( | 
| 476 | raw_pp[p1] | ||
| 477 | )] | ||
| 476 | 478 | ||
| 477 | # Set up item locks. | 479 | # Set up item locks. | 
| 478 | _item_locks = {} | 480 | _item_locks = {} | 
| diff --git a/apworld/context.py b/apworld/context.py index e2d80cd..52b04ae 100644 --- a/apworld/context.py +++ b/apworld/context.py | |||
| @@ -30,6 +30,16 @@ KEY_STORAGE_MAPPING = { | |||
| 30 | REVERSE_KEY_STORAGE_MAPPING = {t: k for k, t in KEY_STORAGE_MAPPING.items()} | 30 | REVERSE_KEY_STORAGE_MAPPING = {t: k for k, t in KEY_STORAGE_MAPPING.items()} | 
| 31 | 31 | ||
| 32 | 32 | ||
| 33 | # There is a distinction between an object's ID and its AP ID. The latter is stable between releases, whereas the former | ||
| 34 | # can change and is also namespaced based on the object type. We should only store AP IDs in multiworld state (such as | ||
| 35 | # slot data and data storage) to increase compatability between releases. The data we currently store is: | ||
| 36 | # - Port pairings for worldport shuffle (slot data) | ||
| 37 | # - Checked worldports for worldport shuffle (data storage) | ||
| 38 | # - Latched doors (data storage) | ||
| 39 | # The client generally deals in the actual object IDs rather than the stable IDs, although it does have to convert the | ||
| 40 | # port pairing IDs when reading them from slot data. The context (this file here) does the work of converting back and | ||
| 41 | # forth between the values. AP IDs are converted to IDs after reading them from data storage, and IDs are converted to | ||
| 42 | # AP IDs before sending them to data storage. | ||
| 33 | class Lingo2Manager: | 43 | class Lingo2Manager: | 
| 34 | game_ctx: "Lingo2GameContext" | 44 | game_ctx: "Lingo2GameContext" | 
| 35 | client_ctx: "Lingo2ClientContext" | 45 | client_ctx: "Lingo2ClientContext" | 
| @@ -74,6 +84,7 @@ class Lingo2Manager: | |||
| 74 | 84 | ||
| 75 | return ret | 85 | return ret | 
| 76 | 86 | ||
| 87 | # Input should be real IDs, not AP IDs | ||
| 77 | def update_worldports(self, new_worldports: set[int]) -> set[int]: | 88 | def update_worldports(self, new_worldports: set[int]) -> set[int]: | 
| 78 | ret = new_worldports.difference(self.worldports) | 89 | ret = new_worldports.difference(self.worldports) | 
| 79 | self.worldports.update(new_worldports) | 90 | self.worldports.update(new_worldports) | 
| @@ -227,6 +238,7 @@ class Lingo2GameContext: | |||
| 227 | 238 | ||
| 228 | async_start(self.send_msgs([msg]), name="update keyboard") | 239 | async_start(self.send_msgs([msg]), name="update keyboard") | 
| 229 | 240 | ||
| 241 | # Input should be real IDs, not AP IDs | ||
| 230 | def send_update_worldports(self, worldports): | 242 | def send_update_worldports(self, worldports): | 
| 231 | if self.server is None: | 243 | if self.server is None: | 
| 232 | return | 244 | return | 
| @@ -441,13 +453,15 @@ class Lingo2ClientContext(CommonContext): | |||
| 441 | elif args["key"] == self.get_datastorage_key("keyboard2"): | 453 | elif args["key"] == self.get_datastorage_key("keyboard2"): | 
| 442 | self.handle_keyboard_update(2, args) | 454 | self.handle_keyboard_update(2, args) | 
| 443 | elif args["key"] == self.get_datastorage_key("worldports"): | 455 | elif args["key"] == self.get_datastorage_key("worldports"): | 
| 444 | updates = self.manager.update_worldports(set(args["value"])) | 456 | port_ids = set(Lingo2World.static_logic.port_id_by_ap_id[ap_id] for ap_id in args["value"]) | 
| 457 | updates = self.manager.update_worldports(port_ids) | ||
| 445 | if len(updates) > 0: | 458 | if len(updates) > 0: | 
| 446 | self.manager.game_ctx.send_update_worldports(updates) | 459 | self.manager.game_ctx.send_update_worldports(updates) | 
| 447 | elif args["key"] == self.victory_data_storage_key: | 460 | elif args["key"] == self.victory_data_storage_key: | 
| 448 | self.handle_status_update(args["value"]) | 461 | self.handle_status_update(args["value"]) | 
| 449 | elif args["key"] == self.get_datastorage_key("latches"): | 462 | elif args["key"] == self.get_datastorage_key("latches"): | 
| 450 | updates = self.manager.update_latches(set(args["value"])) | 463 | door_ids = set(Lingo2World.static_logic.door_id_by_ap_id[ap_id] for ap_id in args["value"]) | 
| 464 | updates = self.manager.update_latches(door_ids) | ||
| 451 | if len(updates) > 0: | 465 | if len(updates) > 0: | 
| 452 | self.manager.game_ctx.send_update_latches(updates) | 466 | self.manager.game_ctx.send_update_latches(updates) | 
| 453 | 467 | ||
| @@ -515,14 +529,16 @@ class Lingo2ClientContext(CommonContext): | |||
| 515 | if len(updates) > 0: | 529 | if len(updates) > 0: | 
| 516 | self.manager.game_ctx.send_update_keyboard(updates) | 530 | self.manager.game_ctx.send_update_keyboard(updates) | 
| 517 | 531 | ||
| 532 | # Input should be real IDs, not AP IDs | ||
| 518 | async def update_worldports(self, updates: set[int]): | 533 | async def update_worldports(self, updates: set[int]): | 
| 534 | port_ap_ids = [Lingo2World.static_logic.objects.ports[port_id].ap_id for port_id in updates] | ||
| 519 | await self.send_msgs([{ | 535 | await self.send_msgs([{ | 
| 520 | "cmd": "Set", | 536 | "cmd": "Set", | 
| 521 | "key": self.get_datastorage_key("worldports"), | 537 | "key": self.get_datastorage_key("worldports"), | 
| 522 | "want_reply": True, | 538 | "want_reply": True, | 
| 523 | "operations": [{ | 539 | "operations": [{ | 
| 524 | "operation": "update", | 540 | "operation": "update", | 
| 525 | "value": updates | 541 | "value": port_ap_ids | 
| 526 | }] | 542 | }] | 
| 527 | }]) | 543 | }]) | 
| 528 | 544 | ||
| @@ -532,13 +548,14 @@ class Lingo2ClientContext(CommonContext): | |||
| 532 | self.manager.game_ctx.send_accessible_locations() | 548 | self.manager.game_ctx.send_accessible_locations() | 
| 533 | 549 | ||
| 534 | async def update_latches(self, updates: set[int]): | 550 | async def update_latches(self, updates: set[int]): | 
| 551 | door_ap_ids = [Lingo2World.static_logic.objects.doors[door_id].ap_id for door_id in updates] | ||
| 535 | await self.send_msgs([{ | 552 | await self.send_msgs([{ | 
| 536 | "cmd": "Set", | 553 | "cmd": "Set", | 
| 537 | "key": self.get_datastorage_key("latches"), | 554 | "key": self.get_datastorage_key("latches"), | 
| 538 | "want_reply": True, | 555 | "want_reply": True, | 
| 539 | "operations": [{ | 556 | "operations": [{ | 
| 540 | "operation": "update", | 557 | "operation": "update", | 
| 541 | "value": updates | 558 | "value": door_ap_ids | 
| 542 | }] | 559 | }] | 
| 543 | }]) | 560 | }]) | 
| 544 | 561 | ||
| @@ -590,12 +607,14 @@ async def process_game_cmd(manager: Lingo2Manager, args: dict): | |||
| 590 | async_start(manager.client_ctx.update_keyboard(updates), name="client update keyboard") | 607 | async_start(manager.client_ctx.update_keyboard(updates), name="client update keyboard") | 
| 591 | elif cmd == "CheckWorldport": | 608 | elif cmd == "CheckWorldport": | 
| 592 | port_id = args["port_id"] | 609 | port_id = args["port_id"] | 
| 610 | port_ap_id = Lingo2World.static_logic.objects.ports[port_id].ap_id | ||
| 593 | worldports = {port_id} | 611 | worldports = {port_id} | 
| 594 | 612 | ||
| 595 | # Also check the reverse port if it's a two-way connection. | 613 | # Also check the reverse port if it's a two-way connection. | 
| 596 | port_pairings = manager.client_ctx.slot_data["port_pairings"] | 614 | port_pairings = manager.client_ctx.slot_data["port_pairings"] | 
| 597 | if str(port_id) in port_pairings and port_pairings.get(str(port_pairings[str(port_id)]), None) == port_id: | 615 | if str(port_ap_id) in port_pairings and\ | 
| 598 | worldports.add(port_pairings[str(port_id)]) | 616 | port_pairings.get(str(port_pairings[str(port_ap_id)]), None) == port_ap_id: | 
| 617 | worldports.add(Lingo2World.static_logic.port_id_by_ap_id[port_pairings[str(port_ap_id)]]) | ||
| 599 | 618 | ||
| 600 | updates = manager.update_worldports(worldports) | 619 | updates = manager.update_worldports(worldports) | 
| 601 | if len(updates) > 0: | 620 | if len(updates) > 0: | 
| diff --git a/apworld/static_logic.py b/apworld/static_logic.py index 8e07b82..8a84111 100644 --- a/apworld/static_logic.py +++ b/apworld/static_logic.py | |||
| @@ -15,6 +15,9 @@ class Lingo2StaticLogic: | |||
| 15 | 15 | ||
| 16 | letter_weights: dict[str, int] | 16 | letter_weights: dict[str, int] | 
| 17 | 17 | ||
| 18 | door_id_by_ap_id: dict[int, int] | ||
| 19 | port_id_by_ap_id: dict[int, int] | ||
| 20 | |||
| 18 | def __init__(self): | 21 | def __init__(self): | 
| 19 | self.item_id_to_name = {} | 22 | self.item_id_to_name = {} | 
| 20 | self.location_id_to_name = {} | 23 | self.location_id_to_name = {} | 
| @@ -83,6 +86,9 @@ class Lingo2StaticLogic: | |||
| 83 | for letter in panel.answer.upper(): | 86 | for letter in panel.answer.upper(): | 
| 84 | self.letter_weights[letter] = self.letter_weights.get(letter, 0) + 1 | 87 | self.letter_weights[letter] = self.letter_weights.get(letter, 0) + 1 | 
| 85 | 88 | ||
| 89 | self.door_id_by_ap_id = {door.ap_id: door.id for door in self.objects.doors if door.HasField("ap_id")} | ||
| 90 | self.port_id_by_ap_id = {port.ap_id: port.id for port in self.objects.ports if port.HasField("ap_id")} | ||
| 91 | |||
| 86 | def get_door_item_name(self, door: data_pb2.Door) -> str: | 92 | def get_door_item_name(self, door: data_pb2.Door) -> str: | 
| 87 | return f"{self.get_map_object_map_name(door)} - {door.name}" | 93 | return f"{self.get_map_object_map_name(door)} - {door.name}" | 
| 88 | 94 | ||
| diff --git a/apworld/tracker.py b/apworld/tracker.py index c65317c..d473af4 100644 --- a/apworld/tracker.py +++ b/apworld/tracker.py | |||
| @@ -47,7 +47,10 @@ class Tracker: | |||
| 47 | self.world.create_regions() | 47 | self.world.create_regions() | 
| 48 | 48 | ||
| 49 | if self.world.options.shuffle_worldports: | 49 | if self.world.options.shuffle_worldports: | 
| 50 | port_pairings = {int(fp): int(tp) for fp, tp in slot_data["port_pairings"].items()} | 50 | port_pairings = { | 
| 51 | self.world.static_logic.port_id_by_ap_id[int(fp)]: self.world.static_logic.port_id_by_ap_id[int(tp)] | ||
| 52 | for fp, tp in slot_data["port_pairings"].items() | ||
| 53 | } | ||
| 51 | connect_ports_from_ut(port_pairings, self.world) | 54 | connect_ports_from_ut(port_pairings, self.world) | 
| 52 | 55 | ||
| 53 | self.refresh_state() | 56 | self.refresh_state() | 
