explosion.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. // SuperTux -- Explosion object
  2. // Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.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 "object/explosion.hpp"
  17. #include "audio/sound_manager.hpp"
  18. #include "badguy/badguy.hpp"
  19. #include "badguy/walking_badguy.hpp"
  20. #include "math/random.hpp"
  21. #include "object/bonus_block.hpp"
  22. #include "object/brick.hpp"
  23. #include "object/camera.hpp"
  24. #include "object/particles.hpp"
  25. #include "object/player.hpp"
  26. #include "object/weak_block.hpp"
  27. #include "supertux/sector.hpp"
  28. #include "sprite/sprite.hpp"
  29. #include "sprite/sprite_manager.hpp"
  30. #include "supertux/sector.hpp"
  31. Explosion::Explosion(const Vector& pos, float p_push_strength,
  32. int p_num_particles, bool p_short_fuse) :
  33. MovingSprite(pos, "images/objects/explosion/explosion.sprite", LAYER_OBJECTS + 40, COLGROUP_MOVING),
  34. hurt(!p_short_fuse),
  35. push_strength(p_push_strength),
  36. num_particles(p_num_particles),
  37. m_state(E_STATE_WAITING),
  38. m_lightsprite(SpriteManager::current()->create(p_short_fuse ?
  39. "images/objects/lightmap_light/lightmap_light-medium.sprite" :
  40. "images/objects/lightmap_light/lightmap_light-large.sprite")),
  41. m_color(1.f, 0.5f, 0.2f, 0.f),
  42. m_fading_timer(),
  43. short_fuse(p_short_fuse)
  44. {
  45. set_pos(get_pos() - (m_col.m_bbox.get_middle() - get_pos()));
  46. SoundManager::current()->preload(short_fuse ? "sounds/firecracker.ogg" : "sounds/explosion.wav");
  47. m_lightsprite->set_blend(Blend::ADD);
  48. m_lightsprite->set_color(m_color);
  49. }
  50. Explosion::Explosion(const ReaderMapping& reader) :
  51. MovingSprite(reader, "images/objects/explosion/explosion.sprite", LAYER_OBJECTS + 40, COLGROUP_MOVING),
  52. hurt(true),
  53. push_strength(-1),
  54. num_particles(100),
  55. m_state(E_STATE_WAITING),
  56. m_lightsprite(nullptr),
  57. m_color(1.f, 0.5f, 0.2f, 0.f),
  58. m_fading_timer(),
  59. short_fuse(false)
  60. {
  61. SoundManager::current()->preload(short_fuse ? "sounds/firecracker.ogg" : "sounds/explosion.wav");
  62. m_lightsprite = (SpriteManager::current()->create(short_fuse ?
  63. "images/objects/lightmap_light/lightmap_light-medium.sprite" :
  64. "images/objects/lightmap_light/lightmap_light-large.sprite"));
  65. m_lightsprite->set_blend(Blend::ADD);
  66. m_lightsprite->set_color(m_color);
  67. }
  68. void
  69. Explosion::explode()
  70. {
  71. if (m_state != E_STATE_WAITING)
  72. return;
  73. m_state = E_STATE_EXPLODING;
  74. Sector::get().get_camera().shake(.1f, 0.f, 10.f);
  75. set_action(hurt ? "default" : "pop", 1);
  76. m_sprite->set_animation_loops(1); //TODO: This is necessary because set_action will not set "loops" when "action" is the default action.
  77. m_sprite->set_angle(graphicsRandom.randf(0, 360)); // A random rotation on the sprite to make explosions appear more random.
  78. if (hurt)
  79. SoundManager::current()->play("sounds/explosion.wav", get_pos(), 0.98f);
  80. else
  81. SoundManager::current()->play("sounds/firecracker.ogg", get_pos(), 0.7f);
  82. bool does_push = push_strength > 0;
  83. // Spawn some particles.
  84. Vector accel = Vector(0, Sector::get().get_gravity()*100);
  85. Sector::get().add<Particles>(
  86. m_col.m_bbox.get_middle(), -360, 360, 450.0f, 900.0f, accel, num_particles,
  87. Color(.4f, .4f, .4f), 3, .8f, LAYER_OBJECTS-1);
  88. if (does_push) {
  89. Vector center = m_col.m_bbox.get_middle ();
  90. auto near_objects = Sector::get().get_nearby_objects (center, 128.0 * 32.0);
  91. for (auto& obj: near_objects) {
  92. if(!Sector::current()->free_line_of_sight(center, obj->get_pos(), true))
  93. continue;
  94. Vector obj_vector = obj->get_bbox ().get_middle ();
  95. Vector direction = obj_vector - center;
  96. float distance = glm::length(direction);
  97. /* If the distance is very small, for example because "obj" is the badguy
  98. * causing the explosion, skip this object. */
  99. if (distance <= 1.0f)
  100. continue;
  101. /* The force decreases with the distance squared. In the distance of one
  102. * tile (32 pixels) you will have a speed increase of 150 pixels/s. */
  103. float force = push_strength / (distance * distance);
  104. float force_limit = short_fuse ? 400.f : 200.f;
  105. // If we somehow get a force of over the limit, keep it at the limit because
  106. // unexpected behaviour could result otherwise.
  107. if (force > force_limit)
  108. force = force_limit;
  109. Vector add_speed = glm::normalize(direction) * force;
  110. auto player = dynamic_cast<Player*>(obj);
  111. if (player && !player->is_stone()) {
  112. player->add_velocity(add_speed);
  113. }
  114. auto badguy = dynamic_cast<WalkingBadguy*>(obj);
  115. if (badguy && badguy->is_active() && !badguy->is_heavy()) {
  116. badguy->add_velocity(add_speed);
  117. }
  118. bool in_break_range = distance <= 60.f;
  119. bool in_shake_range = distance <= 100.f;
  120. auto bonusblock = dynamic_cast<BonusBlock*>(obj);
  121. if (bonusblock && in_shake_range && hurts()) {
  122. bonusblock->start_bounce(this);
  123. if (in_break_range)
  124. bonusblock->try_open(player);
  125. }
  126. auto brick = dynamic_cast<Brick*>(obj);
  127. if (brick && in_shake_range && hurts()) {
  128. brick->start_bounce(this);
  129. if (in_break_range)
  130. brick->try_break(nullptr);
  131. }
  132. auto weakblock = dynamic_cast<WeakBlock*>(obj);
  133. if (weakblock && in_break_range) {
  134. weakblock->startBurning();
  135. }
  136. }
  137. }
  138. }
  139. void
  140. Explosion::update(float )
  141. {
  142. switch (m_state)
  143. {
  144. case E_STATE_WAITING:
  145. explode();
  146. m_fading_timer.start(0.15f);
  147. break;
  148. case E_STATE_EXPLODING:
  149. m_color.alpha = std::min(m_fading_timer.get_progress(), 1.f);
  150. if (m_fading_timer.check())
  151. {
  152. m_fading_timer.start(short_fuse ? .85f : 1.5f);
  153. m_state = E_STATE_FADING;
  154. }
  155. break;
  156. case E_STATE_FADING:
  157. m_color.alpha = std::max(1.f - m_fading_timer.get_progress(), 0.f);
  158. if (m_fading_timer.check())
  159. {
  160. remove_me();
  161. }
  162. break;
  163. }
  164. }
  165. void
  166. Explosion::draw(DrawingContext& context)
  167. {
  168. m_sprite->draw(context.color(), get_pos(), LAYER_OBJECTS+40);
  169. m_lightsprite->set_color(m_color);
  170. m_lightsprite->draw(context.light(), m_col.m_bbox.get_middle(), 0);
  171. }
  172. HitResponse
  173. Explosion::collision(MovingObject& other, const CollisionHit& )
  174. {
  175. if ((m_state != E_STATE_EXPLODING) || !hurt || m_sprite->get_current_frame() > 8)
  176. return ABORT_MOVE;
  177. auto player = dynamic_cast<Player*>(&other);
  178. if (player != nullptr && !player->is_stone()) {
  179. player->kill(false);
  180. }
  181. auto badguy = dynamic_cast<BadGuy*>(&other);
  182. if (badguy != nullptr) {
  183. badguy->kill_fall();
  184. }
  185. return ABORT_MOVE;
  186. }
  187. /* EOF */