autotile.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  1. // SuperTux
  2. // Copyright (C) 2020 A. Semphris <semphris@protonmail.com>
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  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. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. #include "supertux/autotile.hpp"
  17. #include <bitset>
  18. #include "util/log.hpp"
  19. // AutotileMask.
  20. AutotileMask::AutotileMask(uint8_t mask, bool center) :
  21. m_mask(mask),
  22. m_center(center)
  23. {
  24. }
  25. bool
  26. AutotileMask::matches(uint8_t mask, bool center) const
  27. {
  28. return mask == m_mask && center == m_center;
  29. }
  30. // Autotile.
  31. Autotile::Autotile(uint32_t tile_id, const std::vector<std::pair<uint32_t, AltConditions>>& alt_tiles, const std::vector<AutotileMask>& masks, bool solid) :
  32. m_tile_id(tile_id),
  33. m_alt_tiles(alt_tiles),
  34. m_masks(std::move(masks)),
  35. m_solid(solid)
  36. {
  37. }
  38. bool
  39. Autotile::matches(uint8_t num_mask, bool center) const
  40. {
  41. for (auto& l_mask : m_masks)
  42. {
  43. if (l_mask.matches(num_mask, center))
  44. {
  45. return true;
  46. }
  47. }
  48. return false;
  49. }
  50. uint32_t
  51. Autotile::pick_tile(int x, int y) const
  52. {
  53. // Needed? Not needed?
  54. // Could avoid pointless computation.
  55. if (m_alt_tiles.empty())
  56. return m_tile_id;
  57. // srand() and rand() are inconsistent across platforms (Windows)
  58. // srand(x * 32768 + y);
  59. // float rnd_val = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
  60. float rnd_val = static_cast<float>(
  61. (
  62. x * 371 + y * 49173 + (x * x * y) % 32769 + (y * y * x + x * x * x ) % 65537
  63. ) % 256
  64. ) / 256.0f;
  65. for (const auto& pair : m_alt_tiles)
  66. {
  67. const AltConditions& cond = pair.second;
  68. if (cond.weight <= 0.f && cond.period_x.first == 0 && cond.period_y.first == 0)
  69. continue;
  70. if (cond.period_x.first != 0 && x % cond.period_x.first != cond.period_x.second)
  71. continue;
  72. if (cond.period_y.first != 0 && y % cond.period_y.first != cond.period_y.second)
  73. continue;
  74. if (cond.weight > 0.f)
  75. {
  76. rnd_val -= cond.weight;
  77. if (rnd_val > 0.f)
  78. continue;
  79. }
  80. return pair.first;
  81. }
  82. return m_tile_id;
  83. }
  84. bool
  85. Autotile::is_amongst(uint32_t tile) const
  86. {
  87. if (tile == m_tile_id)
  88. return true;
  89. for (const auto& pair : m_alt_tiles)
  90. if (pair.first == tile)
  91. return true;
  92. return false;
  93. }
  94. uint8_t
  95. Autotile::get_first_mask() const
  96. {
  97. if (!m_masks.empty())
  98. return m_masks[0].get_mask();
  99. return 0;
  100. }
  101. // AutotileSet.
  102. std::vector<std::unique_ptr<AutotileSet>> AutotileSet::m_autotilesets;
  103. AutotileSet::AutotileSet(const std::vector<Autotile*>& tiles, uint32_t default_tile, const std::string& name, bool corner) :
  104. m_autotiles(tiles),
  105. m_default(default_tile),
  106. m_name(name),
  107. m_corner(corner)
  108. {
  109. }
  110. AutotileSet::~AutotileSet()
  111. {
  112. for (Autotile* autotile : m_autotiles)
  113. delete autotile;
  114. }
  115. /*
  116. AutotileSet*
  117. AutotileSet::get_tileset_from_tile(uint32_t tile_id)
  118. {
  119. if (tile_id == 0)
  120. {
  121. return nullptr;
  122. }
  123. if (m_autotilesets->size() == 0)
  124. {
  125. // TODO: Add possibility to include external autotile config files
  126. AutotileParser* parser = new AutotileParser(m_autotilesets, "/images/autotiles.satc");
  127. parser->parse();
  128. }
  129. for (auto& ats : *m_autotilesets)
  130. {
  131. if (ats->is_member(tile_id))
  132. {
  133. return ats;
  134. }
  135. }
  136. return nullptr;
  137. }
  138. */
  139. uint32_t
  140. AutotileSet::get_autotile(uint32_t tile_id,
  141. bool top_left, bool top, bool top_right,
  142. bool left, bool center, bool right,
  143. bool bottom_left, bool bottom, bool bottom_right,
  144. int x, int y
  145. ) const
  146. {
  147. uint8_t num_mask = 0;
  148. if (m_corner)
  149. {
  150. if (bottom_right) num_mask = static_cast<uint8_t>(num_mask + 0x01);
  151. if (bottom_left) num_mask = static_cast<uint8_t>(num_mask + 0x02);
  152. if (top_right) num_mask = static_cast<uint8_t>(num_mask + 0x04);
  153. if (top_left) num_mask = static_cast<uint8_t>(num_mask + 0x08);
  154. center = true;
  155. }
  156. else
  157. {
  158. // num_mask += 0x01;
  159. // clang will complain
  160. // num_mask += static_cast<uint8_t>(0x01);
  161. // gcc will complain
  162. // num_mask = (num_mask + 0x01) & 0xff;
  163. // (from a stackoverflow.com question) gcc still complains
  164. // EDIT : It appears that GCC makes all integers calculations in "int" type,
  165. // so you have to re-cast every single time :^)
  166. if (bottom_right) num_mask = static_cast<uint8_t>(num_mask + 0x01);
  167. if (bottom) num_mask = static_cast<uint8_t>(num_mask + 0x02);
  168. if (bottom_left) num_mask = static_cast<uint8_t>(num_mask + 0x04);
  169. if (right) num_mask = static_cast<uint8_t>(num_mask + 0x08);
  170. if (left) num_mask = static_cast<uint8_t>(num_mask + 0x10);
  171. if (top_right) num_mask = static_cast<uint8_t>(num_mask + 0x20);
  172. if (top) num_mask = static_cast<uint8_t>(num_mask + 0x40);
  173. if (top_left) num_mask = static_cast<uint8_t>(num_mask + 0x80);
  174. }
  175. for (auto* autotile : m_autotiles)
  176. {
  177. if (autotile->matches(num_mask, center))
  178. {
  179. return autotile->pick_tile(x, y);
  180. }
  181. }
  182. return center ? get_default_tile() : 0;
  183. }
  184. bool
  185. AutotileSet::is_member(uint32_t tile_id) const
  186. {
  187. for (auto& tile : m_autotiles)
  188. {
  189. if (tile->get_tile_id() == tile_id)
  190. {
  191. return true;
  192. }
  193. else
  194. {
  195. for (const auto& pair : tile->get_all_tile_ids())
  196. {
  197. if (pair.first == tile_id)
  198. {
  199. return true;
  200. }
  201. }
  202. }
  203. }
  204. // m_default should *never* be 0 (always a valid solid tile,
  205. // even if said tile isn't part of the tileset).
  206. return tile_id == m_default && m_default != 0;
  207. }
  208. bool
  209. AutotileSet::is_solid(uint32_t tile_id) const
  210. {
  211. if (!is_member(tile_id))
  212. return false;
  213. for (auto* tile : m_autotiles)
  214. {
  215. if (tile->get_tile_id() == tile_id)
  216. {
  217. return tile->is_solid();
  218. }
  219. else
  220. {
  221. for (const auto& pair : tile->get_all_tile_ids())
  222. {
  223. if (pair.first == tile_id)
  224. {
  225. return tile->is_solid();
  226. }
  227. }
  228. }
  229. }
  230. //log_warning << "Called AutotileSet::is_solid() with a tile_id that isn't in the Autotileset, yet that returns is_member() = true." << std::endl;
  231. // m_default should *never* be 0 (always a valid solid tile,
  232. // even if said tile isn't part of the tileset).
  233. return tile_id == m_default && m_default != 0;
  234. }
  235. uint8_t
  236. AutotileSet::get_mask_from_tile(uint32_t tile) const
  237. {
  238. for (auto* autotile : m_autotiles)
  239. {
  240. if (autotile->is_amongst(tile)) {
  241. return autotile->get_first_mask();
  242. }
  243. }
  244. return static_cast<uint8_t>(0);
  245. }
  246. void
  247. AutotileSet::validate(int32_t start, int32_t end) const
  248. {
  249. // Corner autotiles are always empty if all 4 corners are, but regular
  250. // autotiles should have a valid tile ID that can be surrounded by emptiness.
  251. for (int mask = (m_corner ? 1 : 0); mask <= (m_corner ? 15 : 255); mask++)
  252. {
  253. uint8_t num_mask = static_cast<uint8_t>(mask);
  254. bool tile_exists = false;
  255. uint32_t tile_nonsolid = 0; // Relevant only for non-corner autotiles.
  256. uint32_t tile_with_that_mask = 0; // Used to help users debug.
  257. for (auto* autotile : m_autotiles)
  258. {
  259. if (autotile->matches(num_mask, true))
  260. {
  261. if (tile_exists)
  262. {
  263. log_warning << "Autotileset '" << m_name
  264. << "' (range " << (start ? std::to_string(start) : "...") << "-" << (end ? std::to_string(end) : "...") << "): mask "
  265. << (m_corner ? std::bitset<4>(mask).to_string() : std::bitset<8>(mask).to_string()) << " corresponds both to tile " << tile_with_that_mask << " and " << autotile->get_tile_id() << std::endl;
  266. }
  267. else
  268. {
  269. tile_exists = true;
  270. tile_with_that_mask = autotile->get_tile_id();
  271. }
  272. }
  273. if (autotile->matches(num_mask, false))
  274. {
  275. if (tile_nonsolid)
  276. {
  277. log_warning << "Autotileset '" << m_name
  278. << "' (range " << (start ? std::to_string(start) : "...") << "-" << (end ? std::to_string(end) : "...") << "): non-solid mask "
  279. << (m_corner ? std::bitset<4>(mask).to_string() : std::bitset<8>(mask).to_string()) << " corresponds both to tile " << tile_with_that_mask << " and " << autotile->get_tile_id() << std::endl;
  280. }
  281. else
  282. {
  283. tile_nonsolid = autotile->get_tile_id();
  284. }
  285. }
  286. }
  287. if (!tile_exists)
  288. {
  289. log_warning << "Autotileset '" << m_name
  290. << "' (range " << (start ? std::to_string(start) : "...") << "-" << (end ? std::to_string(end) : "...") << "): mask "
  291. << (m_corner ? std::bitset<4>(mask).to_string() : std::bitset<8>(mask).to_string()) << " has no corresponding tile" << std::endl;
  292. }
  293. }
  294. }
  295. /* EOF */