Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Preserving players and grabbed objects between sectors #2596

Merged
merged 4 commits into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 32 additions & 8 deletions src/object/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,6 @@ Player::Player(PlayerStatus& player_status, const std::string& name_, int player
m_idle_timer(),
m_idle_stage(0),
m_climbing(nullptr),
m_climbing_remove_listener(nullptr),
m_ending_direction(0),
m_collected_keys(0)
{
Expand Down Expand Up @@ -311,6 +310,21 @@ Player::do_scripting_controller(const std::string& control_text, bool pressed)
}
}

void
Player::move_to_sector(Sector& other)
tobbi marked this conversation as resolved.
Show resolved Hide resolved
{
stop_climbing(*m_climbing);
if (m_grabbed_object)
{
auto grabbed_game_object = dynamic_cast<GameObject*>(m_grabbed_object);
if (grabbed_game_object)
get_parent()->move_object(grabbed_game_object->get_uid(), other);
}

// Move the player.
get_parent()->move_object(get_uid(), other);
}

bool
Player::adjust_height(float new_height, float bottom_offset)
{
Expand Down Expand Up @@ -1589,28 +1603,35 @@ Player::handle_input()
}

void
Player::position_grabbed_object()
Player::position_grabbed_object(bool teleport)
{
if (!m_grabbed_object)
return;

auto moving_object = dynamic_cast<MovingObject*>(m_grabbed_object);
assert(moving_object);
const auto& object_bbox = moving_object->get_bbox();

Vector pos;
if (!m_swimming && !m_water_jump)
{
// Position where we will hold the lower-inner corner
Vector pos(m_col.m_bbox.get_left() + m_col.m_bbox.get_width() / 2,
m_col.m_bbox.get_top() + m_col.m_bbox.get_height()*0.66666f);
pos = Vector(m_col.m_bbox.get_left() + m_col.m_bbox.get_width() / 2,
m_col.m_bbox.get_top() + m_col.m_bbox.get_height() * 0.66666f);
// Adjust to find the grabbed object's upper-left corner
if (m_dir == Direction::LEFT)
pos.x -= object_bbox.get_width();
pos.y -= object_bbox.get_height();
m_grabbed_object->grab(*this, pos, m_dir);
}
else
{
Vector pos(m_col.m_bbox.get_left() + (std::cos(m_swimming_angle) * 32.f),
m_col.m_bbox.get_top() + (std::sin(m_swimming_angle) * 32.f));
m_grabbed_object->grab(*this, pos, m_dir);
pos = Vector(m_col.m_bbox.get_left() + (std::cos(m_swimming_angle) * 32.f),
m_col.m_bbox.get_top() + (std::sin(m_swimming_angle) * 32.f));
}

if (teleport)
moving_object->set_pos(pos);
m_grabbed_object->grab(*this, pos, m_dir);
}

bool
Expand Down Expand Up @@ -2358,6 +2379,9 @@ Player::move(const Vector& vector)
m_last_ground_y = vector.y;
if (m_climbing) stop_climbing(*m_climbing);

// Make sure grabbed objects move directly with the player.
position_grabbed_object(true);

m_physic.reset();
}

Expand Down
8 changes: 6 additions & 2 deletions src/object/player.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ class Player final : public MovingObject,
void use_scripting_controller(bool use_or_release);
void do_scripting_controller(const std::string& control, bool pressed);

/** Move the player to a different sector, including any objects that it points to, or references. */
void move_to_sector(Sector& other);

void make_invincible();

bool is_invincible() const { return m_invincible_timer.started(); }
Expand Down Expand Up @@ -229,7 +232,7 @@ class Player final : public MovingObject,
void set_dir(bool right);
void stop_backflipping();

void position_grabbed_object();
void position_grabbed_object(bool teleport = false);
bool try_grab();

/** Boosts Tux in a certain direction, sideways. Useful for bumpers/walljumping. */
Expand All @@ -243,6 +246,8 @@ class Player final : public MovingObject,
int get_collected_keys() { return m_collected_keys; }
void add_collected_keys(int keynum) { m_collected_keys += keynum; }

bool track_state() const override { return false; }

private:
void handle_input();
void handle_input_ghost(); /**< input handling while in ghost mode */
Expand Down Expand Up @@ -387,7 +392,6 @@ class Player final : public MovingObject,
unsigned int m_idle_stage;

