explosion.cpp 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  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. state(STATE_WAITING),
  38. lightsprite(SpriteManager::current()->create("images/objects/lightmap_light/lightmap_light-large.sprite")),
  39. short_fuse(p_short_fuse)
  40. {
  41. SoundManager::current()->preload(short_fuse ? "sounds/firecracker.ogg" : "sounds/explosion.wav");
  42. set_pos(get_pos() - (m_col.m_bbox.get_middle() - get_pos()));
  43. lightsprite->set_blend(Blend::ADD);
  44. lightsprite->set_color(Color(0.6f, 0.6f, 0.6f));
  45. }
  46. Explosion::Explosion(const ReaderMapping& reader) :
  47. MovingSprite(reader, "images/objects/explosion/explosion.sprite", LAYER_OBJECTS + 40, COLGROUP_MOVING),
  48. hurt(true),
  49. push_strength(-1),
  50. num_particles(100),
  51. state(STATE_WAITING),
  52. lightsprite(SpriteManager::current()->create("images/objects/lightmap_light/lightmap_light-large.sprite")),
  53. short_fuse(false)
  54. {
  55. SoundManager::current()->preload(short_fuse ? "sounds/firecracker.ogg" : "sounds/explosion.wav");
  56. lightsprite->set_blend(Blend::ADD);
  57. lightsprite->set_color(Color(0.6f, 0.6f, 0.6f));
  58. }
  59. void
  60. Explosion::explode()
  61. {
  62. if (state != STATE_WAITING)
  63. return;
  64. state = STATE_EXPLODING;
  65. Sector::get().get_camera().shake(.1f, 0.f, 10.f);
  66. set_action(hurt ? "default" : "pop", 1);
  67. m_sprite->set_animation_loops(1); //TODO: This is necessary because set_action will not set "loops" when "action" is the default action.
  68. m_sprite->set_angle(graphicsRandom.randf(0, 360)); // A random rotation on the sprite to make explosions appear more random.
  69. if (hurt)
  70. SoundManager::current()->play("sounds/explosion.wav", get_pos(), 0.98f);
  71. else
  72. SoundManager::current()->play("sounds/firecracker.ogg", get_pos(), 0.7f);
  73. bool does_push = push_strength > 0;
  74. // Spawn some particles.
  75. Vector accel = Vector(0, Sector::get().get_gravity()*100);
  76. Sector::get().add<Particles>(
  77. m_col.m_bbox.get_middle(), -360, 360, 450.0f, 900.0f, accel, num_particles,
  78. Color(.4f, .4f, .4f), 3, .8f, LAYER_OBJECTS-1);
  79. if (does_push) {
  80. Vector center = m_col.m_bbox.get_middle ();
  81. auto near_objects = Sector::get().get_nearby_objects (center, 128.0 * 32.0);
  82. for (auto& obj: near_objects) {
  83. if(!Sector::current()->free_line_of_sight(center, obj->get_pos(), true))
  84. continue;
  85. Vector obj_vector = obj->get_bbox ().get_middle ();
  86. Vector direction = obj_vector - center;
  87. float distance = glm::length(direction);
  88. /* If the distance is very small, for example because "obj" is the badguy
  89. * causing the explosion, skip this object. */
  90. if (distance <= 1.0f)
  91. continue;
  92. /* The force decreases with the distance squared. In the distance of one
  93. * tile (32 pixels) you will have a speed increase of 150 pixels/s. */
  94. float force = push_strength / (distance * distance);
  95. float force_limit = short_fuse ? 400.f : 200.f;
  96. // If we somehow get a force of over the limit, keep it at the limit because
  97. // unexpected behaviour could result otherwise.
  98. if (force > force_limit)
  99. force = force_limit;
  100. Vector add_speed = glm::normalize(direction) * force;
  101. auto player = dynamic_cast<Player*>(obj);
  102. if (player && !player->is_stone()) {
  103. player->add_velocity(add_speed);
  104. }
  105. auto badguy = dynamic_cast<WalkingBadguy*>(obj);
  106. if (badguy && badguy->is_active()) {
  107. badguy->add_velocity(add_speed);
  108. }
  109. bool in_break_range = distance <= 60.f;
  110. bool in_shake_range = distance <= 100.f;
  111. auto bonusblock = dynamic_cast<BonusBlock*>(obj);
  112. if (bonusblock && in_shake_range && hurts()) {
  113. bonusblock->start_bounce(this);
  114. if (in_break_range)
  115. bonusblock->try_open(player);
  116. }
  117. auto brick = dynamic_cast<Brick*>(obj);
  118. if (brick && in_shake_range && hurts()) {
  119. brick->start_bounce(this);
  120. if (in_break_range)
  121. brick->try_break(nullptr);
  122. }
  123. auto weakblock = dynamic_cast<WeakBlock*>(obj);
  124. if (weakblock && in_break_range) {
  125. weakblock->startBurning();
  126. }
  127. }
  128. }
  129. }
  130. void
  131. Explosion::update(float )
  132. {
  133. switch (state) {
  134. case STATE_WAITING:
  135. explode();
  136. break;
  137. case STATE_EXPLODING:
  138. if (m_sprite->animation_done()) {
  139. remove_me();
  140. }
  141. break;
  142. }
  143. }
  144. void
  145. Explosion::draw(DrawingContext& context)
  146. {
  147. m_sprite->draw(context.color(), get_pos(), LAYER_OBJECTS+40);
  148. lightsprite->draw(context.light(), m_col.m_bbox.get_middle(), 0);
  149. }
  150. HitResponse
  151. Explosion::collision(GameObject& other, const CollisionHit& )
  152. {
  153. if ((state != STATE_EXPLODING) || !hurt || m_sprite->get_current_frame() > 8)
  154. return ABORT_MOVE;
  155. auto player = dynamic_cast<Player*>(&other);
  156. if (player != nullptr && !player->is_stone()) {
  157. player->kill(false);
  158. }
  159. auto badguy = dynamic_cast<BadGuy*>(&other);
  160. if (badguy != nullptr) {
  161. badguy->kill_fall();
  162. }
  163. return ABORT_MOVE;
  164. }
  165. /* EOF */