From f5c25d8c8a6ff182ac2e91e4b928228d9f576c95 Mon Sep 17 00:00:00 2001 From: Vankata453 <78196474+Vankata453@users.noreply.github.com> Date: Mon, 17 Jul 2023 21:16:56 +0300 Subject: [PATCH] PowerUp type support, object versioning --- src/badguy/dispenser.cpp | 4 +- src/badguy/dispenser.hpp | 2 +- src/editor/object_menu.cpp | 17 +++ src/editor/object_menu.hpp | 3 +- src/editor/tip.cpp | 20 ++- src/editor/tip.hpp | 1 + src/object/bonus_block.cpp | 18 +-- src/object/bonus_block.hpp | 2 +- src/object/moving_sprite.cpp | 6 + src/object/moving_sprite.hpp | 1 + src/object/powerup.cpp | 199 ++++++++++++++++++++------- src/object/powerup.hpp | 22 ++- src/supertux/colorscheme.cpp | 1 + src/supertux/colorscheme.hpp | 1 + src/supertux/game_object.cpp | 4 + src/supertux/game_object.hpp | 12 ++ src/supertux/game_object_manager.cpp | 12 +- 17 files changed, 251 insertions(+), 74 deletions(-) diff --git a/src/badguy/dispenser.cpp b/src/badguy/dispenser.cpp index 6827e803bef..bbb7d9ed86c 100644 --- a/src/badguy/dispenser.cpp +++ b/src/badguy/dispenser.cpp @@ -75,7 +75,7 @@ Dispenser::Dispenser(const ReaderMapping& reader) : // if (badguys.size() <= 0) // throw std::runtime_error("No badguys in dispenser."); - m_col.m_bbox.set_size(m_sprite->get_current_hitbox_width(), m_sprite->get_current_hitbox_height()); + update_hitbox(); m_countMe = false; } @@ -385,8 +385,8 @@ GameObjectTypes Dispenser::get_types() const { return { - { "cannon", _("cannon") }, { "dropper", _("dropper") }, + { "cannon", _("cannon") }, { "point", _("invisible") } }; } diff --git a/src/badguy/dispenser.hpp b/src/badguy/dispenser.hpp index ebdba937e41..9542d4cef6e 100644 --- a/src/badguy/dispenser.hpp +++ b/src/badguy/dispenser.hpp @@ -28,7 +28,7 @@ class Dispenser final : public BadGuy, { private: enum DispenserType { - CANNON, DROPPER, POINT + DROPPER, CANNON, POINT }; public: diff --git a/src/editor/object_menu.cpp b/src/editor/object_menu.cpp index 0617688a87c..f57c1b48b53 100644 --- a/src/editor/object_menu.cpp +++ b/src/editor/object_menu.cpp @@ -17,6 +17,7 @@ #include "editor/object_menu.hpp" #include "editor/editor.hpp" +#include "gui/dialog.hpp" #include "gui/menu_item.hpp" #include "gui/menu_manager.hpp" #include "supertux/d_scope.hpp" @@ -43,6 +44,12 @@ ObjectMenu::ObjectMenu(GameObject* go, const std::function& } } + if (!m_object->is_up_to_date()) + { + add_hl(); + add_entry(MNID_UPDATE, _("Update")); + } + if (m_remove_function) { add_hl(); @@ -62,6 +69,16 @@ ObjectMenu::menu_action(MenuItem& item) { switch (item.get_id()) { + case MNID_UPDATE: + Dialog::show_confirmation(_("This will update the object to its latest functionality.") + "\n \n" + + _("Keep in mind this is very likely to break the proper behaviour of the object.") + "\n" + + _("Make sure to re-check any behaviour, related to the object."), [this]() { + m_object->update_version(); + m_editor.m_reactivate_request = true; + MenuManager::instance().pop_menu(); + }); + break; + case MNID_REMOVE: m_editor.delete_markers(); m_editor.m_reactivate_request = true; diff --git a/src/editor/object_menu.hpp b/src/editor/object_menu.hpp index 1a7f6642266..229dde9d72a 100644 --- a/src/editor/object_menu.hpp +++ b/src/editor/object_menu.hpp @@ -28,10 +28,11 @@ class ObjectMenu final : public Menu { public: enum { + MNID_UPDATE, MNID_REMOVE, MNID_REMOVEFUNCTION, MNID_TEST_FROM_HERE, - MNID_OPEN_PARTICLE_EDITOR, + MNID_OPEN_PARTICLE_EDITOR }; public: diff --git a/src/editor/tip.cpp b/src/editor/tip.cpp index 7ded5ab3c58..9e64d3cfbc1 100644 --- a/src/editor/tip.cpp +++ b/src/editor/tip.cpp @@ -26,6 +26,7 @@ Tip::Tip(GameObject& object) : m_strings(), + m_warnings(), m_header() { auto os = object.get_settings(); @@ -48,6 +49,10 @@ Tip::Tip(GameObject& object) : } } } + + if (!object.is_up_to_date()) + m_warnings.push_back(_("This object's current functionality is deprecated.") + "\n" + + _("Updating to get its latest functionality is recommended.")); } Tip::Tip(std::string text) : @@ -68,12 +73,21 @@ Tip::draw(DrawingContext& context, const Vector& pos, const bool align_right) auto position = pos; position.y += 35; context.color().draw_text(Resources::normal_font, m_header, position, - align_right ? ALIGN_RIGHT : ALIGN_LEFT, LAYER_GUI + 10, g_config->labeltextcolor); + align_right ? ALIGN_RIGHT : ALIGN_LEFT, LAYER_GUI + 10, g_config->labeltextcolor); + + for (const auto& str : m_strings) + { + position.y += 22; + context.color().draw_text(Resources::normal_font, str, position, + align_right ? ALIGN_RIGHT : ALIGN_LEFT, LAYER_GUI + 10, ColorScheme::Menu::default_color); + } - for (const auto& str : m_strings) { + position.y += 35; + for (const auto& str : m_warnings) + { position.y += 22; context.color().draw_text(Resources::normal_font, str, position, - align_right ? ALIGN_RIGHT : ALIGN_LEFT, LAYER_GUI + 10, ColorScheme::Menu::default_color); + align_right ? ALIGN_RIGHT : ALIGN_LEFT, LAYER_GUI + 10, ColorScheme::Menu::warning_color); } } diff --git a/src/editor/tip.hpp b/src/editor/tip.hpp index 1818967e295..b41f69f887c 100644 --- a/src/editor/tip.hpp +++ b/src/editor/tip.hpp @@ -37,6 +37,7 @@ class Tip final private: std::vector m_strings; + std::vector m_warnings; std::string m_header; private: diff --git a/src/object/bonus_block.cpp b/src/object/bonus_block.cpp index b0eb391da96..73fae8efdcc 100644 --- a/src/object/bonus_block.cpp +++ b/src/object/bonus_block.cpp @@ -374,7 +374,7 @@ BonusBlock::try_open(Player* player) } case Content::POTION: { - Sector::get().add(get_pos(), std::make_unique(get_pos(), "images/powerups/potions/red-potion.sprite")); + Sector::get().add(get_pos(), std::make_unique(get_pos(), PowerUp::FLIP)); break; } case Content::RAIN: @@ -447,25 +447,25 @@ BonusBlock::try_drop(Player *player) case Content::FIREGROW: { - drop_growup_bonus(player, "images/powerups/fireflower/fireflower.sprite", direction, countdown); + drop_growup_bonus(player, PowerUp::FIRE, direction, countdown); break; } case Content::ICEGROW: { - drop_growup_bonus(player, "images/powerups/iceflower/iceflower.sprite", direction, countdown); + drop_growup_bonus(player, PowerUp::ICE, direction, countdown); break; } case Content::AIRGROW: { - drop_growup_bonus(player, "images/powerups/airflower/airflower.sprite", direction, countdown); + drop_growup_bonus(player, PowerUp::AIR, direction, countdown); break; } case Content::EARTHGROW: { - drop_growup_bonus(player, "images/powerups/earthflower/earthflower.sprite", direction, countdown); + drop_growup_bonus(player, PowerUp::EARTH, direction, countdown); break; } @@ -523,7 +523,7 @@ BonusBlock::try_drop(Player *player) } case Content::POTION: { - Sector::get().add(get_pos() + Vector(0, 32), "images/powerups/potions/red-potion.sprite"); + Sector::get().add(get_pos() + Vector(0, 32), PowerUp::FLIP); countdown = true; break; } @@ -570,7 +570,7 @@ BonusBlock::raise_growup_bonus(Player* player, const BonusType& bonus, const Dir } void -BonusBlock::drop_growup_bonus(Player* player, const std::string& bonus_sprite_name, const Direction& dir, bool& countdown) +BonusBlock::drop_growup_bonus(Player* player, int type, const Direction& dir, bool& countdown) { if (player->get_status().bonus[player->get_id()] == NO_BONUS) { @@ -578,7 +578,7 @@ BonusBlock::drop_growup_bonus(Player* player, const std::string& bonus_sprite_na } else { - Sector::get().add(get_pos() + Vector(0, 32), bonus_sprite_name); + Sector::get().add(get_pos() + Vector(0, 32), type); } SoundManager::current()->play("sounds/upgrade.wav", get_pos(), upgrade_sound_gain); countdown = true; @@ -690,7 +690,7 @@ BonusBlock::preload_contents(int d) break; case 12: // Red potion - m_object = std::make_unique(get_pos(), "images/powerups/potions/red-potion.sprite"); + m_object = std::make_unique(get_pos(), PowerUp::FLIP); break; default: diff --git a/src/object/bonus_block.hpp b/src/object/bonus_block.hpp index 9412e033f3d..81c43328cfe 100644 --- a/src/object/bonus_block.hpp +++ b/src/object/bonus_block.hpp @@ -72,7 +72,7 @@ class BonusBlock final : public Block void preload_contents(int d); void raise_growup_bonus(Player* player, const BonusType& bonus, const Direction& dir); - void drop_growup_bonus(Player* player, const std::string& bonus_sprite_name, const Direction& dir, bool& countdown); + void drop_growup_bonus(Player* player, int type, const Direction& dir, bool& countdown); BonusBlock::Content get_content_by_data(int tile_data) const; BonusBlock::Content get_content_from_string(const std::string& contentstring) const; diff --git a/src/object/moving_sprite.cpp b/src/object/moving_sprite.cpp index 0ef3d614743..6d0bd7abb39 100644 --- a/src/object/moving_sprite.cpp +++ b/src/object/moving_sprite.cpp @@ -123,6 +123,12 @@ MovingSprite::on_type_change(int old_type) change_sprite(get_default_sprite_name()); } +bool +MovingSprite::matches_sprite(const std::string& sprite_file) +{ + return m_sprite_name == sprite_file || m_sprite_name == "/" + sprite_file; +} + void MovingSprite::update_hitbox() { diff --git a/src/object/moving_sprite.hpp b/src/object/moving_sprite.hpp index 10ca7d35b9a..3c0374e32c0 100644 --- a/src/object/moving_sprite.hpp +++ b/src/object/moving_sprite.hpp @@ -61,6 +61,7 @@ class MovingSprite : public MovingObject const std::string& get_sprite_name() const { return m_sprite_name; } virtual std::string get_default_sprite_name() const { return m_default_sprite_name; } + bool matches_sprite(const std::string& sprite_file); void change_sprite(const std::string& new_sprite_name); void spawn_explosion_sprites(int count, const std::string& sprite_path); diff --git a/src/object/powerup.cpp b/src/object/powerup.cpp index b7b9fd5d0f5..e0786f5b5bb 100644 --- a/src/object/powerup.cpp +++ b/src/object/powerup.cpp @@ -20,10 +20,10 @@ #include "math/random.hpp" #include "object/player.hpp" #include "object/sprite_particle.hpp" -#include "scripting/level.hpp" #include "sprite/sprite.hpp" #include "sprite/sprite_manager.hpp" #include "supertux/flip_level_transformer.hpp" +#include "supertux/game_session.hpp" #include "supertux/sector.hpp" #include "util/reader_mapping.hpp" @@ -34,46 +34,134 @@ PowerUp::PowerUp(const ReaderMapping& mapping) : no_physics(), lightsprite(SpriteManager::current()->create("images/objects/lightmap_light/lightmap_light-small.sprite")) { + parse_type(mapping); mapping.get("script", script, ""); mapping.get("disable-physics", no_physics, false); initialize(); } -PowerUp::PowerUp(const Vector& pos, const std::string& sprite_name_) : - MovingSprite(pos, sprite_name_, LAYER_OBJECTS, COLGROUP_MOVING), +PowerUp::PowerUp(const Vector& pos, int type) : + MovingSprite(pos, "images/powerups/egg/egg.sprite", LAYER_OBJECTS, COLGROUP_MOVING), physic(), script(), no_physics(false), lightsprite(SpriteManager::current()->create("images/objects/lightmap_light/lightmap_light-small.sprite")) { + m_type = type; + on_type_change(); initialize(); } +GameObjectTypes +PowerUp::get_types() const +{ + return { + { "egg", _("Egg") }, + { "fire", _("Fire Flower") }, + { "ice", _("Ice Flower") }, + { "air", _("Air Flower") }, + { "earth", _("Earth Flower") }, + { "star", _("Star") }, + { "oneup", _("Tux Doll") }, + { "flip", _("Flip Potion") } + }; +} + +std::string +PowerUp::get_default_sprite_name() const +{ + switch (m_type) + { + case FIRE: + return "images/powerups/fireflower/fireflower.sprite"; + case ICE: + return "images/powerups/iceflower/iceflower.sprite"; + case AIR: + return "images/powerups/airflower/airflower.sprite"; + case EARTH: + return "images/powerups/earthflower/earthflower.sprite"; + case STAR: + return "images/powerups/star/star.sprite"; + case ONEUP: + return "images/powerups/1up/1up.sprite"; + case FLIP: + return "images/powerups/potions/red-potion.sprite"; + default: + return "images/powerups/egg/egg.sprite"; + } +} + void PowerUp::initialize() { physic.enable_gravity(true); SoundManager::current()->preload("sounds/grow.ogg"); SoundManager::current()->preload("sounds/fire-flower.wav"); - SoundManager::current()->preload("sounds/gulp.wav"); - //set default light for glow effect for standard sprites + SoundManager::current()->preload("sounds/gulp.wav"); + + // Older levels utilize hardcoded behaviour from the chosen sprite + if (get_version() == 1) + { + if (matches_sprite("images/powerups/egg/egg.sprite")) + m_type = EGG; + else if (matches_sprite("images/powerups/fireflower/fireflower.sprite")) + m_type = FIRE; + else if (matches_sprite("images/powerups/iceflower/iceflower.sprite")) + m_type = ICE; + else if (matches_sprite("images/powerups/airflower/airflower.sprite")) + m_type = AIR; + else if (matches_sprite("images/powerups/earthflower/earthflower.sprite")) + m_type = EARTH; + else if (matches_sprite("images/powerups/star/star.sprite")) + m_type = STAR; + else if (matches_sprite("images/powerups/1up/1up.sprite")) + m_type = ONEUP; + else if (matches_sprite("images/powerups/potions/red-potion.sprite")) + m_type = FLIP; + } + + setup_lightsprite(); +} + +void +PowerUp::setup_lightsprite() +{ lightsprite->set_blend(Blend::ADD); lightsprite->set_color(Color(0.0f, 0.0f, 0.0f)); - if (m_sprite_name == "images/powerups/egg/egg.sprite" || m_sprite_name == "/images/powerups/egg/egg.sprite") { - lightsprite->set_color(Color(0.2f, 0.2f, 0.0f)); - } else if (m_sprite_name == "images/powerups/fireflower/fireflower.sprite" || m_sprite_name == "/images/powerups/fireflower/fireflower.sprite") { - lightsprite->set_color(Color(0.3f, 0.0f, 0.0f)); - } else if (m_sprite_name == "images/powerups/iceflower/iceflower.sprite" || m_sprite_name == "/images/powerups/iceflower/iceflower.sprite") { - lightsprite->set_color(Color(0.0f, 0.1f, 0.2f)); - } else if (m_sprite_name == "images/powerups/airflower/airflower.sprite" || m_sprite_name == "/images/powerups/airflower/airflower.sprite") { - lightsprite->set_color(Color(0.15f, 0.0f, 0.15f)); - } else if (m_sprite_name == "images/powerups/earthflower/earthflower.sprite" || m_sprite_name == "/images/powerups/earthflower/earthflower.sprite") { - lightsprite->set_color(Color(0.0f, 0.3f, 0.0f)); - } else if (m_sprite_name == "images/powerups/star/star.sprite" || m_sprite_name == "/images/powerups/star/star.sprite") { - lightsprite->set_color(Color(0.4f, 0.4f, 0.4f)); + // Set default light for glow effect for default sprites. + if (matches_sprite(get_default_sprite_name())) + { + switch (m_type) + { + case EGG: + lightsprite->set_color(Color(0.2f, 0.2f, 0.0f)); + break; + case FIRE: + lightsprite->set_color(Color(0.3f, 0.0f, 0.0f)); + break; + case ICE: + lightsprite->set_color(Color(0.0f, 0.1f, 0.2f)); + break; + case AIR: + lightsprite->set_color(Color(0.15f, 0.0f, 0.15f)); + break; + case EARTH: + lightsprite->set_color(Color(0.0f, 0.3f, 0.0f)); + break; + case STAR: + lightsprite->set_color(Color(0.4f, 0.4f, 0.4f)); + break; + } } } +void +PowerUp::after_editor_set() +{ + MovingSprite::after_editor_set(); + setup_lightsprite(); +} + void PowerUp::collision_solid(const CollisionHit& hit) { @@ -89,49 +177,56 @@ HitResponse PowerUp::collision(GameObject& other, const CollisionHit&) { Player* player = dynamic_cast(&other); - if (player == nullptr) + if (!player) return FORCE_MOVE; - if (m_sprite_name == "images/powerups/potions/blue-potion.sprite" || - m_sprite_name == "images/powerups/potions/red-potion.sprite" || - m_sprite_name == "/images/powerups/potions/blue-potion.sprite" || - m_sprite_name == "/images/powerups/potions/red-potion.sprite") { - SoundManager::current()->play("sounds/gulp.wav", get_pos()); - } + if (m_type == FLIP) + SoundManager::current()->play("sounds/gulp.wav", get_pos()); - if (!script.empty()) { + if (!script.empty()) + { Sector::get().run_script(script, "powerup-script"); remove_me(); return ABORT_MOVE; } - // some defaults if no script has been set - if (m_sprite_name == "images/powerups/egg/egg.sprite" || m_sprite_name == "/images/powerups/egg/egg.sprite") { - if (!player->add_bonus(GROWUP_BONUS, true)) - return FORCE_MOVE; - SoundManager::current()->play("sounds/grow.ogg", get_pos()); - } else if (m_sprite_name == "images/powerups/fireflower/fireflower.sprite" || m_sprite_name == "/images/powerups/fireflower/fireflower.sprite") { - if (!player->add_bonus(FIRE_BONUS, true)) - return FORCE_MOVE; - SoundManager::current()->play("sounds/fire-flower.wav", get_pos()); - } else if (m_sprite_name == "images/powerups/iceflower/iceflower.sprite" || m_sprite_name == "/images/powerups/iceflower/iceflower.sprite") { - if (!player->add_bonus(ICE_BONUS, true)) - return FORCE_MOVE; - SoundManager::current()->play("sounds/fire-flower.wav", get_pos()); - } else if (m_sprite_name == "images/powerups/airflower/airflower.sprite" || m_sprite_name == "/images/powerups/airflower/airflower.sprite") { - if (!player->add_bonus(AIR_BONUS, true)) - return FORCE_MOVE; - SoundManager::current()->play("sounds/fire-flower.wav", get_pos()); - } else if (m_sprite_name == "images/powerups/earthflower/earthflower.sprite" || m_sprite_name == "/images/powerups/earthflower/earthflower.sprite") { - if (!player->add_bonus(EARTH_BONUS, true)) - return FORCE_MOVE; - SoundManager::current()->play("sounds/fire-flower.wav", get_pos()); - } else if (m_sprite_name == "images/powerups/star/star.sprite" || m_sprite_name == "/images/powerups/star/star.sprite") { - player->make_invincible(); - } else if (m_sprite_name == "images/powerups/1up/1up.sprite" || m_sprite_name == "/images/powerups/1up/1up.sprite") { - player->get_status().add_coins(100); - } else if (m_sprite_name == "images/powerups/potions/red-potion.sprite" || m_sprite_name == "/images/powerups/potions/red-potion.sprite") { - scripting::Level_flip_vertically(); + switch (m_type) + { + case EGG: + if (!player->add_bonus(GROWUP_BONUS, true)) + return FORCE_MOVE; + SoundManager::current()->play("sounds/grow.ogg", get_pos()); + break; + case FIRE: + if (!player->add_bonus(FIRE_BONUS, true)) + return FORCE_MOVE; + SoundManager::current()->play("sounds/fire-flower.wav", get_pos()); + break; + case ICE: + if (!player->add_bonus(ICE_BONUS, true)) + return FORCE_MOVE; + SoundManager::current()->play("sounds/fire-flower.wav", get_pos()); + break; + case AIR: + if (!player->add_bonus(AIR_BONUS, true)) + return FORCE_MOVE; + SoundManager::current()->play("sounds/fire-flower.wav", get_pos()); + break; + case EARTH: + if (!player->add_bonus(EARTH_BONUS, true)) + return FORCE_MOVE; + SoundManager::current()->play("sounds/fire-flower.wav", get_pos()); + break; + case STAR: + player->make_invincible(); + break; + case ONEUP: + player->get_status().add_coins(100); + break; + case FLIP: + FlipLevelTransformer flip_transformer; + flip_transformer.transform(GameSession::current()->get_current_level()); + break; } remove_me(); diff --git a/src/object/powerup.hpp b/src/object/powerup.hpp index 9204f421ee3..5f5d3bf2ed5 100644 --- a/src/object/powerup.hpp +++ b/src/object/powerup.hpp @@ -24,7 +24,10 @@ class PowerUp final : public MovingSprite { public: PowerUp(const ReaderMapping& mapping); - PowerUp(const Vector& pos, const std::string& sprite_name); + PowerUp(const Vector& pos, int type); + + GameObjectTypes get_types() const override; + std::string get_default_sprite_name() const override; virtual void update(float dt_sec) override; virtual void draw(DrawingContext& context) override; @@ -37,11 +40,26 @@ class PowerUp final : public MovingSprite static std::string display_name() { return _("Powerup"); } virtual std::string get_display_name() const override { return display_name(); } + int get_latest_version() const override { return 2; } virtual ObjectSettings get_settings() override; + virtual void after_editor_set() override; private: /** Initialize power up sprites and other defaults */ - virtual void initialize(); + void initialize(); + void setup_lightsprite(); + +public: + enum Type { + EGG, + FIRE, + ICE, + AIR, + EARTH, + STAR, + ONEUP, + FLIP + }; private: Physic physic; diff --git a/src/supertux/colorscheme.cpp b/src/supertux/colorscheme.cpp index e64848bf4f9..a53836f0abf 100644 --- a/src/supertux/colorscheme.cpp +++ b/src/supertux/colorscheme.cpp @@ -48,6 +48,7 @@ Color ColorScheme::Menu::active_color(0.4f,0.66f,1.f); Color ColorScheme::Menu::inactive_color(0.5f,0.5f,0.5f); Color ColorScheme::Menu::label_color(0.f,1.f,1.f); Color ColorScheme::Menu::field_color(1.f,1.f,0.6f); +Color ColorScheme::Menu::warning_color(1.f,1.f,0.6f); Color PlayerStatusHUD::text_color(1.f,1.f,0.6f); diff --git a/src/supertux/colorscheme.hpp b/src/supertux/colorscheme.hpp index 3451f4c0f9c..0a0eb8cc25f 100644 --- a/src/supertux/colorscheme.hpp +++ b/src/supertux/colorscheme.hpp @@ -35,6 +35,7 @@ class ColorScheme final static Color inactive_color; static Color label_color; static Color field_color; + static Color warning_color; }; class Text diff --git a/src/supertux/game_object.cpp b/src/supertux/game_object.cpp index 497db22958d..6792e059403 100644 --- a/src/supertux/game_object.cpp +++ b/src/supertux/game_object.cpp @@ -31,6 +31,7 @@ GameObject::GameObject() : m_fade_helpers(), m_track_undo(true), m_previous_type(-1), + m_version(1), m_uid(), m_scheduled_for_removal(false), m_last_state(), @@ -46,6 +47,7 @@ GameObject::GameObject(const std::string& name) : m_fade_helpers(), m_track_undo(true), m_previous_type(-1), + m_version(1), m_uid(), m_scheduled_for_removal(false), m_last_state(), @@ -58,6 +60,7 @@ GameObject::GameObject(const ReaderMapping& reader) : GameObject() { reader.get("name", m_name, ""); + reader.get("version", m_version, 1); } GameObject::~GameObject() @@ -108,6 +111,7 @@ GameObject::get_settings() { ObjectSettings result(get_display_name()); + result.add_int(_("Version"), &m_version, "version", 1, OPTION_HIDDEN); result.add_text(_("Name"), &m_name, "name", std::string()); const GameObjectTypes types = get_types(); diff --git a/src/supertux/game_object.hpp b/src/supertux/game_object.hpp index 843e6202d53..1ba357cce48 100644 --- a/src/supertux/game_object.hpp +++ b/src/supertux/game_object.hpp @@ -87,6 +87,12 @@ class GameObject virtual std::string get_class_name() const { return "game-object"; } virtual std::string get_display_name() const { return _("Unknown object"); } + /** Version checking/updating */ + int get_version() const { return m_version; } + virtual int get_latest_version() const { return 1; } + bool is_up_to_date() const { return m_version >= get_latest_version(); } + virtual void update_version() { m_version = get_latest_version(); } + /** If true only a single object of this type is allowed in a given GameObjectManager */ virtual bool is_singleton() const { return false; } @@ -226,6 +232,12 @@ class GameObject Used to check if the type has changed. **/ int m_previous_type; + /** Indicates the object's version. By default, this is equal to 1. + Useful for retaining retrocompatibility for objects, whilst allowing for + updated behaviour in newer levels. + The version of an object can be updated from the editor. */ + int m_version; + /** A unique id for the object to safely refer to it. This will be set by the GameObjectManager. */ UID m_uid; diff --git a/src/supertux/game_object_manager.cpp b/src/supertux/game_object_manager.cpp index 17b0490b35c..aa4169ab6fc 100644 --- a/src/supertux/game_object_manager.cpp +++ b/src/supertux/game_object_manager.cpp @@ -128,9 +128,15 @@ GameObjectManager::add_object(std::unique_ptr object) } #endif - // Attempt to add object to editor layers - if (m_initialized && Editor::is_active()) - Editor::current()->add_layer(object.get()); + if (m_initialized) + { + // Attempt to add object to editor layers + if (Editor::current()) + Editor::current()->add_layer(object.get()); + + // Any objects added after initialization should be on their latest version + object->update_version(); + } GameObject& tmp = *object; m_gameobjects_new.push_back(std::move(object));