tilemap.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981
  1. // SuperTux
  2. // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
  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 "object/tilemap.hpp"
  17. #include <tuple>
  18. #include "editor/editor.hpp"
  19. #include "supertux/autotile.hpp"
  20. #include "supertux/debug.hpp"
  21. #include "supertux/gameconfig.hpp"
  22. #include "supertux/globals.hpp"
  23. #include "supertux/resources.hpp"
  24. #include "supertux/sector.hpp"
  25. #include "supertux/tile.hpp"
  26. #include "supertux/tile_set.hpp"
  27. #include "supertux/flip_level_transformer.hpp"
  28. #include "collision/collision_object.hpp"
  29. #include "collision/collision_movement_manager.hpp"
  30. #include "util/reader.hpp"
  31. #include "util/reader_mapping.hpp"
  32. #include "util/writer.hpp"
  33. #include "video/drawing_context.hpp"
  34. #include "video/layer.hpp"
  35. #include "video/surface.hpp"
  36. TileMap::TileMap(const TileSet *new_tileset) :
  37. ExposedObject<TileMap, scripting::TileMap>(this),
  38. PathObject(),
  39. m_editor_active(true),
  40. m_tileset(new_tileset),
  41. m_tiles(),
  42. m_real_solid(false),
  43. m_effective_solid(false),
  44. m_speed_x(1),
  45. m_speed_y(1),
  46. m_width(0),
  47. m_height(0),
  48. m_z_pos(0),
  49. m_offset(Vector(0,0)),
  50. m_movement(0, 0),
  51. m_objects_hit_bottom(),
  52. m_ground_movement_manager(nullptr),
  53. m_flip(NO_FLIP),
  54. m_alpha(1.0),
  55. m_current_alpha(1.0),
  56. m_remaining_fade_time(0),
  57. m_tint(1, 1, 1),
  58. m_current_tint(1, 1, 1),
  59. m_remaining_tint_fade_time(0),
  60. m_draw_target(DrawingTarget::COLORMAP),
  61. m_new_size_x(0),
  62. m_new_size_y(0),
  63. m_new_offset_x(0),
  64. m_new_offset_y(0),
  65. m_add_path(false),
  66. m_starting_node(0)
  67. {
  68. }
  69. TileMap::TileMap(const TileSet *tileset_, const ReaderMapping& reader) :
  70. GameObject(reader),
  71. ExposedObject<TileMap, scripting::TileMap>(this),
  72. PathObject(),
  73. m_editor_active(true),
  74. m_tileset(tileset_),
  75. m_tiles(),
  76. m_real_solid(false),
  77. m_effective_solid(false),
  78. m_speed_x(1),
  79. m_speed_y(1),
  80. m_width(-1),
  81. m_height(-1),
  82. m_z_pos(0),
  83. m_offset(Vector(0, 0)),
  84. m_movement(Vector(0, 0)),
  85. m_objects_hit_bottom(),
  86. m_ground_movement_manager(nullptr),
  87. m_flip(NO_FLIP),
  88. m_alpha(1.0),
  89. m_current_alpha(1.0),
  90. m_remaining_fade_time(0),
  91. m_tint(1, 1, 1),
  92. m_current_tint(1, 1, 1),
  93. m_remaining_tint_fade_time(0),
  94. m_draw_target(DrawingTarget::COLORMAP),
  95. m_new_size_x(0),
  96. m_new_size_y(0),
  97. m_new_offset_x(0),
  98. m_new_offset_y(0),
  99. m_add_path(false),
  100. m_starting_node(0)
  101. {
  102. assert(m_tileset);
  103. reader.get("solid", m_real_solid);
  104. bool backward_compatibility_fudge = false;
  105. if (!reader.get("speed-x", m_speed_x)) {
  106. if (reader.get("speed", m_speed_x)) {
  107. backward_compatibility_fudge = true;
  108. }
  109. }
  110. if (!reader.get("speed-y", m_speed_y)) {
  111. if (backward_compatibility_fudge) {
  112. m_speed_y = m_speed_x;
  113. }
  114. }
  115. m_z_pos = reader_get_layer(reader, 0);
  116. if (!Editor::is_active()) {
  117. if (m_real_solid && ((m_speed_x != 1) || (m_speed_y != 1))) {
  118. log_warning << "Speed of solid tilemap is not 1. fixing" << std::endl;
  119. m_speed_x = 1;
  120. m_speed_y = 1;
  121. }
  122. }
  123. reader.get("starting-node", m_starting_node, 0);
  124. init_path(reader, false);
  125. std::string draw_target_s = "normal";
  126. reader.get("draw-target", draw_target_s);
  127. if (draw_target_s == "normal") m_draw_target = DrawingTarget::COLORMAP;
  128. if (draw_target_s == "lightmap") m_draw_target = DrawingTarget::LIGHTMAP;
  129. if (reader.get("alpha", m_alpha)) {
  130. m_current_alpha = m_alpha;
  131. }
  132. std::vector<float> vColor;
  133. if (reader.get("tint", vColor)) {
  134. m_current_tint = Color(vColor);
  135. m_tint = m_current_tint;
  136. }
  137. /* Initialize effective_solid based on real_solid and current_alpha. */
  138. m_effective_solid = m_real_solid;
  139. update_effective_solid(false);
  140. reader.get("width", m_width);
  141. reader.get("height", m_height);
  142. if (m_width < 0 || m_height < 0) {
  143. //throw std::runtime_error("Invalid/No width/height specified in tilemap.");
  144. m_width = 0;
  145. m_height = 0;
  146. m_tiles.clear();
  147. resize(static_cast<int>(Sector::get().get_width() / 32.0f),
  148. static_cast<int>(Sector::get().get_height() / 32.0f));
  149. m_editor_active = false;
  150. } else {
  151. if (!reader.get("tiles", m_tiles))
  152. throw std::runtime_error("No tiles in tilemap.");
  153. if (int(m_tiles.size()) != m_width * m_height) {
  154. throw std::runtime_error("wrong number of tiles in tilemap.");
  155. }
  156. }
  157. bool empty = true;
  158. // make sure all tiles used on the tilemap are loaded and tilemap isn't empty
  159. for (const auto& tile : m_tiles) {
  160. if (tile != 0) {
  161. empty = false;
  162. }
  163. m_tileset->get(tile);
  164. }
  165. if (empty)
  166. {
  167. log_info << "Tilemap '" << get_name() << "', z-pos '" << m_z_pos << "' is empty." << std::endl;
  168. }
  169. }
  170. void
  171. TileMap::finish_construction()
  172. {
  173. get_parent()->update_solid(this);
  174. if (get_path() && get_path()->get_nodes().size() > 0) {
  175. if (m_starting_node >= static_cast<int>(get_path()->get_nodes().size()))
  176. m_starting_node = static_cast<int>(get_path()->get_nodes().size()) - 1;
  177. set_offset(m_path_handle.get_pos(get_size() * 32, get_path()->get_nodes()[m_starting_node].position));
  178. get_walker()->jump_to_node(m_starting_node);
  179. }
  180. m_add_path = get_walker() && get_path() && get_path()->is_valid();
  181. }
  182. TileMap::~TileMap()
  183. {
  184. }
  185. void
  186. TileMap::float_channel(float target, float &current, float remaining_time, float dt_sec)
  187. {
  188. float amt = (target - current) / (remaining_time / dt_sec);
  189. if (amt > 0) current = std::min(current + amt, target);
  190. if (amt < 0) current = std::max(current + amt, target);
  191. }
  192. void
  193. TileMap::apply_offset_x(int fill_id, int xoffset)
  194. {
  195. if (!xoffset)
  196. return;
  197. for (int y = 0; y < m_height; y++) {
  198. for (int x = 0; x < m_width; x++) {
  199. int X = (xoffset < 0) ? x : (m_width - x - 1);
  200. if (X - xoffset < 0 || X - xoffset >= m_width) {
  201. m_tiles[y * m_width + X] = fill_id;
  202. } else {
  203. m_tiles[y * m_width + X] = m_tiles[y * m_width + X - xoffset];
  204. }
  205. }
  206. }
  207. }
  208. void
  209. TileMap::apply_offset_y(int fill_id, int yoffset)
  210. {
  211. if (!yoffset)
  212. return;
  213. for (int y = 0; y < m_height; y++) {
  214. int Y = (yoffset < 0) ? y : (m_height - y - 1);
  215. for (int x = 0; x < m_width; x++) {
  216. if (Y - yoffset < 0 || Y - yoffset >= m_height) {
  217. m_tiles[Y * m_width + x] = fill_id;
  218. } else {
  219. m_tiles[Y * m_width + x] = m_tiles[(Y - yoffset) * m_width + x];
  220. }
  221. }
  222. }
  223. }
  224. ObjectSettings
  225. TileMap::get_settings()
  226. {
  227. m_new_size_x = m_width;
  228. m_new_size_y = m_height;
  229. m_new_offset_x = 0;
  230. m_new_offset_y = 0;
  231. ObjectSettings result = GameObject::get_settings();
  232. result.add_bool(_("Solid"), &m_real_solid, "solid");
  233. result.add_int(_("Resize offset x"), &m_new_offset_x);
  234. result.add_int(_("Resize offset y"), &m_new_offset_y);
  235. result.add_int(_("Width"), &m_new_size_x);
  236. result.add_int(_("Height"), &m_new_size_y);
  237. result.add_float(_("Alpha"), &m_alpha, "alpha", 1.0f);
  238. result.add_float(_("Speed x"), &m_speed_x, "speed-x", 1.0f);
  239. result.add_float(_("Speed y"), &m_speed_y, "speed-y", 1.0f);
  240. result.add_color(_("Tint"), &m_tint, "tint", Color::WHITE);
  241. result.add_int(_("Z-pos"), &m_z_pos, "z-pos");
  242. result.add_enum(_("Draw target"), reinterpret_cast<int*>(&m_draw_target),
  243. {_("Normal"), _("Lightmap")},
  244. {"normal", "lightmap"},
  245. static_cast<int>(DrawingTarget::COLORMAP),
  246. "draw-target");
  247. result.add_path_ref(_("Path"), *this, get_path_ref(), "path-ref");
  248. result.add_int(_("Starting Node"), &m_starting_node, "starting-node", 0, 0U);
  249. m_add_path = get_walker() && get_path() && get_path()->is_valid();
  250. result.add_bool(_("Following path"), &m_add_path);
  251. if (get_walker() && get_path() && get_path()->is_valid()) {
  252. result.add_walk_mode(_("Path Mode"), &get_path()->m_mode, {}, {});
  253. result.add_bool(_("Adapt Speed"), &get_path()->m_adapt_speed, {}, {});
  254. result.add_bool(_("Running"), &get_walker()->m_running, "running", false);
  255. result.add_path_handle(_("Handle"), m_path_handle, "handle");
  256. }
  257. result.add_tiles(_("Tiles"), this, "tiles");
  258. result.reorder({"solid", "running", "speed-x", "speed-y", "tint", "draw-target", "alpha", "z-pos", "name", "path-ref", "width", "height", "tiles"});
  259. if (!m_editor_active) {
  260. result.add_remove();
  261. }
  262. return result;
  263. }
  264. void
  265. TileMap::after_editor_set()
  266. {
  267. if ((m_new_size_x != m_width || m_new_size_y != m_height ||
  268. m_new_offset_x || m_new_offset_y) &&
  269. m_new_size_x > 0 && m_new_size_y > 0) {
  270. resize(m_new_size_x, m_new_size_y, 0, m_new_offset_x, m_new_offset_y);
  271. }
  272. if (get_walker() && get_path() && get_path()->is_valid()) {
  273. if (!m_add_path) {
  274. get_path()->m_nodes.clear();
  275. }
  276. } else {
  277. if (m_add_path) {
  278. init_path_pos(m_offset);
  279. }
  280. }
  281. m_current_tint = m_tint;
  282. m_current_alpha = m_alpha;
  283. }
  284. void
  285. TileMap::save_state()
  286. {
  287. GameObject::save_state();
  288. PathObject::save_state();
  289. }
  290. void
  291. TileMap::check_state()
  292. {
  293. GameObject::check_state();
  294. PathObject::check_state();
  295. }
  296. void
  297. TileMap::update(float dt_sec)
  298. {
  299. // handle tilemap fading
  300. if (m_current_alpha != m_alpha) {
  301. m_remaining_fade_time = std::max(0.0f, m_remaining_fade_time - dt_sec);
  302. if (m_remaining_fade_time == 0.0f) {
  303. m_current_alpha = m_alpha;
  304. } else {
  305. float_channel(m_alpha, m_current_alpha, m_remaining_fade_time, dt_sec);
  306. }
  307. update_effective_solid ();
  308. }
  309. // handle tint fading
  310. if (m_current_tint != m_tint) {
  311. m_remaining_tint_fade_time = std::max(0.0f, m_remaining_tint_fade_time - dt_sec);
  312. if (m_remaining_tint_fade_time == 0.0f) {
  313. m_current_tint = m_tint;
  314. } else {
  315. float_channel(m_tint.red , m_current_tint.red , m_remaining_tint_fade_time, dt_sec);
  316. float_channel(m_tint.green, m_current_tint.green, m_remaining_tint_fade_time, dt_sec);
  317. float_channel(m_tint.blue , m_current_tint.blue , m_remaining_tint_fade_time, dt_sec);
  318. float_channel(m_tint.alpha, m_current_tint.alpha, m_remaining_tint_fade_time, dt_sec);
  319. }
  320. }
  321. // if we have a path to follow, follow it
  322. if (get_walker()) {
  323. m_movement = Vector(0, 0);
  324. get_walker()->update(dt_sec);
  325. Vector v = get_walker()->get_pos(get_size() * 32, m_path_handle);
  326. if (get_path() && get_path()->is_valid()) {
  327. m_movement = v - get_offset();
  328. set_offset(v);
  329. if (m_ground_movement_manager != nullptr) {
  330. for (CollisionObject* other_object : m_objects_hit_bottom) {
  331. m_ground_movement_manager->register_movement(*this, *other_object, m_movement);
  332. other_object->propagate_movement(m_movement);
  333. }
  334. }
  335. } else {
  336. set_offset(m_path_handle.get_pos(get_size() * 32, Vector(0, 0)));
  337. }
  338. }
  339. m_objects_hit_bottom.clear();
  340. }
  341. void
  342. TileMap::editor_update()
  343. {
  344. if (get_walker()) {
  345. if (get_path() && get_path()->is_valid()) {
  346. m_movement = get_walker()->get_pos(get_size() * 32, m_path_handle) - get_offset();
  347. set_offset(get_walker()->get_pos(get_size() * 32, m_path_handle));
  348. if (!get_path()) return;
  349. if (!get_path()->is_valid()) return;
  350. if (m_starting_node >= static_cast<int>(get_path()->get_nodes().size()))
  351. m_starting_node = static_cast<int>(get_path()->get_nodes().size()) - 1;
  352. m_movement += get_path()->get_nodes()[m_starting_node].position - get_offset();
  353. set_offset(m_path_handle.get_pos(get_size() * 32, get_path()->get_nodes()[m_starting_node].position));
  354. } else {
  355. set_offset(m_path_handle.get_pos(get_size() * 32, Vector(0, 0)));
  356. }
  357. }
  358. }
  359. void
  360. TileMap::on_flip(float height)
  361. {
  362. for (int x = 0; x < get_width(); ++x) {
  363. for (int y = 0; y < get_height()/2; ++y) {
  364. // swap tiles
  365. int y2 = get_height() - 1 - y;
  366. uint32_t t1 = get_tile_id(x, y);
  367. uint32_t t2 = get_tile_id(x, y2);
  368. change(x, y, t2);
  369. change(x, y2, t1);
  370. }
  371. }
  372. FlipLevelTransformer::transform_flip(m_flip);
  373. Vector offset = get_offset();
  374. offset.y = height - offset.y - get_bbox().get_height();
  375. set_offset(offset);
  376. PathObject::on_flip();
  377. }
  378. void
  379. TileMap::draw(DrawingContext& context)
  380. {
  381. // skip draw if current opacity is 0.0
  382. if (m_current_alpha == 0.0f) return;
  383. context.push_transform();
  384. if (m_flip != NO_FLIP) context.set_flip(m_flip);
  385. if (m_editor_active) {
  386. if (m_current_alpha != 1.0f) {
  387. context.set_alpha(m_current_alpha);
  388. }
  389. } else {
  390. context.set_alpha(m_current_alpha/2);
  391. }
  392. const float trans_x = context.get_translation().x;
  393. const float trans_y = context.get_translation().y;
  394. const bool normal_speed = m_editor_active && Editor::is_active();
  395. context.set_translation(Vector(trans_x * (normal_speed ? 1.0f : m_speed_x),
  396. trans_y * (normal_speed ? 1.0f : m_speed_y)));
  397. Rectf draw_rect = context.get_cliprect();
  398. Rect t_draw_rect = get_tiles_overlapping(draw_rect);
  399. Vector start = get_tile_position(t_draw_rect.left, t_draw_rect.top);
  400. Vector pos(0.0f, 0.0f);
  401. int tx, ty;
  402. std::unordered_map<SurfacePtr,
  403. std::tuple<std::vector<Rectf>,
  404. std::vector<Rectf>>> batches;
  405. for (pos.x = start.x, tx = t_draw_rect.left; tx < t_draw_rect.right; pos.x += 32, ++tx) {
  406. for (pos.y = start.y, ty = t_draw_rect.top; ty < t_draw_rect.bottom; pos.y += 32, ++ty) {
  407. int index = ty*m_width + tx;
  408. assert (index >= 0);
  409. assert (index < (m_width * m_height));
  410. if (m_tiles[index] == 0) continue;
  411. const Tile& tile = m_tileset->get(m_tiles[index]);
  412. if (g_debug.show_collision_rects) {
  413. tile.draw_debug(context.color(), pos, LAYER_FOREGROUND1);
  414. }
  415. // If the tilemap is active in editor and showing deprecated tiles is enabled, draw indication over each deprecated tile
  416. if (Editor::is_active() && m_editor_active &&
  417. g_config->editor_show_deprecated_tiles && tile.is_deprecated())
  418. {
  419. context.color().draw_text(Resources::normal_font, "!", pos + Vector(16, 8),
  420. ALIGN_CENTER, LAYER_GUI - 10, Color::RED);
  421. }
  422. const SurfacePtr& surface = Editor::is_active() ? tile.get_current_editor_surface() : tile.get_current_surface();
  423. if (surface) {
  424. std::get<0>(batches[surface]).emplace_back(surface->get_region());
  425. std::get<1>(batches[surface]).emplace_back(pos,
  426. Sizef(static_cast<float>(surface->get_width()),
  427. static_cast<float>(surface->get_height())));
  428. }
  429. }
  430. }
  431. Canvas& canvas = context.get_canvas(m_draw_target);
  432. for (auto& it : batches)
  433. {
  434. const SurfacePtr& surface = it.first;
  435. if (surface) {
  436. canvas.draw_surface_batch(surface,
  437. std::move(std::get<0>(it.second)),
  438. std::move(std::get<1>(it.second)),
  439. m_current_tint, m_z_pos);
  440. }
  441. }
  442. context.pop_transform();
  443. }
  444. void
  445. TileMap::goto_node(int node_idx)
  446. {
  447. if (!get_walker()) return;
  448. get_walker()->goto_node(node_idx);
  449. }
  450. void
  451. TileMap::jump_to_node(int node_idx, bool instantaneous)
  452. {
  453. if (!get_walker()) return;
  454. get_walker()->jump_to_node(node_idx, instantaneous);
  455. }
  456. void
  457. TileMap::start_moving()
  458. {
  459. if (!get_walker()) return;
  460. get_walker()->start_moving();
  461. }
  462. void
  463. TileMap::stop_moving()
  464. {
  465. if (!get_walker()) return;
  466. get_walker()->stop_moving();
  467. }
  468. void
  469. TileMap::set(int newwidth, int newheight, const std::vector<unsigned int>&newt,
  470. int new_z_pos, bool newsolid)
  471. {
  472. if (int(newt.size()) != newwidth * newheight)
  473. throw std::runtime_error("Wrong tilecount count.");
  474. m_width = newwidth;
  475. m_height = newheight;
  476. m_tiles.resize(newt.size());
  477. m_tiles = newt;
  478. if (new_z_pos > (LAYER_GUI - 100))
  479. m_z_pos = LAYER_GUI - 100;
  480. else
  481. m_z_pos = new_z_pos;
  482. m_real_solid = newsolid;
  483. update_effective_solid ();
  484. // make sure all tiles are loaded
  485. for (const auto& tile : m_tiles)
  486. m_tileset->get(tile);
  487. }
  488. void
  489. TileMap::resize(int new_width, int new_height, int fill_id,
  490. int xoffset, int yoffset)
  491. {
  492. bool offset_finished_x = false;
  493. bool offset_finished_y = false;
  494. if (xoffset < 0 && new_width - m_width < 0)
  495. {
  496. apply_offset_x(fill_id, xoffset);
  497. offset_finished_x = true;
  498. }
  499. if (yoffset < 0 && new_height - m_height < 0)
  500. {
  501. apply_offset_y(fill_id, yoffset);
  502. offset_finished_y = true;
  503. }
  504. if (new_width < m_width) {
  505. // remap tiles for new width
  506. for (int y = 0; y < m_height && y < new_height; ++y) {
  507. for (int x = 0; x < new_width; ++x) {
  508. m_tiles[y * new_width + x] = m_tiles[y * m_width + x];
  509. }
  510. }
  511. }
  512. m_tiles.resize(new_width * new_height, fill_id);
  513. if (new_width > m_width) {
  514. // remap tiles
  515. for (int y = std::min(m_height, new_height)-1; y >= 0; --y) {
  516. for (int x = new_width-1; x >= 0; --x) {
  517. if (x >= m_width) {
  518. m_tiles[y * new_width + x] = fill_id;
  519. continue;
  520. }
  521. m_tiles[y * new_width + x] = m_tiles[y * m_width + x];
  522. }
  523. }
  524. }
  525. m_height = new_height;
  526. m_width = new_width;
  527. if (!offset_finished_x)
  528. apply_offset_x(fill_id, xoffset);
  529. if (!offset_finished_y)
  530. apply_offset_y(fill_id, yoffset);
  531. }
  532. void TileMap::resize(const Size& newsize, const Size& resize_offset) {
  533. resize(newsize.width, newsize.height, 0, resize_offset.width, resize_offset.height);
  534. }
  535. Rect
  536. TileMap::get_tiles_overlapping(const Rectf &rect) const
  537. {
  538. Rectf rect2 = rect;
  539. rect2.move(-m_offset);
  540. int t_left = std::max(0 , int(floorf(rect2.get_left () / 32)));
  541. int t_right = std::min(m_width , int(ceilf (rect2.get_right () / 32)));
  542. int t_top = std::max(0 , int(floorf(rect2.get_top () / 32)));
  543. int t_bottom = std::min(m_height, int(ceilf (rect2.get_bottom() / 32)));
  544. return Rect(t_left, t_top, t_right, t_bottom);
  545. }
  546. void
  547. TileMap::hits_object_bottom(CollisionObject& object)
  548. {
  549. m_objects_hit_bottom.insert(&object);
  550. }
  551. void
  552. TileMap::notify_object_removal(CollisionObject* other)
  553. {
  554. m_objects_hit_bottom.erase(other);
  555. }
  556. void
  557. TileMap::set_solid(bool solid)
  558. {
  559. m_real_solid = solid;
  560. update_effective_solid ();
  561. }
  562. uint32_t
  563. TileMap::get_tile_id(int x, int y) const
  564. {
  565. if (x < 0) x = 0;
  566. if (x >= m_width) x = m_width - 1;
  567. if (y < 0) y = 0;
  568. if (y >= m_height) y = m_height - 1;
  569. if (x < 0 || x >= m_width || y < 0 || y >= m_height) {
  570. //log_warning << "tile outside tilemap requested" << std::endl;
  571. return 0;
  572. }
  573. return m_tiles[y*m_width + x];
  574. }
  575. bool
  576. TileMap::is_outside_bounds(const Vector& pos) const
  577. {
  578. auto pos_ = (pos - m_offset) / 32.0f;
  579. float width = static_cast<float>(m_width);
  580. float height = static_cast<float>(m_height);
  581. return pos_.x < 0 || pos_.x >= width || pos_.y < 0 || pos_.y >= height;
  582. }
  583. const Tile&
  584. TileMap::get_tile(int x, int y) const
  585. {
  586. uint32_t id = get_tile_id(x, y);
  587. return m_tileset->get(id);
  588. }
  589. uint32_t
  590. TileMap::get_tile_id_at(const Vector& pos) const
  591. {
  592. Vector xy = (pos - m_offset) / 32.0f;
  593. return get_tile_id(int(xy.x), int(xy.y));
  594. }
  595. const Tile&
  596. TileMap::get_tile_at(const Vector& pos) const
  597. {
  598. uint32_t id = get_tile_id_at(pos);
  599. return m_tileset->get(id);
  600. }
  601. void
  602. TileMap::change(int x, int y, uint32_t newtile)
  603. {
  604. if(x < 0 || x >= m_width || y < 0 || y >= m_height)
  605. return;
  606. m_tiles[y*m_width + x] = newtile;
  607. }
  608. void
  609. TileMap::change_at(const Vector& pos, uint32_t newtile)
  610. {
  611. Vector xy = (pos - m_offset) / 32.0f;
  612. change(int(xy.x), int(xy.y), newtile);
  613. }
  614. void
  615. TileMap::change_all(uint32_t oldtile, uint32_t newtile)
  616. {
  617. for (int x = 0; x < get_width(); x++) {
  618. for (int y = 0; y < get_height(); y++) {
  619. if (get_tile_id(x,y) != oldtile)
  620. continue;
  621. change(x,y,newtile);
  622. }
  623. }
  624. }
  625. void
  626. TileMap::autotile(int x, int y, uint32_t tile)
  627. {
  628. if (x < 0 || x >= m_width || y < 0 || y >= m_height)
  629. return;
  630. uint32_t current_tile = m_tiles[y*m_width + x];
  631. AutotileSet* curr_set;
  632. if (current_tile == 0)
  633. {
  634. // Special case 1 : If the tile is empty, check if we can use a non-solid
  635. // tile from the currently selected tile's autotile set (if any).
  636. curr_set = m_tileset->get_autotileset_from_tile(tile);
  637. }
  638. else if (m_tileset->get_autotileset_from_tile(tile) != nullptr &&
  639. m_tileset->get_autotileset_from_tile(tile)->is_member(current_tile))
  640. {
  641. // Special case 2 : If the tile is in multiple autotilesets, check if it
  642. // is in the same tileset as the selected tile. (Example : tile 47)
  643. curr_set = m_tileset->get_autotileset_from_tile(tile);
  644. }
  645. else
  646. {
  647. curr_set = m_tileset->get_autotileset_from_tile(current_tile);
  648. }
  649. // If tile is not autotileable, abort
  650. // If tile is from a corner autotileset, abort as well
  651. if (curr_set == nullptr)
  652. {
  653. return;
  654. }
  655. uint32_t realtile = curr_set->get_autotile(current_tile,
  656. curr_set->is_solid(get_tile_id(x-1, y-1)),
  657. curr_set->is_solid(get_tile_id(x , y-1)),
  658. curr_set->is_solid(get_tile_id(x+1, y-1)),
  659. curr_set->is_solid(get_tile_id(x-1, y )),
  660. curr_set->is_solid(get_tile_id(x , y )),
  661. curr_set->is_solid(get_tile_id(x+1, y )),
  662. curr_set->is_solid(get_tile_id(x-1, y+1)),
  663. curr_set->is_solid(get_tile_id(x , y+1)),
  664. curr_set->is_solid(get_tile_id(x+1, y+1)),
  665. x, y);
  666. m_tiles[y*m_width + x] = realtile;
  667. }
  668. void
  669. TileMap::autotile_corner(int x, int y, uint32_t tile, AutotileCornerOperation op)
  670. {
  671. if (x < 0 || x >= m_width || y < 0 || y >= m_height)
  672. return;
  673. if (!m_tileset->get_autotileset_from_tile(tile)->is_corner())
  674. return;
  675. AutotileSet* curr_set = m_tileset->get_autotileset_from_tile(tile);
  676. // If tile is not autotileable, abort
  677. if (curr_set == nullptr)
  678. {
  679. return;
  680. }
  681. // If tile is not empty or already of the appropriate tileset, abort
  682. uint32_t current_tile = m_tiles[y*m_width + x];
  683. if (current_tile != 0 && (m_tileset->get_autotileset_from_tile(tile) != nullptr
  684. && !m_tileset->get_autotileset_from_tile(tile)->is_member(current_tile)))
  685. {
  686. return;
  687. }
  688. // If the current tile is 0, it will automatically return 0
  689. uint8_t mask = curr_set->get_mask_from_tile(current_tile);
  690. if (op == AutotileCornerOperation::REMOVE_TOP_LEFT) mask = static_cast<uint8_t>(mask & 0x07);
  691. if (op == AutotileCornerOperation::REMOVE_TOP_RIGHT) mask = static_cast<uint8_t>(mask & 0x0B);
  692. if (op == AutotileCornerOperation::REMOVE_BOTTOM_LEFT) mask = static_cast<uint8_t>(mask & 0x0D);
  693. if (op == AutotileCornerOperation::REMOVE_BOTTOM_RIGHT) mask = static_cast<uint8_t>(mask & 0x0E);
  694. if (op == AutotileCornerOperation::ADD_TOP_LEFT) mask = static_cast<uint8_t>(mask | 0x08);
  695. if (op == AutotileCornerOperation::ADD_TOP_RIGHT) mask = static_cast<uint8_t>(mask | 0x04);
  696. if (op == AutotileCornerOperation::ADD_BOTTOM_LEFT) mask = static_cast<uint8_t>(mask | 0x02);
  697. if (op == AutotileCornerOperation::ADD_BOTTOM_RIGHT) mask = static_cast<uint8_t>(mask | 0x01);
  698. uint32_t realtile = (!mask) ? 0 : curr_set->get_autotile(current_tile,
  699. (mask & 0x08) != 0,
  700. false,
  701. (mask & 0x04) != 0,
  702. false,
  703. false,
  704. false,
  705. (mask & 0x02) != 0,
  706. false,
  707. (mask & 0x01) != 0,
  708. x, y);
  709. m_tiles[y*m_width + x] = realtile;
  710. }
  711. bool
  712. TileMap::is_corner(uint32_t tile) const
  713. {
  714. auto* ats = m_tileset->get_autotileset_from_tile(tile);
  715. return ats && ats->is_corner();
  716. }
  717. void
  718. TileMap::autotile_erase(const Vector& pos, const Vector& corner_pos)
  719. {
  720. if (pos.x < 0.f || pos.x >= static_cast<float>(m_width) ||
  721. pos.y < 0.f || pos.y >= static_cast<float>(m_height))
  722. return;
  723. if (corner_pos.x < 0.f || corner_pos.x >= static_cast<float>(m_width) ||
  724. corner_pos.y < 0.f || corner_pos.y >= static_cast<float>(m_height))
  725. return;
  726. uint32_t current_tile = m_tiles[static_cast<int>(pos.y)*m_width
  727. + static_cast<int>(pos.x)];
  728. AutotileSet* curr_set = m_tileset->get_autotileset_from_tile(current_tile);
  729. if (curr_set && curr_set->is_corner()) {
  730. int x = static_cast<int>(corner_pos.x), y = static_cast<int>(corner_pos.y);
  731. autotile_corner(x, y, current_tile, AutotileCornerOperation::REMOVE_TOP_LEFT);
  732. autotile_corner(x-1, y, current_tile, AutotileCornerOperation::REMOVE_TOP_RIGHT);
  733. autotile_corner(x, y-1, current_tile, AutotileCornerOperation::REMOVE_BOTTOM_LEFT);
  734. autotile_corner(x-1, y-1, current_tile, AutotileCornerOperation::REMOVE_BOTTOM_RIGHT);
  735. }
  736. else
  737. {
  738. int x = static_cast<int>(pos.x), y = static_cast<int>(pos.y);
  739. m_tiles[y*m_width + x] = 0;
  740. if (x - 1 >= 0 && y - 1 >= 0 && !is_corner(m_tiles[(y-1)*m_width + x-1])) {
  741. if (m_tiles[y*m_width + x] == 0)
  742. autotile(x, y, m_tiles[(y-1)*m_width + x-1]);
  743. autotile(x-1, y-1, m_tiles[(y-1)*m_width + x-1]);
  744. }
  745. if (y - 1 >= 0 && !is_corner(m_tiles[(y-1)*m_width + x])) {
  746. if (m_tiles[y*m_width + x] == 0)
  747. autotile(x, y, m_tiles[(y-1)*m_width + x]);
  748. autotile(x, y-1, m_tiles[(y-1)*m_width + x]);
  749. }
  750. if (y - 1 >= 0 && x + 1 < m_width && !is_corner(m_tiles[(y-1)*m_width + x+1])) {
  751. if (m_tiles[y*m_width + x] == 0)
  752. autotile(x, y, m_tiles[(y-1)*m_width + x+1]);
  753. autotile(x+1, y-1, m_tiles[(y-1)*m_width + x+1]);
  754. }
  755. if (x - 1 >= 0 && !is_corner(m_tiles[y*m_width + x-1])) {
  756. if (m_tiles[y*m_width + x] == 0)
  757. autotile(x, y, m_tiles[y*m_width + x-1]);
  758. autotile(x-1, y, m_tiles[y*m_width + x-1]);
  759. }
  760. if (x + 1 < m_width && !is_corner(m_tiles[y*m_width + x+1])) {
  761. if (m_tiles[y*m_width + x] == 0)
  762. autotile(x, y, m_tiles[y*m_width + x+1]);
  763. autotile(x+1, y, m_tiles[y*m_width + x+1]);
  764. }
  765. if (x - 1 >= 0 && y + 1 < m_height && !is_corner(m_tiles[(y+1)*m_width + x-1])) {
  766. if (m_tiles[y*m_width + x] == 0)
  767. autotile(x, y, m_tiles[(y+1)*m_width + x-1]);
  768. autotile(x-1, y+1, m_tiles[(y+1)*m_width + x-1]);
  769. }
  770. if (y + 1 < m_height && !is_corner(m_tiles[(y+1)*m_width + x])) {
  771. if (m_tiles[y*m_width + x] == 0)
  772. autotile(x, y, m_tiles[(y+1)*m_width + x]);
  773. autotile(x, y+1, m_tiles[(y+1)*m_width + x]);
  774. }
  775. if (y + 1 < m_height && x + 1 < m_width && !is_corner(m_tiles[(y+1)*m_width + x+1])) {
  776. if (m_tiles[y*m_width + x] == 0)
  777. autotile(x, y, m_tiles[(y+1)*m_width + x+1]);
  778. autotile(x+1, y+1, m_tiles[(y+1)*m_width + x+1]);
  779. }
  780. }
  781. }
  782. AutotileSet*
  783. TileMap::get_autotileset(uint32_t tile) const
  784. {
  785. return m_tileset->get_autotileset_from_tile(tile);
  786. }
  787. void
  788. TileMap::fade(float alpha_, float seconds)
  789. {
  790. m_alpha = alpha_;
  791. m_remaining_fade_time = seconds;
  792. }
  793. void
  794. TileMap::tint_fade(const Color& new_tint, float seconds)
  795. {
  796. m_tint = new_tint;
  797. m_remaining_tint_fade_time = seconds;
  798. }
  799. void
  800. TileMap::set_alpha(float alpha_)
  801. {
  802. m_alpha = alpha_;
  803. m_current_alpha = m_alpha;
  804. m_remaining_fade_time = 0;
  805. update_effective_solid ();
  806. }
  807. float
  808. TileMap::get_alpha() const
  809. {
  810. return m_current_alpha;
  811. }
  812. void
  813. TileMap::move_by(const Vector& shift)
  814. {
  815. if (!get_path()) {
  816. init_path_pos(m_offset);
  817. m_add_path = true;
  818. }
  819. get_path()->move_by(shift);
  820. m_offset += shift;
  821. }
  822. void
  823. TileMap::update_effective_solid(bool update_manager)
  824. {
  825. if (!m_real_solid)
  826. m_effective_solid = false;
  827. else if (m_effective_solid && (m_current_alpha < 0.25f))
  828. m_effective_solid = false;
  829. else if (!m_effective_solid && (m_current_alpha >= 0.75f))
  830. m_effective_solid = true;
  831. if (update_manager)
  832. get_parent()->update_solid(this);
  833. }
  834. void
  835. TileMap::set_tileset(const TileSet* new_tileset)
  836. {
  837. m_tileset = new_tileset;
  838. }
  839. /* EOF */