game_session.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959
  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 "supertux/game_session.hpp"
  17. #include <cfloat>
  18. #include <fmt/format.h>
  19. #include <stdexcept>
  20. #include "audio/sound_manager.hpp"
  21. #include "control/input_manager.hpp"
  22. #include "editor/editor.hpp"
  23. #include "gui/menu_manager.hpp"
  24. #include "math/vector.hpp"
  25. #include "object/camera.hpp"
  26. #include "object/endsequence_fireworks.hpp"
  27. #include "object/endsequence_walk.hpp"
  28. #include "object/level_time.hpp"
  29. #include "object/music_object.hpp"
  30. #include "object/player.hpp"
  31. #include "object/spawnpoint.hpp"
  32. #include "sdk/integration.hpp"
  33. #include "squirrel/squirrel_virtual_machine.hpp"
  34. #include "supertux/constants.hpp"
  35. #include "supertux/fadetoblack.hpp"
  36. #include "supertux/gameconfig.hpp"
  37. #include "supertux/level.hpp"
  38. #include "supertux/level_parser.hpp"
  39. #include "supertux/levelintro.hpp"
  40. #include "supertux/levelset_screen.hpp"
  41. #include "supertux/menu/menu_storage.hpp"
  42. #include "supertux/savegame.hpp"
  43. #include "supertux/screen_manager.hpp"
  44. #include "supertux/sector.hpp"
  45. #include "supertux/shrinkfade.hpp"
  46. #include "util/file_system.hpp"
  47. #include "video/compositor.hpp"
  48. #include "video/drawing_context.hpp"
  49. #include "video/surface.hpp"
  50. #include "worldmap/worldmap.hpp"
  51. static const float SAFE_TIME = 1.0f;
  52. static const int SHRINKFADE_LAYER = LAYER_LIGHTMAP - 1;
  53. static const float TELEPORT_FADE_TIME = 1.0f;
  54. GameSession::GameSession(const std::string& levelfile_, Savegame& savegame, Statistics* statistics) :
  55. reset_button(false),
  56. reset_checkpoint_button(false),
  57. m_prevent_death(false),
  58. m_level(),
  59. m_statistics_backdrop(Surface::from_file("images/engine/menu/score-backdrop.png")),
  60. m_data_table(SquirrelVirtualMachine::current()->get_vm().findTable("Level").getOrCreateTable("data")),
  61. m_currentsector(nullptr),
  62. m_end_sequence(nullptr),
  63. m_game_pause(false),
  64. m_speed_before_pause(ScreenManager::current()->get_speed()),
  65. m_levelfile(levelfile_),
  66. m_spawnpoints(),
  67. m_activated_checkpoint(),
  68. m_newsector(),
  69. m_newspawnpoint(),
  70. m_spawn_fade_type(ScreenFade::FadeType::NONE),
  71. m_spawn_fade_timer(),
  72. m_spawn_with_invincibility(false),
  73. m_best_level_statistics(statistics),
  74. m_savegame(savegame),
  75. m_play_time(0),
  76. m_levelintro_shown(false),
  77. m_coins_at_start(),
  78. m_boni_at_start(),
  79. m_max_fire_bullets_at_start(),
  80. m_max_ice_bullets_at_start(),
  81. m_active(false),
  82. m_end_seq_started(false),
  83. m_pause_target_timer(false),
  84. m_current_cutscene_text(),
  85. m_endsequence_timer()
  86. {
  87. set_start_point(DEFAULT_SECTOR_NAME, DEFAULT_SPAWNPOINT_NAME);
  88. m_boni_at_start.resize(InputManager::current()->get_num_users(), BONUS_NONE);
  89. m_data_table.clear();
  90. }
  91. void
  92. GameSession::reset_level()
  93. {
  94. for (const auto& p : m_currentsector->get_players())
  95. {
  96. try
  97. {
  98. p->set_bonus(m_boni_at_start.at(p->get_id()));
  99. }
  100. catch(const std::out_of_range&)
  101. {
  102. }
  103. }
  104. PlayerStatus& currentStatus = m_savegame.get_player_status();
  105. currentStatus.coins = m_coins_at_start;
  106. currentStatus.bonus = m_boni_at_start;
  107. clear_respawn_points();
  108. m_activated_checkpoint = nullptr;
  109. m_pause_target_timer = false;
  110. m_spawn_with_invincibility = false;
  111. m_spawn_fade_timer.stop();
  112. m_data_table.clear();
  113. }
  114. void
  115. GameSession::on_player_added(int id)
  116. {
  117. auto& player_status = m_savegame.get_player_status();
  118. if (player_status.m_num_players <= id)
  119. player_status.add_player();
  120. // ID = 0 is impossible, so no need to write `(id == 0) ? "" : ...`
  121. auto& player = m_currentsector->add<Player>(player_status, "Tux" + std::to_string(id + 1), id);
  122. player.multiplayer_prepare_spawn();
  123. }
  124. bool
  125. GameSession::on_player_removed(int id)
  126. {
  127. // Sectors in worldmaps have no Player's of that class
  128. if (!m_currentsector)
  129. return false;
  130. for (Player* player : m_currentsector->get_players())
  131. {
  132. if (player->get_id() == id)
  133. {
  134. player->remove_me();
  135. return true;
  136. }
  137. }
  138. return false;
  139. }
  140. void
  141. GameSession::restart_level(bool after_death, bool preserve_music)
  142. {
  143. const PlayerStatus& currentStatus = m_savegame.get_player_status();
  144. m_coins_at_start = currentStatus.coins;
  145. m_boni_at_start = currentStatus.bonus;
  146. // Needed for the title screen apparently.
  147. if (m_currentsector)
  148. {
  149. try
  150. {
  151. for (const auto& p : m_currentsector->get_players())
  152. {
  153. p->set_bonus(m_boni_at_start.at(p->get_id()), false);
  154. m_boni_at_start[p->get_id()] = currentStatus.bonus[p->get_id()];
  155. }
  156. }
  157. catch (const std::out_of_range&)
  158. {
  159. }
  160. }
  161. m_game_pause = false;
  162. m_end_sequence = nullptr;
  163. m_endsequence_timer.stop();
  164. m_spawn_with_invincibility = false;
  165. InputManager::current()->reset();
  166. m_currentsector = nullptr;
  167. const std::string base_dir = FileSystem::dirname(m_levelfile);
  168. if (base_dir == "./") {
  169. m_levelfile = FileSystem::basename(m_levelfile);
  170. }
  171. try {
  172. m_level = LevelParser::from_file(m_levelfile, false, false);
  173. /* Determine the spawnpoint to spawn/respawn Tux to. */
  174. const GameSession::SpawnPoint* spawnpoint = nullptr;
  175. if (m_activated_checkpoint && reset_checkpoint_button) // Checkpoint is activated and respawn from it is requested.
  176. {
  177. reset_checkpoint_button = false;
  178. // Attempt to find the last checkpoint's spawnpoint.
  179. for (int i = static_cast<int>(m_spawnpoints.size()) - 1; i >= 0; i--)
  180. {
  181. if (m_spawnpoints.at(i).is_checkpoint) // Checkpoint found.
  182. {
  183. spawnpoint = &m_spawnpoints.at(i);
  184. // Remove any spawnpoints after the checkpoint one.
  185. if (i < static_cast<int>(m_spawnpoints.size()) - 1)
  186. m_spawnpoints.erase(m_spawnpoints.begin() + i + 1, m_spawnpoints.end());
  187. break;
  188. }
  189. }
  190. if (!spawnpoint) // Spawnpoint has not been found.
  191. {
  192. throw std::runtime_error("Cannot find the position of the last activated checkpoint.");
  193. }
  194. }
  195. else if (after_death && m_spawnpoints.size() > 1) // Respawn from the last respawn position, because Tux is respawning.
  196. {
  197. spawnpoint = &get_last_spawnpoint();
  198. }
  199. else // Respawn from the start position.
  200. {
  201. spawnpoint = &m_spawnpoints.front();
  202. m_play_time = 0; // Reset play time.
  203. m_data_table.clear();
  204. }
  205. /* Perform the respawn from the chosen spawnpoint. */
  206. // Load the spawn sector.
  207. m_currentsector = m_level->get_sector(spawnpoint->sector);
  208. if (!m_currentsector)
  209. {
  210. throw std::runtime_error(fmt::format("Couldn't find sector '{}' to spawn/respawn Tux.", spawnpoint->sector));
  211. }
  212. // Activate on either the spawnpoint (if set), or the spawn position.
  213. if (spawnpoint->spawnpoint.empty())
  214. {
  215. m_currentsector->activate(spawnpoint->position);
  216. }
  217. else
  218. {
  219. m_currentsector->activate(spawnpoint->spawnpoint);
  220. }
  221. }
  222. catch (std::exception& e) {
  223. throw std::runtime_error(std::string("Couldn't start level: ") + e.what());
  224. }
  225. if (m_levelintro_shown)
  226. {
  227. const Vector shrinkpos = get_fade_point();
  228. ScreenManager::current()->set_screen_fade(std::make_unique<ShrinkFade>(shrinkpos, TELEPORT_FADE_TIME, SHRINKFADE_LAYER, ShrinkFade::FADEIN));
  229. }
  230. if (!preserve_music)
  231. {
  232. auto& music_object = m_currentsector->get_singleton_by_type<MusicObject>();
  233. if (after_death == true)
  234. {
  235. music_object.resume_music();
  236. }
  237. else
  238. {
  239. SoundManager::current()->stop_music();
  240. music_object.play_music(LEVEL_MUSIC);
  241. }
  242. }
  243. auto level_times = m_currentsector->get_objects_by_type<LevelTime>();
  244. for(auto& level_time : level_times)
  245. {
  246. level_time.set_time(level_time.get_time() - m_play_time);
  247. }
  248. }
  249. void
  250. GameSession::on_escape_press(bool force_quick_respawn)
  251. {
  252. auto players = m_currentsector->get_players();
  253. int alive = m_currentsector->get_object_count<Player>([](const Player& p) {
  254. return p.is_alive();
  255. });
  256. if ((!alive && (m_play_time > 2.0f || force_quick_respawn)) || m_end_sequence)
  257. {
  258. // Let the timers run out, we fast-forward them to force past a sequence.
  259. if (m_end_sequence)
  260. {
  261. if (m_end_sequence->is_running())
  262. {
  263. m_end_sequence->stop();
  264. }
  265. else
  266. {
  267. m_endsequence_timer.start(FLT_EPSILON);
  268. }
  269. }
  270. for (auto* player : players)
  271. player->m_dying_timer.start(FLT_EPSILON);
  272. return; // Don't let the player open the menu, when Tux is dying.
  273. }
  274. if (m_level->m_is_in_cutscene && !m_level->m_skip_cutscene)
  275. {
  276. m_level->m_skip_cutscene = true;
  277. return;
  278. }
  279. if (m_level->m_suppress_pause_menu && ScreenManager::current()->has_pending_fadeout())
  280. return;
  281. if (!m_level->m_suppress_pause_menu) {
  282. toggle_pause();
  283. } else {
  284. abort_level();
  285. }
  286. }
  287. Vector
  288. GameSession::get_fade_point(const Vector& position) const
  289. {
  290. Vector fade_point(0.0f, 0.0f);
  291. if (position.x != 0.0f && position.y != 0.0f)
  292. {
  293. fade_point = position;
  294. }
  295. else
  296. {
  297. if (m_level->m_is_in_cutscene || m_currentsector->get_camera().get_mode() == Camera::Mode::MANUAL)
  298. {
  299. fade_point = m_currentsector->get_camera().get_center();
  300. }
  301. else
  302. {
  303. // Get "middle" of all alive players
  304. Vector average_position(0.0f, 0.0f);
  305. size_t alive_players = 0U;
  306. for (const auto* player : m_currentsector->get_players())
  307. {
  308. if (player->is_alive())
  309. {
  310. average_position += player->get_bbox().get_middle();
  311. alive_players++;
  312. }
  313. }
  314. if (alive_players > 0U)
  315. {
  316. fade_point = average_position / alive_players;
  317. }
  318. else
  319. {
  320. fade_point = m_currentsector->get_camera().get_center();
  321. }
  322. }
  323. }
  324. const Camera& camera = m_currentsector->get_camera();
  325. return (fade_point - camera.get_translation()) * camera.get_current_scale();
  326. }
  327. void
  328. GameSession::toggle_pause()
  329. {
  330. // Pause.
  331. if (!m_game_pause && !MenuManager::instance().is_active())
  332. {
  333. m_speed_before_pause = ScreenManager::current()->get_speed();
  334. ScreenManager::current()->set_speed(0);
  335. MenuManager::instance().set_menu(MenuStorage::GAME_MENU);
  336. SoundManager::current()->pause_sounds();
  337. m_currentsector->stop_looping_sounds();
  338. SoundManager::current()->pause_music();
  339. m_game_pause = true;
  340. }
  341. // Unpause is done in update() after the menu is processed.
  342. }
  343. void
  344. GameSession::abort_level()
  345. {
  346. MenuManager::instance().clear_menu_stack();
  347. ScreenManager::current()->pop_screen();
  348. for (const auto& p : m_currentsector->get_players())
  349. {
  350. try
  351. {
  352. p->set_bonus(m_boni_at_start.at(p->get_id()));
  353. }
  354. catch(const std::out_of_range&)
  355. {
  356. }
  357. }
  358. PlayerStatus& currentStatus = m_savegame.get_player_status();
  359. currentStatus.coins = m_coins_at_start;
  360. SoundManager::current()->stop_sounds();
  361. }
  362. bool
  363. GameSession::is_active() const
  364. {
  365. return !m_game_pause && m_active && !(m_end_sequence && m_end_sequence->is_running());
  366. }
  367. void
  368. GameSession::check_end_conditions()
  369. {
  370. bool all_dead = true;
  371. for (const auto* p : m_currentsector->get_players())
  372. if (!(all_dead &= p->is_dead()))
  373. break;
  374. bool all_dead_or_winning = true;
  375. for (const auto* p : m_currentsector->get_players())
  376. if (!(all_dead_or_winning &= (!p->is_active())))
  377. break;
  378. /* End of level? */
  379. if (m_endsequence_timer.check() || (all_dead_or_winning && m_end_sequence && m_endsequence_timer.get_period() > 0.f)) {
  380. m_endsequence_timer.stop();
  381. for (auto* p : m_currentsector->get_players())
  382. p->set_winning();
  383. start_sequence(nullptr, Sequence::SEQ_ENDSEQUENCE);
  384. } else if (m_end_sequence && m_end_sequence->is_done()) {
  385. finish(true);
  386. } else if (!m_end_sequence && all_dead) {
  387. restart_level(true);
  388. }
  389. }
  390. void
  391. GameSession::draw(Compositor& compositor)
  392. {
  393. auto& context = compositor.make_context();
  394. if (m_game_pause) {
  395. context.set_time_offset(0.0f);
  396. }
  397. m_currentsector->draw(context);
  398. drawstatus(context);
  399. if (m_game_pause)
  400. draw_pause(context);
  401. }
  402. void
  403. GameSession::draw_pause(DrawingContext& context)
  404. {
  405. context.color().draw_filled_rect(
  406. Rectf(context.get_rect()),
  407. Color(0.0f, 0.0f, 0.0f, 0.25f),
  408. LAYER_FOREGROUND1);
  409. }
  410. void
  411. GameSession::setup()
  412. {
  413. if (m_currentsector == nullptr)
  414. return;
  415. if (m_currentsector != Sector::current()) {
  416. m_currentsector->activate(m_currentsector->get_players()[0]->get_pos());
  417. }
  418. m_currentsector->get_singleton_by_type<MusicObject>().play_music(LEVEL_MUSIC);
  419. int total_stats_to_be_collected = m_level->m_stats.m_total_coins + m_level->m_stats.m_total_badguys + m_level->m_stats.m_total_secrets;
  420. if ((!m_levelintro_shown) && (total_stats_to_be_collected > 0)) {
  421. m_levelintro_shown = true;
  422. m_active = false;
  423. ScreenManager::current()->push_screen(std::make_unique<LevelIntro>(*m_level, m_best_level_statistics, m_savegame.get_player_status()));
  424. ScreenManager::current()->set_screen_fade(std::make_unique<FadeToBlack>(FadeToBlack::FADEIN, TELEPORT_FADE_TIME));
  425. }
  426. else
  427. {
  428. const Vector shrinkpos = get_fade_point();
  429. ScreenManager::current()->set_screen_fade(std::make_unique<ShrinkFade>(shrinkpos, TELEPORT_FADE_TIME, SHRINKFADE_LAYER, ShrinkFade::FADEIN));
  430. }
  431. m_end_seq_started = false;
  432. }
  433. void
  434. GameSession::leave()
  435. {
  436. m_data_table.clear();
  437. }
  438. void
  439. GameSession::update(float dt_sec, const Controller& controller)
  440. {
  441. // Set active flag.
  442. if (!m_active)
  443. {
  444. m_active = true;
  445. }
  446. // Handle controller.
  447. if (controller.pressed_any(Control::ESCAPE, Control::START))
  448. {
  449. on_escape_press(controller.hold(Control::LEFT)
  450. || controller.hold(Control::RIGHT));
  451. }
  452. if (controller.pressed(Control::CHEAT_MENU) &&
  453. (g_config->developer_mode || (Editor::current() && Editor::current()->is_testing_level()))
  454. )
  455. {
  456. if (!MenuManager::instance().is_active())
  457. {
  458. toggle_pause();
  459. MenuManager::instance().set_menu(MenuStorage::CHEAT_MENU);
  460. }
  461. }
  462. if (controller.pressed(Control::DEBUG_MENU) && g_config->developer_mode)
  463. {
  464. if (!MenuManager::instance().is_active())
  465. {
  466. toggle_pause();
  467. MenuManager::instance().set_menu(MenuStorage::DEBUG_MENU);
  468. }
  469. }
  470. // Animate the full-completion stats stuff - do this even when the game isn't paused (that's a
  471. // design choice, if you prefer it not to animate when paused, add `if (!m_game_pause)`).
  472. m_level->m_stats.update_timers(dt_sec);
  473. // Unpause the game if the menu has been closed.
  474. if (m_game_pause && !MenuManager::instance().is_active()) {
  475. ScreenManager::current()->set_speed(m_speed_before_pause);
  476. SoundManager::current()->resume_music();
  477. SoundManager::current()->resume_sounds();
  478. assert(m_currentsector != nullptr);
  479. m_currentsector->play_looping_sounds();
  480. m_game_pause = false;
  481. }
  482. check_end_conditions();
  483. // Respawning in new sector?
  484. if (!m_newsector.empty() && !m_newspawnpoint.empty() && (m_spawn_fade_timer.check() || m_spawn_fade_type == ScreenFade::FadeType::NONE)) {
  485. auto sector = m_level->get_sector(m_newsector);
  486. std::string current_music = m_currentsector->get_singleton_by_type<MusicObject>().get_music();
  487. if (sector == nullptr) {
  488. log_warning << "Sector '" << m_newsector << "' not found" << std::endl;
  489. sector = m_level->get_sector(m_spawnpoints.at(0).sector); // Assign start sector.
  490. }
  491. assert(m_currentsector != nullptr);
  492. m_currentsector->stop_looping_sounds();
  493. sector->activate(m_newspawnpoint);
  494. // Start the new sector's music only if it's different from the current one.
  495. if (current_music != sector->get_singleton_by_type<MusicObject>().get_music())
  496. sector->get_singleton_by_type<MusicObject>().play_music(LEVEL_MUSIC);
  497. m_currentsector = sector;
  498. m_currentsector->play_looping_sounds();
  499. switch (m_spawn_fade_type)
  500. {
  501. case ScreenFade::FadeType::FADE:
  502. {
  503. ScreenManager::current()->set_screen_fade(std::make_unique<FadeToBlack>(FadeToBlack::FADEIN, TELEPORT_FADE_TIME));
  504. break;
  505. }
  506. case ScreenFade::FadeType::CIRCLE:
  507. {
  508. const Vector spawn_point_position = sector->get_spawn_point_position(m_newspawnpoint);
  509. const Vector shrinkpos = get_fade_point(spawn_point_position);
  510. ScreenManager::current()->set_screen_fade(std::make_unique<ShrinkFade>(shrinkpos, TELEPORT_FADE_TIME, SHRINKFADE_LAYER, ShrinkFade::FADEIN));
  511. break;
  512. }
  513. default:
  514. break;
  515. }
  516. for (auto* p : m_currentsector->get_players())
  517. {
  518. // Give back control to the player
  519. p->activate();
  520. if (m_spawn_with_invincibility)
  521. {
  522. // Reset velocity to avoid taking any movement from last sector to new one
  523. p->set_velocity(0.0f, 0.0f);
  524. // Make all players temporarily safe after spawning
  525. p->make_temporarily_safe(SAFE_TIME);
  526. }
  527. }
  528. m_newsector = "";
  529. m_newspawnpoint = "";
  530. }
  531. // Update the world state and all objects in the world.
  532. if (!m_game_pause) {
  533. assert(m_currentsector != nullptr);
  534. // Update the world.
  535. if (!m_end_sequence || !m_end_sequence->is_running()) {
  536. if (!m_level->m_is_in_cutscene && !m_pause_target_timer)
  537. {
  538. m_play_time += dt_sec;
  539. m_level->m_stats.finish(m_play_time);
  540. }
  541. for (Player* player : m_currentsector->get_players())
  542. {
  543. if (player->is_active() && player->is_scripting_activated() &&
  544. player->get_controller().pressed(Control::ITEM) &&
  545. m_savegame.get_player_status().m_item_pockets.size() > 0)
  546. {
  547. player->get_status().give_item_from_pocket(player);
  548. }
  549. }
  550. m_currentsector->update(dt_sec);
  551. } else {
  552. bool are_all_stopped = true;
  553. for (const auto& player : m_currentsector->get_players())
  554. {
  555. if (!(m_end_sequence->is_tux_stopped(player->get_id())
  556. || player->get_ending_direction() == 0))
  557. {
  558. are_all_stopped = false;
  559. break;
  560. }
  561. }
  562. if (!are_all_stopped) {
  563. m_currentsector->update(dt_sec);
  564. } else {
  565. m_end_sequence->update(dt_sec);
  566. }
  567. }
  568. }
  569. if (m_currentsector == nullptr)
  570. return;
  571. // Update sounds.
  572. SoundManager::current()->set_listener_position(m_currentsector->get_camera().get_center());
  573. /* Handle music: */
  574. if (m_end_sequence)
  575. return;
  576. bool invincible_timer_started = false;
  577. float max_invincible_timer_left = 0.f;
  578. for (const auto* p : m_currentsector->get_players())
  579. {
  580. invincible_timer_started |= (p->m_invincible_timer.started() && !p->is_winning());
  581. max_invincible_timer_left = std::max(max_invincible_timer_left, p->m_invincible_timer.get_timeleft());
  582. }
  583. auto& music_object = m_currentsector->get_singleton_by_type<MusicObject>();
  584. if (invincible_timer_started) {
  585. if (max_invincible_timer_left <= TUX_INVINCIBLE_TIME_WARNING) {
  586. if (music_object.get_music_type() != HERRING_WARNING_MUSIC)
  587. music_object.play_music(HERRING_WARNING_MUSIC);
  588. } else {
  589. if (music_object.get_music_type() != HERRING_MUSIC)
  590. music_object.play_music(HERRING_MUSIC);
  591. }
  592. } else if (music_object.get_music_type() != LEVEL_MUSIC) {
  593. music_object.play_music(LEVEL_MUSIC);
  594. }
  595. if (reset_button) {
  596. reset_button = false;
  597. reset_level();
  598. restart_level();
  599. } else if(reset_checkpoint_button) {
  600. for (auto* p : m_currentsector->get_players())
  601. p->kill(true);
  602. }
  603. }
  604. IntegrationStatus
  605. GameSession::get_status() const
  606. {
  607. IntegrationStatus status;
  608. status.m_details.push_back(Editor::current() ? "Testing" : "Playing");
  609. if (!Editor::current() || !g_config->hide_editor_levelnames)
  610. {
  611. const std::string label = get_current_level().is_worldmap() ? "In worldmap: " : "In level: ";
  612. status.m_details.push_back(label + get_current_level().get_name());
  613. }
  614. return status;
  615. }
  616. void
  617. GameSession::finish(bool win)
  618. {
  619. if (m_end_seq_started)
  620. return;
  621. m_end_seq_started = true;
  622. using namespace worldmap;
  623. if (win) {
  624. if (WorldMapSector::current())
  625. {
  626. WorldMapSector::current()->finished_level(m_level.get());
  627. }
  628. if (LevelsetScreen::current())
  629. {
  630. LevelsetScreen::current()->finished_level(win);
  631. }
  632. }
  633. ScreenManager::current()->pop_screen();
  634. }
  635. void
  636. GameSession::respawn(const std::string& sector, const std::string& spawnpoint)
  637. {
  638. m_newsector = sector;
  639. m_newspawnpoint = spawnpoint;
  640. m_spawn_with_invincibility = false;
  641. m_spawn_fade_type = ScreenFade::FadeType::NONE;
  642. }
  643. void
  644. GameSession::respawn_with_fade(const std::string& sector,
  645. const std::string& spawnpoint,
  646. const ScreenFade::FadeType fade_type,
  647. const Vector& fade_point,
  648. const bool make_invincible)
  649. {
  650. respawn(sector, spawnpoint);
  651. m_spawn_fade_type = fade_type;
  652. m_spawn_with_invincibility = make_invincible;
  653. bool transition_takes_time = false;
  654. switch (m_spawn_fade_type)
  655. {
  656. case ScreenFade::FadeType::FADE:
  657. {
  658. ScreenManager::current()->set_screen_fade(std::make_unique<FadeToBlack>(FadeToBlack::FADEOUT, TELEPORT_FADE_TIME));
  659. transition_takes_time = true;
  660. break;
  661. }
  662. case ScreenFade::FadeType::CIRCLE:
  663. {
  664. const Vector shrinkpos = get_fade_point(fade_point);
  665. ScreenManager::current()->set_screen_fade(std::make_unique<ShrinkFade>(shrinkpos, TELEPORT_FADE_TIME, SHRINKFADE_LAYER, ShrinkFade::FADEOUT));
  666. transition_takes_time = true;
  667. break;
  668. }
  669. default:
  670. break;
  671. }
  672. if (transition_takes_time)
  673. {
  674. m_spawn_fade_timer.start(TELEPORT_FADE_TIME);
  675. // Make all players safe during the fadeout transition
  676. for (Player* player : m_currentsector->get_players())
  677. {
  678. player->make_temporarily_safe(TELEPORT_FADE_TIME);
  679. }
  680. }
  681. }
  682. void
  683. GameSession::set_start_point(const std::string& sector,
  684. const std::string& spawnpoint)
  685. {
  686. if (!m_spawnpoints.empty()) m_spawnpoints.erase(m_spawnpoints.begin());
  687. m_spawnpoints.insert(m_spawnpoints.begin(), { sector, spawnpoint });
  688. }
  689. void
  690. GameSession::set_start_pos(const std::string& sector, const Vector& pos)
  691. {
  692. if (!m_spawnpoints.empty()) m_spawnpoints.erase(m_spawnpoints.begin());
  693. m_spawnpoints.insert(m_spawnpoints.begin(), { sector, pos });
  694. }
  695. void
  696. GameSession::clear_respawn_points()
  697. {
  698. // Delete all respawn points (all, other than the start one).
  699. if (m_spawnpoints.size() > 1)
  700. m_spawnpoints.erase(m_spawnpoints.begin() + 1, m_spawnpoints.end());
  701. }
  702. void
  703. GameSession::set_checkpoint_pos(const std::string& sector, const Vector& pos)
  704. {
  705. m_spawnpoints.push_back({ sector, pos, true });
  706. m_activated_checkpoint = &m_spawnpoints.back();
  707. }
  708. std::string
  709. GameSession::get_working_directory() const
  710. {
  711. return FileSystem::dirname(m_levelfile);
  712. }
  713. void
  714. GameSession::start_sequence(Player* caller, Sequence seq, const SequenceData* data)
  715. {
  716. // Handle special "stoptux" sequence.
  717. if (seq == SEQ_STOPTUX) {
  718. if (!m_end_sequence) {
  719. log_warning << "Final target reached without an active end sequence" << std::endl;
  720. start_sequence(caller, SEQ_ENDSEQUENCE);
  721. }
  722. if (m_end_sequence)
  723. {
  724. if (caller)
  725. {
  726. m_end_sequence->stop_tux(caller->get_id());
  727. }
  728. else
  729. {
  730. for (const auto* player : Sector::get().get_players())
  731. m_end_sequence->stop_tux(player->get_id());
  732. }
  733. }
  734. return;
  735. }
  736. if (caller)
  737. caller->set_winning();
  738. // Abort if a sequence is already playing.
  739. if (m_end_sequence && m_end_sequence->is_running())
  740. return;
  741. // Set the sequence to prepare it.
  742. if (!m_end_sequence) {
  743. std::unique_ptr<EndSequence> end_sequence;
  744. if (seq == SEQ_ENDSEQUENCE) {
  745. end_sequence = std::make_unique<EndSequenceWalk>();
  746. } else if (seq == SEQ_FIREWORKS) {
  747. end_sequence = std::make_unique<EndSequenceFireworks>();
  748. } else {
  749. log_warning << "Unknown sequence '" << static_cast<int>(seq) << "'. Ignoring." << std::endl;
  750. return;
  751. }
  752. m_end_sequence = static_cast<EndSequence*>(&m_currentsector->add_object(std::move(end_sequence)));
  753. }
  754. if (caller)
  755. {
  756. caller->set_ending_direction((caller->get_physic().get_velocity_x() < 0) ? -1 : 1);
  757. caller->set_controller(m_end_sequence->get_controller(caller->get_id()));
  758. caller->set_speedlimit(230); // MAX_WALK_XM
  759. }
  760. int remaining_players = get_current_sector().get_object_count<Player>([](const Player& p){
  761. return p.is_active();
  762. });
  763. // Don't play the prepared sequence if there are more players that are still playing.
  764. if (remaining_players > 0)
  765. {
  766. if (!m_endsequence_timer.started())
  767. m_endsequence_timer.start(10.f);
  768. return;
  769. }
  770. m_endsequence_timer.stop();
  771. if (const auto& worldmap_sector = worldmap::WorldMapSector::current())
  772. {
  773. if (data != nullptr)
  774. {
  775. if (!data->fade_tilemap.empty())
  776. {
  777. worldmap_sector->set_initial_fade_tilemap(data->fade_tilemap, data->fade_type);
  778. }
  779. if (!data->spawnpoint.empty())
  780. {
  781. worldmap_sector->get_worldmap().set_initial_spawnpoint(data->spawnpoint);
  782. }
  783. }
  784. }
  785. /* Slow down the game for end-sequence. */
  786. ScreenManager::current()->set_speed(0.5f);
  787. m_end_sequence->start();
  788. SoundManager::current()->play_music("music/misc/leveldone.ogg", false);
  789. for (auto* p : m_currentsector->get_players())
  790. {
  791. p->set_winning();
  792. p->set_controller(m_end_sequence->get_controller(p->get_id()));
  793. p->set_speedlimit(230); // MAX_WALK_XM.
  794. }
  795. // Stop all clocks.
  796. for (LevelTime& lt : m_currentsector->get_objects_by_type<LevelTime>())
  797. {
  798. lt.stop();
  799. }
  800. }
  801. void
  802. GameSession::set_target_timer_paused(bool paused)
  803. {
  804. m_pause_target_timer = paused;
  805. for (LevelTime& lt : m_currentsector->get_objects_by_type<LevelTime>())
  806. {
  807. if(paused)
  808. lt.stop();
  809. else
  810. lt.start();
  811. }
  812. }
  813. /* (Status): */
  814. void
  815. GameSession::drawstatus(DrawingContext& context)
  816. {
  817. // Draw level stats while end_sequence is running.
  818. if (m_end_sequence && m_end_sequence->is_running()) {
  819. m_level->m_stats.draw_endseq_panel(context, m_best_level_statistics, m_statistics_backdrop, m_level->m_target_time);
  820. }
  821. m_level->m_stats.draw_ingame_stats(context, m_game_pause);
  822. }
  823. /* EOF */