about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md14
-rw-r--r--VERSION2
-rw-r--r--src/ap_state.cpp21
-rw-r--r--src/ap_state.h4
-rw-r--r--src/game_data.cpp122
-rw-r--r--src/game_data.h9
-rw-r--r--src/subway_map.cpp33
-rw-r--r--src/tracker_state.cpp95
-rw-r--r--src/version.cpp5
-rw-r--r--src/version.h2
10 files changed, 271 insertions, 36 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index cd237c6..67e32ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -1,5 +1,19 @@
1# lingo-ap-tracker Releases 1# lingo-ap-tracker Releases
2 2
3## v0.10.6 - 2024-07-16
4
5- The status bar now shows the name and server for the connected slot.
6- Fixed an issue with pilgrimage detection when paintings are shuffled and
7 paintings are not allowed for pilgrimage.
8- Fixed an issue with pilgrimage starting from the wrong place when sunwarps are
9 shuffled.
10- Fixed incorrect doors showing for sunwarps on subway map when sunwarps are
11 shuffled.
12
13Download:
14[lingo-ap-tracker-v0.10.6-win64.zip](https://files.fourisland.com/releases/lingo-ap-tracker/lingo-ap-tracker-v0.10.6-win64.zip)<br/>
15Source: [v0.10.6](https://code.fourisland.com/lingo-ap-tracker/tag/?h=v0.10.6)
16
3## v0.10.5 - 2024-07-12 17## v0.10.5 - 2024-07-12
4 18
5- Increased length of connection history to 10. 19- Increased length of connection history to 10.
diff --git a/VERSION b/VERSION index a6eeb03..7b63f4b 100644 --- a/VERSION +++ b/VERSION
@@ -1 +1 @@
v0.10.5 \ No newline at end of file v0.10.6 \ No newline at end of file
diff --git a/src/ap_state.cpp b/src/ap_state.cpp index 876fdd8..d501e81 100644 --- a/src/ap_state.cpp +++ b/src/ap_state.cpp
@@ -58,6 +58,7 @@ struct APState {
58 std::optional<std::tuple<int, int>> player_pos; 58 std::optional<std::tuple<int, int>> player_pos;
59 59
60 DoorShuffleMode door_shuffle_mode = kNO_DOORS; 60 DoorShuffleMode door_shuffle_mode = kNO_DOORS;
61 bool group_doors = false;
61 bool color_shuffle = false; 62 bool color_shuffle = false;
62 bool painting_shuffle = false; 63 bool painting_shuffle = false;
63 int mastery_requirement = 21; 64 int mastery_requirement = 21;
@@ -137,6 +138,7 @@ struct APState {
137 player_pos = std::nullopt; 138 player_pos = std::nullopt;
138 victory_data_storage_key.clear(); 139 victory_data_storage_key.clear();
139 door_shuffle_mode = kNO_DOORS; 140 door_shuffle_mode = kNO_DOORS;
141 group_doors = false;
140 color_shuffle = false; 142 color_shuffle = false;
141 painting_shuffle = false; 143 painting_shuffle = false;
142 painting_mapping.clear(); 144 painting_mapping.clear();
@@ -221,14 +223,27 @@ struct APState {
221 RefreshTracker(false); 223 RefreshTracker(false);
222 }); 224 });
223 225
224 apclient->set_slot_connected_handler([this, &connection_mutex]( 226 apclient->set_slot_connected_handler([this, player, server,
227 &connection_mutex](
225 const nlohmann::json& slot_data) { 228 const nlohmann::json& slot_data) {
226 tracker_frame->SetStatusMessage("Connected to Archipelago!"); 229 tracker_frame->SetStatusMessage(
230 fmt::format("Connected to Archipelago! ({}@{})", player, server));
227 TrackerLog("Connected to Archipelago!"); 231 TrackerLog("Connected to Archipelago!");
228 232
229 data_storage_prefix = 233 data_storage_prefix =
230 fmt::format("Lingo_{}_", apclient->get_player_number()); 234 fmt::format("Lingo_{}_", apclient->get_player_number());
231 door_shuffle_mode = slot_data["shuffle_doors"].get<DoorShuffleMode>(); 235 door_shuffle_mode = slot_data["shuffle_doors"].get<DoorShuffleMode>();
236 if (slot_data.contains("group_doors")) {
237 group_doors = slot_data.contains("group_doors") &&
238 slot_data["group_doors"].get<int>() == 1;
239 } else {
240 // If group_doors doesn't exist yet, that means kPANELS_MODE is actually
241 // kSIMPLE_DOORS.
242 if (door_shuffle_mode == kPANELS_MODE) {
243 door_shuffle_mode = kDOORS_MODE;
244 group_doors = true;
245 }
246 }
232 color_shuffle = slot_data["shuffle_colors"].get<int>() == 1; 247 color_shuffle = slot_data["shuffle_colors"].get<int>() == 1;
233 painting_shuffle = slot_data["shuffle_paintings"].get<int>() == 1; 248 painting_shuffle = slot_data["shuffle_paintings"].get<int>() == 1;
234 mastery_requirement = slot_data["mastery_achievements"].get<int>(); 249 mastery_requirement = slot_data["mastery_achievements"].get<int>();
@@ -525,6 +540,8 @@ std::string AP_GetItemName(int item_id) {
525 540
526DoorShuffleMode AP_GetDoorShuffleMode() { return GetState().door_shuffle_mode; } 541DoorShuffleMode AP_GetDoorShuffleMode() { return GetState().door_shuffle_mode; }
527 542
543bool AP_AreDoorsGrouped() { return GetState().group_doors; }
544
528bool AP_IsColorShuffle() { return GetState().color_shuffle; } 545bool AP_IsColorShuffle() { return GetState().color_shuffle; }
529 546
530bool AP_IsPaintingShuffle() { return GetState().painting_shuffle; } 547bool AP_IsPaintingShuffle() { return GetState().painting_shuffle; }
diff --git a/src/ap_state.h b/src/ap_state.h index 7af7395..190b21f 100644 --- a/src/ap_state.h +++ b/src/ap_state.h
@@ -11,7 +11,7 @@
11 11
12class TrackerFrame; 12class TrackerFrame;
13 13
14enum DoorShuffleMode { kNO_DOORS = 0, kSIMPLE_DOORS = 1, kCOMPLEX_DOORS = 2 }; 14enum DoorShuffleMode { kNO_DOORS = 0, kPANELS_MODE = 1, kDOORS_MODE = 2 };
15 15
16enum VictoryCondition { 16enum VictoryCondition {
17 kTHE_END = 0, 17 kTHE_END = 0,
@@ -53,6 +53,8 @@ std::string AP_GetItemName(int item_id);
53 53
54DoorShuffleMode AP_GetDoorShuffleMode(); 54DoorShuffleMode AP_GetDoorShuffleMode();
55 55
56bool AP_AreDoorsGrouped();
57
56bool AP_IsColorShuffle(); 58bool AP_IsColorShuffle();
57 59
58bool AP_IsPaintingShuffle(); 60bool AP_IsPaintingShuffle();
diff --git a/src/game_data.cpp b/src/game_data.cpp index e75170e..39ea360 100644 --- a/src/game_data.cpp +++ b/src/game_data.cpp
@@ -42,6 +42,7 @@ struct GameData {
42 std::vector<Room> rooms_; 42 std::vector<Room> rooms_;
43 std::vector<Door> doors_; 43 std::vector<Door> doors_;
44 std::vector<Panel> panels_; 44 std::vector<Panel> panels_;
45 std::vector<PanelDoor> panel_doors_;
45 std::vector<MapArea> map_areas_; 46 std::vector<MapArea> map_areas_;
46 std::vector<SubwayItem> subway_items_; 47 std::vector<SubwayItem> subway_items_;
47 std::vector<PaintingExit> paintings_; 48 std::vector<PaintingExit> paintings_;
@@ -49,6 +50,7 @@ struct GameData {
49 std::map<std::string, int> room_by_id_; 50 std::map<std::string, int> room_by_id_;
50 std::map<std::string, int> door_by_id_; 51 std::map<std::string, int> door_by_id_;
51 std::map<std::string, int> panel_by_id_; 52 std::map<std::string, int> panel_by_id_;
53 std::map<std::string, int> panel_doors_by_id_;
52 std::map<std::string, int> area_by_id_; 54 std::map<std::string, int> area_by_id_;
53 std::map<std::string, int> painting_by_id_; 55 std::map<std::string, int> painting_by_id_;
54 56
@@ -109,6 +111,7 @@ struct GameData {
109 auto process_single_entrance = 111 auto process_single_entrance =
110 [this, room_id, from_room_id](const YAML::Node &option) { 112 [this, room_id, from_room_id](const YAML::Node &option) {
111 Exit exit_obj; 113 Exit exit_obj;
114 exit_obj.source_room = from_room_id;
112 exit_obj.destination_room = room_id; 115 exit_obj.destination_room = room_id;
113 116
114 if (option["door"]) { 117 if (option["door"]) {
@@ -143,7 +146,7 @@ struct GameData {
143 switch (entrance_it.second.Type()) { 146 switch (entrance_it.second.Type()) {
144 case YAML::NodeType::Scalar: { 147 case YAML::NodeType::Scalar: {
145 // This is just "true". 148 // This is just "true".
146 rooms_[from_room_id].exits.push_back({.destination_room = room_id}); 149 rooms_[from_room_id].exits.push_back({.source_room = from_room_id, .destination_room = room_id});
147 break; 150 break;
148 } 151 }
149 case YAML::NodeType::Map: { 152 case YAML::NodeType::Map: {
@@ -424,6 +427,55 @@ struct GameData {
424 } 427 }
425 } 428 }
426 429
430 if (room_it.second["panel_doors"]) {
431 for (const auto &panel_door_it : room_it.second["panel_doors"]) {
432 std::string panel_door_name = panel_door_it.first.as<std::string>();
433 int panel_door_id =
434 AddOrGetPanelDoor(rooms_[room_id].name, panel_door_name);
435
436 for (const auto &panel_node : panel_door_it.second["panels"]) {
437 int panel_id = -1;
438
439 if (panel_node.IsScalar()) {
440 panel_id = AddOrGetPanel(rooms_[room_id].name,
441 panel_node.as<std::string>());
442 } else {
443 panel_id = AddOrGetPanel(panel_node["room"].as<std::string>(),
444 panel_node["panel"].as<std::string>());
445 }
446
447 Panel &panel = panels_[panel_id];
448 panel.panel_door = panel_door_id;
449 }
450
451 if (ids_config["panel_doors"] &&
452 ids_config["panel_doors"][rooms_[room_id].name] &&
453 ids_config["panel_doors"][rooms_[room_id].name]
454 [panel_door_name]) {
455 panel_doors_[panel_door_id].ap_item_id =
456 ids_config["panel_doors"][rooms_[room_id].name][panel_door_name]
457 .as<int>();
458 } else {
459 wxLogError("Missing AP item ID for panel door %s - %s",
460 rooms_[room_id].name, panel_door_name);
461 }
462
463 if (panel_door_it.second["panel_group"]) {
464 std::string panel_group =
465 panel_door_it.second["panel_group"].as<std::string>();
466
467 if (ids_config["panel_groups"] &&
468 ids_config["panel_groups"][panel_group]) {
469 panel_doors_[panel_door_id].group_ap_item_id =
470 ids_config["panel_groups"][panel_group].as<int>();
471 } else {
472 wxLogError("Missing AP item ID for panel door group %s",
473 panel_group);
474 }
475 }
476 }
477 }
478
427 if (room_it.second["paintings"]) { 479 if (room_it.second["paintings"]) {
428 for (const auto &painting : room_it.second["paintings"]) { 480 for (const auto &painting : room_it.second["paintings"]) {
429 std::string internal_id = painting["id"].as<std::string>(); 481 std::string internal_id = painting["id"].as<std::string>();
@@ -477,23 +529,47 @@ struct GameData {
477 progressive_item_name)); 529 progressive_item_name));
478 } 530 }
479 531
480 int index = 1; 532 if (progression_it.second["doors"]) {
481 for (const auto &stage : progression_it.second) { 533 int index = 1;
482 int door_id = -1; 534 for (const auto &stage : progression_it.second["doors"]) {
535 int door_id = -1;
483 536
484 if (stage.IsScalar()) { 537 if (stage.IsScalar()) {
485 door_id = 538 door_id =
486 AddOrGetDoor(rooms_[room_id].name, stage.as<std::string>()); 539 AddOrGetDoor(rooms_[room_id].name, stage.as<std::string>());
487 } else { 540 } else {
488 door_id = AddOrGetDoor(stage["room"].as<std::string>(), 541 door_id = AddOrGetDoor(stage["room"].as<std::string>(),
489 stage["door"].as<std::string>()); 542 stage["door"].as<std::string>());
543 }
544
545 doors_[door_id].progressives.push_back(
546 {.item_name = progressive_item_name,
547 .ap_item_id = progressive_item_id,
548 .quantity = index});
549 index++;
490 } 550 }
551 }
491 552
492 doors_[door_id].progressives.push_back( 553 if (progression_it.second["panel_doors"]) {
493 {.item_name = progressive_item_name, 554 int index = 1;
494 .ap_item_id = progressive_item_id, 555 for (const auto &stage : progression_it.second["panel_doors"]) {
495 .quantity = index}); 556 int panel_door_id = -1;
496 index++; 557
558 if (stage.IsScalar()) {
559 panel_door_id = AddOrGetPanelDoor(rooms_[room_id].name,
560 stage.as<std::string>());
561 } else {
562 panel_door_id =
563 AddOrGetPanelDoor(stage["room"].as<std::string>(),
564 stage["panel_door"].as<std::string>());
565 }
566
567 panel_doors_[panel_door_id].progressives.push_back(
568 {.item_name = progressive_item_name,
569 .ap_item_id = progressive_item_id,
570 .quantity = index});
571 index++;
572 }
497 } 573 }
498 } 574 }
499 } 575 }
@@ -752,6 +828,18 @@ struct GameData {
752 return panel_by_id_[full_name]; 828 return panel_by_id_[full_name];
753 } 829 }
754 830
831 int AddOrGetPanelDoor(std::string room, std::string panel) {
832 std::string full_name = room + " - " + panel;
833
834 if (!panel_doors_by_id_.count(full_name)) {
835 int panel_door_id = panel_doors_.size();
836 panel_doors_by_id_[full_name] = panel_door_id;
837 panel_doors_.push_back({});
838 }
839
840 return panel_doors_by_id_[full_name];
841 }
842
755 int AddOrGetArea(std::string area) { 843 int AddOrGetArea(std::string area) {
756 if (!area_by_id_.count(area)) { 844 if (!area_by_id_.count(area)) {
757 if (loaded_area_data_) { 845 if (loaded_area_data_) {
@@ -802,6 +890,10 @@ const std::vector<Door> &GD_GetDoors() { return GetState().doors_; }
802 890
803const Door &GD_GetDoor(int door_id) { return GetState().doors_.at(door_id); } 891const Door &GD_GetDoor(int door_id) { return GetState().doors_.at(door_id); }
804 892
893const PanelDoor &GD_GetPanelDoor(int panel_door_id) {
894 return GetState().panel_doors_.at(panel_door_id);
895}
896
805int GD_GetDoorByName(const std::string &name) { 897int GD_GetDoorByName(const std::string &name) {
806 return GetState().door_by_id_.at(name); 898 return GetState().door_by_id_.at(name);
807} 899}
diff --git a/src/game_data.h b/src/game_data.h index b787e6f..197585c 100644 --- a/src/game_data.h +++ b/src/game_data.h
@@ -55,6 +55,7 @@ struct Panel {
55 bool non_counting = false; 55 bool non_counting = false;
56 int ap_location_id = -1; 56 int ap_location_id = -1;
57 bool hunt = false; 57 bool hunt = false;
58 int panel_door = -1;
58}; 59};
59 60
60struct ProgressiveRequirement { 61struct ProgressiveRequirement {
@@ -82,7 +83,14 @@ struct Door {
82 DoorType type = DoorType::kNormal; 83 DoorType type = DoorType::kNormal;
83}; 84};
84 85
86struct PanelDoor {
87 int ap_item_id = -1;
88 int group_ap_item_id = -1;
89 std::vector<ProgressiveRequirement> progressives;
90};
91
85struct Exit { 92struct Exit {
93 int source_room;
86 int destination_room; 94 int destination_room;
87 std::optional<int> door; 95 std::optional<int> door;
88 EntranceType type = EntranceType::kNormal; 96 EntranceType type = EntranceType::kNormal;
@@ -157,6 +165,7 @@ const std::vector<Door>& GD_GetDoors();
157const Door& GD_GetDoor(int door_id); 165const Door& GD_GetDoor(int door_id);
158int GD_GetDoorByName(const std::string& name); 166int GD_GetDoorByName(const std::string& name);
159const Panel& GD_GetPanel(int panel_id); 167const Panel& GD_GetPanel(int panel_id);
168const PanelDoor& GD_GetPanelDoor(int panel_door_id);
160const PaintingExit& GD_GetPaintingExit(int painting_id); 169const PaintingExit& GD_GetPaintingExit(int painting_id);
161int GD_GetPaintingByName(const std::string& name); 170int GD_GetPaintingByName(const std::string& name);
162const std::vector<int>& GD_GetAchievementPanels(); 171const std::vector<int>& GD_GetAchievementPanels();
diff --git a/src/subway_map.cpp b/src/subway_map.cpp index e3b844d..044e6fa 100644 --- a/src/subway_map.cpp +++ b/src/subway_map.cpp
@@ -16,6 +16,29 @@ constexpr int OWL_ACTUAL_SIZE = 32;
16 16
17enum class ItemDrawType { kNone, kBox, kOwl }; 17enum class ItemDrawType { kNone, kBox, kOwl };
18 18
19namespace {
20
21std::optional<int> GetRealSubwayDoor(const SubwayItem subway_item) {
22 std::optional<int> subway_door = subway_item.door;
23 if (AP_IsSunwarpShuffle() && subway_item.sunwarp &&
24 subway_item.sunwarp->type != SubwaySunwarpType::kFinal) {
25 int sunwarp_index = subway_item.sunwarp->dots - 1;
26 if (subway_item.sunwarp->type == SubwaySunwarpType::kExit) {
27 sunwarp_index += 6;
28 }
29
30 for (const auto &[start_index, mapping] : AP_GetSunwarpMapping()) {
31 if (start_index == sunwarp_index || mapping.exit_index == sunwarp_index) {
32 subway_door = GD_GetSunwarpDoors().at(mapping.dots - 1);
33 }
34 }
35
36 return subway_door;
37 }
38}
39
40} // namespace
41
19SubwayMap::SubwayMap(wxWindow *parent) : wxPanel(parent, wxID_ANY) { 42SubwayMap::SubwayMap(wxWindow *parent) : wxPanel(parent, wxID_ANY) {
20 SetBackgroundStyle(wxBG_STYLE_PAINT); 43 SetBackgroundStyle(wxBG_STYLE_PAINT);
21 44
@@ -267,9 +290,11 @@ void SubwayMap::OnPaint(wxPaintEvent &event) {
267 // Note that these requirements are duplicated on OnMouseClick so that it 290 // Note that these requirements are duplicated on OnMouseClick so that it
268 // knows when an item has a hover effect. 291 // knows when an item has a hover effect.
269 const SubwayItem &subway_item = GD_GetSubwayItem(*hovered_item_); 292 const SubwayItem &subway_item = GD_GetSubwayItem(*hovered_item_);
270 if (subway_item.door && !GetDoorRequirements(*subway_item.door).empty()) { 293 std::optional<int> subway_door = GetRealSubwayDoor(subway_item);
294
295 if (subway_door && !GetDoorRequirements(*subway_door).empty()) {
271 const std::map<std::string, bool> &report = 296 const std::map<std::string, bool> &report =
272 GetDoorRequirements(*subway_item.door); 297 GetDoorRequirements(*subway_door);
273 298
274 int acc_height = 10; 299 int acc_height = 10;
275 int col_width = 0; 300 int col_width = 0;
@@ -450,7 +475,9 @@ void SubwayMap::OnMouseClick(wxMouseEvent &event) {
450 475
451 if (actual_hover_) { 476 if (actual_hover_) {
452 const SubwayItem &subway_item = GD_GetSubwayItem(*actual_hover_); 477 const SubwayItem &subway_item = GD_GetSubwayItem(*actual_hover_);
453 if ((subway_item.door && !GetDoorRequirements(*subway_item.door).empty()) || 478 std::optional<int> subway_door = GetRealSubwayDoor(subway_item);
479
480 if ((subway_door && !GetDoorRequirements(*subway_door).empty()) ||
454 networks_.IsItemInNetwork(*hovered_item_)) { 481 networks_.IsItemInNetwork(*hovered_item_)) {
455 if (actual_hover_ != hovered_item_) { 482 if (actual_hover_ != hovered_item_) {
456 hovered_item_ = actual_hover_; 483 hovered_item_ = actual_hover_;
diff --git a/src/tracker_state.cpp b/src/tracker_state.cpp index 1a2d116..66a9f94 100644 --- a/src/tracker_state.cpp +++ b/src/tracker_state.cpp
@@ -1,5 +1,8 @@
1#include "tracker_state.h" 1#include "tracker_state.h"
2 2
3#include <fmt/core.h>
4#include <hkutil/string.h>
5
3#include <list> 6#include <list>
4#include <map> 7#include <map>
5#include <mutex> 8#include <mutex>
@@ -9,17 +12,19 @@
9 12
10#include "ap_state.h" 13#include "ap_state.h"
11#include "game_data.h" 14#include "game_data.h"
15#include "logger.h"
12 16
13namespace { 17namespace {
14 18
15struct Requirements { 19struct Requirements {
16 bool disabled = false; 20 bool disabled = false;
17 21
18 std::set<int> doors; // non-grouped, handles progressive 22 std::set<int> doors; // non-grouped, handles progressive
19 std::set<int> items; // all other items 23 std::set<int> panel_doors; // non-grouped, handles progressive
20 std::set<int> rooms; // maybe 24 std::set<int> items; // all other items
21 bool mastery = false; // maybe 25 std::set<int> rooms; // maybe
22 bool panel_hunt = false; // maybe 26 bool mastery = false; // maybe
27 bool panel_hunt = false; // maybe
23 28
24 void Merge(const Requirements& rhs) { 29 void Merge(const Requirements& rhs) {
25 if (rhs.disabled) { 30 if (rhs.disabled) {
@@ -29,6 +34,9 @@ struct Requirements {
29 for (int id : rhs.doors) { 34 for (int id : rhs.doors) {
30 doors.insert(id); 35 doors.insert(id);
31 } 36 }
37 for (int id : rhs.panel_doors) {
38 panel_doors.insert(id);
39 }
32 for (int id : rhs.items) { 40 for (int id : rhs.items) {
33 items.insert(id); 41 items.insert(id);
34 } 42 }
@@ -74,15 +82,14 @@ class RequirementCalculator {
74 requirements.doors.insert(door_obj.id); 82 requirements.doors.insert(door_obj.id);
75 break; 83 break;
76 } 84 }
77 } else if (AP_GetDoorShuffleMode() == kNO_DOORS || door_obj.skip_item) { 85 } else if (AP_GetDoorShuffleMode() != kDOORS_MODE || door_obj.skip_item) {
78 requirements.rooms.insert(door_obj.room); 86 requirements.rooms.insert(door_obj.room);
79 87
80 for (int panel_id : door_obj.panels) { 88 for (int panel_id : door_obj.panels) {
81 const Requirements& panel_reqs = GetPanel(panel_id); 89 const Requirements& panel_reqs = GetPanel(panel_id);
82 requirements.Merge(panel_reqs); 90 requirements.Merge(panel_reqs);
83 } 91 }
84 } else if (AP_GetDoorShuffleMode() == kSIMPLE_DOORS && 92 } else if (AP_AreDoorsGrouped() && !door_obj.group_name.empty()) {
85 !door_obj.group_name.empty()) {
86 requirements.items.insert(door_obj.group_ap_item_id); 93 requirements.items.insert(door_obj.group_ap_item_id);
87 } else { 94 } else {
88 requirements.doors.insert(door_obj.id); 95 requirements.doors.insert(door_obj.id);
@@ -129,6 +136,17 @@ class RequirementCalculator {
129 requirements.items.insert(GD_GetItemIdForColor(color)); 136 requirements.items.insert(GD_GetItemIdForColor(color));
130 } 137 }
131 } 138 }
139
140 if (panel_obj.panel_door != -1 &&
141 AP_GetDoorShuffleMode() == kPANELS_MODE) {
142 const PanelDoor& panel_door_obj = GD_GetPanelDoor(panel_obj.panel_door);
143
144 if (panel_door_obj.group_ap_item_id != -1 && AP_AreDoorsGrouped()) {
145 requirements.items.insert(panel_door_obj.group_ap_item_id);
146 } else {
147 requirements.panel_doors.insert(panel_obj.panel_door);
148 }
149 }
132 150
133 panels_[panel_id] = requirements; 151 panels_[panel_id] = requirements;
134 } 152 }
@@ -217,7 +235,9 @@ class StateCalculator {
217 PaintingExit target_painting = 235 PaintingExit target_painting =
218 GD_GetPaintingExit(GD_GetPaintingByName( 236 GD_GetPaintingExit(GD_GetPaintingByName(
219 AP_GetPaintingMapping().at(cur_painting.internal_id))); 237 AP_GetPaintingMapping().at(cur_painting.internal_id)));
238 painting_exit.source_room = cur_painting.room;
220 painting_exit.destination_room = target_painting.room; 239 painting_exit.destination_room = target_painting.room;
240 painting_exit.type = EntranceType::kPainting;
221 241
222 new_boundary.push_back(painting_exit); 242 new_boundary.push_back(painting_exit);
223 } 243 }
@@ -244,6 +264,12 @@ class StateCalculator {
244 reachable_rooms_.insert(room_exit.destination_room); 264 reachable_rooms_.insert(room_exit.destination_room);
245 reachable_changed = true; 265 reachable_changed = true;
246 266
267#ifndef NDEBUG
268 std::list<int> room_path = paths_[room_exit.source_room];
269 room_path.push_back(room_exit.destination_room);
270 paths_[room_exit.destination_room] = room_path;
271#endif
272
247 const Room& room_obj = GD_GetRoom(room_exit.destination_room); 273 const Room& room_obj = GD_GetRoom(room_exit.destination_room);
248 for (const Exit& out_edge : room_obj.exits) { 274 for (const Exit& out_edge : room_obj.exits) {
249 if (out_edge.type == EntranceType::kPainting && 275 if (out_edge.type == EntranceType::kPainting &&
@@ -282,20 +308,33 @@ class StateCalculator {
282 308
283 if (AP_HasEarlyColorHallways() && room_obj.name == "Starting Room") { 309 if (AP_HasEarlyColorHallways() && room_obj.name == "Starting Room") {
284 new_boundary.push_back( 310 new_boundary.push_back(
285 {.destination_room = GD_GetRoomByName("Color Hallways"), 311 {.source_room = room_exit.destination_room,
312 .destination_room = GD_GetRoomByName("Color Hallways"),
286 .type = EntranceType::kPainting}); 313 .type = EntranceType::kPainting});
287 } 314 }
288 315
289 if (AP_IsPilgrimageEnabled()) { 316 if (AP_IsPilgrimageEnabled()) {
290 if (room_obj.name == "Hub Room") { 317 int pilgrimage_start_id = GD_GetRoomByName("Hub Room");
318 if (AP_IsSunwarpShuffle()) {
319 for (const auto& [start_index, mapping] :
320 AP_GetSunwarpMapping()) {
321 if (mapping.dots == 1) {
322 pilgrimage_start_id = GD_GetRoomForSunwarp(start_index);
323 }
324 }
325 }
326
327 if (room_exit.destination_room == pilgrimage_start_id) {
291 new_boundary.push_back( 328 new_boundary.push_back(
292 {.destination_room = GD_GetRoomByName("Pilgrim Antechamber"), 329 {.source_room = room_exit.destination_room,
330 .destination_room = GD_GetRoomByName("Pilgrim Antechamber"),
293 .type = EntranceType::kPilgrimage}); 331 .type = EntranceType::kPilgrimage});
294 } 332 }
295 } else { 333 } else {
296 if (room_obj.name == "Starting Room") { 334 if (room_obj.name == "Starting Room") {
297 new_boundary.push_back( 335 new_boundary.push_back(
298 {.destination_room = GD_GetRoomByName("Pilgrim Antechamber"), 336 {.source_room = room_exit.destination_room,
337 .destination_room = GD_GetRoomByName("Pilgrim Antechamber"),
299 .door = 338 .door =
300 GD_GetDoorByName("Pilgrim Antechamber - Sun Painting"), 339 GD_GetDoorByName("Pilgrim Antechamber - Sun Painting"),
301 .type = EntranceType::kPainting}); 340 .type = EntranceType::kPainting});
@@ -336,8 +375,22 @@ class StateCalculator {
336 return door_report_; 375 return door_report_;
337 } 376 }
338 377
378 std::string GetPathToRoom(int room_id) const {
379 if (!paths_.count(room_id)) {
380 return "";
381 }
382
383 const std::list<int>& path = paths_.at(room_id);
384 std::vector<std::string> room_names;
385 for (int room_id : path) {
386 room_names.push_back(GD_GetRoom(room_id).name);
387 }
388 return hatkirby::implode(room_names, " -> ");
389 }
390
339 private: 391 private:
340 Decision IsNonGroupedDoorReachable(const Door& door_obj) { 392 template <typename T>
393 Decision IsNonGroupedDoorReachable(const T& door_obj) {
341 bool has_item = AP_HasItem(door_obj.ap_item_id); 394 bool has_item = AP_HasItem(door_obj.ap_item_id);
342 395
343 if (!has_item) { 396 if (!has_item) {
@@ -373,6 +426,20 @@ class StateCalculator {
373 } 426 }
374 } 427 }
375 428
429 for (int panel_door_id : reqs.panel_doors) {
430 const PanelDoor& panel_door_obj = GD_GetPanelDoor(panel_door_id);
431 Decision decision = IsNonGroupedDoorReachable(panel_door_obj);
432
433 if (report) {
434 (*report)[AP_GetItemName(panel_door_obj.ap_item_id)] =
435 (decision == kYes);
436 }
437
438 if (decision != kYes) {
439 final_decision = decision;
440 }
441 }
442
376 for (int item_id : reqs.items) { 443 for (int item_id : reqs.items) {
377 bool has_item = AP_HasItem(item_id); 444 bool has_item = AP_HasItem(item_id);
378 if (report) { 445 if (report) {
@@ -574,6 +641,8 @@ class StateCalculator {
574 std::set<int> solveable_panels_; 641 std::set<int> solveable_panels_;
575 std::set<int> reachable_paintings_; 642 std::set<int> reachable_paintings_;
576 std::map<int, std::map<std::string, bool>> door_report_; 643 std::map<int, std::map<std::string, bool>> door_report_;
644
645 std::map<int, std::list<int>> paths_;
577}; 646};
578 647
579} // namespace 648} // namespace
diff --git a/src/version.cpp b/src/version.cpp new file mode 100644 index 0000000..3b4d5f3 --- /dev/null +++ b/src/version.cpp
@@ -0,0 +1,5 @@
1#include "version.h"
2
3std::ostream& operator<<(std::ostream& out, const Version& ver) {
4 return out << "v" << ver.major << "." << ver.minor << "." << ver.revision;
5}
diff --git a/src/version.h b/src/version.h index b688e27..8f93e18 100644 --- a/src/version.h +++ b/src/version.h
@@ -36,6 +36,6 @@ struct Version {
36 } 36 }
37}; 37};
38 38
39constexpr const Version kTrackerVersion = Version(0, 10, 5); 39constexpr const Version kTrackerVersion = Version(0, 10, 6);
40 40
41#endif /* end of include guard: VERSION_H_C757E53C */ \ No newline at end of file 41#endif /* end of include guard: VERSION_H_C757E53C */ \ No newline at end of file