about summary refs log tree commit diff stats
path: root/data
diff options
context:
space:
mode:
Diffstat (limited to 'data')
-rw-r--r--data/maps/the_symbolic/doors.txtpb38
1 files changed, 33 insertions, 5 deletions
diff --git a/data/maps/the_symbolic/doors.txtpb b/data/maps/the_symbolic/doors.txtpb index 5a443e7..7728e0d 100644 --- a/data/maps/the_symbolic/doors.txtpb +++ b/data/maps/the_symbolic/doors.txtpb
@@ -139,17 +139,13 @@ doors {
139doors { 139doors {
140 name: "Poetry Room Panels" 140 name: "Poetry Room Panels"
141 type: LOCATION_ONLY 141 type: LOCATION_ONLY
142 panels { room: "Poetry Room 1" name: "ABSORBED" }
143 panels { room: "Poetry Room 1" name: "PRIMORDIAL" }
144 panels { room: "Poetry Room 2" name: "NOT" } 142 panels { room: "Poetry Room 2" name: "NOT" }
145 panels { room: "Poetry Room 2" name: "THERE" } 143 panels { room: "Poetry Room 2" name: "THERE" }
146 panels { room: "Poetry Room 2" name: "NOT THERE" } 144 panels { room: "Poetry Room 2" name: "NOT THERE" }
147 panels { room: "Poetry Room 3" name: "NOT" } 145 panels { room: "Poetry Room 3" name: "NOT" }
148 panels { room: "Poetry Room 3" name: "PRETTY" } 146 panels { room: "Poetry Room 3" name: "PRETTY" }
149 panels { room: "Poetry Room 3" name: "NOT PRETTY" }
150 panels { room: "Poetry Room Left" name: "NOT" } 147 panels { room: "Poetry Room Left" name: "NOT" }
151 panels { room: "Poetry Room Left" name: "TRUE" } 148 panels { room: "Poetry Room Left" name: "TRUE" }
152 panels { room: "Poetry Room Left" name: "NOT TRUE" }
153 panels { room: "Poetry Room Left Left" name: "NOT (1)" } 149 panels { room: "Poetry Room Left Left" name: "NOT (1)" }
154 panels { room: "Poetry Room Left Left" name: "NOT (2)" } 150 panels { room: "Poetry Room Left Left" name: "NOT (2)" }
155 panels { room: "Poetry Room Left Left" name: "LEFT" } 151 panels { room: "Poetry Room Left Left" name: "LEFT" }
@@ -160,7 +156,6 @@ doors {
160 panels { room: "Poetry Room Left Right" name: "NOT NOT MISS" } 156 panels { room: "Poetry Room Left Right" name: "NOT NOT MISS" }
161 panels { room: "Poetry Room Right" name: "NOT" } 157 panels { room: "Poetry Room Right" name: "NOT" }
162 panels { room: "Poetry Room Right" name: "BETTER" } 158 panels { room: "Poetry Room Right" name: "BETTER" }
163 panels { room: "Poetry Room Right" name: "NOT BETTER" }
164 panels { room: "Poetry Room Right Left" name: "NOT (1)" } 159 panels { room: "Poetry Room Right Left" name: "NOT (1)" }
165 panels { room: "Poetry Room Right Left" name: "NOT (2)" } 160 panels { room: "Poetry Room Right Left" name: "NOT (2)" }
166 panels { room: "Poetry Room Right Left" name: "TABLET" } 161 panels { room: "Poetry Room Right Left" name: "TABLET" }
@@ -276,6 +271,9 @@ doors {
276doors { 271doors {
277 name: "Main Area Exit" 272 name: "Main Area Exit"
278 type: EVENT 273 type: EVENT
274 # The game logic here requires you to solve every panel on the map, EXCEPT:
275 # 1) The four panels past the door, and
276 # 2) Any panel that has a proxy.
279 panels { room: "Main Area" name: "JUSTICE" } 277 panels { room: "Main Area" name: "JUSTICE" }
280 panels { room: "Main Area" name: "NOTICE (1)" } 278 panels { room: "Main Area" name: "NOTICE (1)" }
281 panels { room: "Main Area" name: "NOTICE (2)" } 279 panels { room: "Main Area" name: "NOTICE (2)" }
@@ -353,6 +351,36 @@ doors {
353 panels { room: "Main Area" name: "LIKE" } 351 panels { room: "Main Area" name: "LIKE" }
354 panels { room: "Main Area" name: "NEEDLESS" } 352 panels { room: "Main Area" name: "NEEDLESS" }
355 panels { room: "Main Area" name: "RESTLESS" } 353 panels { room: "Main Area" name: "RESTLESS" }
354 panels { room: "Poetry Room 2" name: "NOT" }
355 panels { room: "Poetry Room 2" name: "THERE" }
356 panels { room: "Poetry Room 2" name: "NOT THERE" }
357 panels { room: "Poetry Room 3" name: "NOT" }
358 panels { room: "Poetry Room 3" name: "PRETTY" }
359 panels { room: "Poetry Room Left" name: "NOT" }
360 panels { room: "Poetry Room Left" name: "TRUE" }
361 panels { room: "Poetry Room Left Left" name: "NOT (1)" }
362 panels { room: "Poetry Room Left Left" name: "NOT (2)" }
363 panels { room: "Poetry Room Left Left" name: "LEFT" }
364 panels { room: "Poetry Room Left Left" name: "NOT NOT LEFT" }
365 panels { room: "Poetry Room Left Right" name: "NOT (1)" }
366 panels { room: "Poetry Room Left Right" name: "NOT (2)" }
367 panels { room: "Poetry Room Left Right" name: "MISS" }
368 panels { room: "Poetry Room Left Right" name: "NOT NOT MISS" }
369 panels { room: "Poetry Room Right" name: "NOT" }
370 panels { room: "Poetry Room Right" name: "BETTER" }
371 panels { room: "Poetry Room Right Left" name: "NOT (1)" }
372 panels { room: "Poetry Room Right Left" name: "NOT (2)" }
373 panels { room: "Poetry Room Right Left" name: "TABLET" }
374 panels { room: "Poetry Room Right Left" name: "NOT NOT TABLET" }
375 panels { room: "Poetry Room Right Right" name: "NOT (1)" }
376 panels { room: "Poetry Room Right Right" name: "NOT (2)" }
377 panels { room: "Poetry Room Right Right" name: "NOT (3)" }
378 panels { room: "Poetry Room Right Right" name: "NOT NOT NOT" }
379 panels { room: "Whirred Room" name: "TAIPEI" }
380 panels { room: "Whirred Room" name: "NAYSAYER" }
381 panels { room: "Whirred Room" name: "NAY" }
382 panels { room: "Whirred Room" name: "INDEX (1)" }
383 panels { room: "Whirred Room" name: "INDEX (2)" }
356} 384}
357doors { 385doors {
358 name: "Mastery" 386 name: "Mastery"
>(self.send_msgs([msg]), name="location info") def send_text_message(self, parts): msg = { "cmd": "TextMessage", "data": parts, } async_start(self.send_msgs([msg]), name="notif") def send_accessible_locations(self): msg = { "cmd": "AccessibleLocations", "locations": list(self.tracker.accessible_locations), } async_start(self.send_msgs([msg]), name="accessible locations") def send_update_locations(self, locations): msg = { "cmd": "UpdateLocations", "locations": locations, } async_start(self.send_msgs([msg]), name="update locations") async def send_msgs(self, msgs: list[Any]) -> None: """ `msgs` JSON serializable """ if not self.server or not self.server.socket.open or self.server.socket.closed: return await self.server.socket.send(encode(msgs)) class Lingo2ClientContext(CommonContext): game_ctx: Lingo2GameContext game = "Lingo 2" items_handling = 0b111 slot_data: dict[str, Any] | None def __init__(self, server_address: str | None = None, password: str | None = None): super().__init__(server_address, password) def make_gui(self): ui = super().make_gui() ui.base_title = "Archipelago Lingo 2 Client" return ui async def server_auth(self, password_requested: bool = False): self.auth = self.username await self.send_connect() def on_package(self, cmd: str, args: dict): if cmd == "RoomInfo": self.seed_name = args.get("seed_name", None) elif cmd == "Connected": self.slot_data = args.get("slot_data", None) if self.game_ctx.server is not None: self.game_ctx.send_connected() self.game_ctx.tracker.setup_slot(self.slot_data) elif cmd == "RoomUpdate": if self.game_ctx.server is not None: self.game_ctx.send_update_locations(args["checked_locations"]) elif cmd == "ReceivedItems": self.game_ctx.tracker.set_collected_items(self.items_received) if self.game_ctx.server is not None: cur_index = 0 items = [] for item in args["items"]: index = cur_index + args["index"] cur_index += 1 item_msg = { "id": item.item, "index": index, "flags": item.flags, "text": self.item_names.lookup_in_slot(item.item, self.slot), } if item.player != self.slot: item_msg["sender"] = self.player_names.get(item.player) items.append(item_msg) self.game_ctx.send_item_received(items) if any(ItemClassification.progression in ItemClassification(item.flags) for item in args["items"]): self.game_ctx.send_accessible_locations() elif cmd == "PrintJSON": if self.game_ctx.server is not None: if "receiving" in args and "item" in args and args["item"].player == self.slot: item_name = self.item_names.lookup_in_slot(args["item"].item, args["receiving"]) location_name = self.location_names.lookup_in_slot(args["item"].location, args["item"].player) receiver_name = self.player_names.get(args["receiving"]) if args["type"] == "Hint" and not args.get("found", False): self.game_ctx.send_hint_received(item_name, location_name, receiver_name, args["item"].flags, int(args["receiving"]) == self.slot) elif args["receiving"] != self.slot: self.game_ctx.send_item_sent_notification(item_name, receiver_name, args["item"].flags) parts = [] for message_part in args["data"]: if "type" not in message_part and "text" in message_part: parts.append({"type": "text", "text": message_part["text"]}) elif message_part["type"] == "player_id": parts.append({ "type": "player", "text": self.player_names.get(int(message_part["text"])), "self": int(int(message_part["text"]) == self.slot), }) elif message_part["type"] == "item_id": parts.append({ "type": "item", "text": self.item_names.lookup_in_slot(int(message_part["text"]), message_part["player"]), "flags": message_part["flags"], }) elif message_part["type"] == "location_id": parts.append({ "type": "location", "text": self.location_names.lookup_in_slot(int(message_part["text"]), message_part["player"]) }) elif "text" in message_part: parts.append({"type": "text", "text": message_part["text"]}) self.game_ctx.send_text_message(parts) elif cmd == "LocationInfo": if self.game_ctx.server is not None: locations = [] for location in args["locations"]: locations.append({ "id": location.location, "item": self.item_names.lookup_in_slot(location.item, location.player), "player": self.player_names.get(location.player), "flags": location.flags, "self": int(location.player) == self.slot, }) self.game_ctx.send_location_info(locations) if cmd in ["Connected", "RoomUpdate"]: self.game_ctx.tracker.set_checked_locations(self.checked_locations) async def pipe_loop(ctx: Lingo2GameContext): while not ctx.client.exit_event.is_set(): try: socket = await websockets.connect("ws://localhost", port=PORT, ping_timeout=None, ping_interval=None, max_size=MESSAGE_MAX_SIZE) ctx.server = Endpoint(socket) logger.info("Connected to Lingo 2!") if ctx.client.auth is not None: ctx.send_connected() ctx.send_accessible_locations() async for data in ctx.server.socket: for msg in decode(data): await process_game_cmd(ctx, msg) except ConnectionRefusedError: logger.info("Could not connect to Lingo 2.") finally: ctx.server = None async def process_game_cmd(ctx: Lingo2GameContext, args: dict): cmd = args["cmd"] if cmd == "Connect": server = args.get("server") player = args.get("player") password = args.get("password") if password != "": server_address = f"{player}:{password}@{server}" else: server_address = f"{player}:None@{server}" async_start(ctx.client.connect(server_address), name="client connect") elif cmd == "Disconnect": async_start(ctx.client.disconnect(), name="client disconnect") elif cmd in ["Sync", "LocationChecks", "Say", "StatusUpdate", "LocationScouts"]: async_start(ctx.client.send_msgs([args]), name="client forward") elif cmd == "Quit": ctx.client.exit_event.set() async def run_game(): exe_file = settings.get_settings().lingo2_options.exe_file # This ensures we can use Steam features without having to open the game # through steam. steam_appid_path = os.path.join(os.path.dirname(exe_file), "steam_appid.txt") with open(steam_appid_path, "w") as said_handle: said_handle.write("2523310") if Lingo2World.zip_path is not None: # This is a packaged apworld. init_scene = pkgutil.get_data(__name__, "client/run_from_apworld.tscn") init_path = Utils.local_path("data", "lingo2_init.tscn") with open(init_path, "wb") as file_handle: file_handle.write(init_scene) subprocess.Popen( [ exe_file, "--scene", init_path, "--", str(Lingo2World.zip_path.absolute()), ], cwd=os.path.dirname(exe_file), ) else: # The world is unzipped and being run in source. subprocess.Popen( [ exe_file, "--scene", Utils.local_path("worlds", "lingo2", "client", "run_from_source.tscn"), "--", Utils.local_path("worlds", "lingo2", "client"), ], cwd=os.path.dirname(exe_file), ) def client_main(*launch_args: str) -> None: async def main(args): async_start(run_game()) client_ctx = Lingo2ClientContext(args.connect, args.password) game_ctx = Lingo2GameContext() client_ctx.game_ctx = game_ctx game_ctx.client = client_ctx client_ctx.server_task = asyncio.create_task(server_loop(client_ctx), name="ServerLoop") if gui_enabled: client_ctx.run_gui() client_ctx.run_cli() pipe_task = asyncio.create_task(pipe_loop(game_ctx), name="GameWatcher") try: await pipe_task except Exception as e: logger.exception(e) await client_ctx.exit_event.wait() client_ctx.ui.stop() await client_ctx.shutdown() Utils.init_logging("Lingo2Client", exception_logger="Client") import colorama parser = get_base_parser(description="Lingo 2 Archipelago Client") parser.add_argument('--name', default=None, help="Slot Name to connect as.") parser.add_argument("url", nargs="?", help="Archipelago connection url") args = parser.parse_args(launch_args) args = handle_url_arg(args, parser=parser) colorama.just_fix_windows_console() asyncio.run(main(args)) colorama.deinit()