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
17#include "badguy/kugelblitz.hpp"
18
19#include <math.h>
20
21#include "audio/sound_manager.hpp"
22#include "math/random.hpp"
23#include "object/electrifier.hpp"
24#include "object/player.hpp"
25#include "sprite/sprite.hpp"
26#include "sprite/sprite_manager.hpp"
27#include "supertux/sector.hpp"
28
29namespace {
30
31const float LIFETIME = 5.0f;
32const float MOVETIME = 0.75f;
33const int BASE_SPEED = 200;
34const int RAND_SPEED = 150;
35
36} // namespace
37
38Kugelblitz::Kugelblitz(const ReaderMapping& reader) :
39 BadGuy(reader, "images/creatures/kugelblitz/kugelblitz.sprite"),
40 pos_groundhit(),
41 groundhit_pos_set(false),
42 dying(),
43 movement_timer(),
44 lifetime(),
45 direction(),
46 lightsprite(SpriteManager::current()->create("images/objects/lightmap_light/lightmap_light.sprite"))
47{
48 m_start_position.x = m_col.m_bbox.get_left();
49 m_sprite->set_action("falling");
50 m_physic.enable_gravity(false);
51 m_countMe = false;
52
53 lightsprite->set_blend(Blend::ADD);
54 lightsprite->set_color(Color(0.2f, 0.1f, 0.0f));
55
56 SoundManager::current()->preload("sounds/lightning.wav");
57}
58
59void
60Kugelblitz::initialize()
61{
62 m_physic.set_velocity_y(300);
63 m_physic.set_velocity_x(-20); //fall a little to the left
64 direction = 1;
65 dying = false;
66}
67
68void
69Kugelblitz::collision_solid(const CollisionHit& chit)
70{
71 hit(chit);
72}
73
74HitResponse
75Kugelblitz::collision_player(Player& player, const CollisionHit& )
76{
77 if (player.is_invincible()) {
78 explode();
79 return ABORT_MOVE;
80 }
81 // hit from above?
82 if (player.get_movement().y - get_movement().y > 0 && player.get_bbox().get_bottom() <
83 (m_col.m_bbox.get_top() + m_col.m_bbox.get_bottom()) / 2) {
84 // if it's not is it possible to squish us, then this will hurt
85 if (!collision_squished(player))
86 player.kill(false);
87 explode();
88 return FORCE_MOVE;
89 }
90 player.kill(false);
91 explode();
92 return FORCE_MOVE;
93}
94
95HitResponse
96Kugelblitz::collision_badguy(BadGuy& other , const CollisionHit& chit)
97{
98 //Let the Kugelblitz explode, too? The problem with that is that
99 //two Kugelblitzes would cancel each other out on contact...
100 other.kill_fall();
101 return hit(chit);
102}
103
104HitResponse
105Kugelblitz::hit(const CollisionHit& hit_)
106{
107 // hit floor?
108 if (hit_.bottom) {
109 if (!groundhit_pos_set)
110 {
111 pos_groundhit = get_pos();
112 groundhit_pos_set = true;
113 }
114 m_sprite->set_action("flying");
115 m_physic.set_velocity_y(0);
116 //Set random initial speed and direction
117 direction = gameRandom.rand(2)? 1: -1;
118 int speed = (BASE_SPEED + (gameRandom.rand(RAND_SPEED))) * direction;
119 m_physic.set_velocity_x(static_cast<float>(speed));
120 movement_timer.start(MOVETIME);
121 lifetime.start(LIFETIME);
122
123 }
124
125 return CONTINUE;
126}
127
128void
129Kugelblitz::active_update(float dt_sec)
130{
131 if (lifetime.check()) {
132 explode();
133 }
134 else {
135 if (groundhit_pos_set) {
136 if (movement_timer.check()) {
137 if (direction == 1) direction = -1; else direction = 1;
138 int speed = (BASE_SPEED + (gameRandom.rand(RAND_SPEED))) * direction;
139 m_physic.set_velocity_x(static_cast<float>(speed));
140 movement_timer.start(MOVETIME);
141 }
142 }
143
144 if (is_in_water()) {
145 Sector::get().add<Electrifier>(Electrifier::TileChangeMap({ {75, 1421}, {76, 1422} }), 1.5);
146 explode();
147 }
148 }
149 BadGuy::active_update(dt_sec);
150}
151
152void
153Kugelblitz::draw(DrawingContext& context)
154{
155 m_sprite->draw(context.color(), get_pos(), m_layer);
156 lightsprite->draw(context.light(), m_col.m_bbox.get_middle(), 0);
157}
158
159void
160Kugelblitz::kill_fall()
161{
162 explode();
163}
164
165void
166Kugelblitz::explode()
167{
168 if (!dying) {
169 SoundManager::current()->play("sounds/lightning.wav", m_col.m_bbox.p1());
170 m_sprite->set_action("pop");
171 lifetime.start(0.2f);
172 dying = true;
173 }
174 else remove_me();
175}
176
177void
178Kugelblitz::try_activate()
179{
180 // Much smaller offscreen distances to pop out of nowhere and surprise Tux
181 float X_OFFSCREEN_DISTANCE = 400;
182 float Y_OFFSCREEN_DISTANCE = 600;
183
184 auto player_ = get_nearest_player();
185 if (!player_) return;
186 Vector dist = player_->get_bbox().get_middle() - m_col.m_bbox.get_middle();
187 if ((fabsf(dist.x) <= X_OFFSCREEN_DISTANCE) && (fabsf(dist.y) <= Y_OFFSCREEN_DISTANCE)) {
188 set_state(STATE_ACTIVE);
189 if (!m_is_initialized) {
190
191 // if starting direction was set to AUTO, this is our chance to re-orient the badguy
192 if (m_start_dir == Direction::AUTO) {
193 Player* player__ = get_nearest_player();
194 if (player__ && (player__->get_bbox().get_left() > m_col.m_bbox.get_right())) {
195 m_dir = Direction::RIGHT;
196 } else {
197 m_dir = Direction::LEFT;
198 }
199 }
200
201 initialize();
202 m_is_initialized = true;
203 }
204 activate();
205 }
206}
207
208bool
209Kugelblitz::is_flammable() const
210{
211 return false;
212}
213
214/* EOF */
215