autotile.cpp 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321
  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, float>>& 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.size() == 0)
  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. rnd_val -= pair.second;
  68. if (rnd_val <= 0)
  69. {
  70. return pair.first;
  71. }
  72. }
  73. return m_tile_id;
  74. }
  75. bool
  76. Autotile::is_amongst(uint32_t tile) const
  77. {
  78. if (tile == m_tile_id)
  79. return true;
  80. for (const auto& pair : m_alt_tiles)
  81. if (pair.first == tile)
  82. return true;
  83. return false;
  84. }
  85. uint8_t
  86. Autotile::get_first_mask() const
  87. {
  88. if (!m_masks.empty())
  89. return m_masks[0].get_mask();
  90. return 0;
  91. }
  92. // AutotileSet.
  93. std::vector<std::unique_ptr<AutotileSet>> AutotileSet::m_autotilesets;
  94. AutotileSet::AutotileSet(const std::vector<Autotile*>& tiles, uint32_t default_tile, const std::string& name, bool corner) :
  95. m_autotiles(tiles),
  96. m_default(default_tile),
  97. m_name(name),
  98. m_corner(corner)
  99. {
  100. }
  101. AutotileSet::~AutotileSet()
  102. {
  103. for (Autotile* autotile : m_autotiles)
  104. delete autotile;
  105. }
  106. /*
  107. AutotileSet*
  108. AutotileSet::get_tileset_from_tile(uint32_t tile_id)
  109. {
  110. if (tile_id == 0)
  111. {
  112. return nullptr;
  113. }
  114. if (m_autotilesets->size() == 0)
  115. {
  116. // TODO: Add possibility to include external autotile config files
  117. AutotileParser* parser = new AutotileParser(m_autotilesets, "/images/autotiles.satc");
  118. parser->parse();
  119. }
  120. for (auto& ats : *m_autotilesets)
  121. {
  122. if (ats->is_member(tile_id))
  123. {
  124. return ats;
  125. }
  126. }
  127. return nullptr;
  128. }
  129. */
  130. uint32_t
  131. AutotileSet::get_autotile(uint32_t tile_id,
  132. bool top_left, bool top, bool top_right,
  133. bool left, bool center, bool right,
  134. bool bottom_left, bool bottom, bool bottom_right,
  135. int x, int y
  136. ) const
  137. {
  138. uint8_t num_mask = 0;
  139. if (m_corner)
  140. {
  141. if (bottom_right) num_mask = static_cast<uint8_t>(num_mask + 0x01);
  142. if (bottom_left) num_mask = static_cast<uint8_t>(num_mask + 0x02);
  143. if (top_right) num_mask = static_cast<uint8_t>(num_mask + 0x04);
  144. if (top_left) num_mask = static_cast<uint8_t>(num_mask + 0x08);
  145. center = true;
  146. }
  147. else
  148. {
  149. // num_mask += 0x01;
  150. // clang will complain
  151. // num_mask += static_cast<uint8_t>(0x01);
  152. // gcc will complain
  153. // num_mask = (num_mask + 0x01) & 0xff;
  154. // (from a stackoverflow.com question) gcc still complains
  155. // EDIT : It appears that GCC makes all integers calculations in "int" type,
  156. // so you have to re-cast every single time :^)
  157. if (bottom_right) num_mask = static_cast<uint8_t>(num_mask + 0x01);
  158. if (bottom) num_mask = static_cast<uint8_t>(num_mask + 0x02);
  159. if (bottom_left) num_mask = static_cast<uint8_t>(num_mask + 0x04);
  160. if (right) num_mask = static_cast<uint8_t>(num_mask + 0x08);
  161. if (left) num_mask = static_cast<uint8_t>(num_mask + 0x10);
  162. if (top_right) num_mask = static_cast<uint8_t>(num_mask + 0x20);
  163. if (top) num_mask = static_cast<uint8_t>(num_mask + 0x40);
  164. if (top_left) num_mask = static_cast<uint8_t>(num_mask + 0x80);
  165. }
  166. for (auto* autotile : m_autotiles)
  167. {
  168. if (autotile->matches(num_mask, center))
  169. {
  170. return autotile->pick_tile(x, y);
  171. }
  172. }
  173. return center ? get_default_tile() : 0;
  174. }
  175. bool
  176. AutotileSet::is_member(uint32_t tile_id) const
  177. {
  178. for (auto& tile : m_autotiles)
  179. {
  180. if (tile->get_tile_id() == tile_id)
  181. {
  182. return true;
  183. }
  184. else
  185. {
  186. for (const auto& pair : tile->get_all_tile_ids())
  187. {
  188. if (pair.first == tile_id)
  189. {
  190. return true;
  191. }
  192. }
  193. }
  194. }
  195. // m_default should *never* be 0 (always a valid solid tile,
  196. // even if said tile isn't part of the tileset).
  197. return tile_id == m_default && m_default != 0;
  198. }
  199. bool
  200. AutotileSet::is_solid(uint32_t tile_id) const
  201. {
  202. if (!is_member(tile_id))
  203. return false;
  204. for (auto* tile : m_autotiles)
  205. {
  206. if (tile->get_tile_id() == tile_id)
  207. {
  208. return tile->is_solid();
  209. }
  210. else
  211. {
  212. for (const auto& pair : tile->get_all_tile_ids())
  213. {
  214. if (pair.first == tile_id)
  215. {
  216. return tile->is_solid();
  217. }
  218. }
  219. }
  220. }
  221. //log_warning << "Called AutotileSet::is_solid() with a tile_id that isn't in the Autotileset, yet that returns is_member() = true." << std::endl;
  222. // m_default should *never* be 0 (always a valid solid tile,
  223. // even if said tile isn't part of the tileset).
  224. return tile_id == m_default && m_default != 0;
  225. }
  226. uint8_t
  227. AutotileSet::get_mask_from_tile(uint32_t tile) const
  228. {
  229. for (auto* autotile : m_autotiles)
  230. {
  231. if (autotile->is_amongst(tile)) {
  232. return autotile->get_first_mask();
  233. }
  234. }
  235. return static_cast<uint8_t>(0);
  236. }
  237. void
  238. AutotileSet::validate() const
  239. {
  240. // Corner autotiles are always empty if all 4 corners are, but regular
  241. // autotiles should have a valid tile ID that can be surrounded by emptiness.
  242. for (int mask = (m_corner ? 1 : 0); mask <= (m_corner ? 15 : 255); mask++)
  243. {
  244. uint8_t num_mask = static_cast<uint8_t>(mask);
  245. bool tile_exists = false;
  246. uint32_t tile_nonsolid = 0; // Relevant only for non-corner autotiles.
  247. uint32_t tile_with_that_mask = 0; // Used to help users debug.
  248. for (auto* autotile : m_autotiles)
  249. {
  250. if (autotile->matches(num_mask, true))
  251. {
  252. if (tile_exists)
  253. {
  254. log_warning << "Autotileset '" << m_name << "': mask " << (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;
  255. }
  256. else
  257. {
  258. tile_exists = true;
  259. tile_with_that_mask = autotile->get_tile_id();
  260. }
  261. }
  262. if (autotile->matches(num_mask, false))
  263. {
  264. if (tile_nonsolid)
  265. {
  266. log_warning << "Autotileset '" << m_name << "': non-solid mask " << (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;
  267. }
  268. else
  269. {
  270. tile_nonsolid = autotile->get_tile_id();
  271. }
  272. }
  273. }
  274. if (!tile_exists)
  275. {
  276. log_warning << "Autotileset '" << m_name << "': mask " << (m_corner ? std::bitset<4>(mask).to_string() : std::bitset<8>(mask).to_string()) << " has no corresponding tile" << std::endl;
  277. }
  278. }
  279. }
  280. /* EOF */