Use supertux2 on Windows, zeekling squish animation fix.
[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 = systemRandom.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 = systemRandom.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(hit.top || hit.bottom) {
113     onBumpVertical();
114   } else if(hit.left || hit.right) {
115     onBumpHorizontal();
116   }
117 }
118
119 /**
120  * linear prediction of player and badguy positions to decide if we should enter the DIVING state
121  */
122 bool
123 Zeekling::should_we_dive() {
124
125   const MovingObject* player = this->get_nearest_player();
126   if (player && last_player && (player == last_player)) {
127
128     // get positions, calculate movement
129     const Vector player_pos = player->get_pos();
130     const Vector player_mov = (player_pos - last_player_pos);
131     const Vector self_pos = this->get_pos();
132     const Vector self_mov = (self_pos - last_self_pos);
133
134     // new vertical speed to test with
135     float vy = 2*fabsf(self_mov.x);
136
137     // do not dive if we are not above the player
138     float height = player_pos.y - self_pos.y;
139     if (height <= 0) return false;
140
141     // do not dive if we are too far above the player
142     if (height > 512) return false;
143
144     // do not dive if we would not descend faster than the player
145     float relSpeed = vy - player_mov.y;
146     if (relSpeed <= 0) return false;
147
148     // guess number of frames to descend to same height as player
149     float estFrames = height / relSpeed;
150
151     // guess where the player would be at this time
152     float estPx = (player_pos.x + (estFrames * player_mov.x));
153
154     // guess where we would be at this time
155     float estBx = (self_pos.x + (estFrames * self_mov.x));
156
157     // near misses are OK, too
158     if (fabsf(estPx - estBx) < 8) return true;
159   }
160
161   // update last player tracked, as well as our positions
162   last_player = player;
163   if (player) {
164     last_player_pos = player->get_pos();
165     last_self_pos = this->get_pos();
166   }
167
168   return false;
169 }
170
171 void
172 Zeekling::active_update(float elapsed_time) {
173   if (state == FLYING) {
174     if (should_we_dive()) {
175       state = DIVING;
176       physic.set_velocity_y(2*fabsf(physic.get_velocity_x()));
177       sprite->set_action(dir == LEFT ? "diving-left" : "diving-right");
178     }
179     BadGuy::active_update(elapsed_time);
180     return;
181   } else if (state == DIVING) {
182     BadGuy::active_update(elapsed_time);
183     return;
184   } else if (state == CLIMBING) {
185     // stop climbing when we're back at initial height
186     if (get_pos().y <= start_position.y) {
187       state = FLYING;
188       physic.set_velocity_y(0);
189     }
190     BadGuy::active_update(elapsed_time);
191     return;
192   } else {
193     assert(false);
194   }
195 }
196
197 /* EOF */