mriceblock.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. // SuperTux
  2. // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU General Public License
  15. // along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. #include "badguy/mriceblock.hpp"
  17. #include <math.h>
  18. #include "audio/sound_manager.hpp"
  19. #include "object/player.hpp"
  20. #include "sprite/sprite.hpp"
  21. #include "supertux/sector.hpp"
  22. namespace {
  23. const float KICKSPEED = 500;
  24. const int MAXSQUISHES = 10;
  25. const float NOKICK_TIME = 0.1f;
  26. }
  27. MrIceBlock::MrIceBlock(const ReaderMapping& reader) :
  28. WalkingBadguy(reader, "images/creatures/mr_iceblock/mr_iceblock.sprite", "left", "right"),
  29. ice_state(ICESTATE_NORMAL),
  30. nokick_timer(),
  31. flat_timer(),
  32. squishcount(0)
  33. {
  34. walk_speed = 80;
  35. max_drop_height = 600;
  36. SoundManager::current()->preload("sounds/iceblock_bump.wav");
  37. SoundManager::current()->preload("sounds/stomp.wav");
  38. SoundManager::current()->preload("sounds/kick.wav");
  39. }
  40. void
  41. MrIceBlock::initialize()
  42. {
  43. WalkingBadguy::initialize();
  44. set_state(ICESTATE_NORMAL);
  45. }
  46. void
  47. MrIceBlock::active_update(float dt_sec)
  48. {
  49. if (ice_state == ICESTATE_GRABBED)
  50. return;
  51. if (ice_state == ICESTATE_FLAT && flat_timer.check()) {
  52. set_state(ICESTATE_WAKING);
  53. }
  54. if (ice_state == ICESTATE_WAKING && m_sprite->animation_done()) {
  55. set_state(ICESTATE_NORMAL);
  56. }
  57. if (ice_state == ICESTATE_NORMAL)
  58. {
  59. WalkingBadguy::active_update(dt_sec);
  60. return;
  61. }
  62. BadGuy::active_update(dt_sec);
  63. }
  64. bool
  65. MrIceBlock::can_break() const {
  66. return ice_state == ICESTATE_KICKED;
  67. }
  68. void
  69. MrIceBlock::collision_solid(const CollisionHit& hit)
  70. {
  71. update_on_ground_flag(hit);
  72. if (hit.top || hit.bottom) { // floor or roof
  73. m_physic.set_velocity_y(0);
  74. }
  75. // hit left or right
  76. switch (ice_state) {
  77. case ICESTATE_NORMAL:
  78. WalkingBadguy::collision_solid(hit);
  79. break;
  80. case ICESTATE_KICKED: {
  81. if ((hit.right && m_dir == Direction::RIGHT) || (hit.left && m_dir == Direction::LEFT)) {
  82. m_dir = (m_dir == Direction::LEFT) ? Direction::RIGHT : Direction::LEFT;
  83. SoundManager::current()->play("sounds/iceblock_bump.wav", get_pos());
  84. m_physic.set_velocity_x(-m_physic.get_velocity_x()*.975f);
  85. }
  86. set_action(m_dir == Direction::LEFT ? "flat-left" : "flat-right", /* loops = */ -1);
  87. if (fabsf(m_physic.get_velocity_x()) < walk_speed * 1.5f)
  88. set_state(ICESTATE_NORMAL);
  89. break;
  90. }
  91. case ICESTATE_FLAT:
  92. case ICESTATE_WAKING:
  93. m_physic.set_velocity_x(0);
  94. break;
  95. case ICESTATE_GRABBED:
  96. break;
  97. }
  98. }
  99. HitResponse
  100. MrIceBlock::collision(GameObject& object, const CollisionHit& hit)
  101. {
  102. if (ice_state == ICESTATE_GRABBED)
  103. return FORCE_MOVE;
  104. return BadGuy::collision(object, hit);
  105. }
  106. HitResponse
  107. MrIceBlock::collision_player(Player& player, const CollisionHit& hit)
  108. {
  109. // handle kicks from left or right side
  110. if ((ice_state == ICESTATE_WAKING || ice_state == ICESTATE_FLAT) && get_state() == STATE_ACTIVE) {
  111. if (hit.left) {
  112. m_dir = Direction::RIGHT;
  113. player.kick();
  114. set_state(ICESTATE_KICKED);
  115. return FORCE_MOVE;
  116. } else if (hit.right) {
  117. m_dir = Direction::LEFT;
  118. player.kick();
  119. set_state(ICESTATE_KICKED);
  120. return FORCE_MOVE;
  121. }
  122. }
  123. return BadGuy::collision_player(player, hit);
  124. }
  125. HitResponse
  126. MrIceBlock::collision_badguy(BadGuy& badguy, const CollisionHit& hit)
  127. {
  128. switch (ice_state) {
  129. case ICESTATE_NORMAL:
  130. return WalkingBadguy::collision_badguy(badguy, hit);
  131. case ICESTATE_FLAT:
  132. case ICESTATE_WAKING:
  133. return FORCE_MOVE;
  134. case ICESTATE_KICKED:
  135. badguy.kill_fall();
  136. return FORCE_MOVE;
  137. default:
  138. assert(false);
  139. }
  140. return ABORT_MOVE;
  141. }
  142. bool
  143. MrIceBlock::collision_squished(GameObject& object)
  144. {
  145. Player* player = dynamic_cast<Player*>(&object);
  146. if (player && (player->m_does_buttjump || player->is_invincible())) {
  147. player->bounce(*this);
  148. kill_fall();
  149. return true;
  150. }
  151. switch (ice_state)
  152. {
  153. case ICESTATE_KICKED:
  154. {
  155. auto badguy = dynamic_cast<BadGuy*>(&object);
  156. if (badguy) {
  157. badguy->kill_fall();
  158. break;
  159. }
  160. }
  161. BOOST_FALLTHROUGH;
  162. case ICESTATE_NORMAL:
  163. {
  164. squishcount++;
  165. if (squishcount >= MAXSQUISHES) {
  166. kill_fall();
  167. return true;
  168. }
  169. }
  170. SoundManager::current()->play("sounds/stomp.wav", get_pos());
  171. m_physic.set_velocity_x(0);
  172. m_physic.set_velocity_y(0);
  173. set_state(ICESTATE_FLAT);
  174. nokick_timer.start(NOKICK_TIME);
  175. break;
  176. case ICESTATE_FLAT:
  177. case ICESTATE_WAKING:
  178. {
  179. auto movingobject = dynamic_cast<MovingObject*>(&object);
  180. if (movingobject && (movingobject->get_pos().x < get_pos().x)) {
  181. m_dir = Direction::RIGHT;
  182. } else {
  183. m_dir = Direction::LEFT;
  184. }
  185. }
  186. if (nokick_timer.check()) set_state(ICESTATE_KICKED);
  187. break;
  188. case ICESTATE_GRABBED:
  189. assert(false);
  190. break;
  191. }
  192. if (player) player->bounce(*this);
  193. return true;
  194. }
  195. void
  196. MrIceBlock::set_state(IceState state_)
  197. {
  198. if (ice_state == state_)
  199. return;
  200. switch (state_) {
  201. case ICESTATE_NORMAL:
  202. set_action(m_dir == Direction::LEFT ? "left" : "right", /* loops = */ -1);
  203. WalkingBadguy::initialize();
  204. break;
  205. case ICESTATE_FLAT:
  206. set_action(m_dir == Direction::LEFT ? "flat-left" : "flat-right", /* loops = */ -1);
  207. flat_timer.start(4);
  208. break;
  209. case ICESTATE_KICKED:
  210. SoundManager::current()->play("sounds/kick.wav", get_pos());
  211. m_physic.set_velocity_x(m_dir == Direction::LEFT ? -KICKSPEED : KICKSPEED);
  212. set_action(m_dir == Direction::LEFT ? "flat-left" : "flat-right", /* loops = */ -1);
  213. // we should slide above 1 block holes now...
  214. m_col.m_bbox.set_size(34, 31.8f);
  215. break;
  216. case ICESTATE_GRABBED:
  217. flat_timer.stop();
  218. break;
  219. case ICESTATE_WAKING:
  220. m_sprite->set_action(m_dir == Direction::LEFT ? "waking-left" : "waking-right",
  221. /* loops = */ 1);
  222. break;
  223. default:
  224. assert(false);
  225. }
  226. ice_state = state_;
  227. }
  228. void
  229. MrIceBlock::grab(MovingObject& object, const Vector& pos, Direction dir_)
  230. {
  231. Portable::grab(object, pos, dir_);
  232. m_col.m_movement = pos - get_pos();
  233. m_dir = dir_;
  234. set_action(dir_ == Direction::LEFT ? "flat-left" : "flat-right", /* loops = */ -1);
  235. set_state(ICESTATE_GRABBED);
  236. set_colgroup_active(COLGROUP_DISABLED);
  237. }
  238. void
  239. MrIceBlock::ungrab(MovingObject& object, Direction dir_)
  240. {
  241. if (dir_ == Direction::UP) {
  242. m_physic.set_velocity_y(-KICKSPEED);
  243. set_state(ICESTATE_FLAT);
  244. } else if (dir_ == Direction::DOWN) {
  245. Vector mov(0, 32);
  246. if (Sector::get().is_free_of_statics(get_bbox().moved(mov), this)) {
  247. // There is free space, so throw it down
  248. SoundManager::current()->play("sounds/kick.wav", get_pos());
  249. m_physic.set_velocity_y(KICKSPEED);
  250. }
  251. set_state(ICESTATE_FLAT);
  252. } else {
  253. m_dir = dir_;
  254. set_state(ICESTATE_KICKED);
  255. }
  256. set_colgroup_active(COLGROUP_MOVING);
  257. Portable::ungrab(object, dir_);
  258. }
  259. bool
  260. MrIceBlock::is_portable() const
  261. {
  262. return ice_state == ICESTATE_FLAT;
  263. }
  264. void
  265. MrIceBlock::ignite() {
  266. set_state(ICESTATE_NORMAL);
  267. BadGuy::ignite();
  268. }
  269. /* EOF */