tux.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411
  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 == GROWUP_BONUS)
  90. return "large";
  91. if (bonus == FIRE_BONUS)
  92. return "fire";
  93. if (bonus == ICE_BONUS)
  94. return "ice";
  95. if (bonus == AIR_BONUS)
  96. return "air";
  97. if (bonus == EARTH_BONUS)
  98. return "earth";
  99. if (bonus == NO_BONUS)
  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::set_direction(Direction dir)
  161. {
  162. m_input_direction = dir;
  163. }
  164. void
  165. Tux::set_ghost_mode(bool enabled)
  166. {
  167. m_ghost_mode = enabled;
  168. }
  169. bool
  170. Tux::get_ghost_mode() const
  171. {
  172. return m_ghost_mode;
  173. }
  174. void
  175. Tux::try_start_walking()
  176. {
  177. if (m_moving)
  178. return;
  179. if (m_input_direction == Direction::NONE)
  180. return;
  181. auto level = m_worldmap->get_sector().at_object<LevelTile>();
  182. // We got a new direction, so lets start walking when possible
  183. Vector next_tile(0.0f, 0.0f);
  184. if ((!level || level->is_solved() || level->is_perfect()
  185. || (Editor::current() && Editor::current()->is_testing_level()))
  186. && m_worldmap->get_sector().path_ok(m_input_direction, m_tile_pos, &next_tile)) {
  187. m_tile_pos = next_tile;
  188. m_moving = true;
  189. m_direction = m_input_direction;
  190. m_back_direction = reverse_dir(m_direction);
  191. } else if (m_ghost_mode || (m_input_direction == m_back_direction)) {
  192. m_moving = true;
  193. m_direction = m_input_direction;
  194. m_tile_pos = m_worldmap->get_sector().get_next_tile(m_tile_pos, m_direction);
  195. m_back_direction = reverse_dir(m_direction);
  196. }
  197. }
  198. bool
  199. Tux::can_walk(int tile_data, Direction dir) const
  200. {
  201. return m_ghost_mode ||
  202. ((tile_data & Tile::WORLDMAP_NORTH && dir == Direction::NORTH) ||
  203. (tile_data & Tile::WORLDMAP_SOUTH && dir == Direction::SOUTH) ||
  204. (tile_data & Tile::WORLDMAP_EAST && dir == Direction::EAST) ||
  205. (tile_data & Tile::WORLDMAP_WEST && dir == Direction::WEST));
  206. }
  207. void
  208. Tux::change_sprite(SpriteChange* sprite_change)
  209. {
  210. //SpriteChange* sprite_change = m_worldmap->at_object<SpriteChange>();
  211. if (sprite_change != nullptr) {
  212. m_sprite = sprite_change->clone_sprite();
  213. sprite_change->clear_stay_action();
  214. m_worldmap->get_savegame().get_player_status().worldmap_sprite = sprite_change->get_sprite_name();
  215. }
  216. }
  217. void
  218. Tux::try_continue_walking(float dt_sec)
  219. {
  220. if (!m_moving)
  221. return;
  222. // Let tux walk
  223. m_offset += TUXSPEED * dt_sec;
  224. // Do nothing if we have not yet reached the next tile
  225. if (m_offset <= 32)
  226. return;
  227. m_offset -= 32;
  228. auto worldmap_sector = &m_worldmap->get_sector();
  229. auto sprite_change = worldmap_sector->at_object<SpriteChange>(m_tile_pos);
  230. change_sprite(sprite_change);
  231. // if this is a special_tile with passive_message, display it
  232. auto special_tile = worldmap_sector->at_object<SpecialTile>();
  233. if (special_tile)
  234. {
  235. // direction and the apply_action_ are opposites, since they "see"
  236. // directions in a different way
  237. if ((m_direction == Direction::NORTH && special_tile->get_apply_action_south()) ||
  238. (m_direction == Direction::SOUTH && special_tile->get_apply_action_north()) ||
  239. (m_direction == Direction::WEST && special_tile->get_apply_action_east()) ||
  240. (m_direction == Direction::EAST && special_tile->get_apply_action_west()))
  241. {
  242. process_special_tile(special_tile);
  243. }
  244. }
  245. // check if we are at a Teleporter
  246. auto teleporter = worldmap_sector->at_object<Teleporter>(m_tile_pos);
  247. // stop if we reached a level, a WORLDMAP_STOP tile, a teleporter or a special tile without a passive_message
  248. if ((worldmap_sector->at_object<LevelTile>()) ||
  249. (worldmap_sector->tile_data_at(m_tile_pos) & Tile::WORLDMAP_STOP) ||
  250. (special_tile && !special_tile->is_passive_message() && special_tile->get_script().empty()) ||
  251. (teleporter) ||
  252. m_ghost_mode)
  253. {
  254. if (special_tile && !special_tile->get_map_message().empty() && !special_tile->is_passive_message()) {
  255. m_worldmap->set_passive_message({}, 0.0f);
  256. }
  257. stop();
  258. return;
  259. }
  260. // if user wants to change direction, try changing, else guess the direction in which to walk next
  261. const int tile_data = worldmap_sector->tile_data_at(m_tile_pos);
  262. if ((m_direction != m_input_direction) && can_walk(tile_data, m_input_direction)) {
  263. m_direction = m_input_direction;
  264. m_back_direction = reverse_dir(m_direction);
  265. } else {
  266. Direction dir = Direction::NONE;
  267. if (tile_data & Tile::WORLDMAP_NORTH && m_back_direction != Direction::NORTH)
  268. dir = Direction::NORTH;
  269. else if (tile_data & Tile::WORLDMAP_SOUTH && m_back_direction != Direction::SOUTH)
  270. dir = Direction::SOUTH;
  271. else if (tile_data & Tile::WORLDMAP_EAST && m_back_direction != Direction::EAST)
  272. dir = Direction::EAST;
  273. else if (tile_data & Tile::WORLDMAP_WEST && m_back_direction != Direction::WEST)
  274. dir = Direction::WEST;
  275. if (dir == Direction::NONE) {
  276. // Should never be reached if tiledata is good
  277. log_warning << "Could not determine where to walk next" << std::endl;
  278. stop();
  279. return;
  280. }
  281. m_direction = dir;
  282. m_input_direction = m_direction;
  283. m_back_direction = reverse_dir(m_direction);
  284. }
  285. // Walk automatically to the next tile
  286. if (m_direction == Direction::NONE)
  287. return;
  288. Vector next_tile(0.0f, 0.0f);
  289. if (!m_ghost_mode && !worldmap_sector->path_ok(m_direction, m_tile_pos, &next_tile)) {
  290. log_debug << "Tilemap data is buggy" << std::endl;
  291. stop();
  292. return;
  293. }
  294. auto next_sprite = worldmap_sector->at_object<SpriteChange>(next_tile);
  295. if (next_sprite != nullptr && next_sprite->change_on_touch()) {
  296. change_sprite(next_sprite);
  297. }
  298. //SpriteChange* last_sprite = m_worldmap->at_object<SpriteChange>(next_tile);
  299. if (sprite_change != nullptr && next_sprite != nullptr) {
  300. log_debug << "Old: " << m_tile_pos << " New: " << next_tile << std::endl;
  301. sprite_change->set_stay_action();
  302. }
  303. m_tile_pos = next_tile;
  304. }
  305. void
  306. Tux::update_input_direction()
  307. {
  308. if (m_controller.hold(Control::UP))
  309. m_input_direction = Direction::NORTH;
  310. else if (m_controller.hold(Control::DOWN))
  311. m_input_direction = Direction::SOUTH;
  312. else if (m_controller.hold(Control::LEFT))
  313. m_input_direction = Direction::WEST;
  314. else if (m_controller.hold(Control::RIGHT))
  315. m_input_direction = Direction::EAST;
  316. }
  317. void
  318. Tux::update(float dt_sec)
  319. {
  320. if (m_worldmap->get_sector().get_camera().is_panning()) return;
  321. update_input_direction();
  322. if (m_moving)
  323. try_continue_walking(dt_sec);
  324. else
  325. try_start_walking();
  326. }
  327. void
  328. Tux::setup()
  329. {
  330. // Set initial tile position, if provided
  331. if (m_initial_tile_pos != Vector())
  332. m_tile_pos = m_initial_tile_pos;
  333. // check if we already touch a SpriteChange object
  334. auto sprite_change = m_worldmap->get_sector().at_object<SpriteChange>(m_tile_pos);
  335. change_sprite(sprite_change);
  336. }
  337. void
  338. Tux::process_special_tile(SpecialTile* special_tile)
  339. {
  340. if (!special_tile) {
  341. return;
  342. }
  343. if (special_tile->is_passive_message())
  344. m_worldmap->set_passive_message(special_tile->get_map_message(), map_message_TIME);
  345. if (!special_tile->get_script().empty())
  346. {
  347. try
  348. {
  349. m_worldmap->get_sector().run_script(special_tile->get_script(), "specialtile");
  350. }
  351. catch(std::exception& e)
  352. {
  353. log_warning << "Couldn't execute special tile script: " << e.what()
  354. << std::endl;
  355. }
  356. }
  357. }
  358. } // namespace worldmap
  359. /* EOF */