From 738d970d26794db8bb3dcf459c4a787b624910ba Mon Sep 17 00:00:00 2001
From: art0007i <art0007i@gmail.com>
Date: Tue, 24 Sep 2024 15:11:32 +0200
Subject: make paintings 1 directional and add "arrows" the "arrows" are
 circles because I couldn't figure out how to use the DrawPolygon function...

---
 assets/subway.yaml  | 115 +++++++++++++++++++++++++++-------------------------
 src/game_data.cpp   |  17 ++++++++
 src/game_data.h     |   6 ++-
 src/network_set.cpp |  29 ++++++++-----
 src/network_set.h   |  16 ++++++--
 src/subway_map.cpp  |  80 +++++++++++++++++++++++++++---------
 6 files changed, 172 insertions(+), 91 deletions(-)

diff --git a/assets/subway.yaml b/assets/subway.yaml
index dcc58b2..87be8cc 100644
--- a/assets/subway.yaml
+++ b/assets/subway.yaml
@@ -14,42 +14,42 @@
   door: Painting Shortcut
   paintings:
     - garden_painting_tower2
-  tags:
+  entrances:
     - garden_starting
 - pos: [1066, 841]
   room: Courtyard
   door: Painting Shortcut
   paintings:
     - flower_painting_8
-  tags:
+  entrances:
     - flower_starting
 - pos: [905, 895]
   room: The Wondrous (Doorknob)
   door: Painting Shortcut
   paintings:
     - symmetry_painting_a_starter
-  tags:
+  entrances:
     - symmetry_starting
 - pos: [1066, 868]
   room: Outside The Bold
   door: Painting Shortcut
   paintings:
     - pencil_painting6
-  tags:
+  entrances:
     - pencil_starting
 - pos: [1066, 895]
   room: Outside The Undeterred
   door: Painting Shortcut
   paintings:
     - blueman_painting_3
-  tags:
+  entrances:
     - blueman_starting
 - pos: [905, 868]
   room: Outside The Agreeable
   door: Painting Shortcut
   paintings:
     - eyes_yellow_painting2
-  tags:
+  entrances:
     - street_starting
 - pos: [1211, 879]
   room: Hidden Room
@@ -66,7 +66,7 @@
 - pos: [1116, 939]
   paintings:
     - owl_painting
-  tags:
+  entrances:
     - owl_hidden
 - pos: [986, 793]
   room: Second Room
@@ -89,7 +89,7 @@
 - pos: [1313, 686]
   paintings:
     - maze_painting
-  tags:
+  exits:
     - green_owl
     - green_numbers
 - pos: [1172, 760]
@@ -108,7 +108,7 @@
 - pos: [1263, 867]
   paintings:
     - smile_painting_6
-  tags:
+  entrances:
     - smiley_deadend
 - pos: [1012, 1086]
   sunwarp:
@@ -154,7 +154,7 @@
 - pos: [756, 400]
   paintings:
     - smile_painting_4
-  tags:
+  entrances:
     - smiley_crossroads
 - pos: [878, 509]
   sunwarp:
@@ -208,7 +208,7 @@
 - pos: [1138, 287]
   paintings:
     - eyes_yellow_painting
-  tags:
+  exits:
     - street_starting
 - pos: [1088, 385]
   sunwarp:
@@ -220,7 +220,7 @@
 - pos: [1214, 457]
   paintings:
     - pencil_painting7
-  tags:
+  entrances:
     - pencil_compass
 - pos: [1196, 417]
   invisible: true
@@ -251,7 +251,7 @@
 - pos: [1477, 343]
   paintings:
     - garden_painting_tower
-  tags:
+  exits:
     - garden_starting
 - pos: [1565, 311]
   room: The Fearless (First Floor)
@@ -271,17 +271,17 @@
 - pos: [1784, 569]
   paintings:
     - crown_painting
-  tags:
+  exits:
     - crown_tower6
 - pos: [1653, 717]
   paintings:
     - eight_painting2
-  tags:
+  entrances:
     - eight_alcove
 - pos: [1653, 662]
   paintings:
     - eight_painting
-  tags:
+  exits:
     - eight_alcove
 - pos: [697, 1471]
   room: Orange Tower
