about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--ap_state.cpp94
-rw-r--r--ap_state.h5
-rw-r--r--game_data.cpp33
-rw-r--r--game_data.h6
-rw-r--r--tracker_state.cpp13
5 files changed, 98 insertions, 53 deletions
diff --git a/ap_state.cpp b/ap_state.cpp index c901dce..8b7339a 100644 --- a/ap_state.cpp +++ b/ap_state.cpp
@@ -4,9 +4,10 @@
4#define _WEBSOCKETPP_CPP11_STRICT_ 4#define _WEBSOCKETPP_CPP11_STRICT_
5#pragma comment(lib, "crypt32") 5#pragma comment(lib, "crypt32")
6 6
7#include <hkutil/string.h>
8
7#include <apclient.hpp> 9#include <apclient.hpp>
8#include <apuuid.hpp> 10#include <apuuid.hpp>
9#include <hkutil/string.h>
10#include <chrono> 11#include <chrono>
11#include <exception> 12#include <exception>
12#include <list> 13#include <list>
@@ -63,11 +64,13 @@ void APState::Connect(std::string server, std::string player,
63 has_connection_result_ = false; 64 has_connection_result_ = false;
64 65
65 apclient->set_room_info_handler([this, player, password]() { 66 apclient->set_room_info_handler([this, player, password]() {
67 inventory_.clear();
68
66 tracker_frame_->SetStatusMessage( 69 tracker_frame_->SetStatusMessage(
67 "Connected to Archipelago server. Authenticating..."); 70 "Connected to Archipelago server. Authenticating...");
68 71
69 apclient->ConnectSlot(player, password, ITEM_HANDLING, {"Tracker"}, 72 apclient->ConnectSlot(player, password, ITEM_HANDLING, {"Tracker"},
70 {AP_MAJOR, AP_MINOR, AP_REVISION}); 73 {AP_MAJOR, AP_MINOR, AP_REVISION});
71 }); 74 });
72 75
73 apclient->set_location_checked_handler( 76 apclient->set_location_checked_handler(
@@ -81,19 +84,19 @@ void APState::Connect(std::string server, std::string player,
81 }); 84 });
82 85
83 apclient->set_slot_disconnected_handler([this]() { 86 apclient->set_slot_disconnected_handler([this]() {
84 tracker_frame_->SetStatusMessage("Disconnected from Archipelago. Attempting to reconnect..."); 87 tracker_frame_->SetStatusMessage(
88 "Disconnected from Archipelago. Attempting to reconnect...");
85 }); 89 });
86 90
87 apclient->set_socket_disconnected_handler([this]() { 91 apclient->set_socket_disconnected_handler([this]() {
88 tracker_frame_->SetStatusMessage("Disconnected from Archipelago. Attempting to reconnect..."); 92 tracker_frame_->SetStatusMessage(
93 "Disconnected from Archipelago. Attempting to reconnect...");
89 }); 94 });
90 95
91 apclient->set_items_received_handler( 96 apclient->set_items_received_handler(
92 [this](const std::list<APClient::NetworkItem>& items) { 97 [this](const std::list<APClient::NetworkItem>& items) {
93 for (const APClient::NetworkItem& item : items) { 98 for (const APClient::NetworkItem& item : items) {
94 // TODO: Progressive items. 99 inventory_[item.item]++;
95
96 inventory_.insert(item.item);
97 std::cout << "Item: " << item.item << std::endl; 100 std::cout << "Item: " << item.item << std::endl;
98 } 101 }
99 102
@@ -110,7 +113,8 @@ void APState::Connect(std::string server, std::string player,
110 if (painting_shuffle_ && slot_data.contains("painting_entrance_to_exit")) { 113 if (painting_shuffle_ && slot_data.contains("painting_entrance_to_exit")) {
111 painting_mapping_.clear(); 114 painting_mapping_.clear();
112 115
113 for (const auto& mapping_it : slot_data["painting_entrance_to_exit"].items()) { 116 for (const auto& mapping_it :
117 slot_data["painting_entrance_to_exit"].items()) {
114 painting_mapping_[mapping_it.key()] = mapping_it.value(); 118 painting_mapping_[mapping_it.key()] = mapping_it.value();
115 } 119 }
116 } 120 }
@@ -119,41 +123,40 @@ void APState::Connect(std::string server, std::string player,
119 has_connection_result_ = true; 123 has_connection_result_ = true;
120 }); 124 });
121 125
122 apclient->set_slot_refused_handler( 126 apclient->set_slot_refused_handler([this](
123 [this](const std::list<std::string>& errors) { 127 const std::list<std::string>& errors) {
124 connected_ = false; 128 connected_ = false;
125 has_connection_result_ = true; 129 has_connection_result_ = true;
126
127 tracker_frame_->SetStatusMessage("Disconnected from Archipelago.");
128
129 std::vector<std::string> error_messages;
130 error_messages.push_back("Could not connect to Archipelago.");
131
132 for (const std::string& error : errors) {
133 if (error == "InvalidSlot") {
134 error_messages.push_back("Invalid player name.");
135 } else if (error == "InvalidGame") {
136 error_messages.push_back(
137 "The specified player is not playing Lingo.");
138 } else if (error == "IncompatibleVersion") {
139 error_messages.push_back(
140 "The Archipelago server is not the correct version for this "
141 "client.");
142 } else if (error == "InvalidPassword") {
143 error_messages.push_back("Incorrect password.");
144 } else if (error == "InvalidItemsHandling") {
145 error_messages.push_back(
146 "Invalid item handling flag. This is a bug with the tracker. "
147 "Please report it to the lingo-ap-tracker GitHub.");
148 } else {
149 error_messages.push_back("Unknown error.");
150 }
151 }
152 130
153 std::string full_message = hatkirby::implode(error_messages, " "); 131 tracker_frame_->SetStatusMessage("Disconnected from Archipelago.");
132
133 std::vector<std::string> error_messages;
134 error_messages.push_back("Could not connect to Archipelago.");
135
136 for (const std::string& error : errors) {
137 if (error == "InvalidSlot") {
138 error_messages.push_back("Invalid player name.");
139 } else if (error == "InvalidGame") {
140 error_messages.push_back("The specified player is not playing Lingo.");
141 } else if (error == "IncompatibleVersion") {
142 error_messages.push_back(
143 "The Archipelago server is not the correct version for this "
144 "client.");
145 } else if (error == "InvalidPassword") {
146 error_messages.push_back("Incorrect password.");
147 } else if (error == "InvalidItemsHandling") {
148 error_messages.push_back(
149 "Invalid item handling flag. This is a bug with the tracker. "
150 "Please report it to the lingo-ap-tracker GitHub.");
151 } else {
152 error_messages.push_back("Unknown error.");
153 }
154 }
154 155
155 wxMessageBox(full_message, "Connection failed", wxOK | wxICON_ERROR); 156 std::string full_message = hatkirby::implode(error_messages, " ");
156 }); 157
158 wxMessageBox(full_message, "Connection failed", wxOK | wxICON_ERROR);
159 });
157 160
158 client_active_ = true; 161 client_active_ = true;
159 162
@@ -202,6 +205,10 @@ void APState::Connect(std::string server, std::string player,
202 !ap_id_by_item_name_.count(door.group_name)) { 205 !ap_id_by_item_name_.count(door.group_name)) {
203 ap_id_by_item_name_[door.group_name] = GetItemId(door.group_name); 206 ap_id_by_item_name_[door.group_name] = GetItemId(door.group_name);
204 } 207 }
208
209 for (const ProgressiveRequirement& prog_req : door.progressives) {
210 ap_id_by_item_name_[prog_req.item_name] = GetItemId(prog_req.item_name);
211 }
205 } 212 }
206 } 213 }
207 214
@@ -239,9 +246,10 @@ bool APState::HasColorItem(LingoColor color) const {
239 } 246 }
240} 247}
241 248
242bool APState::HasItem(const std::string& item) const { 249bool APState::HasItem(const std::string& item, int quantity) const {
243 if (ap_id_by_item_name_.count(item)) { 250 if (ap_id_by_item_name_.count(item)) {
244 return inventory_.count(ap_id_by_item_name_.at(item)); 251 int64_t ap_id = ap_id_by_item_name_.at(item);
252 return inventory_.count(ap_id) && inventory_.at(ap_id) >= quantity;
245 } else { 253 } else {
246 return false; 254 return false;
247 } 255 }
diff --git a/ap_state.h b/ap_state.h index ce7bafc..ce73528 100644 --- a/ap_state.h +++ b/ap_state.h
@@ -26,7 +26,7 @@ class APState {
26 26
27 bool HasColorItem(LingoColor color) const; 27 bool HasColorItem(LingoColor color) const;
28 28
29 bool HasItem(const std::string& item) const; 29 bool HasItem(const std::string& item, int quantity = 1) const;
30 30
31 DoorShuffleMode GetDoorShuffleMode() const { return door_shuffle_mode_; } 31 DoorShuffleMode GetDoorShuffleMode() const { return door_shuffle_mode_; }
32 32
@@ -53,12 +53,13 @@ class APState {
53 bool connected_ = false; 53 bool connected_ = false;
54 bool has_connection_result_ = false; 54 bool has_connection_result_ = false;
55 55
56 std::set<int64_t> inventory_; 56 std::map<int64_t, int> inventory_;
57 std::set<int64_t> checked_locations_; 57 std::set<int64_t> checked_locations_;
58 58
59 std::map<std::tuple<int, int>, int64_t> ap_id_by_location_id_; 59 std::map<std::tuple<int, int>, int64_t> ap_id_by_location_id_;
60 std::map<std::string, int64_t> ap_id_by_item_name_; 60 std::map<std::string, int64_t> ap_id_by_item_name_;
61 std::map<LingoColor, int64_t> ap_id_by_color_; 61 std::map<LingoColor, int64_t> ap_id_by_color_;
62 std::map<int64_t, std::string> progressive_item_by_ap_id_;
62 63
63 DoorShuffleMode door_shuffle_mode_ = kNO_DOORS; 64 DoorShuffleMode door_shuffle_mode_ = kNO_DOORS;
64 bool color_shuffle_ = false; 65 bool color_shuffle_ = false;
diff --git a/game_data.cpp b/game_data.cpp index 2f8f505..9b31f89 100644 --- a/game_data.cpp +++ b/game_data.cpp
@@ -235,7 +235,7 @@ GameData::GameData() {
235 } 235 }
236 236
237 if (room_it.second["paintings"]) { 237 if (room_it.second["paintings"]) {
238 for (const auto& painting : room_it.second["paintings"]) { 238 for (const auto &painting : room_it.second["paintings"]) {
239 std::string painting_id = painting["id"].as<std::string>(); 239 std::string painting_id = painting["id"].as<std::string>();
240 room_by_painting_[painting_id] = room_id; 240 room_by_painting_[painting_id] = room_id;
241 241
@@ -250,14 +250,36 @@ GameData::GameData() {
250 } 250 }
251 251
252 painting_exit.door = AddOrGetDoor( 252 painting_exit.door = AddOrGetDoor(
253 rd_room, 253 rd_room, painting["required_door"]["door"].as<std::string>());
254 painting["required_door"]["door"].as<std::string>());
255 } 254 }
256 255
257 room_obj.paintings.push_back(painting_exit); 256 room_obj.paintings.push_back(painting_exit);
258 } 257 }
259 } 258 }
260 } 259 }
260
261 if (room_it.second["progression"]) {
262 for (const auto &progression_it : room_it.second["progression"]) {
263 std::string progressive_item_name =
264 progression_it.first.as<std::string>();
265
266 int index = 1;
267 for (const auto &stage : progression_it.second) {
268 int door_id = -1;
269
270 if (stage.IsScalar()) {
271 door_id = AddOrGetDoor(room_obj.name, stage.as<std::string>());
272 } else {
273 door_id = AddOrGetDoor(stage["room"].as<std::string>(),
274 stage["door"].as<std::string>());
275 }
276
277 doors_[door_id].progressives.push_back(
278 {.item_name = progressive_item_name, .quantity = index});
279 index++;
280 }
281 }
282 }
261 } 283 }
262 284
263 map_areas_.reserve(areas_config.size()); 285 map_areas_.reserve(areas_config.size());
@@ -341,10 +363,7 @@ int GameData::AddOrGetDoor(std::string room, std::string door) {
341 363
342 if (!door_by_id_.count(full_name)) { 364 if (!door_by_id_.count(full_name)) {
343 door_by_id_[full_name] = doors_.size(); 365 door_by_id_[full_name] = doors_.size();
344 doors_.push_back({ 366 doors_.push_back({.room = AddOrGetRoom(room), .name = door});
345 .room = AddOrGetRoom(room),
346 .name = door
347 });
348 } 367 }
349 368
350 return door_by_id_[full_name]; 369 return door_by_id_[full_name];
diff --git a/game_data.h b/game_data.h index 981f56f..2df8bc5 100644 --- a/game_data.h +++ b/game_data.h
@@ -30,6 +30,11 @@ struct Panel {
30 bool exclude_reduce = false; 30 bool exclude_reduce = false;
31}; 31};
32 32
33struct ProgressiveRequirement {
34 std::string item_name;
35 int quantity = 0;
36};
37
33struct Door { 38struct Door {
34 int room; 39 int room;
35 std::string name; 40 std::string name;
@@ -40,6 +45,7 @@ struct Door {
40 bool skip_item = false; 45 bool skip_item = false;
41 std::vector<int> panels; 46 std::vector<int> panels;
42 bool exclude_reduce = true; 47 bool exclude_reduce = true;
48 std::vector<ProgressiveRequirement> progressives;
43}; 49};
44 50
45struct Exit { 51struct Exit {
diff --git a/tracker_state.cpp b/tracker_state.cpp index 169d301..4921d3f 100644 --- a/tracker_state.cpp +++ b/tracker_state.cpp
@@ -58,7 +58,18 @@ bool IsDoorReachable_Helper(int door_id, const std::set<int>& reachable_rooms) {
58 !door_obj.group_name.empty()) { 58 !door_obj.group_name.empty()) {
59 return GetAPState().HasItem(door_obj.group_name); 59 return GetAPState().HasItem(door_obj.group_name);
60 } else { 60 } else {
61 return GetAPState().HasItem(door_obj.item_name); 61 bool has_item = GetAPState().HasItem(door_obj.item_name);
62
63 if (!has_item) {
64 for (const ProgressiveRequirement& prog_req : door_obj.progressives) {
65 if (GetAPState().HasItem(prog_req.item_name, prog_req.quantity)) {
66 has_item = true;
67 break;
68 }
69 }
70 }
71
72 return has_item;
62 } 73 }
63} 74}
64 75