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
17#include "object/explosion.hpp"
18
19#include "audio/sound_manager.hpp"
20#include "badguy/badguy.hpp"
21#include "badguy/walking_badguy.hpp"
22#include "math/random.hpp"
23#include "object/particles.hpp"
24#include "object/player.hpp"
25#include "sprite/sprite.hpp"
26#include "sprite/sprite_manager.hpp"
27#include "supertux/sector.hpp"
28
29Explosion::Explosion(const Vector& pos) :
30 MovingSprite(pos, "images/objects/explosion/explosion.sprite", LAYER_OBJECTS+40, COLGROUP_MOVING),
31 hurt(true),
32 push(false),
33 state(STATE_WAITING),
34 lightsprite(SpriteManager::current()->create("images/objects/lightmap_light/lightmap_light-large.sprite"))
35{
36 SoundManager::current()->preload("sounds/explosion.wav");
37 SoundManager::current()->preload("sounds/firecracker.ogg");
38 set_pos(get_pos() - (m_col.m_bbox.get_middle() - get_pos()));
39 lightsprite->set_blend(Blend::ADD);
40 lightsprite->set_color(Color(0.6f, 0.6f, 0.6f));
41}
42
43Explosion::Explosion(const ReaderMapping& reader) :
44 MovingSprite(reader, "images/objects/explosion/explosion.sprite", LAYER_OBJECTS+40, COLGROUP_MOVING),
45 hurt(true),
46 push(false),
47 state(STATE_WAITING),
48 lightsprite(SpriteManager::current()->create("images/objects/lightmap_light/lightmap_light-large.sprite"))
49{
50 SoundManager::current()->preload("sounds/explosion.wav");
51 SoundManager::current()->preload("sounds/firecracker.ogg");
52 lightsprite->set_blend(Blend::ADD);
53 lightsprite->set_color(Color(0.6f, 0.6f, 0.6f));
54}
55
56void
57Explosion::explode()
58{
59 if (state != STATE_WAITING)
60 return;
61 state = STATE_EXPLODING;
62
63 set_action(hurt ? "default" : "pop", 1);
64 m_sprite->set_animation_loops(1); //TODO: this is necessary because set_action will not set "loops" when "action" is the default action
65 m_sprite->set_angle(graphicsRandom.randf(0, 360)); // a random rotation on the sprite to make explosions appear more random
66 if (hurt)
67 SoundManager::current()->play("sounds/explosion.wav", get_pos(), 0.98f);
68 else
69 SoundManager::current()->play("sounds/firecracker.ogg", get_pos(), 0.7f);
70
71 // spawn some particles
72 int pnumber = push ? 8 : 100;
73 Vector accel = Vector(0, Sector::get().get_gravity()*100);
74 Sector::get().add<Particles>(
75 m_col.m_bbox.get_middle(), -360, 360, 450, 900, accel , pnumber, Color(.4f, .4f, .4f), 3, .8f, LAYER_OBJECTS-1);
76
77 if (push) {
78 Vector center = m_col.m_bbox.get_middle ();
79 auto near_objects = Sector::get().get_nearby_objects (center, 10.0 * 32.0);
80
81 for (auto& obj: near_objects) {
82 Vector obj_vector = obj->get_bbox ().get_middle ();
83 Vector direction = obj_vector - center;
84 float distance = direction.norm ();
85
86 /* If the distance is very small, for example because "obj" is the badguy
87 * causing the explosion, skip this object. */
88 if (distance <= 1.0f)
89 continue;
90
91 /* The force decreases with the distance squared. In the distance of one
92 * tile (32 pixels) you will have a speed increase of 150 pixels/s. */
93 float force = 150.0f * 32.0f * 32.0f / (distance * distance);
94 if (force > 200.0f)
95 force = 200.0;
96
97 Vector add_speed = direction.unit () * force;
98
99 auto player = dynamic_cast<Player *> (obj);
100 if (player) {
101 player->add_velocity (add_speed);
102 }
103
104 auto badguy = dynamic_cast<WalkingBadguy *> (obj);
105 if (badguy && badguy->is_active()) {
106 badguy->add_velocity (add_speed);
107 }
108 }
109 }
110}
111
112void
113Explosion::update(float )
114{
115 switch (state) {
116 case STATE_WAITING:
117 explode();
118 break;
119 case STATE_EXPLODING:
120 if (m_sprite->animation_done()) {
121 remove_me();
122 }
123 break;
124 }
125}
126
127void
128Explosion::draw(DrawingContext& context)
129{
130 m_sprite->draw(context.color(), get_pos(), LAYER_OBJECTS+40);
131 lightsprite->draw(context.light(), m_col.m_bbox.get_middle(), 0);
132}
133
134HitResponse
135Explosion::collision(GameObject& other, const CollisionHit& )
136{
137 if ((state != STATE_EXPLODING) || !hurt || m_sprite->get_current_frame() > 8)
138 return ABORT_MOVE;
139
140 auto player = dynamic_cast<Player*>(&other);
141 if (player != nullptr) {
142 player->kill(false);
143 }
144
145 auto badguy = dynamic_cast<BadGuy*>(&other);
146 if (badguy != nullptr) {
147 badguy->kill_fall();
148 }
149
150 return ABORT_MOVE;
151}
152
153/* EOF */
154