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