Fix coverity #29357
[supertux.git] / src / badguy / zeekling.cpp
1 //  Zeekling - flyer that swoops down when she spots the player
2 //  Copyright (C) 2005 Matthias Braun <matze@braunis.de>
3 //  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
4 //
5 //  This program is free software: you can redistribute it and/or modify
6 //  it under the terms of the GNU General Public License as published by
7 //  the Free Software Foundation, either version 3 of the License, or
8 //  (at your option) any later version.
9 //
10 //  This program is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU General Public License for more details.
14 //
15 //  You should have received a copy of the GNU General Public License
16 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18 #include "badguy/zeekling.hpp"
19
20 #include <math.h>
21
22 #include "math/random_generator.hpp"
23 #include "object/player.hpp"
24 #include "sprite/sprite.hpp"
25 #include "supertux/object_factory.hpp"
26
27 Zeekling::Zeekling(const Reader& reader) :
28   BadGuy(reader, "images/creatures/zeekling/zeekling.sprite"),
29   speed(),
30   diveRecoverTimer(),
31   state(),
32   last_player(0),
33   last_player_pos(),
34   last_self_pos()
35 {
36   state = FLYING;
37   speed = gameRandom.rand(130, 171);
38   physic.enable_gravity(false);
39 }
40
41 Zeekling::Zeekling(const Vector& pos, Direction d) :
42   BadGuy(pos, d, "images/creatures/zeekling/zeekling.sprite"),
43   speed(),
44   diveRecoverTimer(),
45   state(),
46   last_player(0),
47   last_player_pos(),
48   last_self_pos()
49 {
50   state = FLYING;
51   speed = gameRandom.rand(130, 171);
52   physic.enable_gravity(false);
53 }
54
55 void
56 Zeekling::initialize()
57 {
58   physic.set_velocity_x(dir == LEFT ? -speed : speed);
59   sprite->set_action(dir == LEFT ? "left" : "right");
60 }
61
62 bool
63 Zeekling::collision_squished(GameObject& object)
64 {
65   sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
66   kill_squished(object);
67   return true;
68 }
69
70 void
71 Zeekling::onBumpHorizontal() {
72   if (frozen)
73   {
74     physic.set_velocity_x(0);
75     return;
76   }
77   if (state == FLYING) {
78     dir = (dir == LEFT ? RIGHT : LEFT);
79     sprite->set_action(dir == LEFT ? "left" : "right");
80     physic.set_velocity_x(dir == LEFT ? -speed : speed);
81   } else
82     if (state == DIVING) {
83       dir = (dir == LEFT ? RIGHT : LEFT);
84       state = FLYING;
85       sprite->set_action(dir == LEFT ? "left" : "right");
86       physic.set_velocity_x(dir == LEFT ? -speed : speed);
87       physic.set_velocity_y(0);
88     } else
89       if (state == CLIMBING) {
90         dir = (dir == LEFT ? RIGHT : LEFT);
91         sprite->set_action(dir == LEFT ? "left" : "right");
92         physic.set_velocity_x(dir == LEFT ? -speed : speed);
93       } else {
94         assert(false);
95       }
96 }
97
98 void
99 Zeekling::onBumpVertical() {
100   if (frozen)
101   {
102     physic.set_velocity_y(0);
103     physic.set_velocity_x(0);
104     return;
105   }
106   if (state == FLYING) {
107     physic.set_velocity_y(0);
108   } else
109     if (state == DIVING) {
110       state = CLIMBING;
111       physic.set_velocity_y(-speed);
112       sprite->set_action(dir == LEFT ? "left" : "right");
113     } else
114       if (state == CLIMBING) {
115         state = FLYING;
116         physic.set_velocity_y(0);
117       }
118 }
119
120 void
121 Zeekling::collision_solid(const CollisionHit& hit)
122 {
123   if(sprite->get_action() == "squished-left" ||
124      sprite->get_action() == "squished-right")
125   {
126     return;
127   }
128
129   if(hit.top || hit.bottom) {
130     onBumpVertical();
131   } else if(hit.left || hit.right) {
132     onBumpHorizontal();
133   }
134 }
135
136 /**
137  * linear prediction of player and badguy positions to decide if we should enter the DIVING state
138  */
139 bool
140 Zeekling::should_we_dive() {
141   if (frozen)
142     return false;
143
144   const MovingObject* player = this->get_nearest_player();
145   if (player && last_player && (player == last_player)) {
146
147     // get positions, calculate movement
148     const Vector player_pos = player->get_pos();
149     const Vector player_mov = (player_pos - last_player_pos);
150     const Vector self_pos = this->get_pos();
151     const Vector self_mov = (self_pos - last_self_pos);
152
153     // new vertical speed to test with
154     float vy = 2*fabsf(self_mov.x);
155
156     // do not dive if we are not above the player
157     float height = player_pos.y - self_pos.y;
158     if (height <= 0) return false;
159
160     // do not dive if we are too far above the player
161     if (height > 512) return false;
162
163     // do not dive if we would not descend faster than the player
164     float relSpeed = vy - player_mov.y;
165     if (relSpeed <= 0) return false;
166
167     // guess number of frames to descend to same height as player
168     float estFrames = height / relSpeed;
169
170     // guess where the player would be at this time
171     float estPx = (player_pos.x + (estFrames * player_mov.x));
172
173     // guess where we would be at this time
174     float estBx = (self_pos.x + (estFrames * self_mov.x));
175
176     // near misses are OK, too
177     if (fabsf(estPx - estBx) < 8) return true;
178   }
179
180   // update last player tracked, as well as our positions
181   last_player = player;
182   if (player) {
183     last_player_pos = player->get_pos();
184     last_self_pos = this->get_pos();
185   }
186
187   return false;
188 }
189
190 void
191 Zeekling::active_update(float elapsed_time) {
192   if (state == FLYING) {
193     if (should_we_dive()) {
194       state = DIVING;
195       physic.set_velocity_y(2*fabsf(physic.get_velocity_x()));
196       sprite->set_action(dir == LEFT ? "diving-left" : "diving-right");
197     }
198     BadGuy::active_update(elapsed_time);
199     return;
200   } else if (state == DIVING) {
201     BadGuy::active_update(elapsed_time);
202     return;
203   } else if (state == CLIMBING) {
204     // stop climbing when we're back at initial height
205     if (get_pos().y <= start_position.y) {
206       state = FLYING;
207       physic.set_velocity_y(0);
208     }
209     BadGuy::active_update(elapsed_time);
210     return;
211   } else {
212     assert(false);
213   }
214 }
215
216 void
217 Zeekling::freeze()
218 {
219   BadGuy::freeze();
220   physic.enable_gravity(true);
221 }
222
223 void
224 Zeekling::unfreeze()
225 {
226   BadGuy::unfreeze();
227   physic.enable_gravity(false);
228   state = FLYING;
229   initialize();
230 }
231
232 bool
233 Zeekling::is_freezable() const
234 {
235   return true;
236 }
237
238 /* EOF */