123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411 |
- // SuperTux - A Jump'n Run
- // Copyright (C) 2004 Ingo Ruhnke <grumbel@gmail.com>
- // Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
- //
- // 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 "worldmap/tux.hpp"
- #include <sstream>
- #include "control/input_manager.hpp"
- #include "editor/editor.hpp"
- #include "sprite/sprite.hpp"
- #include "sprite/sprite_manager.hpp"
- #include "supertux/savegame.hpp"
- #include "supertux/tile.hpp"
- #include "util/log.hpp"
- #include "worldmap/camera.hpp"
- #include "worldmap/direction.hpp"
- #include "worldmap/level_tile.hpp"
- #include "worldmap/special_tile.hpp"
- #include "worldmap/sprite_change.hpp"
- #include "worldmap/teleporter.hpp"
- #include "worldmap/worldmap.hpp"
- namespace worldmap {
- static const float TUXSPEED = 200;
- static const float map_message_TIME = 2.8f;
- Tux::Tux(WorldMap* worldmap) :
- m_back_direction(),
- m_worldmap(worldmap),
- m_sprite(SpriteManager::current()->create(m_worldmap->get_savegame().get_player_status().worldmap_sprite)),
- m_controller(InputManager::current()->get_controller()),
- m_input_direction(Direction::NONE),
- m_direction(Direction::NONE),
- m_initial_tile_pos(),
- m_tile_pos(),
- m_offset(0),
- m_moving(false),
- m_ghost_mode(false)
- {
- }
- void
- Tux::draw(DrawingContext& context)
- {
- if (m_worldmap->get_sector().get_camera().is_panning()) return;
- std::string action = get_action_prefix_for_bonus(m_worldmap->get_savegame().get_player_status().bonus[0]);
- if (!action.empty())
- {
- if (m_moving && (get_axis().x != 0 || get_axis().y != 0))
- {
- std::string direct = "-up";
- if (get_axis().x == 1) direct = "-right";
- if (get_axis().x == -1) direct = "-left";
- if (get_axis().y == 1) direct = "-up";
- if (get_axis().y == -1) direct = "-down";
- if (m_sprite->has_action(action + "-walking" + direct))
- {
- m_sprite->set_action(action + "-walking" + direct);
- }
- else if (m_sprite->has_action(action + "-walking"))
- {
- m_sprite->set_action(action + "-walking");
- }
- // else, keep the same animation that was already playing
- }
- else
- {
- m_sprite->set_action(action + "-stop");
- }
- }
- else
- {
- log_debug << "Bonus type not handled in worldmap." << std::endl;
- m_sprite->set_action("large-stop");
- }
- m_sprite->draw(context.color(), get_pos(), LAYER_OBJECTS + 1);
- }
- std::string
- Tux::get_action_prefix_for_bonus(const BonusType& bonus) const
- {
- if (bonus == GROWUP_BONUS)
- return "large";
- if (bonus == FIRE_BONUS)
- return "fire";
- if (bonus == ICE_BONUS)
- return "ice";
- if (bonus == AIR_BONUS)
- return "air";
- if (bonus == EARTH_BONUS)
- return "earth";
- if (bonus == NO_BONUS)
- return "small";
- return "";
- }
- Vector
- Tux::get_pos() const
- {
- float x = m_tile_pos.x * 32;
- float y = m_tile_pos.y * 32;
- switch (m_direction)
- {
- case Direction::WEST:
- x -= m_offset - 32;
- break;
- case Direction::EAST:
- x += m_offset - 32;
- break;
- case Direction::NORTH:
- y -= m_offset - 32;
- break;
- case Direction::SOUTH:
- y += m_offset - 32;
- break;
- case Direction::NONE:
- break;
- }
- return Vector(x, y);
- }
- Vector
- Tux::get_axis() const
- {
- float x = 0.0f;
- float y = 0.0f;
- switch (m_direction)
- {
- case Direction::WEST:
- x = -1.0f;
- break;
- case Direction::EAST:
- x = 1.0f;
- break;
- case Direction::NORTH:
- y = 1.0f;
- break;
- case Direction::SOUTH:
- y = -1.0f;
- break;
- case Direction::NONE:
- break;
- }
- return Vector(x, y);
- }
- void
- Tux::stop()
- {
- m_offset = 0;
- m_direction = Direction::NONE;
- m_input_direction = Direction::NONE;
- m_moving = false;
- }
- void
- Tux::set_direction(Direction dir)
- {
- m_input_direction = dir;
- }
- void
- Tux::set_ghost_mode(bool enabled)
- {
- m_ghost_mode = enabled;
- }
- bool
- Tux::get_ghost_mode() const
- {
- return m_ghost_mode;
- }
- void
- Tux::try_start_walking()
- {
- if (m_moving)
- return;
- if (m_input_direction == Direction::NONE)
- return;
- auto level = m_worldmap->get_sector().at_object<LevelTile>();
- // We got a new direction, so lets start walking when possible
- Vector next_tile(0.0f, 0.0f);
- if ((!level || level->is_solved() || level->is_perfect()
- || (Editor::current() && Editor::current()->is_testing_level()))
- && m_worldmap->get_sector().path_ok(m_input_direction, m_tile_pos, &next_tile)) {
- m_tile_pos = next_tile;
- m_moving = true;
- m_direction = m_input_direction;
- m_back_direction = reverse_dir(m_direction);
- } else if (m_ghost_mode || (m_input_direction == m_back_direction)) {
- m_moving = true;
- m_direction = m_input_direction;
- m_tile_pos = m_worldmap->get_sector().get_next_tile(m_tile_pos, m_direction);
- m_back_direction = reverse_dir(m_direction);
- }
- }
- bool
- Tux::can_walk(int tile_data, Direction dir) const
- {
- return m_ghost_mode ||
- ((tile_data & Tile::WORLDMAP_NORTH && dir == Direction::NORTH) ||
- (tile_data & Tile::WORLDMAP_SOUTH && dir == Direction::SOUTH) ||
- (tile_data & Tile::WORLDMAP_EAST && dir == Direction::EAST) ||
- (tile_data & Tile::WORLDMAP_WEST && dir == Direction::WEST));
- }
- void
- Tux::change_sprite(SpriteChange* sprite_change)
- {
- //SpriteChange* sprite_change = m_worldmap->at_object<SpriteChange>();
- if (sprite_change != nullptr) {
- m_sprite = sprite_change->clone_sprite();
- sprite_change->clear_stay_action();
- m_worldmap->get_savegame().get_player_status().worldmap_sprite = sprite_change->get_sprite_name();
- }
- }
- void
- Tux::try_continue_walking(float dt_sec)
- {
- if (!m_moving)
- return;
- // Let tux walk
- m_offset += TUXSPEED * dt_sec;
- // Do nothing if we have not yet reached the next tile
- if (m_offset <= 32)
- return;
- m_offset -= 32;
- auto worldmap_sector = &m_worldmap->get_sector();
- auto sprite_change = worldmap_sector->at_object<SpriteChange>(m_tile_pos);
- change_sprite(sprite_change);
- // if this is a special_tile with passive_message, display it
- auto special_tile = worldmap_sector->at_object<SpecialTile>();
- if (special_tile)
- {
- // direction and the apply_action_ are opposites, since they "see"
- // directions in a different way
- if ((m_direction == Direction::NORTH && special_tile->get_apply_action_south()) ||
- (m_direction == Direction::SOUTH && special_tile->get_apply_action_north()) ||
- (m_direction == Direction::WEST && special_tile->get_apply_action_east()) ||
- (m_direction == Direction::EAST && special_tile->get_apply_action_west()))
- {
- process_special_tile(special_tile);
- }
- }
- // check if we are at a Teleporter
- auto teleporter = worldmap_sector->at_object<Teleporter>(m_tile_pos);
- // stop if we reached a level, a WORLDMAP_STOP tile, a teleporter or a special tile without a passive_message
- if ((worldmap_sector->at_object<LevelTile>()) ||
- (worldmap_sector->tile_data_at(m_tile_pos) & Tile::WORLDMAP_STOP) ||
- (special_tile && !special_tile->is_passive_message() && special_tile->get_script().empty()) ||
- (teleporter) ||
- m_ghost_mode)
- {
- if (special_tile && !special_tile->get_map_message().empty() && !special_tile->is_passive_message()) {
- m_worldmap->set_passive_message({}, 0.0f);
- }
- stop();
- return;
- }
- // if user wants to change direction, try changing, else guess the direction in which to walk next
- const int tile_data = worldmap_sector->tile_data_at(m_tile_pos);
- if ((m_direction != m_input_direction) && can_walk(tile_data, m_input_direction)) {
- m_direction = m_input_direction;
- m_back_direction = reverse_dir(m_direction);
- } else {
- Direction dir = Direction::NONE;
- if (tile_data & Tile::WORLDMAP_NORTH && m_back_direction != Direction::NORTH)
- dir = Direction::NORTH;
- else if (tile_data & Tile::WORLDMAP_SOUTH && m_back_direction != Direction::SOUTH)
- dir = Direction::SOUTH;
- else if (tile_data & Tile::WORLDMAP_EAST && m_back_direction != Direction::EAST)
- dir = Direction::EAST;
- else if (tile_data & Tile::WORLDMAP_WEST && m_back_direction != Direction::WEST)
- dir = Direction::WEST;
- if (dir == Direction::NONE) {
- // Should never be reached if tiledata is good
- log_warning << "Could not determine where to walk next" << std::endl;
- stop();
- return;
- }
- m_direction = dir;
- m_input_direction = m_direction;
- m_back_direction = reverse_dir(m_direction);
- }
- // Walk automatically to the next tile
- if (m_direction == Direction::NONE)
- return;
- Vector next_tile(0.0f, 0.0f);
- if (!m_ghost_mode && !worldmap_sector->path_ok(m_direction, m_tile_pos, &next_tile)) {
- log_debug << "Tilemap data is buggy" << std::endl;
- stop();
- return;
- }
- auto next_sprite = worldmap_sector->at_object<SpriteChange>(next_tile);
- if (next_sprite != nullptr && next_sprite->change_on_touch()) {
- change_sprite(next_sprite);
- }
- //SpriteChange* last_sprite = m_worldmap->at_object<SpriteChange>(next_tile);
- if (sprite_change != nullptr && next_sprite != nullptr) {
- log_debug << "Old: " << m_tile_pos << " New: " << next_tile << std::endl;
- sprite_change->set_stay_action();
- }
- m_tile_pos = next_tile;
- }
- void
- Tux::update_input_direction()
- {
- if (m_controller.hold(Control::UP))
- m_input_direction = Direction::NORTH;
- else if (m_controller.hold(Control::DOWN))
- m_input_direction = Direction::SOUTH;
- else if (m_controller.hold(Control::LEFT))
- m_input_direction = Direction::WEST;
- else if (m_controller.hold(Control::RIGHT))
- m_input_direction = Direction::EAST;
- }
- void
- Tux::update(float dt_sec)
- {
- if (m_worldmap->get_sector().get_camera().is_panning()) return;
- update_input_direction();
- if (m_moving)
- try_continue_walking(dt_sec);
- else
- try_start_walking();
- }
- void
- Tux::setup()
- {
- // Set initial tile position, if provided
- if (m_initial_tile_pos != Vector())
- m_tile_pos = m_initial_tile_pos;
- // check if we already touch a SpriteChange object
- auto sprite_change = m_worldmap->get_sector().at_object<SpriteChange>(m_tile_pos);
- change_sprite(sprite_change);
- }
- void
- Tux::process_special_tile(SpecialTile* special_tile)
- {
- if (!special_tile) {
- return;
- }
- if (special_tile->is_passive_message())
- m_worldmap->set_passive_message(special_tile->get_map_message(), map_message_TIME);
- if (!special_tile->get_script().empty())
- {
- try
- {
- m_worldmap->get_sector().run_script(special_tile->get_script(), "specialtile");
- }
- catch(std::exception& e)
- {
- log_warning << "Couldn't execute special tile script: " << e.what()
- << std::endl;
- }
- }
- }
- } // namespace worldmap
- /* EOF */
|