tux.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. // SuperTux - A Jump'n Run
  2. // Copyright (C) 2004 Ingo Ruhnke <grumbel@gmail.com>
  3. // Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
  4. //
  5. // This program is free software: you can redistribute it and/or modify
  6. // it under the terms of the GNU General Public License as published by
  7. // the Free Software Foundation, either version 3 of the License, or
  8. // (at your option) any later version.
  9. //
  10. // This program is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. // GNU General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU General Public License
  16. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. #include "worldmap/tux.hpp"
  18. #include <sstream>
  19. #include "control/input_manager.hpp"
  20. #include "editor/editor.hpp"
  21. #include "sprite/sprite.hpp"
  22. #include "sprite/sprite_manager.hpp"
  23. #include "supertux/savegame.hpp"
  24. #include "supertux/tile.hpp"
  25. #include "util/log.hpp"
  26. #include "worldmap/camera.hpp"
  27. #include "worldmap/direction.hpp"
  28. #include "worldmap/level_tile.hpp"
  29. #include "worldmap/special_tile.hpp"
  30. #include "worldmap/sprite_change.hpp"
  31. #include "worldmap/teleporter.hpp"
  32. #include "worldmap/worldmap.hpp"
  33. namespace worldmap {
  34. static const float TUXSPEED = 200;
  35. static const float map_message_TIME = 2.8f;
  36. Tux::Tux(WorldMap* worldmap) :
  37. m_back_direction(),
  38. m_worldmap(worldmap),
  39. m_sprite(SpriteManager::current()->create(m_worldmap->get_savegame().get_player_status().worldmap_sprite)),
  40. m_controller(InputManager::current()->get_controller()),
  41. m_input_direction(Direction::NONE),
  42. m_direction(Direction::NONE),
  43. m_initial_tile_pos(),
  44. m_tile_pos(),
  45. m_offset(0),
  46. m_moving(false),
  47. m_ghost_mode(false)
  48. {
  49. }
  50. void
  51. Tux::draw(DrawingContext& context)
  52. {
  53. if (m_worldmap->get_sector().get_camera().is_panning()) return;
  54. std::string action = get_action_prefix_for_bonus(m_worldmap->get_savegame().get_player_status().bonus[0]);
  55. if (!action.empty())
  56. {
  57. if (m_moving && (get_axis().x != 0 || get_axis().y != 0))
  58. {
  59. std::string direct = "-up";
  60. if (get_axis().x == 1) direct = "-right";
  61. if (get_axis().x == -1) direct = "-left";
  62. if (get_axis().y == 1) direct = "-up";
  63. if (get_axis().y == -1) direct = "-down";
  64. if (m_sprite->has_action(action + "-walking" + direct))
  65. {
  66. m_sprite->set_action(action + "-walking" + direct);
  67. }
  68. else if (m_sprite->has_action(action + "-walking"))
  69. {
  70. m_sprite->set_action(action + "-walking");
  71. }
  72. // else, keep the same animation that was already playing
  73. }
  74. else
  75. {
  76. m_sprite->set_action(action + "-stop");
  77. }
  78. }
  79. else
  80. {
  81. log_debug << "Bonus type not handled in worldmap." << std::endl;
  82. m_sprite->set_action("large-stop");
  83. }
  84. m_sprite->draw(context.color(), get_pos(), LAYER_OBJECTS + 1);
  85. }
  86. std::string
  87. Tux::get_action_prefix_for_bonus(const BonusType& bonus) const
  88. {
  89. if (bonus == BONUS_GROWUP)
  90. return "large";
  91. if (bonus == BONUS_FIRE)
  92. return "fire";
  93. if (bonus == BONUS_ICE)
  94. return "ice";
  95. if (bonus == BONUS_AIR)
  96. return "air";
  97. if (bonus == BONUS_EARTH)
  98. return "earth";
  99. if (bonus == BONUS_NONE)
  100. return "small";
  101. return "";
  102. }
  103. Vector
  104. Tux::get_pos() const
  105. {
  106. float x = m_tile_pos.x * 32;
  107. float y = m_tile_pos.y * 32;
  108. switch (m_direction)
  109. {
  110. case Direction::WEST:
  111. x -= m_offset - 32;
  112. break;
  113. case Direction::EAST:
  114. x += m_offset - 32;
  115. break;
  116. case Direction::NORTH:
  117. y -= m_offset - 32;
  118. break;
  119. case Direction::SOUTH:
  120. y += m_offset - 32;
  121. break;
  122. case Direction::NONE:
  123. break;
  124. }
  125. return Vector(x, y);
  126. }
  127. Vector
  128. Tux::get_axis() const
  129. {
  130. float x = 0.0f;
  131. float y = 0.0f;
  132. switch (m_direction)
  133. {
  134. case Direction::WEST:
  135. x = -1.0f;
  136. break;
  137. case Direction::EAST:
  138. x = 1.0f;
  139. break;
  140. case Direction::NORTH:
  141. y = 1.0f;
  142. break;
  143. case Direction::SOUTH:
  144. y = -1.0f;
  145. break;
  146. case Direction::NONE:
  147. break;
  148. }
  149. return Vector(x, y);
  150. }
  151. void
  152. Tux::stop()
  153. {
  154. m_offset = 0;
  155. m_direction = Direction::NONE;
  156. m_input_direction = Direction::NONE;
  157. m_moving = false;
  158. }
  159. void
  160. Tux::try_start_walking()
  161. {
  162. if (m_moving)
  163. return;
  164. if (m_input_direction == Direction::NONE)
  165. return;
  166. auto level = m_worldmap->get_sector().at_object<LevelTile>();
  167. // We got a new direction, so lets start walking when possible
  168. Vector next_tile(0.0f, 0.0f);
  169. if ((!level || level->is_solved() || level->is_perfect()
  170. || (Editor::current() && Editor::current()->is_testing_level()))
  171. && m_worldmap->get_sector().path_ok(m_input_direction, m_tile_pos, &next_tile)) {
  172. m_tile_pos = next_tile;
  173. m_moving = true;
  174. m_direction = m_input_direction;
  175. m_back_direction = reverse_dir(m_direction);
  176. } else if (m_ghost_mode || (m_input_direction == m_back_direction)) {
  177. m_moving = true;
  178. m_direction = m_input_direction;
  179. m_tile_pos = m_worldmap->get_sector().get_next_tile(m_tile_pos, m_direction);
  180. m_back_direction = reverse_dir(m_direction);
  181. }
  182. }
  183. bool
  184. Tux::can_walk(int tile_data, Direction dir) const
  185. {
  186. return m_ghost_mode ||
  187. ((tile_data & Tile::WORLDMAP_NORTH && dir == Direction::NORTH) ||
  188. (tile_data & Tile::WORLDMAP_SOUTH && dir == Direction::SOUTH) ||
  189. (tile_data & Tile::WORLDMAP_EAST && dir == Direction::EAST) ||
  190. (tile_data & Tile::WORLDMAP_WEST && dir == Direction::WEST));
  191. }
  192. void
  193. Tux::change_sprite(SpriteChange* sprite_change)
  194. {
  195. //SpriteChange* sprite_change = m_worldmap->at_object<SpriteChange>();
  196. if (sprite_change != nullptr) {
  197. m_sprite = sprite_change->clone_sprite();
  198. sprite_change->clear_stay_action();
  199. m_worldmap->get_savegame().get_player_status().worldmap_sprite = sprite_change->get_sprite_name();
  200. }
  201. }
  202. void
  203. Tux::try_continue_walking(float dt_sec)
  204. {
  205. if (!m_moving)
  206. return;
  207. // Let tux walk
  208. m_offset += TUXSPEED * dt_sec;
  209. // Do nothing if we have not yet reached the next tile
  210. if (m_offset <= 32)
  211. return;
  212. m_offset -= 32;
  213. auto worldmap_sector = &m_worldmap->get_sector();
  214. auto sprite_change = worldmap_sector->at_object<SpriteChange>(m_tile_pos);
  215. change_sprite(sprite_change);
  216. // if this is a special_tile with passive_message, display it
  217. auto special_tile = worldmap_sector->at_object<SpecialTile>();
  218. if (special_tile)
  219. {
  220. // direction and the apply_action_ are opposites, since they "see"
  221. // directions in a different way
  222. if ((m_direction == Direction::NORTH && special_tile->get_apply_action_south()) ||
  223. (m_direction == Direction::SOUTH && special_tile->get_apply_action_north()) ||
  224. (m_direction == Direction::WEST && special_tile->get_apply_action_east()) ||
  225. (m_direction == Direction::EAST && special_tile->get_apply_action_west()))
  226. {
  227. process_special_tile(special_tile);
  228. }
  229. }
  230. // check if we are at a Teleporter
  231. auto teleporter = worldmap_sector->at_object<Teleporter>(m_tile_pos);
  232. // stop if we reached a level, a WORLDMAP_STOP tile, a teleporter or a special tile without a passive_message
  233. if ((worldmap_sector->at_object<LevelTile>()) ||
  234. (worldmap_sector->tile_data_at(m_tile_pos) & Tile::WORLDMAP_STOP) ||
  235. (special_tile && !special_tile->is_passive_message() && special_tile->get_script().empty()) ||
  236. (teleporter) ||
  237. m_ghost_mode)
  238. {
  239. if (special_tile && !special_tile->get_map_message().empty() && !special_tile->is_passive_message()) {
  240. m_worldmap->set_passive_message({}, 0.0f);
  241. }
  242. stop();
  243. return;
  244. }
  245. // if user wants to change direction, try changing, else guess the direction in which to walk next
  246. const int tile_data = worldmap_sector->tile_data_at(m_tile_pos);
  247. if ((m_direction != m_input_direction) && can_walk(tile_data, m_input_direction)) {
  248. m_direction = m_input_direction;
  249. m_back_direction = reverse_dir(m_direction);
  250. } else {
  251. Direction dir = Direction::NONE;
  252. if (tile_data & Tile::WORLDMAP_NORTH && m_back_direction != Direction::NORTH)
  253. dir = Direction::NORTH;
  254. else if (tile_data & Tile::WORLDMAP_SOUTH && m_back_direction != Direction::SOUTH)
  255. dir = Direction::SOUTH;
  256. else if (tile_data & Tile::WORLDMAP_EAST && m_back_direction != Direction::EAST)
  257. dir = Direction::EAST;
  258. else if (tile_data & Tile::WORLDMAP_WEST && m_back_direction != Direction::WEST)
  259. dir = Direction::WEST;
  260. if (dir == Direction::NONE) {
  261. // Should never be reached if tiledata is good
  262. log_warning << "Could not determine where to walk next" << std::endl;
  263. stop();
  264. return;
  265. }
  266. m_direction = dir;
  267. m_input_direction = m_direction;
  268. m_back_direction = reverse_dir(m_direction);
  269. }
  270. // Walk automatically to the next tile
  271. if (m_direction == Direction::NONE)
  272. return;
  273. Vector next_tile(0.0f, 0.0f);
  274. if (!m_ghost_mode && !worldmap_sector->path_ok(m_direction, m_tile_pos, &next_tile)) {
  275. log_debug << "Tilemap data is buggy" << std::endl;
  276. stop();
  277. return;
  278. }
  279. auto next_sprite = worldmap_sector->at_object<SpriteChange>(next_tile);
  280. if (next_sprite != nullptr && next_sprite->change_on_touch()) {
  281. change_sprite(next_sprite);
  282. }
  283. //SpriteChange* last_sprite = m_worldmap->at_object<SpriteChange>(next_tile);
  284. if (sprite_change != nullptr && next_sprite != nullptr) {
  285. log_debug << "Old: " << m_tile_pos << " New: " << next_tile << std::endl;
  286. sprite_change->set_stay_action();
  287. }
  288. m_tile_pos = next_tile;
  289. }
  290. void
  291. Tux::update_input_direction()
  292. {
  293. if (m_controller.hold(Control::UP))
  294. m_input_direction = Direction::NORTH;
  295. else if (m_controller.hold(Control::DOWN))
  296. m_input_direction = Direction::SOUTH;
  297. else if (m_controller.hold(Control::LEFT))
  298. m_input_direction = Direction::WEST;
  299. else if (m_controller.hold(Control::RIGHT))
  300. m_input_direction = Direction::EAST;
  301. }
  302. void
  303. Tux::update(float dt_sec)
  304. {
  305. if (m_worldmap->get_sector().get_camera().is_panning()) return;
  306. update_input_direction();
  307. if (m_moving)
  308. try_continue_walking(dt_sec);
  309. else
  310. try_start_walking();
  311. }
  312. void
  313. Tux::setup()
  314. {
  315. // Set initial tile position, if provided
  316. if (m_initial_tile_pos != Vector())
  317. m_tile_pos = m_initial_tile_pos;
  318. // check if we already touch a SpriteChange object
  319. auto sprite_change = m_worldmap->get_sector().at_object<SpriteChange>(m_tile_pos);
  320. change_sprite(sprite_change);
  321. }
  322. void
  323. Tux::process_special_tile(SpecialTile* special_tile)
  324. {
  325. if (!special_tile) {
  326. return;
  327. }
  328. if (special_tile->is_passive_message())
  329. m_worldmap->set_passive_message(special_tile->get_map_message(), map_message_TIME);
  330. if (!special_tile->get_script().empty())
  331. {
  332. try
  333. {
  334. m_worldmap->get_sector().run_script(special_tile->get_script(), "specialtile");
  335. }
  336. catch(std::exception& e)
  337. {
  338. log_warning << "Couldn't execute special tile script: " << e.what()
  339. << std::endl;
  340. }
  341. }
  342. }
  343. } // namespace worldmap
  344. /* EOF */