Tube.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. /*
  2. FinalSun/FinalAlert 2 Mission Editor
  3. Copyright (C) 1999-2024 Electronic Arts, Inc.
  4. Authored by Matthias Wagner
  5. This program is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9. This program is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with this program. If not, see <https://www.gnu.org/licenses/>.
  15. */
  16. #include "StdAfx.h"
  17. #include "Tube.h"
  18. #include <ranges>
  19. #include "inlines.h"
  20. #ifndef __INTELLISENSE__
  21. // This file had problems with Intellisense. Until this is fixed, deactivate intellisense parsing here.
  22. int sgn(int v)
  23. {
  24. if (v < 0) return -1;
  25. return v > 0 ? 1 : 0;
  26. }
  27. CTube::CTube(std::uint16_t tubeID, std::uint16_t startX, std::uint16_t startY, ETubeDirection direction, std::uint16_t endX, std::uint16_t endY, const std::vector<ETubeDirection>& tubeParts):
  28. CTube(startX, startY, direction, endX, endY, tubeParts)
  29. {
  30. m_tubeId = tubeID;
  31. }
  32. CTube::CTube(const std::uint16_t startX, const std::uint16_t startY, const ETubeDirection direction, const std::uint16_t endX, const std::uint16_t endY, const std::vector<ETubeDirection>& tubeParts):
  33. CTube()
  34. {
  35. m_startX = startX;
  36. m_startY = startY;
  37. m_direction = direction;
  38. m_endX = endX;
  39. m_endY = endY;
  40. m_tubeParts = tubeParts;
  41. }
  42. CTube::CTube(std::uint16_t tubeId, const std::string& value):
  43. m_tubeId(tubeId)
  44. {
  45. m_startY = stoi(GetParam(value, 0));
  46. m_startX = stoi(GetParam(value, 1));
  47. m_direction = ToTubeDirection(stoi(GetParam(value, 2)));
  48. m_endY = stoi(GetParam(value, 3));
  49. m_endX = stoi(GetParam(value, 4));
  50. std::string readDirS;
  51. for (std::size_t e = 5; !(readDirS = GetParam(value, e)).empty(); ++e)
  52. {
  53. const auto readDir = stoi(readDirS);
  54. m_tubeParts.push_back(ToTubeDirection(readDir));
  55. }
  56. if (m_tubeParts.empty() || m_tubeParts.back() != ETubeDirection::Undefined)
  57. m_tubeParts.push_back(ETubeDirection::Undefined);
  58. }
  59. bool CTube::isValid() const
  60. {
  61. return m_direction != ETubeDirection::Undefined && m_tubeParts.size() > 1;
  62. }
  63. CTube CTube::reverse(std::uint16_t newTubeID) const
  64. {
  65. CTube ti2;
  66. ti2.m_tubeId = newTubeID;
  67. ti2.m_startX = m_endX;
  68. ti2.m_startY = m_endY;
  69. ti2.m_endX = m_startX;
  70. ti2.m_endY = m_startY;
  71. const auto last_d = getLastDirection();
  72. ti2.m_direction = opposite_dir(last_d);
  73. auto x = m_startX;
  74. auto y = m_startY;
  75. // reverse direction tube parts
  76. auto reversed = m_tubeParts |
  77. std::views::reverse |
  78. std::views::transform([](auto dir) {return opposite_dir(dir);}) |
  79. std::views::common;
  80. // find the first defined direction in the reversed list
  81. auto reversedFirst = std::find_if(reversed.begin(), reversed.end(), [](ETubeDirection dir) { return dir != ETubeDirection::Undefined; });
  82. auto reversedSize = reversed.end() - reversedFirst;
  83. ti2.m_tubeParts.reserve(reversed.size() + 2);
  84. ti2.m_tubeParts.assign(reversedFirst, reversed.end());
  85. ti2.m_tubeParts.push_back(ETubeDirection::Undefined);
  86. return ti2;
  87. }
  88. static const ETubeDirection kDiffToDir[3][3] = {
  89. {ETubeDirection::TopLeft, ETubeDirection::Top, ETubeDirection::TopRight}, // xadd == -1
  90. {ETubeDirection::Left, ETubeDirection::Undefined, ETubeDirection::Right}, // xadd == 0
  91. {ETubeDirection::BottomLeft, ETubeDirection::Bottom, ETubeDirection::BottomRight} // xadd == 1
  92. };
  93. bool CTube::isEqual(const CTube& r, bool ignoreId) const
  94. {
  95. if (!ignoreId)
  96. return *this == r;
  97. const auto& largerTubeParts = r.m_tubeParts.size() >= m_tubeParts.size() ? r.m_tubeParts : m_tubeParts;
  98. const auto& smallerTubeParts = r.m_tubeParts.size() < m_tubeParts.size() ? r.m_tubeParts : m_tubeParts;
  99. for (auto i = 0; i < smallerTubeParts.size(); ++i)
  100. {
  101. if (largerTubeParts[i] != smallerTubeParts[i])
  102. return false;
  103. }
  104. for (auto i = smallerTubeParts.size(); i < largerTubeParts.size(); ++i)
  105. {
  106. if (largerTubeParts[i] != ETubeDirection::Undefined)
  107. return false;
  108. }
  109. return m_direction == r.m_direction && m_startX == r.m_startX && m_startY == r.m_startY && r.m_endX == r.m_endX && r.m_endY == r.m_endY;
  110. }
  111. CTube CTube::autocreate(std::uint16_t startX, std::uint16_t startY, std::uint16_t endX, std::uint16_t endY, int straightStartParts)
  112. {
  113. CTube ti;
  114. ti.m_startX = startX;
  115. ti.m_startY = startY;
  116. ti.m_endX = startX;
  117. ti.m_endY = startY;
  118. ti.append(endX, endY, straightStartParts);
  119. return ti;
  120. /*
  121. int curx = startX;
  122. int cury = startY;
  123. CTube ti;
  124. ti.m_startX = startX;
  125. ti.m_startY = startY;
  126. ti.m_endX = endX;
  127. ti.m_endY = endY;
  128. int xadd = sgn(endX - curx);
  129. int yadd = sgn(endY - cury);
  130. if (straightStartTiles > 0)
  131. {
  132. const bool xMajor = abs(endX - startX) > abs(endY - startY);
  133. xadd = xMajor ? xadd : 0;
  134. yadd = xMajor ? 0 : yadd;
  135. }
  136. curx += xadd;
  137. cury += yadd;
  138. ti.m_direction = kDiffToDir[xadd + 1][yadd + 1];
  139. int n = 1;
  140. while (endX != curx || endY != cury)
  141. {
  142. xadd = n < straightStartTiles ? xadd : sgn(endX - curx); // keep initial direction for straightStartTiles
  143. yadd = n < straightStartTiles ? yadd : sgn(endY - cury);
  144. n++;
  145. curx += xadd;
  146. cury += yadd;
  147. ti.m_tubeParts.push_back(kDiffToDir[xadd + 1][yadd + 1]);
  148. }
  149. ti.m_tubeParts.push_back(ETubeDirection::Undefined);
  150. return ti;
  151. */
  152. }
  153. ETubeDirection opposite_dir(const ETubeDirection dir)
  154. {
  155. static const ETubeDirection opposite_dir_tbl[8] = {
  156. ETubeDirection::Bottom,
  157. ETubeDirection::BottomLeft,
  158. ETubeDirection::Left,
  159. ETubeDirection::TopLeft,
  160. ETubeDirection::Top,
  161. ETubeDirection::TopRight,
  162. ETubeDirection::Right,
  163. ETubeDirection::BottomRight
  164. };
  165. const auto iDir = to_int(dir);
  166. if (iDir < 0 || iDir > 7)
  167. return ETubeDirection::Undefined;
  168. return opposite_dir_tbl[iDir];
  169. }
  170. bool dir_to_xy(ETubeDirection dir, MapVec& vec)
  171. {
  172. static int dir_to_xy_table[8][2] = {
  173. {0, -1}, // 0
  174. {1, -1}, // 1
  175. {1, 0}, // 2
  176. {1, 1}, // 3
  177. {0, 1}, // 4
  178. {-1, 1}, // 5
  179. {-1, 0}, // 6
  180. {-1, -1}, // 7
  181. };
  182. const auto iDir = to_int(dir);
  183. if (iDir < 0 || iDir > 7)
  184. {
  185. vec.x = 0;
  186. vec.y = 0;
  187. return false;
  188. }
  189. vec.x = dir_to_xy_table[iDir][1]; // swap x/y
  190. vec.y = dir_to_xy_table[iDir][0];
  191. return true;
  192. }
  193. ETubeDirection CTube::getLastDirection() const
  194. {
  195. if (m_tubeParts.empty())
  196. return m_direction;
  197. auto last = std::find_if(m_tubeParts.crbegin(), m_tubeParts.crend(), [](ETubeDirection dir) { return dir != ETubeDirection::Undefined; });
  198. return last == m_tubeParts.crend() ? m_direction : *last;
  199. }
  200. bool CTube::touches(const MapCoords& mc) const
  201. {
  202. bool touches = false;
  203. walk([this, &mc, &touches](const auto& wi) {
  204. if (wi.pos == mc)
  205. {
  206. touches = true;
  207. return false;
  208. }
  209. return true;
  210. });
  211. return touches;
  212. }
  213. std::string CTube::toString() const
  214. {
  215. auto partsAsInt = m_tubeParts | std::views::transform([](auto dir) {return to_int(dir); }) | std::views::common;
  216. std::vector<int> values;
  217. values.reserve(5 + m_tubeParts.size());
  218. values.assign({ m_startY, m_startX, to_int(m_direction), m_endY, m_endX });
  219. values.insert(values.end(), partsAsInt.begin(), partsAsInt.end());
  220. std::string res = Join(",", values | std::views::transform([](auto v) {return std::to_string(v); }));
  221. if (m_tubeParts.empty() || m_tubeParts.back() != ETubeDirection::Undefined)
  222. res += ",-1"; // TS/RA2 crash when no delimiter exists
  223. return res;
  224. }
  225. bool CTube::append(std::uint16_t endX, std::uint16_t endY, int forceStraightParts)
  226. {
  227. auto newTubeParts = m_tubeParts;
  228. MapCoords end(endX, endY);
  229. // remove delimiters
  230. auto oldLast = std::find(newTubeParts.begin(), newTubeParts.end(), ETubeDirection::Undefined);
  231. newTubeParts.resize(oldLast - newTubeParts.begin());
  232. // now find current x/y
  233. MapCoords cur = getStartCoords();
  234. std::vector<MapCoords> existingPositions;
  235. if(!walk([&cur, &existingPositions](const WalkInfo& wi) {
  236. cur = wi.pos;
  237. existingPositions.push_back(wi.pos);
  238. return true;
  239. }))
  240. return false;
  241. // if no enter direction was given, set it now - this should only be true if there are no existingPositions
  242. if (m_direction == ETubeDirection::Undefined)
  243. {
  244. const bool xMajor = abs(end.x - m_startX) > abs(end.y - m_startY);
  245. MapVec add(sgn(end.x - m_startX), sgn(end.y - m_startY));
  246. add.x = xMajor ? add.x : 0;
  247. add.y = xMajor ? 0 : add.y;
  248. m_direction = kDiffToDir[add.x + 1][add.y + 1];
  249. }
  250. auto endOnExistingIt = std::find(existingPositions.begin(), existingPositions.end(), end);
  251. if (endOnExistingIt != existingPositions.end())
  252. {
  253. if (endOnExistingIt == existingPositions.begin())
  254. return false; // zero length remaining
  255. // shorten
  256. auto remaining = endOnExistingIt - existingPositions.begin();
  257. newTubeParts.resize(remaining);
  258. newTubeParts.push_back(ETubeDirection::Undefined);
  259. m_endX = end.x;
  260. m_endY = end.y;
  261. m_tubeParts = std::move(newTubeParts);
  262. return true;
  263. }
  264. int n = 0;
  265. while (end != cur)
  266. {
  267. MapVec add(sgn(end.x - cur.x), sgn(end.y - cur.y));
  268. if (newTubeParts.size() < 1 && forceStraightParts < 0)
  269. {
  270. // the first tube part should be in the same direction as the enter direction
  271. dir_to_xy(m_direction, add);
  272. }
  273. if (n++ < forceStraightParts)
  274. {
  275. const bool xMajorPart = abs(end.x - m_endX) > abs(end.y - m_endY);
  276. add.x = xMajorPart ? add.x : 0;
  277. add.y = xMajorPart ? 0 : add.y;
  278. }
  279. cur += add;
  280. //if (std::find(existingPositions.begin(), existingPositions.end(), std::make_pair(curX, curY)) != existingPositions.end())
  281. // return false; // intersection - maybe shorten? maybe we can even allow it if TS accepts it?
  282. newTubeParts.push_back(kDiffToDir[add.x + 1][add.y + 1]);
  283. }
  284. newTubeParts.push_back(ETubeDirection::Undefined);
  285. m_endX = cur.x;
  286. m_endY = cur.y;
  287. m_tubeParts = std::move(newTubeParts);
  288. return true;
  289. }
  290. bool CTube::walk(const std::function<bool(const WalkInfo&)>& walker) const
  291. {
  292. MapVec diff;
  293. WalkInfo wi;
  294. wi.pos = MapCoords(m_startX, m_startY);
  295. for (auto dir : m_tubeParts)
  296. {
  297. wi.direction = dir;
  298. dir_to_xy(wi.direction, diff);
  299. wi.next_pos = (wi.direction == ETubeDirection::Undefined) ? MapCoords(-1, -1) : wi.pos + diff;
  300. if (!walker(wi))
  301. return false;
  302. if (wi.direction == ETubeDirection::Undefined)
  303. break;
  304. wi.pos = wi.next_pos;
  305. }
  306. return true;
  307. }
  308. #endif