game_object_manager.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569
  1. // SuperTux
  2. // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
  3. // 2018 Ingo Ruhnke <grumbel@gmail.com>
  4. // 2023 Vankata453
  5. //
  6. // This program is free software: you can redistribute it and/or modify
  7. // it under the terms of the GNU General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // This program is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU General Public License
  17. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. #include "supertux/game_object_manager.hpp"
  19. #include <algorithm>
  20. #include "editor/editor.hpp"
  21. #include "object/tilemap.hpp"
  22. #include "supertux/game_object_factory.hpp"
  23. #include "supertux/moving_object.hpp"
  24. bool GameObjectManager::s_draw_solids_only = false;
  25. GameObjectManager::GameObjectManager(bool undo_tracking) :
  26. m_initialized(false),
  27. m_uid_generator(),
  28. m_change_uid_generator(),
  29. m_undo_tracking(undo_tracking),
  30. m_undo_stack_size(20),
  31. m_undo_stack(),
  32. m_redo_stack(),
  33. m_pending_change_stack(),
  34. m_last_saved_change(),
  35. m_gameobjects(),
  36. m_gameobjects_new(),
  37. m_solid_tilemaps(),
  38. m_all_tilemaps(),
  39. m_objects_by_name(),
  40. m_objects_by_uid(),
  41. m_objects_by_type_index(),
  42. m_name_resolve_requests()
  43. {
  44. }
  45. GameObjectManager::~GameObjectManager()
  46. {
  47. // clear_objects() must be called before destructing the GameObjectManager.
  48. assert(m_gameobjects.size() == 0);
  49. assert(m_gameobjects_new.size() == 0);
  50. }
  51. void
  52. GameObjectManager::request_name_resolve(const std::string& name, std::function<void (UID)> callback)
  53. {
  54. m_name_resolve_requests.push_back({name, std::move(callback)});
  55. }
  56. void
  57. GameObjectManager::process_resolve_requests()
  58. {
  59. // FIXME: Why is this assertion needed?
  60. // Removed to allow for resolving name requests in Sector before object creation,
  61. // despite there being queued objects.
  62. //assert(m_gameobjects_new.empty());
  63. for (const auto& request : m_name_resolve_requests)
  64. {
  65. GameObject* object = get_object_by_name<GameObject>(request.name);
  66. if (!object)
  67. {
  68. log_warning << "GameObjectManager: Name resolve for '" << request.name << "' failed." << std::endl;
  69. request.callback({});
  70. }
  71. else
  72. {
  73. request.callback(object->get_uid());
  74. }
  75. }
  76. m_name_resolve_requests.clear();
  77. }
  78. void
  79. GameObjectManager::try_process_resolve_requests()
  80. {
  81. // FIXME: Why is this assertion needed?
  82. // Removed to allow for resolving name requests in Sector before object creation,
  83. // despite there being queued objects.
  84. //assert(m_gameobjects_new.empty());
  85. std::vector<GameObjectManager::NameResolveRequest> new_list;
  86. for (const auto& request : m_name_resolve_requests)
  87. {
  88. auto* object = get_object_by_name<GameObject>(request.name);
  89. if (!object)
  90. {
  91. // Unlike process_resolve_requests(), we just keep that one in mind.
  92. new_list.push_back(request);
  93. }
  94. else
  95. {
  96. request.callback(object->get_uid());
  97. }
  98. }
  99. m_name_resolve_requests.clear();
  100. m_name_resolve_requests.assign(new_list.begin(), new_list.end());
  101. }
  102. const std::vector<std::unique_ptr<GameObject> >&
  103. GameObjectManager::get_objects() const
  104. {
  105. return m_gameobjects;
  106. }
  107. GameObject&
  108. GameObjectManager::add_object(std::unique_ptr<GameObject> object)
  109. {
  110. assert(object);
  111. object->m_parent = this;
  112. if (!object->get_uid())
  113. {
  114. object->set_uid(m_uid_generator.next());
  115. // No object UID would indicate the object is not a result of undo/redo.
  116. // Any newly placed object in the editor should be on its latest version.
  117. if (m_initialized && Editor::is_active())
  118. object->update_version();
  119. }
  120. // Make sure the object isn't already in the list.
  121. #ifndef NDEBUG
  122. for (const auto& game_object : m_gameobjects) {
  123. assert(game_object != object);
  124. }
  125. for (const auto& gameobject : m_gameobjects_new) {
  126. assert(gameobject != object);
  127. }
  128. #endif
  129. // Attempt to add object to editor layers.
  130. if (m_initialized && Editor::is_active())
  131. Editor::current()->add_layer(object.get());
  132. GameObject& tmp = *object;
  133. m_gameobjects_new.push_back(std::move(object));
  134. return tmp;
  135. }
  136. MovingObject&
  137. GameObjectManager::add_object_scripting(const std::string& class_name, const std::string& name,
  138. const Vector& pos, const std::string& direction,
  139. const std::string& data)
  140. {
  141. if (class_name.empty())
  142. throw std::runtime_error("Object class name cannot be empty.");
  143. if (!name.empty() && get_object_by_name<GameObject>(name))
  144. throw std::runtime_error("Object with name '" + name + "' already exists.");
  145. auto obj = GameObjectFactory::instance().create(class_name, pos,
  146. direction.empty() ? Direction::AUTO : string_to_dir(direction),
  147. data);
  148. auto moving_object = dynamic_cast<MovingObject*>(obj.get());
  149. if (!moving_object)
  150. throw std::runtime_error("Only MovingObject instances can be created via scripting.");
  151. if (!name.empty())
  152. obj->set_name(name);
  153. add_object(std::move(obj));
  154. return *moving_object;
  155. }
  156. void
  157. GameObjectManager::clear_objects()
  158. {
  159. flush_game_objects();
  160. for (const auto& obj: m_gameobjects) {
  161. before_object_remove(*obj);
  162. }
  163. m_gameobjects.clear();
  164. }
  165. void
  166. GameObjectManager::update(float dt_sec)
  167. {
  168. for (const auto& object : m_gameobjects)
  169. {
  170. if (!object->is_valid())
  171. continue;
  172. object->update(dt_sec);
  173. }
  174. }
  175. void
  176. GameObjectManager::draw(DrawingContext& context)
  177. {
  178. for (const auto& object : m_gameobjects)
  179. {
  180. if (!object->is_valid())
  181. continue;
  182. if (s_draw_solids_only)
  183. {
  184. auto tm = dynamic_cast<TileMap*>(object.get());
  185. if (tm && !tm->is_solid())
  186. continue;
  187. }
  188. object->draw(context);
  189. }
  190. }
  191. void
  192. GameObjectManager::flush_game_objects()
  193. {
  194. { // Clean up marked objects.
  195. m_gameobjects.erase(
  196. std::remove_if(m_gameobjects.begin(), m_gameobjects.end(),
  197. [this](const std::unique_ptr<GameObject>& obj) {
  198. if (!obj->is_valid())
  199. {
  200. this_before_object_remove(*obj);
  201. before_object_remove(*obj);
  202. return true;
  203. } else {
  204. return false;
  205. }
  206. }),
  207. m_gameobjects.end());
  208. }
  209. { // Add newly created objects.
  210. // Objects might add new objects in finish_construction(), so we
  211. // loop until no new objects show up.
  212. while (!m_gameobjects_new.empty()) {
  213. auto new_objects = std::move(m_gameobjects_new);
  214. for (auto& object : new_objects)
  215. {
  216. if (before_object_add(*object))
  217. {
  218. if (!m_initialized) object->m_track_undo = false;
  219. this_before_object_add(*object);
  220. if (object->has_object_manager_priority())
  221. m_gameobjects.insert(m_gameobjects.begin(), std::move(object));
  222. else
  223. m_gameobjects.push_back(std::move(object));
  224. }
  225. }
  226. }
  227. }
  228. update_tilemaps();
  229. // A resolve request may depend on an object being added.
  230. try_process_resolve_requests();
  231. // If object changes have been performed since last flush, push them to the undo stack.
  232. if (m_undo_tracking && !m_pending_change_stack.empty())
  233. {
  234. m_undo_stack.push_back({ m_change_uid_generator.next(), std::move(m_pending_change_stack) });
  235. m_redo_stack.clear();
  236. undo_stack_cleanup();
  237. }
  238. m_initialized = true;
  239. }
  240. void
  241. GameObjectManager::update_solid(TileMap* tm) {
  242. auto it = std::find(m_solid_tilemaps.begin(), m_solid_tilemaps.end(), tm);
  243. bool found = it != m_solid_tilemaps.end();
  244. if (tm->is_solid() && !found) {
  245. m_solid_tilemaps.push_back(tm);
  246. } else if(!tm->is_solid() && found) {
  247. m_solid_tilemaps.erase(it);
  248. }
  249. }
  250. void
  251. GameObjectManager::update_tilemaps()
  252. {
  253. m_solid_tilemaps.clear();
  254. m_all_tilemaps.clear();
  255. for (auto tilemap : get_objects_by_type_index(typeid(TileMap)))
  256. {
  257. TileMap* tm = static_cast<TileMap*>(tilemap);
  258. if (tm->is_solid()) m_solid_tilemaps.push_back(tm);
  259. m_all_tilemaps.push_back(tm);
  260. }
  261. }
  262. void
  263. GameObjectManager::move_object(const UID& uid, GameObjectManager& other)
  264. {
  265. if (&other == this)
  266. return;
  267. auto it = std::find_if(m_gameobjects.begin(), m_gameobjects.end(),
  268. [uid](const auto& obj) {
  269. return obj->get_uid() == uid;
  270. });
  271. if (it == m_gameobjects.end())
  272. {
  273. std::ostringstream err;
  274. err << "Object with UID " << uid << " not found.";
  275. throw std::runtime_error(err.str());
  276. }
  277. this_before_object_remove(**it);
  278. before_object_remove(**it);
  279. other.add_object(std::move(*it));
  280. m_gameobjects.erase(it);
  281. other.flush_game_objects();
  282. }
  283. void
  284. GameObjectManager::toggle_undo_tracking(bool enabled)
  285. {
  286. if (m_undo_tracking == enabled)
  287. return;
  288. m_undo_tracking = enabled;
  289. clear_undo_stack();
  290. }
  291. void
  292. GameObjectManager::set_undo_stack_size(int size)
  293. {
  294. if (m_undo_stack_size == size)
  295. return;
  296. m_undo_stack_size = size;
  297. undo_stack_cleanup();
  298. }
  299. void
  300. GameObjectManager::undo_stack_cleanup()
  301. {
  302. const int current_size = static_cast<int>(m_undo_stack.size());
  303. if (current_size > m_undo_stack_size)
  304. m_undo_stack.erase(m_undo_stack.begin(),
  305. m_undo_stack.begin() + (current_size - m_undo_stack_size));
  306. }
  307. void
  308. GameObjectManager::on_editor_save()
  309. {
  310. m_last_saved_change = (m_undo_stack.empty() ? UID() : m_undo_stack.back().uid);
  311. }
  312. void
  313. GameObjectManager::undo()
  314. {
  315. if (m_undo_stack.empty()) return;
  316. ObjectChanges& changes = m_undo_stack.back();
  317. for (auto& obj_change : changes.objects)
  318. process_object_change(obj_change);
  319. m_redo_stack.push_back(std::move(changes));
  320. m_undo_stack.pop_back();
  321. }
  322. void
  323. GameObjectManager::redo()
  324. {
  325. if (m_redo_stack.empty()) return;
  326. ObjectChanges& changes = m_redo_stack.back();
  327. for (auto& obj_change : changes.objects)
  328. process_object_change(obj_change);
  329. m_undo_stack.push_back(std::move(changes));
  330. m_redo_stack.pop_back();
  331. }
  332. void
  333. GameObjectManager::create_object_from_change(const ObjectChange& change)
  334. {
  335. auto object = GameObjectFactory::instance().create(change.name, change.data);
  336. object->m_track_undo = false;
  337. object->set_uid(change.uid);
  338. object->after_editor_set();
  339. add_object(std::move(object));
  340. }
  341. void
  342. GameObjectManager::process_object_change(ObjectChange& change)
  343. {
  344. GameObject* object = get_object_by_uid<GameObject>(change.uid);
  345. if (object) // Object exists, remove it.
  346. {
  347. object->m_track_undo = false;
  348. object->remove_me();
  349. const std::string data = object->save();
  350. // If settings have changed, re-create object with old settings.
  351. if (!change.creation && change.data != data)
  352. create_object_from_change(change);
  353. change.data = std::move(data);
  354. }
  355. else // Object doesn't exist, create it.
  356. {
  357. create_object_from_change(change);
  358. }
  359. }
  360. void
  361. GameObjectManager::save_object_change(GameObject& object, bool creation)
  362. {
  363. if (m_undo_tracking && object.track_state() && object.m_track_undo)
  364. m_pending_change_stack.push_back({ object.get_class_name(), object.get_uid(), object.save(), creation });
  365. object.m_track_undo = true;
  366. }
  367. void
  368. GameObjectManager::save_object_change(GameObject& object, const std::string& data)
  369. {
  370. if (m_undo_tracking)
  371. m_pending_change_stack.push_back({ object.get_class_name(), object.get_uid(), data, false });
  372. }
  373. void
  374. GameObjectManager::clear_undo_stack()
  375. {
  376. m_undo_stack.clear();
  377. m_redo_stack.clear();
  378. m_last_saved_change = UID();
  379. }
  380. bool
  381. GameObjectManager::has_object_changes() const
  382. {
  383. return (m_undo_stack.empty() && m_last_saved_change) ||
  384. (!m_undo_stack.empty() && m_undo_stack.back().uid != m_last_saved_change);
  385. }
  386. void
  387. GameObjectManager::this_before_object_add(GameObject& object)
  388. {
  389. { // By name:
  390. if (!object.get_name().empty())
  391. {
  392. m_objects_by_name[object.get_name()] = &object;
  393. }
  394. }
  395. { // By id:
  396. assert(object.get_uid());
  397. m_objects_by_uid[object.get_uid()] = &object;
  398. }
  399. { // By type index:
  400. m_objects_by_type_index[std::type_index(typeid(object))].push_back(&object);
  401. }
  402. save_object_change(object, true);
  403. }
  404. void
  405. GameObjectManager::this_before_object_remove(GameObject& object)
  406. {
  407. save_object_change(object);
  408. { // By name:
  409. const std::string& name = object.get_name();
  410. if (!name.empty())
  411. {
  412. m_objects_by_name.erase(name);
  413. }
  414. }
  415. { // By id:
  416. m_objects_by_uid.erase(object.get_uid());
  417. }
  418. { // By type index:
  419. auto& vec = m_objects_by_type_index[std::type_index(typeid(object))];
  420. auto it = std::find(vec.begin(), vec.end(), &object);
  421. assert(it != vec.end());
  422. vec.erase(it);
  423. }
  424. }
  425. float
  426. GameObjectManager::get_width() const
  427. {
  428. float width = 0;
  429. for (auto& tilemap : get_solid_tilemaps())
  430. width = std::max(width, tilemap->get_bbox().get_right());
  431. return width;
  432. }
  433. float
  434. GameObjectManager::get_height() const
  435. {
  436. float height = 0;
  437. for (const auto& tilemap : get_solid_tilemaps())
  438. height = std::max(height, tilemap->get_bbox().get_bottom());
  439. return height;
  440. }
  441. float
  442. GameObjectManager::get_editor_width() const
  443. {
  444. float width = 0;
  445. for (const auto& tilemap : get_all_tilemaps()) // Determine from all tilemaps
  446. width = std::max(width, tilemap->get_bbox().get_right());
  447. return width;
  448. }
  449. float
  450. GameObjectManager::get_editor_height() const
  451. {
  452. float height = 0;
  453. for (const auto& tilemap : get_all_tilemaps()) // Determine from all tilemaps
  454. height = std::max(height, tilemap->get_bbox().get_bottom());
  455. return height;
  456. }
  457. float
  458. GameObjectManager::get_tiles_width() const
  459. {
  460. float width = 0;
  461. for (const auto& tilemap : get_solid_tilemaps())
  462. {
  463. if (static_cast<float>(tilemap->get_width()) > width)
  464. width = static_cast<float>(tilemap->get_width());
  465. }
  466. return width;
  467. }
  468. float
  469. GameObjectManager::get_tiles_height() const
  470. {
  471. float height = 0;
  472. for (const auto& tilemap : get_solid_tilemaps())
  473. {
  474. if (static_cast<float>(tilemap->get_height()) > height)
  475. height = static_cast<float>(tilemap->get_height());
  476. }
  477. return height;
  478. }
  479. /* EOF */