@@ -402,7 +402,7 @@
 - pos: [844, 134]
   paintings:
     - smile_painting_8
-  tags:
+  entrances:
     - smiley_hotcrusts
 - pos: [797, 155]
   sunwarp:
@@ -435,12 +435,13 @@
     - colors_painting2
     - cherry_painting2
     - hi_solved_painting
-  tags:
+  entrances:
     - owl_tower6
     - clock_tower6
     - panda_tower6
     - crown_tower6
     - apple_tower6
+  exits:
     - hi_scientific
 - pos: [349, 1124]
   paintings:
@@ -457,7 +458,7 @@
 - pos: [556, 233]
   paintings:
     - flower_painting_7
-  tags:
+  exits:
     - flower_starting
     - flower_arrow
 - pos: [600, 332]
@@ -466,7 +467,7 @@
 - pos: [579, 350]
   paintings:
     - blueman_painting
-  tags:
+  entrances:
     - blueman_courtyard
 - pos: [530, 310]
   room: First Second Third Fourth
@@ -508,7 +509,7 @@
   room: Welcome Back Area
   door: Shortcut to Starting Room
 - pos: [773, 954]
-  tags:
+  exits:
     - hub_wb
     - wondrous_wb
     - undeterred_wb
@@ -519,31 +520,31 @@
     - scientific_wb
     - cellar_wb
 - pos: [1107, 749]
-  tags:
+  entrances:
     - hub_wb
 - pos: [408, 817]
-  tags:
+  entrances:
     - wondrous_wb
 - pos: [281, 1017]
-  tags:
+  entrances:
     - undeterred_wb
 - pos: [1017, 289]
-  tags:
+  entrances:
     - agreeable_wb
 - pos: [907, 1385]
-  tags:
+  entrances:
     - wanderer_wb
 - pos: [1737, 1053]
-  tags:
+  entrances:
     - gallery_wb
 - pos: [1690, 268]
-  tags:
+  entrances:
     - observant_wb
 - pos: [250, 604]
-  tags:
+  entrances:
     - scientific_wb
 - pos: [1553, 1467]
-  tags:
+  entrances:
     - cellar_wb
 - pos: [1478, 498]
   room: Owl Hallway
@@ -554,8 +555,9 @@
     - maze_painting_2
     - owl_painting_2
     - clock_painting_4
-  tags:
+  entrances:
     - green_owl
+  exits:
     - owl_hidden
     - owl_tower6
 - pos: [1478, 938]
@@ -600,13 +602,13 @@
 - pos: [1530, 938]
   paintings:
     - clock_painting_5
-  tags:
+  entrances:
     - clock_initiated
 - pos: [1546, 938]
   paintings:
     - clock_painting_2
     - arrows_painting_2
-  tags:
+  exits:
     - clock_tower6
     - clock_initiated
 - pos: [1579, 813]
@@ -616,7 +618,7 @@
 - pos: [1444, 896]
   paintings:
     - smile_painting_1
-  tags:
+  entrances:
     - smiley_initiated
 - pos: [1430, 691]
   room: Outside The Undeterred
@@ -630,6 +632,7 @@
     - blue_ch
     - yellow_ch
     - green_ch
+  exits:
     - early_ch
 - pos: [1567, 1264]
   tags:
@@ -656,7 +659,7 @@
   paintings:
     - pencil_painting2
     - north_missing2
-  tags:
+  exits:
     - pencil_compass
     - pencil_starting
     - pencil_directional
@@ -664,7 +667,7 @@
   room: Outside The Bold
   door: Steady Entrance
 - pos: [445, 1048]
-  tags:
+  exits:
     - undeterred_artistic
 - pos: [279, 1071]
   room: Number Hunt
@@ -684,7 +687,7 @@
 - pos: [60, 1017]
   paintings:
     - blueman_painting_2
-  tags:
+  exits:
     - blueman_courtyard
     - blueman_starting
 - pos: [402, 1012]
@@ -692,7 +695,7 @@
   door: Green Painting
   paintings:
     - maze_painting_3
-  tags:
+  entrances:
     - green_numbers
 - pos: [134, 1007]
   sunwarp:
@@ -727,7 +730,7 @@
   door: Eights
   paintings:
     - smile_painting_5
