sector.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840
  1. // SuperTux - A Jump'n Run
  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 "supertux/sector.hpp"
  17. #include <physfs.h>
  18. #include <algorithm>
  19. #include "audio/sound_manager.hpp"
  20. #include "badguy/badguy.hpp"
  21. #include "collision/collision.hpp"
  22. #include "editor/editor.hpp"
  23. #include "math/aatriangle.hpp"
  24. #include "math/rect.hpp"
  25. #include "object/ambient_light.hpp"
  26. #include "object/background.hpp"
  27. #include "object/bullet.hpp"
  28. #include "object/camera.hpp"
  29. #include "object/display_effect.hpp"
  30. #include "object/gradient.hpp"
  31. #include "object/music_object.hpp"
  32. #include "object/player.hpp"
  33. #include "object/portable.hpp"
  34. #include "object/pulsing_light.hpp"
  35. #include "object/smoke_cloud.hpp"
  36. #include "object/spawnpoint.hpp"
  37. #include "object/text_array_object.hpp"
  38. #include "object/text_object.hpp"
  39. #include "object/tilemap.hpp"
  40. #include "object/vertical_stripes.hpp"
  41. #include "physfs/ifile_stream.hpp"
  42. #include "scripting/sector.hpp"
  43. #include "squirrel/squirrel_environment.hpp"
  44. #include "supertux/colorscheme.hpp"
  45. #include "supertux/constants.hpp"
  46. #include "supertux/debug.hpp"
  47. #include "supertux/game_object_factory.hpp"
  48. #include "supertux/level.hpp"
  49. #include "supertux/player_status_hud.hpp"
  50. #include "supertux/resources.hpp"
  51. #include "supertux/tile.hpp"
  52. #include "supertux/tile_manager.hpp"
  53. #include "util/file_system.hpp"
  54. #include "util/writer.hpp"
  55. #include "video/video_system.hpp"
  56. #include "video/viewport.hpp"
  57. Sector* Sector::s_current = nullptr;
  58. Sector::Sector(Level& parent) :
  59. Base::Sector("sector"),
  60. m_level(parent),
  61. m_fully_constructed(false),
  62. m_foremost_layer(),
  63. m_foremost_opaque_layer(),
  64. m_gravity(10.0f),
  65. m_collision_system(new CollisionSystem(*this)),
  66. m_text_object(add<TextObject>("Text"))
  67. {
  68. add<DisplayEffect>("Effect");
  69. add<TextArrayObject>("TextArray");
  70. SoundManager::current()->preload("sounds/shoot.wav");
  71. }
  72. Sector::~Sector()
  73. {
  74. try
  75. {
  76. deactivate();
  77. }
  78. catch(const std::exception& err)
  79. {
  80. log_warning << err.what() << std::endl;
  81. }
  82. clear_objects();
  83. }
  84. void
  85. Sector::finish_construction(bool editable)
  86. {
  87. flush_game_objects();
  88. // FIXME: Is it a good idea to process some resolve requests this early?
  89. // I added this to fix https://github.com/SuperTux/supertux/issues/1378
  90. // but I don't know if it's going to introduce other bugs.. ~ Semphris
  91. try_process_resolve_requests();
  92. if (!editable) {
  93. convert_tiles2gameobject();
  94. if (!m_level.is_worldmap())
  95. {
  96. bool has_background = std::any_of(get_objects().begin(), get_objects().end(),
  97. [](const auto& obj) {
  98. return (dynamic_cast<Background*>(obj.get()) ||
  99. dynamic_cast<Gradient*>(obj.get()));
  100. });
  101. if (!has_background)
  102. {
  103. auto& gradient = add<Gradient>();
  104. gradient.set_gradient(Color(0.3f, 0.4f, 0.75f), Color(1.f, 1.f, 1.f));
  105. }
  106. }
  107. }
  108. if (get_solid_tilemaps().empty())
  109. {
  110. if (editable)
  111. {
  112. log_warning << "sector '" << get_name() << "' does not contain a solid tile layer." << std::endl;
  113. }
  114. else
  115. {
  116. log_warning << "sector '" << get_name() << "' does not contain a solid tile layer. Creating an empty one." << std::endl;
  117. TileMap& tilemap = add<TileMap>(TileManager::current()->get_tileset(m_level.get_tileset()));
  118. tilemap.resize(100, 35);
  119. tilemap.set_solid();
  120. }
  121. }
  122. if (!get_object_by_type<Camera>()) {
  123. if (!m_level.is_worldmap())
  124. log_warning << "sector '" << get_name() << "' does not contain a camera." << std::endl;
  125. add<Camera>("Camera");
  126. }
  127. if (!get_object_by_type<AmbientLight>()) {
  128. add<AmbientLight>(Color::WHITE);
  129. }
  130. if (!get_object_by_type<MusicObject>()) {
  131. add<MusicObject>();
  132. }
  133. if (!get_object_by_type<VerticalStripes>()) {
  134. add<VerticalStripes>();
  135. }
  136. m_initialized = false;
  137. flush_game_objects();
  138. m_foremost_layer = calculate_foremost_layer(false);
  139. m_foremost_opaque_layer = calculate_foremost_layer();
  140. process_resolve_requests();
  141. Base::Sector::finish_construction(editable);
  142. m_initialized = false;
  143. flush_game_objects();
  144. m_fully_constructed = true;
  145. }
  146. SpawnPointMarker*
  147. Sector::get_spawn_point(const std::string& spawnpoint)
  148. {
  149. SpawnPointMarker* sp = nullptr;
  150. for (auto& spawn_point : get_objects_by_type<SpawnPointMarker>()) {
  151. if (spawn_point.get_name() == spawnpoint) {
  152. sp = &spawn_point;
  153. break;
  154. }
  155. }
  156. return sp;
  157. }
  158. Vector
  159. Sector::get_spawn_point_position(const std::string& spawnpoint)
  160. {
  161. SpawnPointMarker* sp = get_spawn_point(spawnpoint);
  162. if (sp)
  163. return sp->get_pos();
  164. else
  165. return Vector(0.0f, 0.0f);
  166. }
  167. void
  168. Sector::activate(const std::string& spawnpoint)
  169. {
  170. SpawnPointMarker* sp = get_spawn_point(spawnpoint);
  171. if (!sp) {
  172. if (!m_level.is_worldmap())
  173. log_warning << "Spawnpoint '" << spawnpoint << "' not found." << std::endl;
  174. if (spawnpoint != "main") {
  175. activate("main");
  176. } else {
  177. activate(Vector(0, 0));
  178. }
  179. } else {
  180. activate(sp->get_pos());
  181. }
  182. }
  183. void
  184. Sector::activate(const Vector& player_pos)
  185. {
  186. BIND_SECTOR(*this);
  187. // Make sure all players are moved to this sector.
  188. for (auto& player : m_level.get_players())
  189. player->move_to_sector(*this);
  190. if (s_current != this) {
  191. if (s_current != nullptr)
  192. s_current->deactivate();
  193. s_current = this;
  194. m_squirrel_environment->expose_self();
  195. for (const auto& object : get_objects()) {
  196. m_squirrel_environment->try_expose(*object);
  197. }
  198. }
  199. // The Sector object is called 'settings' as it is accessed as 'sector.settings'
  200. m_squirrel_environment->expose("settings", std::make_unique<scripting::Sector>(this));
  201. if (Editor::is_active())
  202. return;
  203. // two-player hack: move other players to main player's position
  204. // Maybe specify 2 spawnpoints in the level?
  205. for (auto player_ptr : get_objects_by_type_index(typeid(Player))) {
  206. Player& player = *static_cast<Player*>(player_ptr);
  207. // spawn smalltux below spawnpoint
  208. if (!player.is_big()) {
  209. player.move(player_pos + Vector(0,32));
  210. } else {
  211. player.move(player_pos);
  212. }
  213. // spawning tux in the ground would kill him
  214. if (!is_free_of_tiles(player.get_bbox())) {
  215. std::string current_level = "[" + Sector::get().get_level().m_filename + "] ";
  216. log_warning << current_level << "Tried spawning Tux in solid matter. Compensating." << std::endl;
  217. Vector npos = player.get_bbox().p1();
  218. npos.y-=32;
  219. player.move(npos);
  220. }
  221. }
  222. //FIXME: This is a really dirty workaround for this strange camera jump
  223. if (get_players().size() > 0)
  224. {
  225. Player& player = *(get_players()[0]);
  226. Camera& camera = get_camera();
  227. player.move(player.get_pos()+Vector(-32, 0));
  228. camera.reset(player.get_pos());
  229. camera.update(1);
  230. player.move(player.get_pos()+(Vector(32, 0)));
  231. camera.update(1);
  232. }
  233. flush_game_objects();
  234. //Run default.nut just before init script
  235. //Check to see if it's in a levelset (info file)
  236. std::string basedir = FileSystem::dirname(get_level().m_filename);
  237. if (PHYSFS_exists((basedir + "/info").c_str())) {
  238. try {
  239. IFileStream in(basedir + "/default.nut");
  240. m_squirrel_environment->run_script(in, "default.nut");
  241. } catch(std::exception& ) {
  242. // doesn't exist or erroneous; do nothing
  243. }
  244. }
  245. // Run init script
  246. if (!m_init_script.empty() && !Editor::is_active()) {
  247. run_script(m_init_script, "init-script");
  248. }
  249. }
  250. void
  251. Sector::deactivate()
  252. {
  253. BIND_SECTOR(*this);
  254. if (s_current != this)
  255. return;
  256. m_squirrel_environment->unexpose_self();
  257. for (const auto& object: get_objects()) {
  258. m_squirrel_environment->try_unexpose(*object);
  259. }
  260. m_squirrel_environment->unexpose("settings");
  261. s_current = nullptr;
  262. }
  263. Rectf
  264. Sector::get_active_region() const
  265. {
  266. Camera& camera = get_camera();
  267. return Rectf(
  268. camera.get_translation() - Vector(1600, 1200),
  269. camera.get_translation() + Vector(1600, 1200) + Vector(static_cast<float>(SCREEN_WIDTH),
  270. static_cast<float>(SCREEN_HEIGHT)));
  271. }
  272. int
  273. Sector::calculate_foremost_layer(bool including_transparent) const
  274. {
  275. int layer = LAYER_BACKGROUND0;
  276. for (auto& tm : get_objects_by_type<TileMap>())
  277. {
  278. if (tm.get_layer() > layer)
  279. {
  280. if ( including_transparent && tm.get_alpha() < 1.0f )
  281. {
  282. layer = tm.get_layer() - 1;
  283. }
  284. else
  285. {
  286. layer = tm.get_layer() + 1;
  287. }
  288. }
  289. }
  290. log_debug << "Calculated badguy falling layer was: " << layer << std::endl;
  291. return layer;
  292. }
  293. int
  294. Sector::get_foremost_opaque_layer() const
  295. {
  296. return m_foremost_opaque_layer;
  297. }
  298. int
  299. Sector::get_foremost_layer() const
  300. {
  301. return m_foremost_layer;
  302. }
  303. TileSet*
  304. Sector::get_tileset() const
  305. {
  306. return TileManager::current()->get_tileset(m_level.get_tileset());
  307. }
  308. bool
  309. Sector::in_worldmap() const
  310. {
  311. return m_level.is_worldmap();
  312. }
  313. void
  314. Sector::update(float dt_sec)
  315. {
  316. assert(m_fully_constructed);
  317. BIND_SECTOR(*this);
  318. m_squirrel_environment->update(dt_sec);
  319. GameObjectManager::update(dt_sec);
  320. /* Handle all possible collisions. */
  321. m_collision_system->update();
  322. flush_game_objects();
  323. }
  324. bool
  325. Sector::before_object_add(GameObject& object)
  326. {
  327. if (object.is_singleton())
  328. {
  329. const auto& objects = get_objects_by_type_index(std::type_index(typeid(object)));
  330. if (!objects.empty())
  331. {
  332. log_warning << "Can't insert multiple GameObject of type '" << typeid(object).name() << "', ignoring" << std::endl;
  333. return false;
  334. }
  335. }
  336. if (auto* movingobject = dynamic_cast<MovingObject*>(&object))
  337. {
  338. m_collision_system->add(movingobject->get_collision_object());
  339. }
  340. if (auto* tilemap = dynamic_cast<TileMap*>(&object))
  341. {
  342. tilemap->set_ground_movement_manager(m_collision_system->get_ground_movement_manager());
  343. }
  344. if (s_current == this) {
  345. m_squirrel_environment->try_expose(object);
  346. }
  347. if (m_fully_constructed) {
  348. try_process_resolve_requests();
  349. object.finish_construction();
  350. }
  351. return true;
  352. }
  353. void
  354. Sector::before_object_remove(GameObject& object)
  355. {
  356. auto moving_object = dynamic_cast<MovingObject*>(&object);
  357. if (moving_object) {
  358. m_collision_system->remove(moving_object->get_collision_object());
  359. }
  360. if (s_current == this)
  361. m_squirrel_environment->try_unexpose(object);
  362. }
  363. void
  364. Sector::draw(DrawingContext& context)
  365. {
  366. BIND_SECTOR(*this);
  367. #if 0
  368. context.push_transform();
  369. Rect original_clip = context.get_viewport();
  370. context.push_transform();
  371. {
  372. Camera& camera = get_camera();
  373. context.set_translation(camera.get_translation());
  374. context.scale(camera.get_current_scale());
  375. get_singleton_by_type<PlayerStatusHUD>().set_target_player(0);
  376. Rect clip = original_clip;
  377. clip.left = (clip.left + clip.right) / 2 + 16;
  378. context.set_viewport(clip);
  379. GameObjectManager::draw(context);
  380. if (g_debug.show_collision_rects) {
  381. m_collision_system->draw(context);
  382. }
  383. }
  384. context.set_viewport(original_clip);
  385. context.pop_transform();
  386. context.push_transform();
  387. {
  388. Camera& camera = get_camera();
  389. context.set_translation(camera.get_translation());
  390. context.scale(camera.get_current_scale());
  391. get_singleton_by_type<PlayerStatusHUD>().set_target_player(1);
  392. Rect clip = original_clip;
  393. clip.right = (clip.left + clip.right) / 2 - 16;
  394. context.set_viewport(clip);
  395. GameObjectManager::draw(context);
  396. if (g_debug.show_collision_rects) {
  397. m_collision_system->draw(context);
  398. }
  399. }
  400. context.set_viewport(original_clip);
  401. context.pop_transform();
  402. context.pop_transform();
  403. Rect midline = original_clip;
  404. midline.right = (original_clip.left + original_clip.right) / 2 - 16;
  405. midline.left = (original_clip.left + original_clip.right) / 2 + 16;
  406. context.color().draw_filled_rect(midline, Color::BLACK, 99999);
  407. #else
  408. context.push_transform();
  409. Camera& camera = get_camera();
  410. context.set_translation(camera.get_translation());
  411. context.scale(camera.get_current_scale());
  412. GameObjectManager::draw(context);
  413. if (g_debug.show_collision_rects) {
  414. m_collision_system->draw(context);
  415. }
  416. context.pop_transform();
  417. #endif
  418. if (m_level.m_is_in_cutscene && !m_level.m_skip_cutscene)
  419. {
  420. context.color().draw_text(Resources::normal_font,
  421. _("Press escape to skip"),
  422. Vector(32.f, 32.f),
  423. ALIGN_LEFT,
  424. LAYER_OBJECTS + 1000,
  425. ColorScheme::Text::heading_color);
  426. }
  427. }
  428. bool
  429. Sector::is_free_of_tiles(const Rectf& rect, const bool ignoreUnisolid, uint32_t tiletype) const
  430. {
  431. return m_collision_system->is_free_of_tiles(rect, ignoreUnisolid, tiletype);
  432. }
  433. bool
  434. Sector::is_free_of_statics(const Rectf& rect, const MovingObject* ignore_object, const bool ignoreUnisolid) const
  435. {
  436. return m_collision_system->is_free_of_statics(rect,
  437. ignore_object ? ignore_object->get_collision_object() : nullptr,
  438. ignoreUnisolid);
  439. }
  440. bool
  441. Sector::is_free_of_movingstatics(const Rectf& rect, const MovingObject* ignore_object) const
  442. {
  443. return m_collision_system->is_free_of_movingstatics(rect,
  444. ignore_object ? ignore_object->get_collision_object() : nullptr);
  445. }
  446. bool
  447. Sector::is_free_of_specifically_movingstatics(const Rectf& rect, const MovingObject* ignore_object) const
  448. {
  449. return m_collision_system->is_free_of_specifically_movingstatics(rect,
  450. ignore_object ? ignore_object->get_collision_object() : nullptr);
  451. }
  452. CollisionSystem::RaycastResult
  453. Sector::get_first_line_intersection(const Vector& line_start,
  454. const Vector& line_end,
  455. bool ignore_objects,
  456. const CollisionObject* ignore_object) const {
  457. return m_collision_system->get_first_line_intersection(line_start, line_end, ignore_objects, ignore_object);
  458. }
  459. bool
  460. Sector::free_line_of_sight(const Vector& line_start, const Vector& line_end, bool ignore_objects, const MovingObject* ignore_object) const
  461. {
  462. return m_collision_system->free_line_of_sight(line_start, line_end, ignore_objects,
  463. ignore_object ? ignore_object->get_collision_object() : nullptr);
  464. }
  465. bool
  466. Sector::can_see_player(const Vector& eye) const
  467. {
  468. for (auto player_ptr : get_objects_by_type_index(typeid(Player))) {
  469. Player& player = *static_cast<Player*>(player_ptr);
  470. // test for free line of sight to any of all four corners and the middle of the player's bounding box
  471. if (free_line_of_sight(eye, player.get_bbox().p1(), false, &player)) return true;
  472. if (free_line_of_sight(eye, Vector(player.get_bbox().get_right(), player.get_bbox().get_top()), false, &player)) return true;
  473. if (free_line_of_sight(eye, player.get_bbox().p2(), false, &player)) return true;
  474. if (free_line_of_sight(eye, Vector(player.get_bbox().get_left(), player.get_bbox().get_bottom()), false, &player)) return true;
  475. if (free_line_of_sight(eye, player.get_bbox().get_middle(), false, &player)) return true;
  476. }
  477. return false;
  478. }
  479. bool
  480. Sector::inside(const Rectf& rect) const
  481. {
  482. for (const auto& tilemap : get_all_tilemaps()) {
  483. Rectf bbox = tilemap->get_bbox();
  484. // the top of the sector extends to infinity
  485. if (bbox.get_left() <= rect.get_left() &&
  486. rect.get_right() <= bbox.get_right() &&
  487. rect.get_bottom() <= bbox.get_bottom()) {
  488. return true;
  489. }
  490. }
  491. return false;
  492. }
  493. Size
  494. Sector::get_editor_size() const
  495. {
  496. // Find the tilemap with the greatest surface
  497. size_t max_surface = 0;
  498. Size size;
  499. for (const auto& tilemap : get_all_tilemaps()) {
  500. size_t surface = tilemap->get_width() * tilemap->get_height();
  501. if (surface > max_surface) {
  502. max_surface = surface;
  503. size = tilemap->get_size();
  504. }
  505. }
  506. return size;
  507. }
  508. void
  509. Sector::resize_sector(const Size& old_size, const Size& new_size, const Size& resize_offset)
  510. {
  511. BIND_SECTOR(*this);
  512. bool is_offset = resize_offset.width || resize_offset.height;
  513. Vector obj_shift = Vector(static_cast<float>(resize_offset.width) * 32.0f,
  514. static_cast<float>(resize_offset.height) * 32.0f);
  515. for (const auto& object : get_objects())
  516. {
  517. auto tilemap = dynamic_cast<TileMap*>(object.get());
  518. if (tilemap)
  519. {
  520. if (tilemap->get_size() == old_size)
  521. {
  522. tilemap->save_state();
  523. tilemap->resize(new_size, resize_offset);
  524. tilemap->check_state();
  525. }
  526. else if (is_offset)
  527. {
  528. tilemap->save_state();
  529. tilemap->move_by(obj_shift);
  530. tilemap->check_state();
  531. }
  532. }
  533. else if (is_offset)
  534. {
  535. auto moving_object = dynamic_cast<MovingObject*>(object.get());
  536. if (moving_object)
  537. {
  538. moving_object->save_state();
  539. moving_object->move_to(moving_object->get_pos() + obj_shift);
  540. moving_object->check_state();
  541. }
  542. }
  543. }
  544. }
  545. void
  546. Sector::change_solid_tiles(uint32_t old_tile_id, uint32_t new_tile_id)
  547. {
  548. for (auto& solids: get_solid_tilemaps()) {
  549. solids->change_all(old_tile_id, new_tile_id);
  550. }
  551. }
  552. void
  553. Sector::set_gravity(float gravity)
  554. {
  555. if (gravity != 10.0f)
  556. {
  557. log_warning << "Changing a Sector's gravitational constant might have unforeseen side-effects: " << gravity << std::endl;
  558. }
  559. m_gravity = gravity;
  560. }
  561. Player*
  562. Sector::get_nearest_player (const Vector& pos) const
  563. {
  564. Player *nearest_player = nullptr;
  565. float nearest_dist = std::numeric_limits<float>::max();
  566. for (auto player_ptr : get_objects_by_type_index(typeid(Player)))
  567. {
  568. Player& player = *static_cast<Player*>(player_ptr);
  569. if (player.is_dying() || player.is_dead())
  570. continue;
  571. float dist = player.get_bbox ().distance(pos);
  572. if (dist < nearest_dist) {
  573. nearest_player = &player;
  574. nearest_dist = dist;
  575. }
  576. }
  577. return nearest_player;
  578. }
  579. std::vector<MovingObject*>
  580. Sector::get_nearby_objects(const Vector& center, float max_distance) const
  581. {
  582. std::vector<MovingObject*> result;
  583. for (auto& object : m_collision_system->get_nearby_objects(center, max_distance))
  584. {
  585. auto* moving_object = dynamic_cast<MovingObject*>(&object->get_listener());
  586. if (moving_object) {
  587. result.push_back(moving_object);
  588. }
  589. }
  590. return result;
  591. }
  592. void
  593. Sector::stop_looping_sounds()
  594. {
  595. for (auto& object : get_objects()) {
  596. object->stop_looping_sounds();
  597. }
  598. }
  599. void Sector::play_looping_sounds()
  600. {
  601. for (const auto& object : get_objects()) {
  602. object->play_looping_sounds();
  603. }
  604. }
  605. void
  606. Sector::save(Writer &writer)
  607. {
  608. BIND_SECTOR(*this);
  609. writer.start_list("sector", false);
  610. writer.write("name", m_name, false);
  611. if (!m_level.is_worldmap()) {
  612. if (m_gravity != 10.0f) {
  613. writer.write("gravity", m_gravity);
  614. }
  615. }
  616. if (m_init_script.size()) {
  617. writer.write("init-script", m_init_script,false);
  618. }
  619. // saving objects;
  620. std::vector<GameObject*> objects(get_objects().size());
  621. std::transform(get_objects().begin(), get_objects().end(), objects.begin(), [] (auto& obj) {
  622. return obj.get();
  623. });
  624. std::stable_sort(objects.begin(), objects.end(),
  625. [](const GameObject* lhs, GameObject* rhs) {
  626. return lhs->get_class_name() < rhs->get_class_name();
  627. });
  628. for (auto& obj : objects) {
  629. if (obj->is_saveable()) {
  630. writer.start_list(obj->get_class_name());
  631. obj->save(writer);
  632. writer.end_list(obj->get_class_name());
  633. }
  634. }
  635. writer.end_list("sector");
  636. }
  637. void
  638. Sector::convert_tiles2gameobject()
  639. {
  640. // add lights for special tiles
  641. for (auto& tm : get_objects_by_type<TileMap>())
  642. {
  643. // Since object setup is not yet complete, I have to manually add the offset
  644. // See https://github.com/SuperTux/supertux/issues/1378 for details
  645. Vector tm_offset = tm.get_path() ? tm.get_path()->get_base() : Vector(0, 0);
  646. for (int x=0; x < tm.get_width(); ++x)
  647. {
  648. for (int y=0; y < tm.get_height(); ++y)
  649. {
  650. const Tile& tile = tm.get_tile(x, y);
  651. if (!tile.get_object_name().empty())
  652. {
  653. // If a tile is associated with an object, insert that
  654. // object and remove the tile
  655. if (tile.get_object_name() == "decal" ||
  656. tm.is_solid())
  657. {
  658. Vector pos = tm.get_tile_position(x, y) + tm_offset;
  659. try {
  660. auto object = GameObjectFactory::instance().create(tile.get_object_name(), pos, Direction::AUTO, tile.get_object_data());
  661. add_object(std::move(object));
  662. tm.change(x, y, 0);
  663. } catch(std::exception& e) {
  664. log_warning << e.what() << "" << std::endl;
  665. }
  666. }
  667. }
  668. else
  669. {
  670. // add lights for fire tiles
  671. uint32_t attributes = tile.get_attributes();
  672. Vector pos = tm.get_tile_position(x, y) + tm_offset;
  673. Vector center = pos + Vector(16, 16);
  674. if (attributes & Tile::FIRE) {
  675. if (attributes & Tile::HURTS) {
  676. // lava or lavaflow
  677. // space lights a bit
  678. if ((tm.get_tile(x-1, y).get_attributes() != attributes || x%3 == 0)
  679. && (tm.get_tile(x, y-1).get_attributes() != attributes || y%3 == 0)) {
  680. float pseudo_rnd = static_cast<float>(static_cast<int>(pos.x) % 10) / 10;
  681. add<PulsingLight>(center, 1.0f + pseudo_rnd, 0.8f, 1.0f,
  682. (Color(1.0f, 0.3f, 0.0f, 1.0f) * tm.get_current_tint()).validate());
  683. }
  684. } else {
  685. // torch
  686. float pseudo_rnd = static_cast<float>(static_cast<int>(pos.x) % 10) / 10;
  687. add<PulsingLight>(center, 1.0f + pseudo_rnd, 0.9f, 1.0f,
  688. (Color(1.0f, 1.0f, 0.6f, 1.0f) * tm.get_current_tint()).validate());
  689. }
  690. }
  691. }
  692. }
  693. }
  694. }
  695. }
  696. Camera&
  697. Sector::get_camera() const
  698. {
  699. return get_singleton_by_type<Camera>();
  700. }
  701. std::vector<Player*>
  702. Sector::get_players() const
  703. {
  704. return m_level.get_players();
  705. }
  706. DisplayEffect&
  707. Sector::get_effect() const
  708. {
  709. return get_singleton_by_type<DisplayEffect>();
  710. }
  711. /* EOF */