Climbable* m_climbing; /**< Climbable object we are currently climbing, null if none */
std::unique_ptr<ObjectRemoveListener> m_climbing_remove_listener;

int m_ending_direction;
int m_collected_keys;
Expand Down
26 changes: 26 additions & 0 deletions src/supertux/game_object_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,32 @@ GameObjectManager::update_tilemaps()
}
}

void
GameObjectManager::move_object(const UID& uid, GameObjectManager& other)
{
if (&other == this)
return;

auto it = std::find_if(m_gameobjects.begin(), m_gameobjects.end(),
[uid](const auto& obj) {
return obj->get_uid() == uid;
});
if (it == m_gameobjects.end())
{
std::ostringstream err;
err << "Object with UID " << uid << " not found.";
throw std::runtime_error(err.str());
}

this_before_object_remove(**it);
before_object_remove(**it);

other.add_object(std::move(*it));
m_gameobjects.erase(it);

other.flush_game_objects();
}

void
GameObjectManager::toggle_undo_tracking(bool enabled)
{
Expand Down
3 changes: 3 additions & 0 deletions src/supertux/game_object_manager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,9 @@ class GameObjectManager
}
}

/** Move an object to another GameObjectManager. */
void move_object(const UID& uid, GameObjectManager& other);

/** Register a callback to be called once the given name can be
resolved to a UID. Note that this function is only valid in the
construction phase, not during draw() or update() calls, use
Expand Down
26 changes: 5 additions & 21 deletions src/supertux/game_session.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ GameSession::GameSession(const std::string& levelfile_, Savegame& savegame, Stat
m_activated_checkpoint(),
m_newsector(),
m_newspawnpoint(),
m_invincibilitytimeleft(),
m_best_level_statistics(statistics),
m_savegame(savegame),
m_play_time(0),
Expand Down Expand Up @@ -508,28 +507,21 @@ GameSession::update(float dt_sec, const Controller& controller)
}
assert(m_currentsector != nullptr);
m_currentsector->stop_looping_sounds();

sector->activate(m_newspawnpoint);

// Start the new sector's music only if it's different from the current one.
if (current_music != sector->get_singleton_by_type<MusicObject>().get_music())
sector->get_singleton_by_type<MusicObject>().play_music(LEVEL_MUSIC);

m_currentsector = sector;
m_currentsector->play_looping_sounds();

if (is_playing_demo())
{
reset_demo_controller();
}
// Keep persistent across sectors.
if (m_edit_mode)
for (auto* p : m_currentsector->get_players())
p->set_edit_mode(m_edit_mode);

m_newsector = "";
m_newspawnpoint = "";

// Retain invincibility if the player has it.
auto players = m_currentsector->get_players();
for (const auto& player : players)
player->m_invincible_timer.start(m_invincibilitytimeleft[player->get_id()]);
}

// Update the world state and all objects in the world.
Expand Down Expand Up @@ -647,18 +639,10 @@ GameSession::finish(bool win)
}

void
GameSession::respawn(const std::string& sector, const std::string& spawnpoint,
bool retain_invincibility)
GameSession::respawn(const std::string& sector, const std::string& spawnpoint)
{
m_newsector = sector;
m_newspawnpoint = spawnpoint;

m_invincibilitytimeleft.clear();

if (retain_invincibility)
for (const auto* player : Sector::get().get_players())
if (player->is_invincible())
m_invincibilitytimeleft[player->get_id()] = player->m_invincible_timer.get_timeleft();
}

void
Expand Down
6 changes: 1 addition & 5 deletions src/supertux/game_session.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,7 @@ class GameSession final : public Screen,

/** ends the current level */
void finish(bool win = true);
void respawn(const std::string& sectorname, const std::string& spawnpointname,
bool retain_invincibility = false);
void respawn(const std::string& sectorname, const std::string& spawnpointname);
void reset_level();