-  tags:
+  entrances:
     - smiley_numbers
 - pos: [557, 953]
   room: Number Hunt
@@ -753,24 +756,24 @@
 - pos: [351, 927]
   paintings:
     - boxes_painting
-  tags:
+  entrances:
     - lattice_directional
 - pos: [272, 927]
   paintings:
     - smile_painting_7
-  tags:
+  entrances:
     - smiley_directional
 - pos: [214, 822]
   paintings:
     - cherry_painting
-  tags:
+  entrances:
     - apple_directional
 - pos: [266, 735]
   room: Number Hunt
   door: Sixes
   paintings:
     - pencil_painting3
-  tags:
+  entrances:
     - pencil_directional
 - pos: [215, 735]
   paintings:
@@ -838,7 +841,7 @@
 - pos: [1653, 101]
   paintings:
     - smile_painting_9
-  tags:
+  exits:
     - smiley_crossroads
     - smiley_deadend
     - smiley_hotcrusts
@@ -851,12 +854,12 @@
   room: The Artistic (Smiley)
   door: Door to Panda
 - pos: [1711, 140]
-  tags:
+  entrances:
     - undeterred_artistic
 - pos: [1653, 169]
   paintings:
     - panda_painting_3
-  tags:
+  exits:
     - panda_tower6
     - panda_hallway
 - pos: [1708, 171]
@@ -865,7 +868,7 @@
 - pos: [1761, 169]
   paintings:
     - boxes_painting2
-  tags:
+  exits:
     - lattice_directional
 - pos: [1762, 139]
   room: The Artistic (Lattice)
@@ -873,7 +876,7 @@
 - pos: [1761, 101]
   paintings:
     - cherry_painting3
-  tags:
+  exits:
     - apple_tower6
     - apple_directional
 - pos: [1708, 107]
@@ -886,7 +889,7 @@
   paintings:
     - eye_painting_2
     - smile_painting_2
-  tags:
+  entrances:
     - smiley_theysee
 - pos: [310, 750]
   room: The Eyes They See
@@ -908,7 +911,7 @@
     - symmetry_painting_b_2
     - symmetry_painting_a_6
     - symmetry_painting_b_6
-  tags:
+  exits:
     - symmetry_starting
 - pos: [407, 755]
   room: The Wondrous
@@ -918,12 +921,12 @@
 - pos: [449, 755]
   paintings:
     - flower_painting_6
-  tags:
+  entrances:
     - flower_arrow
 - pos: [1101, 222]
   paintings:
     - panda_painting
-  tags:
+  entrances:
     - panda_hallway
 - pos: [1152, 209]
   room: Hallway Room (1)
@@ -977,16 +980,16 @@
     - scenery_painting_0a
     - map_painting
     - fruitbowl_painting4
-  tags:
+  entrances:
     - smiley_gallery
 - pos: [1120, 1286]
   room: Rhyme Room (Smiley)
   door: Door to Target
 - pos: [1120, 1315]
-  tags:
+  entrances: # this could be considered 2 way since the subway map has a one way gate at the exit anyway
     - rhyme_smiley_target
 - pos: [792, 1137]
-  tags:
+  exits:
     - rhyme_smiley_target
 - pos: [895, 1217]
   room: Number Hunt
@@ -1030,7 +1033,7 @@
 - pos: [294, 602]
   paintings:
     - hi_solved_painting4
-  tags:
+  entrances:
     - hi_scientific
 - pos: [814, 1001]
   room: Challenge Room
diff --git a/src/game_data.cpp b/src/game_data.cpp
index c39e239..7b805df 100644
--- a/src/game_data.cpp
+++ b/src/game_data.cpp
@@ -681,6 +681,18 @@ struct GameData {
         }
       }
 
