block.cpp 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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 <physfs.h>
  17. #include "object/block.hpp"
  18. #include "audio/sound_manager.hpp"
  19. #include "badguy/badguy.hpp"
  20. #include "badguy/bomb.hpp"
  21. #include "math/random.hpp"
  22. #include "object/coin.hpp"
  23. #include "object/growup.hpp"
  24. #include "object/player.hpp"
  25. #include "object/sprite_particle.hpp"
  26. #include "supertux/constants.hpp"
  27. #include "supertux/flip_level_transformer.hpp"
  28. #include "supertux/sector.hpp"
  29. #include "util/reader_mapping.hpp"
  30. #include "util/writer.hpp"
  31. static const float BOUNCY_BRICK_MAX_OFFSET = 8;
  32. static const float BOUNCY_BRICK_SPEED = 90;
  33. static const float BUMP_ROTATION_ANGLE = 10;
  34. Block::Block(const Vector& pos, const std::string& sprite_file) :
  35. MovingSprite(pos, sprite_file),
  36. m_bouncing(false),
  37. m_breaking(false),
  38. m_bounce_dir(0),
  39. m_bounce_offset(0),
  40. m_original_y(-1)
  41. {
  42. m_col.m_bbox.set_size(32, 32.1f);
  43. set_group(COLGROUP_STATIC);
  44. SoundManager::current()->preload("sounds/upgrade.wav");
  45. SoundManager::current()->preload("sounds/brick.wav");
  46. }
  47. Block::Block(const ReaderMapping& mapping, const std::string& sprite_file) :
  48. MovingSprite(mapping, sprite_file),
  49. m_bouncing(false),
  50. m_breaking(false),
  51. m_bounce_dir(0),
  52. m_bounce_offset(0),
  53. m_original_y(-1)
  54. {
  55. m_col.m_bbox.set_size(32, 32.1f);
  56. set_group(COLGROUP_STATIC);
  57. SoundManager::current()->preload("sounds/upgrade.wav");
  58. SoundManager::current()->preload("sounds/brick.wav");
  59. }
  60. HitResponse
  61. Block::collision(GameObject& other, const CollisionHit& )
  62. {
  63. auto player = dynamic_cast<Player*> (&other);
  64. if (player)
  65. {
  66. bool in_line_with_player = ((player->get_physic().get_velocity_x() > 0.f &&
  67. player->get_bbox().get_right() < get_bbox().get_left()) ||
  68. (player->get_physic().get_velocity_x() < 0.f &&
  69. player->get_bbox().get_left() > get_bbox().get_right()));
  70. if(player->is_swimboosting() || ((player->is_sliding() || player->is_stone()) && in_line_with_player))
  71. {
  72. hit(*player);
  73. }
  74. else if (!player->is_water_jumping() && !player->is_swimming())
  75. {
  76. bool x_coordinates_intersect =
  77. player->get_bbox().get_right() >= m_col.m_bbox.get_left() &&
  78. player->get_bbox().get_left() <= m_col.m_bbox.get_right();
  79. if (player->get_bbox().get_top() > m_col.m_bbox.get_bottom() - SHIFT_DELTA &&
  80. x_coordinates_intersect)
  81. {
  82. hit(*player);
  83. }
  84. }
  85. }
  86. // Only interact with other objects if:
  87. // 1) we are bouncing
  88. // 2) the object is not portable (either never or not currently)
  89. // 3) the object is being hit from below (baguys don't get killed for activating boxes).
  90. auto badguy = dynamic_cast<BadGuy*> (&other);
  91. auto portable = dynamic_cast<Portable*> (&other);
  92. auto moving_object = dynamic_cast<MovingObject*> (&other);
  93. auto bomb = dynamic_cast<Bomb*> (&other);
  94. bool is_portable = ((portable != nullptr) && portable->is_portable());
  95. bool is_bomb = (bomb != nullptr); // bombs need to explode, although they are considered portable
  96. bool hit_mo_from_below = ((moving_object == nullptr) || (moving_object->get_bbox().get_bottom() < (m_col.m_bbox.get_top() + SHIFT_DELTA)));
  97. if (m_bouncing && (!is_portable || badguy || is_bomb) && hit_mo_from_below) {
  98. // Badguys get killed.
  99. if (badguy) {
  100. badguy->kill_fall();
  101. }
  102. // Coins get collected.
  103. auto coin = dynamic_cast<Coin*> (&other);
  104. if (coin) {
  105. coin->collect();
  106. }
  107. // Eggs get jumped.
  108. auto growup = dynamic_cast<GrowUp*> (&other);
  109. if (growup) {
  110. growup->do_jump();
  111. }
  112. }
  113. return FORCE_MOVE;
  114. }
  115. void
  116. Block::update(float dt_sec)
  117. {
  118. if (!m_bouncing)
  119. return;
  120. float offset = m_original_y - get_pos().y;
  121. if (offset > BOUNCY_BRICK_MAX_OFFSET) {
  122. m_bounce_dir = BOUNCY_BRICK_SPEED;
  123. m_col.set_movement(Vector(0, m_bounce_dir * dt_sec));
  124. if (m_breaking){
  125. break_me();
  126. }
  127. } else if (offset < BOUNCY_BRICK_SPEED * dt_sec && m_bounce_dir > 0) {
  128. m_col.set_movement(Vector(0, offset));
  129. m_bounce_dir = 0;
  130. m_bouncing = false;
  131. m_sprite->set_angle(0);
  132. } else {
  133. m_col.set_movement(Vector(0, m_bounce_dir * dt_sec));
  134. }
  135. }
  136. void
  137. Block::draw(DrawingContext& context)
  138. {
  139. m_sprite->draw(context.color(), get_pos(), LAYER_OBJECTS+1, m_flip);
  140. }
  141. void
  142. Block::start_bounce(GameObject* hitter)
  143. {
  144. if (m_original_y == -1){
  145. m_original_y = m_col.m_bbox.get_top();
  146. }
  147. m_bouncing = true;
  148. m_bounce_dir = -BOUNCY_BRICK_SPEED;
  149. m_bounce_offset = 0;
  150. MovingObject* hitter_mo = dynamic_cast<MovingObject*>(hitter);
  151. if (hitter_mo) {
  152. float center_of_hitter = hitter_mo->get_bbox().get_middle().x;
  153. float offset = (m_col.m_bbox.get_middle().x - center_of_hitter)*2 / m_col.m_bbox.get_width();
  154. // Without this, hitting a multi-coin bonus block from the side (e. g. with
  155. // an ice block or a snail) would turn the block 90 degrees.
  156. if (offset > 2 || offset < -2)
  157. offset = 0;
  158. m_sprite->set_angle(BUMP_ROTATION_ANGLE*offset);
  159. }
  160. }
  161. void
  162. Block::start_break(GameObject* hitter)
  163. {
  164. start_bounce(hitter);
  165. m_breaking = true;
  166. }
  167. void
  168. Block::break_me()
  169. {
  170. const auto gravity = Sector::get().get_gravity() * 100;
  171. Vector pos = get_pos() + Vector(16.0f, 16.0f);
  172. for (const char* action : {"piece1", "piece2", "piece3", "piece4", "piece5", "piece6"})
  173. {
  174. Vector velocity(graphicsRandom.randf(-100, 100),
  175. graphicsRandom.randf(-400, -300));
  176. Sector::get().add<SpriteParticle>(m_sprite->clone(), action,
  177. pos, ANCHOR_MIDDLE,
  178. velocity, Vector(0, gravity),
  179. LAYER_OBJECTS + 3);
  180. }
  181. remove_me();
  182. }
  183. void
  184. Block::on_flip(float height)
  185. {
  186. MovingObject::on_flip(height);
  187. if (m_original_y != -1) m_original_y = height - m_original_y - get_bbox().get_height();
  188. FlipLevelTransformer::transform_flip(m_flip);
  189. }
  190. /* EOF */