From c0c5431800d0306d01814e9902566c9b4fc9220b Mon Sep 17 00:00:00 2001 From: Star Rauchenberger Date: Thu, 7 Aug 2025 17:18:47 -0400 Subject: Assign AP IDs to doors and panels --- CMakeLists.txt | 1 + apworld/__init__.py | 6 +- apworld/player_logic.py | 10 +- apworld/regions.py | 3 +- apworld/static_logic.py | 23 +- data/ids.txtpb | 531 ++++++++++++++++++++++++++++++++++++++++ proto/data.proto | 2 + proto/human.proto | 13 + tools/assign_ids/CMakeLists.txt | 9 + tools/assign_ids/main.cpp | 172 +++++++++++++ tools/datapacker/main.cpp | 21 ++ 11 files changed, 779 insertions(+), 12 deletions(-) create mode 100644 data/ids.txtpb create mode 100644 tools/assign_ids/CMakeLists.txt create mode 100644 tools/assign_ids/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 10025ce..fab2c86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,4 +3,5 @@ cmake_minimum_required(VERSION 3.28) project(lingo2_archipelago) add_subdirectory(proto) +add_subdirectory(tools/assign_ids) add_subdirectory(tools/datapacker) diff --git a/apworld/__init__.py b/apworld/__init__.py index 013910e..1544c7b 100644 --- a/apworld/__init__.py +++ b/apworld/__init__.py @@ -25,10 +25,10 @@ class Lingo2World(World): options_dataclass = Lingo2Options options: Lingo2Options - item_name_to_id = {} - location_name_to_id = {} - static_logic = Lingo2StaticLogic() + item_name_to_id = static_logic.item_name_to_id + location_name_to_id = static_logic.location_name_to_id + player_logic: Lingo2PlayerLogic def generate_early(self): diff --git a/apworld/player_logic.py b/apworld/player_logic.py index f54573f..675c6ae 100644 --- a/apworld/player_logic.py +++ b/apworld/player_logic.py @@ -1,3 +1,4 @@ +from .generated import common_pb2 as common_pb2 from typing import TYPE_CHECKING, NamedTuple if TYPE_CHECKING: @@ -5,7 +6,6 @@ if TYPE_CHECKING: class PlayerLocation(NamedTuple): - name: str code: int | None @@ -15,10 +15,6 @@ class Lingo2PlayerLogic: 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 + if door.type == common_pb2.DoorType.STANDARD: + self.locations_by_room.setdefault(door.room_id, []).append(PlayerLocation(door.ap_id)) diff --git a/apworld/regions.py b/apworld/regions.py index 24c2281..d388678 100644 --- a/apworld/regions.py +++ b/apworld/regions.py @@ -11,7 +11,8 @@ 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_location = Lingo2Location(world.player, world.static_logic.location_id_to_name[location.code], + location.code, new_region) new_region.locations.append(new_location) return new_region diff --git a/apworld/static_logic.py b/apworld/static_logic.py index 6c38f1f..d3ed85c 100644 --- a/apworld/static_logic.py +++ b/apworld/static_logic.py @@ -1,9 +1,30 @@ +from .generated import common_pb2 as common_pb2 from .generated import data_pb2 as data_pb2 import pkgutil class Lingo2StaticLogic: + item_id_to_name: dict[int, str] + location_id_to_name: dict[int, str] + + item_name_to_id: dict[str, int] + location_name_to_id: dict[str, int] + def __init__(self): - file = pkgutil.get_data(__name__, "generated/data.binpb") + self.item_id_to_name = {} + self.location_id_to_name = {} + file = pkgutil.get_data(__name__, "generated/data.binpb") self.objects = data_pb2.AllObjects() self.objects.ParseFromString(bytearray(file)) + + for door in self.objects.doors: + if door.type == common_pb2.DoorType.STANDARD: + location_name = f"{self.objects.rooms[door.room_id].display_name} - {door.name}" + self.location_id_to_name[door.ap_id] = location_name + + if door.type != common_pb2.DoorType.EVENT: + item_name = f"{self.objects.rooms[door.room_id].display_name} - {door.name}" + self.item_id_to_name[door.ap_id] = item_name + + self.item_name_to_id = {name: ap_id for ap_id, name in self.item_id_to_name.items()} + self.location_name_to_id = {name: ap_id for ap_id, name in self.location_id_to_name.items()} diff --git a/data/ids.txtpb b/data/ids.txtpb new file mode 100644 index 0000000..d6ad423 --- /dev/null +++ b/data/ids.txtpb @@ -0,0 +1,531 @@ +maps { + key: "the_entry" + value { + doors { + key: "Blue Alcove Entrance" + value: 12 + } + doors { + key: "Blue Alcove Exit" + value: 8 + } + doors { + key: "Colored Doors Area Entrance" + value: 33 + } + doors { + key: "Composite Room Entrance" + value: 24 + } + doors { + key: "Control Center White Door" + value: 22 + } + doors { + key: "Corners Painting" + value: 7 + } + doors { + key: "D Room Entrance" + value: 34 + } + doors { + key: "D Room Panels" + value: 36 + } + doors { + key: "Daedalus Entrance" + value: 26 + } + doors { + key: "Flip Area Entrance" + value: 25 + } + doors { + key: "Flipped Pyramid Area Entrance" + value: 30 + } + doors { + key: "Flipped Second Room Left Door" + value: 15 + } + doors { + key: "Flipped Second Room Right Door" + value: 14 + } + doors { + key: "Four Corner Panels" + value: 6 + } + doors { + key: "Gallery Entrance" + value: 37 + } + doors { + key: "L Room Entrance" + value: 38 + } + doors { + key: "Least Blue Last Panels" + value: 39 + } + doors { + key: "Liberated Entrance" + value: 29 + } + doors { + key: "Lime Room Entrance" + value: 20 + } + doors { + key: "Link Area Entrance" + value: 2 + } + doors { + key: "Literate Entrance" + value: 31 + } + doors { + key: "Near D Room Painting" + value: 35 + } + doors { + key: "Noon Door" + value: 10 + } + doors { + key: "Orange Door Hider" + value: 19 + } + doors { + key: "Parthenon Entrance" + value: 32 + } + doors { + key: "Rabbithole Door" + value: 9 + } + doors { + key: "Red Alcove Exit" + value: 5 + } + doors { + key: "Red Blue Area Left Door" + value: 17 + } + doors { + key: "Red Blue Area Right Door" + value: 18 + } + doors { + key: "Red Room Painting" + value: 40 + } + doors { + key: "Repetitive Entrance" + value: 27 + } + doors { + key: "Revitalized Entrance" + value: 21 + } + doors { + key: "Right Eye Entrance" + value: 16 + } + doors { + key: "Scarf Door" + value: 11 + } + doors { + key: "Second Room Left Door" + value: 13 + } + doors { + key: "Second Room Right Door" + value: 4 + } + doors { + key: "Shop Entrance" + value: 28 + } + doors { + key: "Third Eye Painting" + value: 41 + } + doors { + key: "Trick Door" + value: 1 + } + doors { + key: "Trick To Shop Door" + value: 3 + } + doors { + key: "X Area Entrance" + value: 23 + } + rooms { + key: "Blue Alcove" + value { + panels { + key: "ARMY" + value: 80 + } + panels { + key: "BLUE" + value: 79 + } + } + } + rooms { + key: "Colored Doors Area" + value { + panels { + key: "OPEN" + value: 60 + } + } + } + rooms { + key: "Ctrl Tutorial" + value { + panels { + key: "RIGHT" + value: 64 + } + } + } + rooms { + key: "D Room" + value { + panels { + key: "BASEBALL" + value: 70 + } + panels { + key: "BIKERS" + value: 71 + } + panels { + key: "BLACK" + value: 74 + } + panels { + key: "BOWLER" + value: 77 + } + panels { + key: "CARPENTER" + value: 78 + } + panels { + key: "COWBOY" + value: 75 + } + panels { + key: "RED" + value: 72 + } + panels { + key: "SPRAY" + value: 76 + } + panels { + key: "SUN" + value: 73 + } + } + } + rooms { + key: "Eye Room" + value { + panels { + key: "I" + value: 90 + } + } + } + rooms { + key: "Flipped Link Area" + value { + panels { + key: "WANDER" + value: 42 + } + } + } + rooms { + key: "Flipped Pyramid Area" + value { + panels { + key: "TURN (1)" + value: 48 + } + panels { + key: "TURN (2)" + value: 49 + } + } + } + rooms { + key: "Flipped Right Eye" + value { + panels { + key: "HERE" + value: 86 + } + panels { + key: "WHERE" + value: 85 + } + } + } + rooms { + key: "Flipped Second Room" + value { + panels { + key: "CLUE" + value: 46 + } + panels { + key: "SLENDER" + value: 47 + } + } + } + rooms { + key: "Gallery Return" + value { + panels { + key: "RETURN" + value: 61 + } + } + } + rooms { + key: "Least Blue Last" + value { + panels { + key: "AIL" + value: 58 + } + panels { + key: "CAPABLE (1)" + value: 50 + } + panels { + key: "CAPABLE (2)" + value: 51 + } + panels { + key: "CORNERS" + value: 59 + } + panels { + key: "LABEL" + value: 57 + } + panels { + key: "LUSTRE" + value: 52 + } + panels { + key: "OLD" + value: 55 + } + panels { + key: "STEALER" + value: 54 + } + panels { + key: "TRUST" + value: 56 + } + panels { + key: "WANT" + value: 53 + } + } + } + rooms { + key: "Lime Room" + value { + panels { + key: "COLOR" + value: 45 + } + panels { + key: "HIDE" + value: 43 + } + panels { + key: "SEEK" + value: 44 + } + } + } + rooms { + key: "Link Area" + value { + panels { + key: "WANDER" + value: 63 + } + } + } + rooms { + key: "Parthenon Return" + value { + panels { + key: "RETURN" + value: 87 + } + } + } + rooms { + key: "Rabbit Hole" + value { + panels { + key: "PUZZLE" + value: 84 + } + } + } + rooms { + key: "Red Alcove" + value { + panels { + key: "BROW" + value: 88 + } + panels { + key: "DEAD" + value: 89 + } + } + } + rooms { + key: "Red Blue Halls" + value { + panels { + key: "CENTER" + value: 97 + } + panels { + key: "CENTER DAY" + value: 99 + } + panels { + key: "DAY" + value: 98 + } + panels { + key: "RAIN" + value: 101 + } + panels { + key: "RAIN WOMAN" + value: 103 + } + panels { + key: "WANDER" + value: 100 + } + panels { + key: "WOMAN" + value: 102 + } + } + } + rooms { + key: "Right Eye" + value { + panels { + key: "EYE" + value: 81 + } + panels { + key: "FAINT" + value: 83 + } + panels { + key: "WANDER" + value: 82 + } + } + } + rooms { + key: "Shop Entrance" + value { + panels { + key: "TURN" + value: 104 + } + } + } + rooms { + key: "Starting Room" + value { + panels { + key: "EYE" + value: 93 + } + panels { + key: "HI" + value: 91 + } + panels { + key: "HINT" + value: 94 + } + panels { + key: "THAN" + value: 96 + } + panels { + key: "THIN" + value: 95 + } + panels { + key: "TRICK" + value: 92 + } + } + } + rooms { + key: "Trick Room" + value { + panels { + key: "INK" + value: 62 + } + } + } + rooms { + key: "Wrath Room" + value { + panels { + key: "CORN" + value: 69 + } + panels { + key: "DICE" + value: 68 + } + panels { + key: "HOLE" + value: 66 + } + panels { + key: "RABBIT" + value: 65 + } + panels { + key: "WREATH" + value: 67 + } + } + } + } +} diff --git a/proto/data.proto b/proto/data.proto index 3417c4c..bea2563 100644 --- a/proto/data.proto +++ b/proto/data.proto @@ -23,6 +23,7 @@ message Connection { message Door { uint64 id = 1; + uint64 ap_id = 11; uint64 map_id = 9; uint64 room_id = 10; string name = 2; @@ -39,6 +40,7 @@ message Door { message Panel { uint64 id = 1; + uint64 ap_id = 10; uint64 room_id = 2; string name = 3; diff --git a/proto/human.proto b/proto/human.proto index d5d03ff..1dcf2ab 100644 --- a/proto/human.proto +++ b/proto/human.proto @@ -124,3 +124,16 @@ message HumanRoom { repeated Letter letters = 5; repeated HumanPort ports = 6; } + +message IdMappings { + message RoomIds { + map panels = 1; + } + + message MapIds { + map doors = 1; + map rooms = 2; + } + + map maps = 1; +} diff --git a/tools/assign_ids/CMakeLists.txt b/tools/assign_ids/CMakeLists.txt new file mode 100644 index 0000000..0a9f62d --- /dev/null +++ b/tools/assign_ids/CMakeLists.txt @@ -0,0 +1,9 @@ +find_package(Protobuf REQUIRED) + +add_executable(assign_ids + main.cpp +) +set_property(TARGET assign_ids PROPERTY CXX_STANDARD 20) +set_property(TARGET assign_ids PROPERTY CXX_STANDARD_REQUIRED ON) +target_include_directories(assign_ids PUBLIC ${CMAKE_BINARY_DIR}) +target_link_libraries(assign_ids PUBLIC protos protobuf::libprotobuf) diff --git a/tools/assign_ids/main.cpp b/tools/assign_ids/main.cpp new file mode 100644 index 0000000..1824c6f --- /dev/null +++ b/tools/assign_ids/main.cpp @@ -0,0 +1,172 @@ +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "proto/human.pb.h" + +namespace com::fourisland::lingo2_archipelago { +namespace { + +template +T ReadMessageFromFile(const std::string& path) { + std::cout << "Processing " << path << std::endl; + + std::ifstream file(path); + std::stringstream buffer; + buffer << file.rdbuf(); + + T message; + google::protobuf::TextFormat::ParseFromString(buffer.str(), &message); + + return message; +} + +class AssignIds { + public: + AssignIds(const std::string& mapdir) : mapdir_(mapdir) {} + + void Run() { + std::filesystem::path datadir_path = mapdir_; + std::filesystem::path ids_path = datadir_path / "ids.txtpb"; + + ReadIds(ids_path); + + ProcessMaps(datadir_path); + + WriteIds(ids_path); + } + + void ReadIds(std::filesystem::path path) { + id_mappings_ = ReadMessageFromFile(path.string()); + + for (const auto& [_, map] : id_mappings_.maps()) { + for (const auto& [_, id] : map.doors()) { + if (id > next_id_) { + next_id_ = id; + } + } + + for (const auto& [_, room] : map.rooms()) { + for (const auto& [_, id] : room.panels()) { + if (id > next_id_) { + next_id_ = id; + } + } + } + } + + next_id_++; + } + + void WriteIds(std::filesystem::path path) { + std::string output; + google::protobuf::TextFormat::PrintToString(id_mappings_, &output); + + { + std::ofstream outputfile(path.string()); + outputfile << output; + } + } + + void ProcessMaps(std::filesystem::path path) { + std::filesystem::path maps_dir = path / "maps"; + for (auto const& dir_entry : + std::filesystem::directory_iterator(maps_dir)) { + ProcessMap(dir_entry.path()); + } + } + + void ProcessMap(std::filesystem::path path) { + std::string map_name = path.filename(); + + ProcessDoorsFile(path / "doors.txtpb", map_name); + ProcessRooms(path / "rooms", map_name); + } + + void ProcessDoorsFile(std::filesystem::path path, + const std::string& current_map_name) { + if (!std::filesystem::exists(path)) { + return; + } + + auto doors = ReadMessageFromFile(path.string()); + + for (const HumanDoor& door : doors.doors()) { + ProcessDoor(door, current_map_name); + } + } + + void ProcessDoor(const HumanDoor& h_door, + const std::string& current_map_name) { + if (!id_mappings_.maps().contains(current_map_name) || + !id_mappings_.maps() + .at(current_map_name) + .doors() + .contains(h_door.name())) { + auto& maps = *id_mappings_.mutable_maps(); + auto& doors = *maps[current_map_name].mutable_doors(); + doors[h_door.name()] = next_id_++; + } + } + + void ProcessRooms(std::filesystem::path path, + const std::string& current_map_name) { + for (auto const& dir_entry : std::filesystem::directory_iterator(path)) { + auto room = ReadMessageFromFile(dir_entry.path().string()); + ProcessRoom(room, current_map_name); + } + } + + void ProcessRoom(const HumanRoom& h_room, + const std::string& current_map_name) { + for (const HumanPanel& h_panel : h_room.panels()) { + if (!id_mappings_.maps().contains(current_map_name) || + !id_mappings_.maps() + .at(current_map_name) + .rooms() + .contains(h_room.name()) || + !id_mappings_.maps() + .at(current_map_name) + .rooms() + .at(h_room.name()) + .panels() + .contains(h_panel.name())) { + auto& maps = *id_mappings_.mutable_maps(); + auto& rooms = *maps[current_map_name].mutable_rooms(); + auto& panels = *rooms[h_room.name()].mutable_panels(); + panels[h_panel.name()] = next_id_++; + } + } + } + + private: + std::string mapdir_; + + uint64_t next_id_ = 0; + + IdMappings id_mappings_; +}; + +} // namespace +} // namespace com::fourisland::lingo2_archipelago + +int main(int argc, char** argv) { + if (argc != 2) { + std::cout << "Incorrect argument count." << std::endl; + std::cout << "Usage: assign_ids [path to map directory]" << std::endl; + return 1; + } + + std::string mapdir = argv[1]; + + com::fourisland::lingo2_archipelago::AssignIds assign_ids(mapdir); + assign_ids.Run(); + + return 0; +} \ No newline at end of file diff --git a/tools/datapacker/main.cpp b/tools/datapacker/main.cpp index 1dcd109..4b26141 100644 --- a/tools/datapacker/main.cpp +++ b/tools/datapacker/main.cpp @@ -42,6 +42,7 @@ class DataPacker { ProcessConnectionsFile(datadir_path / "connections.txtpb", std::nullopt); ProcessMaps(datadir_path); + ProcessIdsFile(datadir_path / "ids.txtpb"); { std::ofstream outputfile(outputpath_); @@ -389,6 +390,26 @@ class DataPacker { } } + void ProcessIdsFile(std::filesystem::path path) { + auto ids = ReadMessageFromFile(path.string()); + + for (const auto& [map_name, map] : ids.maps()) { + for (const auto& [door_name, ap_id] : map.doors()) { + uint64_t door_id = + container_.FindOrAddDoor(map_name, door_name, std::nullopt); + container_.all_objects().mutable_doors(door_id)->set_ap_id(ap_id); + } + + for (const auto& [room_name, room] : map.rooms()) { + for (const auto& [panel_name, ap_id] : room.panels()) { + uint64_t panel_id = container_.FindOrAddPanel( + map_name, room_name, panel_name, std::nullopt, std::nullopt); + container_.all_objects().mutable_panels(panel_id)->set_ap_id(ap_id); + } + } + } + } + std::string mapdir_; std::string outputpath_; -- cgit 1.4.1