+      if (subway_it["entrances"]) {
+        for (const auto &entrance_it : subway_it["entrances"]) {
+          subway_item.entrances.push_back(entrance_it.as<std::string>());
+        }
+      }
+
+      if (subway_it["exits"]) {
+        for (const auto &exit_it : subway_it["exits"]) {
+          subway_item.exits.push_back(exit_it.as<std::string>());
+        }
+      }
+
       if (subway_it["sunwarp"]) {
         SubwaySunwarp sunwarp;
         sunwarp.dots = subway_it["sunwarp"]["dots"].as<int>();
@@ -792,6 +804,11 @@ GameData &GetState() {
 
 }  // namespace
 
+bool SubwayItem::HasWarps() const {
+  return !(this->tags.empty() && this->entrances.empty() &&
+           this->exits.empty());
+}
+
 bool SubwaySunwarp::operator<(const SubwaySunwarp &rhs) const {
   return std::tie(dots, type) < std::tie(rhs.dots, rhs.type);
 }
diff --git a/src/game_data.h b/src/game_data.h
index 3179365..1f6d247 100644
--- a/src/game_data.h
+++ b/src/game_data.h
@@ -148,9 +148,13 @@ struct SubwayItem {
   int y;
   std::optional<int> door;
   std::vector<std::string> paintings;
-  std::vector<std::string> tags;
+  std::vector<std::string> tags; // 2-way teleports
+  std::vector<std::string> entrances; // teleport entrances
+  std::vector<std::string> exits; // teleport exits
   std::optional<SubwaySunwarp> sunwarp;
   std::optional<std::string> special;
+
+  bool HasWarps() const;
 };
 
 const std::vector<MapArea>& GD_GetMapAreas();
diff --git a/src/network_set.cpp b/src/network_set.cpp
index 2a9e12c..c7639ad 100644
--- a/src/network_set.cpp
+++ b/src/network_set.cpp
@@ -4,9 +4,8 @@ void NetworkSet::Clear() {
   network_by_item_.clear();
 }
 
-void NetworkSet::AddLink(int id1, int id2) {
-  if (id2 > id1) {
-    // Make sure id1 < id2
+void NetworkSet::AddLink(int id1, int id2, bool two_way) {
+  if (two_way && id2 > id1) {
     std::swap(id1, id2);
   }
 
@@ -17,13 +16,14 @@ void NetworkSet::AddLink(int id1, int id2) {
     network_by_item_[id2] = {};
   }
 
-  network_by_item_[id1].insert({id1, id2});
-  network_by_item_[id2].insert({id1, id2});
+  NetworkNode node = {id1, id2, two_way};
+
+  network_by_item_[id1].insert(node);
+  network_by_item_[id2].insert(node);
 }
 
-void NetworkSet::AddLinkToNetwork(int network_id, int id1, int id2) {
-  if (id2 > id1) {
-    // Make sure id1 < id2
+void NetworkSet::AddLinkToNetwork(int network_id, int id1, int id2, bool two_way) {
+  if (two_way && id2 > id1) {
     std::swap(id1, id2);
   }
 
@@ -31,13 +31,22 @@ void NetworkSet::AddLinkToNetwork(int network_id, int id1, int id2) {
     network_by_item_[network_id] = {};
   }
 
-  network_by_item_[network_id].insert({id1, id2});
+  NetworkNode node = {id1, id2, two_way};
+
+  network_by_item_[network_id].insert(node);
 }
 
 bool NetworkSet::IsItemInNetwork(int id) const {
   return network_by_item_.count(id);
 }
 
-const std::set<std::pair<int, int>>& NetworkSet::GetNetworkGraph(int id) const {
+const std::set<NetworkNode>& NetworkSet::GetNetworkGraph(int id) const {
   return network_by_item_.at(id);
 }
+
+bool NetworkNode::operator<(const NetworkNode& rhs) const {
+  if (entry != rhs.entry) return entry < rhs.entry;
+  if (exit != rhs.exit) return exit < rhs.exit;
+  if (two_way != rhs.two_way) return two_way < rhs.two_way;
+  return false;
+}
\ No newline at end of file
diff --git a/src/network_set.h b/src/network_set.h
index cec3f39..0f72052 100644
--- a/src/network_set.h
+++ b/src/network_set.h
@@ -7,21 +7,29 @@
 #include <utility>
 #include <vector>
 
+struct NetworkNode {
+  int entry;
+  int exit;
+  bool two_way;
+
+  bool operator<(const NetworkNode& rhs) const;
+};
+
 class NetworkSet {
  public:
   void Clear();
 
-  void AddLink(int id1, int id2);
+  void AddLink(int id1, int id2, bool two_way);
 
-  void AddLinkToNetwork(int network_id, int id1, int id2);
+  void AddLinkToNetwork(int network_id, int id1, int id2, bool two_way);
 
   bool IsItemInNetwork(int id) const;
 
-  const std::set<std::pair<int, int>>& GetNetworkGraph(int id) const;
+  const std::set<NetworkNode>& GetNetworkGraph(int id) const;
 
  private:
 
-  std::map<int, std::set<std::pair<int, int>>> network_by_item_;
+  std::map<int, std::set<NetworkNode>> network_by_item_;
 };
 
 #endif /* end of include guard: NETWORK_SET_H_3036B8E3 */
diff --git a/src/subway_map.cpp b/src/subway_map.cpp
index 5b3ff5f..44f754f 100644
--- a/src/subway_map.cpp
+++ b/src/subway_map.cpp
@@ -91,10 +91,12 @@ void SubwayMap::OnConnect() {
   networks_.Clear();
 
   std::map<std::string, std::vector<int>> tagged;
+  std::map<std::string, std::vector<int>> entrances;
+  std::map<std::string, std::vector<int>> exits;
   for (const SubwayItem &subway_item : GD_GetSubwayItems()) {
     if (AP_HasEarlyColorHallways() &&
         subway_item.special == "starting_room_paintings") {
-      tagged["early_ch"].push_back(subway_item.id);
+      entrances["early_ch"].push_back(subway_item.id);
     }
 
     if (AP_IsPaintingShuffle() && !subway_item.paintings.empty()) {
@@ -104,17 +106,33 @@ void SubwayMap::OnConnect() {
     for (const std::string &tag : subway_item.tags) {
       tagged[tag].push_back(subway_item.id);
     }
+    for (const std::string &tag : subway_item.entrances) {
+      entrances[tag].push_back(subway_item.id);
+    }
+    for (const std::string &tag : subway_item.exits) {
+      exits[tag].push_back(subway_item.id);
+    }
 
-    if (!AP_IsSunwarpShuffle() && subway_item.sunwarp &&
-        subway_item.sunwarp->type != SubwaySunwarpType::kFinal) {
+    if (!AP_IsSunwarpShuffle() && subway_item.sunwarp) {
       std::string tag = fmt::format("sunwarp{}", subway_item.sunwarp->dots);
-      tagged[tag].push_back(subway_item.id);
+      switch (subway_item.sunwarp->type) {
+        case SubwaySunwarpType::kEnter:
+          entrances[tag].push_back(subway_item.id);
+          break;
+        case SubwaySunwarpType::kExit:
+          exits[tag].push_back(subway_item.id);
+          break;
+        default:
+          break;
+      }
     }
 
-    if (!AP_IsPilgrimageEnabled() &&
-        (subway_item.special == "sun_painting" ||
-         subway_item.special == "sun_painting_exit")) {
-      tagged["sun_painting"].push_back(subway_item.id);
+    if (!AP_IsPilgrimageEnabled()) {
+      if (subway_item.special == "sun_painting") {
+        entrances["sun_painting"].push_back(subway_item.id);
+      } else if (subway_item.special == "sun_painting_exit") {
+        exits["sun_painting"].push_back(subway_item.id);
+      }
     }
   }
 
@@ -143,13 +161,14 @@ void SubwayMap::OnConnect() {
         toWarp.type = SubwaySunwarpType::kExit;
       }
 
-      tagged[tag].push_back(GD_GetSubwayItemForSunwarp(fromWarp));
-      tagged[tag].push_back(GD_GetSubwayItemForSunwarp(toWarp));
-      
+      entrances[tag].push_back(GD_GetSubwayItemForSunwarp(fromWarp));
+      exits[tag].push_back(GD_GetSubwayItemForSunwarp(toWarp));
+
       networks_.AddLinkToNetwork(
           final_sunwarp_item, GD_GetSubwayItemForSunwarp(fromWarp),
           mapping.dots == 6 ? final_sunwarp_item
-                            : GD_GetSubwayItemForSunwarp(toWarp));
+                            : GD_GetSubwayItemForSunwarp(toWarp),
+          false);
     }
   }
 
@@ -159,7 +178,17 @@ void SubwayMap::OnConnect() {
          tag_it1++) {
       for (auto tag_it2 = std::next(tag_it1); tag_it2 != items.end();
            tag_it2++) {
-        networks_.AddLink(*tag_it1, *tag_it2);
+        // two links because tags are bi-directional
+        networks_.AddLink(*tag_it1, *tag_it2, true);
+      }
+    }
+  }
+
+  for (const auto &[tag, items] : entrances) {
+    if (!exits.contains(tag)) continue;
+    for (auto exit : exits[tag]) {
+      for (auto entrance : items) {
+        networks_.AddLink(entrance, exit, false);
       }
     }
   }
@@ -179,7 +208,7 @@ void SubwayMap::UpdateIndicators() {
               AP_GetPaintingMapping().at(painting_id));
 
           if (from_id && to_id) {
-            networks_.AddLink(*from_id, *to_id);
+            networks_.AddLink(*from_id, *to_id, false);
           }
         }
       }
@@ -192,7 +221,7 @@ void SubwayMap::UpdateIndicators() {
 void SubwayMap::UpdateSunwarp(SubwaySunwarp from_sunwarp,
                               SubwaySunwarp to_sunwarp) {
   networks_.AddLink(GD_GetSubwayItemForSunwarp(from_sunwarp),
-                    GD_GetSubwayItemForSunwarp(to_sunwarp));
+                    GD_GetSubwayItemForSunwarp(to_sunwarp), false);
 }
 
 void SubwayMap::Zoom(bool in) {
@@ -358,10 +387,9 @@ void SubwayMap::OnPaint(wxPaintEvent &event) {
     if (networks_.IsItemInNetwork(*hovered_item_)) {
       dc.SetBrush(*wxTRANSPARENT_BRUSH);
 
-      for (const auto &[item_id1, item_id2] :
-           networks_.GetNetworkGraph(*hovered_item_)) {
-        const SubwayItem &item1 = GD_GetSubwayItem(item_id1);
-        const SubwayItem &item2 = GD_GetSubwayItem(item_id2);
+      for (const auto node : networks_.GetNetworkGraph(*hovered_item_)) {
+        const SubwayItem &item1 = GD_GetSubwayItem(node.entry);
+        const SubwayItem &item2 = GD_GetSubwayItem(node.exit);
 
         wxPoint item1_pos = MapPosToRenderPos(
             {item1.x + AREA_ACTUAL_SIZE / 2, item1.y + AREA_ACTUAL_SIZE / 2});
@@ -381,6 +409,12 @@ void SubwayMap::OnPaint(wxPaintEvent &event) {
           dc.DrawLine(item1_pos, item2_pos);
           dc.SetPen(*wxThePenList->FindOrCreatePen(*wxCYAN, 2));
           dc.DrawLine(item1_pos, item2_pos);
+          if (!node.two_way) {
+            dc.SetPen(*wxThePenList->FindOrCreatePen(*wxBLACK, 2));
+            dc.SetBrush(*wxCYAN_BRUSH);
+            dc.DrawCircle(item2_pos, 4);
+            dc.SetBrush(*wxTRANSPARENT_BRUSH);
+          }
         } else {
           int ellipse_x;
           int ellipse_y;
@@ -423,6 +457,12 @@ void SubwayMap::OnPaint(wxPaintEvent &event) {
           dc.SetPen(*wxThePenList->FindOrCreatePen(*wxCYAN, 2));
           dc.DrawEllipticArc(ellipse_x, ellipse_y, halfwidth * 2,
                              halfheight * 2, start, end);
+          if (!node.two_way) {
+            dc.SetPen(*wxThePenList->FindOrCreatePen(*wxBLACK, 2));
+            dc.SetBrush(*wxCYAN_BRUSH);
+            dc.DrawCircle(item2_pos, 4);
+            dc.SetBrush(*wxTRANSPARENT_BRUSH);
+          }
         }
       }
     }
@@ -622,7 +662,7 @@ void SubwayMap::Redraw() {
             }
           }
         }
-      } else if (!subway_item.tags.empty()) {
+      } else if (subway_item.HasWarps()) {
         draw_type = ItemDrawType::kOwl;
       }
     } else if (subway_door) {
-- 
cgit 1.4.1