Don't resurrect zeekling when colliding after squished
[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 (state == FLYING) {
73     dir = (dir == LEFT ? RIGHT : LEFT);
74     sprite->set_action(dir == LEFT ? "left" : "right");
75     physic.set_velocity_x(dir == LEFT ? -speed : speed);
76   } else
77     if (state == DIVING) {
78       dir = (dir == LEFT ? RIGHT : LEFT);
79       state = FLYING;
80       sprite->set_action(dir == LEFT ? "left" : "right");
81       physic.set_velocity_x(dir == LEFT ? -speed : speed);
82       physic.set_velocity_y(0);
83     } else
84       if (state == CLIMBING) {
85         dir = (dir == LEFT ? RIGHT : LEFT);
86         sprite->set_action(dir == LEFT ? "left" : "right");
87         physic.set_velocity_x(dir == LEFT ? -speed : speed);
88       } else {
89         assert(false);
90       }
91 }
92
93 void
94 Zeekling::onBumpVertical() {
95   if (state == FLYING) {
96     physic.set_velocity_y(0);
97   } else
98     if (state == DIVING) {
99       state = CLIMBING;
100       physic.set_velocity_y(-speed);
101       sprite->set_action(dir == LEFT ? "left" : "right");
102     } else
103       if (state == CLIMBING) {
104         state = FLYING;
105         physic.set_velocity_y(0);
106       }
107 }
108
109 void
110 Zeekling::collision_solid(const CollisionHit& hit)
111 {
112   if(sprite->get_action() == "squished-left" ||
113      sprite->get_action() == "squished-right")
114   {
115     return;
116   }
117
118   if(hit.top || hit.bottom) {
119     onBumpVertical();
120   } else if(hit.left || hit.right) {
121     onBumpHorizontal();
122   }
123 }
124
125 /**
126  * linear prediction of player and badguy positions to decide if we should enter the DIVING state
127  */
128 bool
129 Zeekling::should_we_dive() {
130
131   const MovingObject* player = this->get_nearest_player();
132   if (player && last_player && (player == last_player)) {
133
134     // get positions, calculate movement
135     const Vector player_pos = player->get_pos();
136     const Vector player_mov = (player_pos - last_player_pos);
137     const Vector self_pos = this->get_pos();
138     const Vector self_mov = (self_pos - last_self_pos);
139
140     // new vertical speed to test with
141     float vy = 2*fabsf(self_mov.x);
142
143     // do not dive if we are not above the player
144     float height = player_pos.y - self_pos.y;
145     if (height <= 0) return false;
146
147     // do not dive if we are too far above the player
148     if (height > 512) return false;
149
150     // do not dive if we would not descend faster than the player
151     float relSpeed = vy - player_mov.y;
152     if (relSpeed <= 0) return false;
153
154     // guess number of frames to descend to same height as player
155     float estFrames = height / relSpeed;
156
157     // guess where the player would be at this time
158     float estPx = (player_pos.x + (estFrames * player_mov.x));
159
160     // guess where we would be at this time
161     float estBx = (self_pos.x + (estFrames * self_mov.x));
162
163     // near misses are OK, too
164     if (fabsf(estPx - estBx) < 8) return true;
165   }
166
167   // update last player tracked, as well as our positions
168   last_player = player;
169   if (player) {
170     last_player_pos = player->get_pos();
171     last_self_pos = this->get_pos();
172   }
173
174   return false;
175 }
176
177 void
178 Zeekling::active_update(float elapsed_time) {
179   if (state == FLYING) {
180     if (should_we_dive()) {
181       state = DIVING;
182       physic.set_velocity_y(2*fabsf(physic.get_velocity_x()));
183       sprite->set_action(dir == LEFT ? "diving-left" : "diving-right");
184     }
185     BadGuy::active_update(elapsed_time);
186     return;
187   } else if (state == DIVING) {
188     BadGuy::active_update(elapsed_time);
189     return;
190   } else if (state == CLIMBING) {
191     // stop climbing when we're back at initial height
192     if (get_pos().y <= start_position.y) {
193       state = FLYING;
194       physic.set_velocity_y(0);
195     }
196     BadGuy::active_update(elapsed_time);
197     return;
198   } else {
199     assert(false);
200   }
201 }
202
203 /* EOF */