From c9da387ede51f207825b63d9f13036a7b661d4b3 Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Thu, 7 Aug 2025 16:05:15 -0400 Subject: Started apworld vcpkg's libprotobuf is older than what PIP has, but neither are completely up to date either. Ugh. Doors have a room now because that's where the location will go. --- .gitignore | 2 ++ apworld/__init__.py | 38 ++++++++++++++++++++++++++++++++++++++ apworld/locations.py | 5 +++++ apworld/options.py | 8 ++++++++ apworld/player_logic.py | 24 ++++++++++++++++++++++++ apworld/regions.py | 28 ++++++++++++++++++++++++++++ apworld/requirements.txt | 1 + apworld/static_logic.py | 9 +++++++++ data/maps/the_entry/doors.txtpb | 34 ++++++++++++++++++++++++++++++++++ proto/data.proto | 2 ++ proto/human.proto | 1 + tools/datapacker/main.cpp | 8 ++++++++ vcpkg.json | 8 +++++++- 13 files changed, 167 insertions(+), 1 deletion(-) create mode 100644 apworld/__init__.py create mode 100644 apworld/locations.py create mode 100644 apworld/options.py create mode 100644 apworld/player_logic.py create mode 100644 apworld/regions.py create mode 100644 apworld/requirements.txt create mode 100644 apworld/static_logic.py diff --git a/.gitignore b/.gitignore index bf52fcf..84ef9bb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ build/ generated/ .vscode/ +apworld/generated/ +__pycache__ diff --git a/apworld/__init__.py b/apworld/__init__.py new file mode 100644 index 0000000..013910e --- /dev/null +++ b/apworld/__init__.py @@ -0,0 +1,38 @@ +""" +Archipelago init file for Lingo 2 +""" +from worlds.AutoWorld import WebWorld, World +from .options import Lingo2Options +from .player_logic import Lingo2PlayerLogic +from .regions import create_regions +from .static_logic import Lingo2StaticLogic + + +class Lingo2WebWorld(WebWorld): + rich_text_options_doc = True + theme = "grass" + + +class Lingo2World(World): + """ + Lingo 2 is a first person indie puzzle game where you solve word puzzles in a labyrinthe world. Compared to its + predecessor, Lingo 2 has new mechanics, more areas, and a unique progression system where you have to unlock letters + before using them in puzzle solutions. + """ + game = "Lingo 2" + web = Lingo2WebWorld() + + options_dataclass = Lingo2Options + options: Lingo2Options + + item_name_to_id = {} + location_name_to_id = {} + + static_logic = Lingo2StaticLogic() + player_logic: Lingo2PlayerLogic + + def generate_early(self): + self.player_logic = Lingo2PlayerLogic(self) + + def create_regions(self): + create_regions(self) diff --git a/apworld/locations.py b/apworld/locations.py new file mode 100644 index 0000000..818be39 --- /dev/null +++ b/apworld/locations.py @@ -0,0 +1,5 @@ +from BaseClasses import Location + + +class Lingo2Location(Location): + game: str = "Lingo 2" \ No newline at end of file diff --git a/apworld/options.py b/apworld/options.py new file mode 100644 index 0000000..f33f5af --- /dev/null +++ b/apworld/options.py @@ -0,0 +1,8 @@ +from dataclasses import dataclass + +from Options import PerGameCommonOptions + + +@dataclass +class Lingo2Options(PerGameCommonOptions): + pass diff --git a/apworld/player_logic.py b/apworld/player_logic.py new file mode 100644 index 0000000..f54573f --- /dev/null +++ b/apworld/player_logic.py @@ -0,0 +1,24 @@ +from typing import TYPE_CHECKING, NamedTuple + +if TYPE_CHECKING: + from . import Lingo2World + + +class PlayerLocation(NamedTuple): + name: str + code: int | None + + +class Lingo2PlayerLogic: + locations_by_room: dict[int, list[PlayerLocation]] + + def __init__(self, world: "Lingo2World"): + self.locations_by_room = {} + + code = 1 + for door in world.static_logic.objects.doors: + if not door.HasField("room_id"): + continue + + self.locations_by_room.setdefault(door.room_id, []).append(PlayerLocation(door.name, code)) + code += 1 diff --git a/apworld/regions.py b/apworld/regions.py new file mode 100644 index 0000000..24c2281 --- /dev/null +++ b/apworld/regions.py @@ -0,0 +1,28 @@ +from typing import TYPE_CHECKING + +from BaseClasses import Region +from .locations import Lingo2Location + +if TYPE_CHECKING: + from . import Lingo2World + + +def create_region(room, world: "Lingo2World") -> Region: + new_region = Region(room.name, world.player, world.multiworld) + + for location in world.player_logic.locations_by_room.get(room.id, {}): + new_location = Lingo2Location(world.player, location.name, location.code, new_region) + new_region.locations.append(new_location) + + return new_region + + +def create_regions(world: "Lingo2World"): + regions = { + "Menu": Region("Menu", world.player, world.multiworld) + } + + for room in world.static_logic.objects.rooms: + regions[room.name] = create_region(room, world) + + world.multiworld.regions += regions.values() diff --git a/apworld/requirements.txt b/apworld/requirements.txt new file mode 100644 index 0000000..b701d11 --- /dev/null +++ b/apworld/requirements.txt @@ -0,0 +1 @@ +protobuf>=5.29.3 \ No newline at end of file diff --git a/apworld/static_logic.py b/apworld/static_logic.py new file mode 100644 index 0000000..6c38f1f --- /dev/null +++ b/apworld/static_logic.py @@ -0,0 +1,9 @@ +from .generated import data_pb2 as data_pb2 +import pkgutil + +class Lingo2StaticLogic: + def __init__(self): + file = pkgutil.get_data(__name__, "generated/data.binpb") + + self.objects = data_pb2.AllObjects() + self.objects.ParseFromString(bytearray(file)) diff --git a/data/maps/the_entry/doors.txtpb b/data/maps/the_entry/doors.txtpb index c43020c..0ff0797 100644 --- a/data/maps/the_entry/doors.txtpb +++ b/data/maps/the_entry/doors.txtpb @@ -3,12 +3,14 @@ doors { type: STANDARD receivers: "Components/Doors/side_1" panels { room: "Starting Room" name: "TRICK" } + location_room: "Starting Room" } doors { name: "Link Area Entrance" type: STANDARD receivers: "Components/Doors/side_2" panels { room: "Trick Room" name: "INK" answer: "link" } + location_room: "Trick Room" } # side_3 is vanilla because I don't think it's real? doors { @@ -16,12 +18,14 @@ doors { type: STANDARD receivers: "Components/Doors/side_4" panels { room: "Starting Room" name: "TRICK" answer: "treat" } + location_room: "Starting Room" } doors { name: "Second Room Right Door" type: STANDARD receivers: "Components/Doors/second_right" panels { room: "Starting Room" name: "HINT" } + location_room: "Starting Room" } doors { name: "Red Alcove Exit" @@ -29,6 +33,7 @@ doors { receivers: "Components/Doors/second_right2" panels { room: "Red Alcove" name: "DEAD" } panels { room: "Red Alcove" name: "BROW" } + location_room: "Red Alcove" } doors { name: "Four Corner Panels" @@ -43,6 +48,7 @@ doors { type: STANDARD receivers: "Components/Doors/second_right11" panels { room: "Least Blue Last" name: "CORNERS" } + location_room: "Least Blue Last" } doors { name: "Blue Alcove Exit" @@ -50,6 +56,7 @@ doors { receivers: "Components/Doors/second_right3" panels { room: "Blue Alcove" name: "BLUE" } panels { room: "Blue Alcove" name: "ARMY" } + location_room: "Blue Alcove" } doors { name: "Rabbithole Door" @@ -57,6 +64,7 @@ doors { receivers: "Components/Doors/second_right8" panels { room: "Wrath Room" name: "RABBIT" } panels { room: "Wrath Room" name: "HOLE" } + location_room: "Wrath Room" } # second_right is vanilla because it's like LOST door. doors { @@ -65,18 +73,21 @@ doors { receivers: "Components/Doors/second_right5" receivers: "Components/Doors/second_right10" panels { room: "Red Blue Halls" name: "CENTER DAY" } + location_room: "Red Blue Halls" } doors { name: "Scarf Door" type: STANDARD receivers: "Components/Doors/second_right6" panels { room: "Red Blue Halls" name: "RAIN WOMAN" } + location_room: "Red Blue Halls" } doors { name: "Blue Alcove Entrance" type: STANDARD receivers: "Components/Doors/second_right9" panels { room: "Wrath Room" name: "CORN" } + location_room: "Wrath Room" } doors { name: "Second Room Left Door" @@ -84,36 +95,42 @@ doors { receivers: "Components/Doors/second_left" # There's also the special behavior with returning from The Digital. panels { room: "Starting Room" name: "THIN" } + location_room: "Starting Room" } doors { name: "Flipped Second Room Right Door" type: STANDARD receivers: "Components/Doors/second_left_top" panels { room: "Flipped Second Room" name: "SLENDER" } + location_room: "Flipped Second Room" } doors { name: "Flipped Second Room Left Door" type: STANDARD receivers: "Components/Doors/second_right_top" panels { room: "Flipped Second Room" name: "CLUE" } + location_room: "Flipped Second Room" } doors { name: "Right Eye Entrance" type: STANDARD receivers: "Components/Doors/third_right" panels { room: "Trick Room" name: "INK" } + location_room: "Trick Room" } doors { name: "Red Blue Area Left Door" type: STANDARD receivers: "Components/Doors/fourth_right" panels { room: "Right Eye" name: "WANDER" } + location_room: "Right Eye" } doors { name: "Red Blue Area Right Door" type: ITEM_ONLY receivers: "Components/Doors/fifth_right" panels { room: "Right Eye" name: "WANDER" } + location_room: "Right Eye" } # Components/Doors/back_left_1, _3, _4, _6 are vanilla because they're nothing. doors { @@ -122,12 +139,14 @@ doors { receivers: "Components/Doors/back_left_2" panels { room: "Colored Doors Area" name: "OPEN" answer: "orange" } # "wall" is supposed to also work. idk man + location_room: "Colored Doors Area" } doors { name: "Lime Room Entrance" type: STANDARD receivers: "Components/Doors/back_left_5" panels { room: "Ctrl Tutorial" name: "RIGHT" } + location_room: "Ctrl Tutorial" } doors { name: "Revitalized Entrance" @@ -136,6 +155,7 @@ doors { receivers: "Components/Doors/back_left_9" panels { room: "Lime Room" name: "HIDE" } panels { room: "Lime Room" name: "SEEK" } + location_room: "Lime Room" } doors { name: "Control Center White Door" @@ -150,24 +170,28 @@ doors { type: STANDARD receivers: "Components/Doors/Entry/entry_proxied_2" panels { room: "Starting Room" name: "HI" answer: "bye" } + location_room: "Starting Room" } doors { name: "Composite Room Entrance" type: STANDARD receivers: "Components/Doors/Entry/entry_proxied_3" panels { room: "Starting Room" name: "HI" answer: "hidden" } + location_room: "Starting Room" } doors { name: "Flip Area Entrance" type: STANDARD receivers: "Components/Doors/Entry/entry_proxied_4" panels { room: "Starting Room" name: "HI" answer: "high" } + location_room: "Starting Room" } doors { name: "Daedalus Entrance" type: STANDARD receivers: "Components/Doors/Entry/entry_proxied_5" panels { room: "Starting Room" name: "HI" answer: "hide" } + location_room: "Starting Room" } doors { name: "Repetitive Entrance" @@ -180,12 +204,14 @@ doors { type: STANDARD receivers: "Components/Doors/Entry/entry_proxied_6" panels { room: "Shop Entrance" name: "TURN" } + location_room: "Shop Entrance" } doors { name: "Liberated Entrance" type: STANDARD receivers: "Components/Doors/Entry/entry_proxied_10" panels { room: "Flipped Pyramid Area" name: "TURN (1)" } + location_room: "Flipped Pyramid Area" } doors { name: "Flipped Pyramid Area Entrance" @@ -198,24 +224,28 @@ doors { type: STANDARD receivers: "Components/Doors/Entry/entry_proxied_11" panels { room: "Flipped Pyramid Area" name: "TURN (2)" } + location_room: "Flipped Pyramid Area" } doors { name: "Parthenon Entrance" type: STANDARD receivers: "Components/Doors/Entry/entry_proxied_7" panels { room: "Parthenon Return" name: "RETURN" } + location_room: "Parthenon Return" } doors { name: "Colored Doors Area Entrance" type: ITEM_ONLY receivers: "Components/Doors/Entry/entry_proxied_8" panels { room: "Parthenon Return" name: "RETURN" } + location_room: "Parthenon Return" } doors { name: "D Room Entrance" type: STANDARD receivers: "Components/Doors/Entry/d_1" panels { room: "Starting Room" name: "THAN" } + location_room: "Starting Room" } doors { name: "Near D Room Painting" @@ -243,6 +273,7 @@ doors { type: STANDARD receivers: "Components/Doors/Entry/entry_return_1" panels { room: "Gallery Return" name: "RETURN" } + location_room: "Gallery Return" } # entry_front_1 - _6 are part of the vanilla intro. doors { @@ -251,6 +282,7 @@ doors { receivers: "Components/Doors/Entry/entry_front_7" receivers: "Components/Doors/Entry/entry_front_8" panels { room: "Trick Room" name: "INK" } + location_room: "Trick Room" } doors { name: "Least Blue Last Panels" @@ -271,10 +303,12 @@ doors { type: STANDARD move_paintings { room: "Right Eye" name: "PSYCHIC" } panels { room: "Right Eye" name: "FAINT" } + location_room: "Right Eye" } doors { name: "Third Eye Painting" type: STANDARD move_paintings { room: "Eye Room" name: "GALLERY" } panels { room: "Eye Room" name: "I" } + location_room: "Eye Room" } \ No newline at end of file diff --git a/proto/data.proto b/proto/data.proto index 37d59f3..3417c4c 100644 --- a/proto/data.proto +++ b/proto/data.proto @@ -24,6 +24,7 @@ message Connection { message Door { uint64 id = 1; uint64 map_id = 9; + uint64 room_id = 10; string name = 2; repeated string receivers = 3; @@ -88,6 +89,7 @@ message Room { repeated uint64 paintings = 5; repeated Letter letters = 6; repeated uint64 ports = 7; + repeated uint64 doors = 9; } message Map { diff --git a/proto/human.proto b/proto/human.proto index 49eaccd..d5d03ff 100644 --- a/proto/human.proto +++ b/proto/human.proto @@ -72,6 +72,7 @@ message HumanDoor { repeated string switches = 7; DoorType type = 4; + string location_room = 5; } message HumanDoors { diff --git a/tools/datapacker/main.cpp b/tools/datapacker/main.cpp index e63f940..1dcd109 100644 --- a/tools/datapacker/main.cpp +++ b/tools/datapacker/main.cpp @@ -215,6 +215,14 @@ class DataPacker { container_.FindOrAddDoor(current_map_name, h_door.name(), std::nullopt); Door& door = *container_.all_objects().mutable_doors(door_id); + if (h_door.has_location_room()) { + door.set_room_id(container_.FindOrAddRoom( + current_map_name, h_door.location_room(), std::nullopt)); + + Room& room = *container_.all_objects().mutable_rooms(door.room_id()); + room.add_doors(door_id); + } + std::copy( h_door.receivers().begin(), h_door.receivers().end(), google::protobuf::RepeatedFieldBackInserter(door.mutable_receivers())); diff --git a/vcpkg.json b/vcpkg.json index 4b3fb5d..cd12788 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,5 +1,11 @@ { "dependencies": [ "protobuf" + ], + "overrides": [ + { + "name": "protobuf", + "version": "5.29.3" + } ] -} +} \ No newline at end of file -- cgit 1.4.1