123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318 |
- // SuperTux - Unstable Tile
- // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
- // Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
- // Copyright (C) 2010 Florian Forster <supertux at octo.it>
- // Copyright (C) 2021 A. Semphris <semphris@protonmail.com>
- // 2023 Vankata453
- //
- // This program is free software: you can redistribute it and/or modify
- // it under the terms of the GNU General Public License as published by
- // the Free Software Foundation, either version 3 of the License, or
- // (at your option) any later version.
- //
- // This program is distributed in the hope that it will be useful,
- // but WITHOUT ANY WARRANTY; without even the implied warranty of
- // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- // GNU General Public License for more details.
- //
- // You should have received a copy of the GNU General Public License
- // along with this program. If not, see <http://www.gnu.org/licenses/>.
- #include "object/unstable_tile.hpp"
- #include "math/random.hpp"
- #include "object/explosion.hpp"
- #include "object/player.hpp"
- #include "sprite/sprite.hpp"
- #include "supertux/constants.hpp"
- #include "supertux/flip_level_transformer.hpp"
- #include "supertux/sector.hpp"
- static const float CRACK_TIME = 0.3f;
- static const float FALL_TIME = 0.8f;
- static const float RESPAWN_TIME = 5.f;
- static const float FADE_OUT_TIME = 1.f;
- static const float FADE_IN_TIME = .5f;
- static const float DELAY_IF_TUX = 0.001f;
- UnstableTile::UnstableTile(const ReaderMapping& mapping, int type) :
- MovingSprite(mapping, "images/objects/unstable_tile/snow.sprite", LAYER_TILES, COLGROUP_STATIC),
- physic(),
- state(STATE_NORMAL),
- slowfall_timer(),
- m_revive_timer(),
- m_respawn(),
- m_alpha(1.f),
- m_original_pos(m_col.get_pos()),
- m_fall_timer(),
- m_player_hit(false)
- {
- if (type >= 0)
- {
- m_type = type;
- on_type_change(TypeChange::INITIAL);
- }
- else
- {
- parse_type(mapping);
- }
- set_action("normal");
- physic.set_gravity_modifier(.98f);
- physic.enable_gravity(false);
- }
- GameObjectTypes
- UnstableTile::get_types() const
- {
- return {
- { "ice", _("Ice") },
- { "brick", _("Brick") },
- { "delayed", _("Delayed") }
- };
- }
- std::string
- UnstableTile::get_default_sprite_name() const
- {
- switch (m_type)
- {
- case BRICK:
- return "images/objects/unstable_tile/brick.sprite";
- case DELAYED:
- return "images/objects/skull_tile/skull_tile.sprite";
- default:
- return m_default_sprite_name;
- }
- }
- HitResponse
- UnstableTile::collision(GameObject& other, const CollisionHit& )
- {
- if (state == STATE_NORMAL)
- {
- Player* player = dynamic_cast<Player*>(&other);
- if (player != nullptr &&
- (player->get_bbox().get_bottom() < m_col.m_bbox.get_top() + SHIFT_DELTA ||
- player->get_bbox().get_top() < m_col.m_bbox.get_bottom() + SHIFT_DELTA))
- {
- if (m_type == DELAYED)
- m_player_hit = true;
- else
- shake();
- }
- if (m_type != DELAYED && dynamic_cast<Explosion*>(&other))
- {
- shake();
- }
- }
- return FORCE_MOVE;
- }
- void
- UnstableTile::shake()
- {
- if (state != STATE_NORMAL)
- return;
- if (m_sprite->has_action("shake"))
- {
- state = STATE_SHAKE;
- set_action("shake", /* loops = */ 1);
- }
- else
- {
- dissolve();
- }
- }
- void
- UnstableTile::dissolve()
- {
- if ((state != STATE_NORMAL) && (state != STATE_SHAKE))
- return;
- if (m_sprite->has_action("dissolve"))
- {
- state = STATE_DISSOLVE;
- set_action("dissolve", /* loops = */ 1);
- }
- else
- {
- slow_fall();
- }
- }
- void
- UnstableTile::slow_fall()
- {
- /* Only enter slow-fall if neither shake nor dissolve is available. */
- if (state != STATE_NORMAL)
- {
- fall_down();
- return;
- }
- if (m_sprite->has_action("fall-down"))
- {
- state = STATE_SLOWFALL;
- set_action("fall-down", /* loops = */ 1);
- physic.set_gravity_modifier(.10f);
- physic.enable_gravity(true);
- m_original_pos = m_col.get_pos();
- slowfall_timer = 0.5f; /* Fall slowly for half a second. */
- }
- else
- {
- state = STATE_FALL;
- }
- }
- void
- UnstableTile::fall_down()
- {
- if (state == STATE_FALL)
- return;
- state = STATE_FALL;
- const bool has_action = m_sprite->has_action("fall-down");
- if (m_type == DELAYED || has_action)
- {
- if (has_action)
- set_action("fall-down", /* loops = */ 1);
- physic.set_gravity_modifier(.98f);
- physic.enable_gravity(true);
- }
- }
- void
- UnstableTile::revive()
- {
- state = STATE_NORMAL;
- set_group(COLGROUP_STATIC);
- physic.enable_gravity(false);
- physic.set_velocity(Vector(0.0f, 0.0f));
- m_col.set_pos(m_original_pos);
- m_col.set_movement(Vector(0.0f, 0.0f));
- m_revive_timer.stop();
- m_respawn.reset(new FadeHelper(&m_alpha, FADE_IN_TIME, 1.f));
- set_action("normal");
- }
- void
- UnstableTile::update(float dt_sec)
- {
- if (m_respawn)
- {
- m_respawn->update(dt_sec);
- if (m_respawn->completed())
- m_respawn.reset();
- }
- switch (state)
- {
- case STATE_NORMAL:
- if (m_type != DELAYED)
- break;
- /** Manage DELAYED type (behaviour of the former SkullTile object). */
- if (m_player_hit)
- {
- set_action("mad");
- if (m_fall_timer.check())
- fall_down();
- else if (!m_fall_timer.started())
- m_fall_timer.start(FALL_TIME);
- else if (m_fall_timer.get_timegone() > CRACK_TIME) // Should perform shake animation.
- m_col.set_pos(m_original_pos + Vector(static_cast<float>(graphicsRandom.rand(-3, 3)), 0.f));
- }
- else
- {
- set_action("normal");
- m_fall_timer.stop();
- m_col.set_pos(m_original_pos);
- }
- m_player_hit = false; // To be updated next frame in collision().
- break;
- case STATE_SHAKE:
- if (m_sprite->animation_done())
- dissolve();
- break;
- case STATE_DISSOLVE:
- if (m_sprite->animation_done()) {
- /* dissolving is done. Set to non-solid. */
- set_group(COLGROUP_DISABLED);
- fall_down();
- }
- break;
- case STATE_SLOWFALL:
- if (slowfall_timer >= dt_sec)
- slowfall_timer -= dt_sec;
- else /* Switch to normal falling procedure */
- fall_down();
- m_col.set_movement(physic.get_movement(dt_sec));
- break;
- case STATE_FALL:
- // TODO: A state enum for when the tile is "dead"?
- m_alpha = std::max(m_alpha - dt_sec / FADE_OUT_TIME, 0.f);
- if (!m_revive_timer.started())
- {
- if (m_revive_timer.check())
- {
- if (Sector::get().is_free_of_movingstatics(Rectf(m_original_pos, get_bbox().get_size()).grown(-1.f)))
- {
- revive();
- }
- else
- {
- m_revive_timer.start(DELAY_IF_TUX);
- }
- }
- else
- {
- m_revive_timer.start(RESPAWN_TIME);
- }
- }
- else if (m_alpha > 0.f)
- {
- m_col.set_movement(physic.get_movement(dt_sec));
- }
- else
- {
- set_group(COLGROUP_DISABLED);
- }
- break;
- }
- }
- void
- UnstableTile::draw(DrawingContext& context)
- {
- // FIXME: This method is more future-proof, but more ugly than simply copying
- // the draw() function from MovingSprite
- context.push_transform();
- context.transform().alpha *= m_alpha;
- MovingSprite::draw(context);
- context.pop_transform();
- }
- void
- UnstableTile::on_flip(float height)
- {
- MovingObject::on_flip(height);
- m_original_pos.y = height - m_original_pos.y - get_bbox().get_height();
- FlipLevelTransformer::transform_flip(m_flip);
- }
- /* EOF */
|