Committing RandomGenerator patch from Allen King, with a few small changes
[supertux.git] / src / badguy / zeekling.cpp
1 //  $Id$
2 //
3 //  Zeekling - flyer that swoops down when she spots the player
4 //  Copyright (C) 2005 Matthias Braun <matze@braunis.de>
5 //  Copyright (C) 2006 Christoph Sommer <supertux@2006.expires.deltadevelopment.de>
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 //
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 //  02111-1307, USA.
21
22 #include <config.h>
23 #include <math.h>
24
25 #include "zeekling.hpp"
26 #include "random_generator.hpp"
27
28 Zeekling::Zeekling(const lisp::Lisp& reader)
29 {
30   reader.get("x", start_position.x);
31   reader.get("y", start_position.y);
32   bbox.set_size(31.8, 31.8);
33   sprite = sprite_manager->create("images/creatures/zeekling/zeekling.sprite");
34   set_direction = false;
35   state = FLYING;
36 }
37
38 Zeekling::Zeekling(float pos_x, float pos_y, Direction d)
39 {
40   start_position.x = pos_x;
41   start_position.y = pos_y;
42   bbox.set_size(63.8, 50.8);
43   sprite = sprite_manager->create("images/creatures/zeekling/zeekling.sprite");
44   set_direction = true;
45   initial_direction = d;
46   state = FLYING;
47 }
48
49 void
50 Zeekling::write(lisp::Writer& writer)
51 {
52   writer.start_list("zeekling");
53
54   writer.write_float("x", start_position.x);
55   writer.write_float("y", start_position.y);
56
57   writer.end_list("zeekling");
58 }
59
60 void
61 Zeekling::activate()
62 {
63   speed = systemRandom.rand(130, 171);
64   if (set_direction) {dir = initial_direction;}
65   physic.set_velocity_x(dir == LEFT ? -speed : speed);
66   physic.enable_gravity(false);
67   sprite->set_action(dir == LEFT ? "left" : "right");
68 }
69
70 bool
71 Zeekling::collision_squished(Player& player)
72 {
73   sprite->set_action(dir == LEFT ? "squished-left" : "squished-right");
74   kill_squished(player);
75   kill_fall();
76   return true;
77 }
78
79 void 
80 Zeekling::onBumpHorizontal() {
81   if (state == FLYING) {
82     dir = (dir == LEFT ? RIGHT : LEFT);
83     sprite->set_action(dir == LEFT ? "left" : "right");
84     physic.set_velocity_x(dir == LEFT ? -speed : speed);
85   } else
86   if (state == DIVING) {
87     dir = (dir == LEFT ? RIGHT : LEFT);
88     state = FLYING;
89     sprite->set_action(dir == LEFT ? "left" : "right");
90     physic.set_velocity_x(dir == LEFT ? -speed : speed);
91     physic.set_velocity_y(0);
92   } else
93   if (state == CLIMBING) {
94     dir = (dir == LEFT ? RIGHT : LEFT);
95     sprite->set_action(dir == LEFT ? "left" : "right");
96     physic.set_velocity_x(dir == LEFT ? -speed : speed);
97   }
98 }
99
100 void 
101 Zeekling::onBumpVertical() {
102   if (state == FLYING) {
103     physic.set_velocity_y(0);
104   } else
105   if (state == DIVING) {
106     state = CLIMBING;
107     physic.set_velocity_y(speed);
108     sprite->set_action(dir == LEFT ? "left" : "right");
109   } else
110   if (state == CLIMBING) {
111     state = FLYING;
112     physic.set_velocity_y(0);
113   }
114 }
115
116 HitResponse
117 Zeekling::collision_solid(GameObject& , const CollisionHit& hit)
118 {
119   if(fabsf(hit.normal.y) > .5) {
120     onBumpVertical(); 
121   } else {
122     onBumpHorizontal();
123   }
124
125   return CONTINUE;
126 }
127
128 /**
129  * linear prediction of player and badguy positions to decide if we should enter the DIVING state
130  */
131 bool 
132 Zeekling::should_we_dive() {
133   const MovingObject* player = this->get_nearest_player();
134   if (!player) return false;
135
136   const MovingObject* badguy = this;
137
138   const Vector playerPos = player->get_pos();
139   const Vector playerMov = player->get_movement();
140
141   const Vector badguyPos = badguy->get_pos();
142   const Vector badguyMov = badguy->get_movement();
143
144   // new vertical speed to test with
145   float vy = -2*fabsf(badguyMov.x);
146
147   // do not dive if we are not above the player
148   float height = playerPos.y - badguyPos.y;
149   if (height <= 0) return false;
150
151   // do not dive if we would not descend faster than the player
152   float relSpeed = -vy + playerMov.y;
153   if (relSpeed <= 0) return false;
154
155   // guess number of frames to descend to same height as player
156   float estFrames = height / relSpeed;
157   
158   // guess where the player would be at this time
159   float estPx = (playerPos.x + (estFrames * playerMov.x));
160
161   // guess where we would be at this time
162   float estBx = (badguyPos.x + (estFrames * badguyMov.x));
163
164   // near misses are OK, too
165   if (fabsf(estPx - estBx) < 32) return true;
166
167   return false;
168 }
169
170 void 
171 Zeekling::active_update(float elapsed_time) {
172   BadGuy::active_update(elapsed_time);
173
174   if (state == FLYING) {
175     if (should_we_dive()) {
176       state = DIVING;
177       physic.set_velocity_y(-2*fabsf(physic.get_velocity_x()));
178       sprite->set_action(dir == LEFT ? "diving-left" : "diving-right");
179     }
180     return;
181   }
182
183   if (state == DIVING) {
184     return;
185   }
186
187   if (state == CLIMBING) {
188     // stop climbing when we're back at initial height
189     if (get_pos().y <= start_position.y) {
190       state = FLYING;
191       physic.set_velocity_y(0);
192     }
193     return;
194   }
195
196 }
197
198 IMPLEMENT_FACTORY(Zeekling, "zeekling")