void set_start_point(const std::string& sectorname,
Expand Down Expand Up @@ -167,9 +166,6 @@ class GameSession final : public Screen,
std::string m_newsector;
std::string m_newspawnpoint;

// Whether the player had invincibility before spawning in a new sector
std::unordered_map<int, float> m_invincibilitytimeleft;

Statistics* m_best_level_statistics;
Savegame& m_savegame;

Expand Down
64 changes: 61 additions & 3 deletions src/supertux/level.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,26 @@

#include "supertux/level.hpp"

#include <numeric>

#include <physfs.h>

#include "badguy/goldbomb.hpp"
#include "editor/editor.hpp"
#include "object/bonus_block.hpp"
#include "object/coin.hpp"
#include "object/player.hpp"
#include "physfs/util.hpp"
#include "supertux/game_session.hpp"
#include "supertux/player_status_hud.hpp"
#include "supertux/savegame.hpp"
#include "supertux/sector.hpp"
#include "trigger/secretarea_trigger.hpp"
#include "util/file_system.hpp"
#include "util/log.hpp"
#include "util/string_util.hpp"
#include "util/writer.hpp"

#include <physfs.h>
#include <numeric>

Level* Level::s_current = nullptr;

Level::Level(bool worldmap) :
Expand Down Expand Up @@ -59,6 +65,47 @@ Level::~Level()
m_sectors.clear();
}

void
Level::initialize()
{
// Get the "main" sector.
Sector* main_sector = get_sector("main");
if (!main_sector)
throw std::runtime_error("No \"main\" sector found.");

m_stats.init(*this);

Savegame* savegame = (Editor::current() && Editor::is_active()) ?
Editor::current()->m_savegame.get() :
GameSession::current() ? &GameSession::current()->get_savegame() : nullptr;

PlayerStatus dummy_player_status(1);
PlayerStatus& player_status = savegame ? savegame->get_player_status() : dummy_player_status;

if (savegame && !m_suppress_pause_menu && !savegame->is_title_screen())
{
for (auto& sector : m_sectors)
sector->add<PlayerStatusHUD>(player_status);
}

for (int id = 0; id < InputManager::current()->get_num_users() || id == 0; id++)
{
if (!InputManager::current()->has_corresponsing_controller(id)
&& !InputManager::current()->m_uses_keyboard[id]
&& savegame
&& !savegame->is_title_screen()
&& id != 0)
continue;

if (id > 0 && !savegame)
dummy_player_status.add_player();

// Add players only in the main sector. Players will be moved between sectors.
main_sector->add<Player>(player_status, "Tux" + (id == 0 ? "" : std::to_string(id + 1)), id);
}
main_sector->flush_game_objects();
}

void
Level::save(std::ostream& stream)
{
Expand Down Expand Up @@ -250,6 +297,17 @@ Level::get_total_secrets() const
return std::accumulate(m_sectors.begin(), m_sectors.end(), 0, get_secret_count);
}

std::vector<Player*>
Level::get_players() const
{
std::vector<Player*> players;
for (const auto& sector : m_sectors)
for (auto& player : sector->get_objects_by_type_index(typeid(Player)))
players.push_back(static_cast<Player*>(player));

return players;
}

void
Level::reactivate()
{
Expand Down
5 changes: 5 additions & 0 deletions src/supertux/level.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

#include "supertux/statistics.hpp"

class Player;
class ReaderMapping;
class Sector;
class Writer;
Expand Down Expand Up @@ -54,6 +55,8 @@ class Level final
Sector* get_sector(size_t num) const;
const std::vector<std::unique_ptr<Sector> >& get_sectors() const { return m_sectors; }

std::vector<Player*> get_players() const;

std::string get_tileset() const { return m_tileset; }

int get_total_coins() const;
Expand All @@ -67,6 +70,8 @@ class Level final
std::string get_license() const { return m_license; }

private:
void initialize();

void save(Writer& writer);
void load_old_format(const ReaderMapping& reader);

Expand Down
4 changes: 3 additions & 1 deletion src/supertux/level_parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ LevelParser::load(const ReaderDocument& doc)
log_warning << "[" << doc.get_filename() << "] level format version " << version << " is not supported" << std::endl;
}

m_level.m_stats.init(m_level);
m_level.initialize();
}

void
Expand All @@ -203,6 +203,8 @@ LevelParser::load_old_format(const ReaderMapping& reader)

auto sector = SectorParser::from_reader_old_format(m_level, reader, m_editable);
m_level.add_sector(std::move(sector));

m_level.initialize();
}

void
Expand Down
Loading
Loading