- fix debugging functions for killing player
[supertux.git] / src / badguy / yeti.cpp
1 //  $Id$
2 // 
3 //  SuperTux - Boss "Yeti"
4 //  Copyright (C) 2005 Matthias Braun <matze@braunis.de>
5 //  Copyright (C) 2006 Christoph Sommer <christoph.sommer@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 #include <config.h>
22
23 #include <float.h>
24 #include <sstream>
25 #include <memory>
26 #include "yeti.hpp"
27 #include "object/camera.hpp"
28 #include "yeti_stalactite.hpp"
29 #include "bouncing_snowball.hpp"
30 #include "game_session.hpp"
31 #include "level.hpp"
32
33 namespace {
34   const float JUMP_DOWN_VX = 250; /**< horizontal speed while jumping off the dais */
35   const float JUMP_DOWN_VY = 250; /**< vertical speed while jumping off the dais */
36
37   const float RUN_VX = 350; /**< horizontal speed while running */
38
39   const float JUMP_UP_VX = 350; /**< horizontal speed while jumping on the dais */
40   const float JUMP_UP_VY = 800; /**< vertical speed while jumping on the dais */
41
42   const float STOMP_VY = 250; /** vertical speed while stomping on the dais */
43
44   const float LEFT_STAND_X = 16; /**< x-coordinate of left dais' end position */
45   const float RIGHT_STAND_X = 800-60-16; /**< x-coordinate of right dais' end position */ 
46   const float LEFT_JUMP_X = LEFT_STAND_X+224; /**< x-coordinate of from where to jump on the left dais */
47   const float RIGHT_JUMP_X = RIGHT_STAND_X-224; /**< x-coordinate of from where to jump on the right dais */
48   const float STOMP_WAIT = .5; /**< time we stay on the dais before jumping again */
49   const float SAFE_TIME = .5; /**< the time we are safe when tux just hit us */
50   const int INITIAL_HITPOINTS = 3; /**< number of hits we can take */
51
52   const float SQUISH_TIME = 3;
53 }
54
55 Yeti::Yeti(const lisp::Lisp& reader)
56 {
57   reader.get("x", start_position.x);
58   reader.get("y", start_position.y);
59   bbox.set_size(60, 90);
60   sprite = sprite_manager->create("images/creatures/yeti/yeti.sprite");
61   hit_points = INITIAL_HITPOINTS;
62   reader.get("dead-script", dead_script);
63   countMe = false;
64 }
65
66 Yeti::~Yeti()
67 {
68 }
69
70 void
71 Yeti::activate()
72 {
73   dir = RIGHT;
74   jump_down();
75 }
76
77 void
78 Yeti::draw(DrawingContext& context)
79 {
80   // we blink when we are safe
81   if(safe_timer.started() && size_t(game_time*40)%2)
82     return;
83
84   BadGuy::draw(context);
85 }
86
87 void
88 Yeti::active_update(float elapsed_time)
89 {
90   switch(state) {
91     case JUMP_DOWN:
92       physic.set_velocity_x((dir==RIGHT)?+JUMP_DOWN_VX:-JUMP_DOWN_VX);
93       break;
94     case RUN:
95       physic.set_velocity_x((dir==RIGHT)?+RUN_VX:-RUN_VX);
96       if (((dir == RIGHT) && (get_pos().x >= RIGHT_JUMP_X)) || ((dir == LEFT) && (get_pos().x <= LEFT_JUMP_X))) jump_up();
97       break;
98     case JUMP_UP:
99       physic.set_velocity_x((dir==RIGHT)?+JUMP_UP_VX:-JUMP_UP_VX);
100       if (((dir == RIGHT) && (get_pos().x >= RIGHT_STAND_X)) || ((dir == LEFT) && (get_pos().x <= LEFT_STAND_X))) be_angry();
101       break;
102     case BE_ANGRY:
103       if(state_timer.check()) {
104         sound_manager->play("sounds/yeti_gna.wav");
105         physic.set_velocity_y(STOMP_VY);
106         sprite->set_action((dir==RIGHT)?"stomp-right":"stomp-left");
107       }
108       break;
109     case SQUISHED:
110       if (state_timer.check()) {
111         remove_me();
112       }
113       break;
114   }
115
116   movement = physic.get_movement(elapsed_time);
117 }
118
119 void
120 Yeti::jump_down()
121 {
122   sprite->set_action((dir==RIGHT)?"jump-right":"jump-left");
123   physic.set_velocity_x((dir==RIGHT)?(+JUMP_DOWN_VX):(-JUMP_DOWN_VX));
124   physic.set_velocity_y(JUMP_DOWN_VY);
125   state = JUMP_DOWN;
126 }
127
128 void
129 Yeti::run()
130 {
131   sprite->set_action((dir==RIGHT)?"run-right":"run-left");
132   physic.set_velocity_x((dir==RIGHT)?(+RUN_VX):(-RUN_VX));
133   physic.set_velocity_y(0);
134   state = RUN;
135 }
136
137 void
138 Yeti::jump_up()
139 {
140   sprite->set_action((dir==RIGHT)?"jump-right":"jump-left");
141   physic.set_velocity_x((dir==RIGHT)?(+JUMP_UP_VX):(-JUMP_UP_VX));
142   physic.set_velocity_y(JUMP_UP_VY);
143   state = JUMP_UP;
144 }
145
146 void
147 Yeti::be_angry()
148 {
149   //turn around
150   dir = (dir==RIGHT)?LEFT:RIGHT;
151
152   sprite->set_action((dir==RIGHT)?"stand-right":"stand-left");
153   physic.set_velocity_x(0);
154   physic.set_velocity_y(0);
155   if (hit_points < INITIAL_HITPOINTS) summon_snowball();
156   stomp_count = 0;
157   state = BE_ANGRY;
158   state_timer.start(STOMP_WAIT);
159 }
160
161 void
162 Yeti::summon_snowball()
163 {
164   Sector::current()->add_object(new BouncingSnowball(get_pos().x+(dir == RIGHT ? 64 : -64), get_pos().y, dir));
165 }
166
167 bool
168 Yeti::collision_squished(Player& player)
169 {
170   kill_squished(player);
171
172   return true;
173 }
174
175 void
176 Yeti::kill_squished(Player& player)
177 {
178     player.bounce(*this);
179     take_hit(player);
180 }
181
182 void Yeti::take_hit(Player& player)
183 {
184   player.bounce(*this);
185   
186   if(safe_timer.started())
187     return;
188
189   sound_manager->play("sounds/yeti_roar.wav");
190   hit_points--;
191
192   if(hit_points <= 0) {
193     // We're dead
194     physic.enable_gravity(true);
195     physic.set_velocity_x(0);
196     physic.set_velocity_y(0);
197     
198     state = SQUISHED;
199     state_timer.start(SQUISH_TIME);
200     set_group(COLGROUP_MOVING_ONLY_STATIC);
201     sprite->set_action("dead");
202
203     if (countMe) Sector::current()->get_level()->stats.badguys++;
204
205     // start script
206     if(dead_script != "") {
207       std::istringstream stream(dead_script);
208       Sector::current()->run_script(stream, "Yeti - dead-script");
209     }
210   }
211   else {
212     safe_timer.start(SAFE_TIME);
213   }
214 }
215
216 void
217 Yeti::kill_fall()
218 {
219   // shooting bullets or being invincible won't work :)
220   take_hit(*get_nearest_player()); // FIXME: debug only(?)
221 }
222
223 void
224 Yeti::write(lisp::Writer& writer)
225 {
226   writer.start_list("yeti");
227
228   writer.write_float("x", start_position.x);
229   writer.write_float("y", start_position.y);
230
231   if(dead_script != "") {
232     writer.write_string("dead-script", dead_script);
233   }
234
235   writer.end_list("yeti");
236 }
237
238 void
239 Yeti::drop_stalactite()
240 {
241   // make a stalactite falling down and shake camera a bit
242   Sector::current()->camera->shake(.1, 0, 10);
243
244   YetiStalactite* nearest = 0;
245   float dist = FLT_MAX;
246
247   Player* player = this->get_nearest_player();
248   if (!player) return;
249
250   Sector* sector = Sector::current();
251   for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
252       i != sector->gameobjects.end(); ++i) {
253     YetiStalactite* stalactite = dynamic_cast<YetiStalactite*> (*i);
254     if(stalactite && stalactite->is_hanging()) {
255       float sdist 
256         = fabsf(stalactite->get_pos().x - player->get_pos().x);
257       if(sdist < dist) {
258         nearest = stalactite;
259         dist = sdist;
260       }
261     }
262   }
263
264   if(nearest)
265     nearest->start_shaking();
266 }
267
268 HitResponse
269 Yeti::collision_solid(GameObject& , const CollisionHit& hit)
270 {
271   if(fabsf(hit.normal.y) > .5) { 
272     // hit floor or roof
273     physic.set_velocity_y(0);
274     switch (state) {
275       case JUMP_DOWN:
276         run();
277         break;
278       case RUN:
279         break;
280       case JUMP_UP:
281         break;
282       case BE_ANGRY:
283         // we just landed
284         if(!state_timer.started()) {
285           sprite->set_action((dir==RIGHT)?"stand-right":"stand-left");
286           stomp_count++;
287           drop_stalactite();
288
289           // go to other side after 3 jumps
290           if(stomp_count == 3) {
291             jump_down();
292           } else {
293             // jump again
294             state_timer.start(STOMP_WAIT);
295           }
296         }
297         break;
298       case SQUISHED:
299         break;
300     }
301   } else 
302   if(fabsf(hit.normal.x) > .5) {
303     // hit wall
304     jump_up();
305   }
306
307   return CONTINUE;
308 }
309
310 IMPLEMENT_FACTORY(Yeti, "yeti")