B118: visual indication of Yeti's hitpoints. The scaled down image of the Yeti's...
[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 = 5;
53 }
54
55 Yeti::Yeti(const lisp::Lisp& reader)
56         : BadGuy(reader, "images/creatures/yeti/yeti.sprite")
57 {
58   hit_points = INITIAL_HITPOINTS;
59   countMe = false;
60   sound_manager->preload("sounds/yeti_gna.wav");
61   sound_manager->preload("sounds/yeti_roar.wav");
62   hud_head.reset(new Surface("images/creatures/yeti/hudlife.png"));
63   draw_dead_script_hint = 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   draw_hit_points(context);
85
86   BadGuy::draw(context);
87 }
88
89 void
90 Yeti::draw_hit_points(DrawingContext& context)
91 {
92   int i;
93
94   Surface *hh = hud_head.get();
95   if (!hh)
96     return;
97
98   context.push_transform();
99   context.set_translation(Vector(0, 0));
100
101   for (i = 0; i < hit_points; ++i)
102   {
103     context.draw_surface(hh, Vector(BORDER_X + (i * hh->get_width()), BORDER_Y + 1), LAYER_FOREGROUND1);
104   }
105
106   context.pop_transform();
107 }
108
109 void
110 Yeti::active_update(float elapsed_time)
111 {
112   switch(state) {
113     case JUMP_DOWN:
114       physic.set_velocity_x((dir==RIGHT)?+JUMP_DOWN_VX:-JUMP_DOWN_VX);
115       break;
116     case RUN:
117       physic.set_velocity_x((dir==RIGHT)?+RUN_VX:-RUN_VX);
118       if (((dir == RIGHT) && (get_pos().x >= RIGHT_JUMP_X)) || ((dir == LEFT) && (get_pos().x <= LEFT_JUMP_X))) jump_up();
119       break;
120     case JUMP_UP:
121       physic.set_velocity_x((dir==RIGHT)?+JUMP_UP_VX:-JUMP_UP_VX);
122       if (((dir == RIGHT) && (get_pos().x >= RIGHT_STAND_X)) || ((dir == LEFT) && (get_pos().x <= LEFT_STAND_X))) be_angry();
123       break;
124     case BE_ANGRY:
125       if(state_timer.check()) {
126         sound_manager->play("sounds/yeti_gna.wav");
127         physic.set_velocity_y(STOMP_VY);
128         sprite->set_action((dir==RIGHT)?"stomp-right":"stomp-left");
129       }
130       break;
131     case SQUISHED:
132       if (state_timer.check()) {
133         remove_me();
134       }
135       break;
136   }
137
138   movement = physic.get_movement(elapsed_time);
139 }
140
141 void
142 Yeti::jump_down()
143 {
144   sprite->set_action((dir==RIGHT)?"jump-right":"jump-left");
145   physic.set_velocity_x((dir==RIGHT)?(+JUMP_DOWN_VX):(-JUMP_DOWN_VX));
146   physic.set_velocity_y(JUMP_DOWN_VY);
147   state = JUMP_DOWN;
148 }
149
150 void
151 Yeti::run()
152 {
153   sprite->set_action((dir==RIGHT)?"run-right":"run-left");
154   physic.set_velocity_x((dir==RIGHT)?(+RUN_VX):(-RUN_VX));
155   physic.set_velocity_y(0);
156   state = RUN;
157 }
158
159 void
160 Yeti::jump_up()
161 {
162   sprite->set_action((dir==RIGHT)?"jump-right":"jump-left");
163   physic.set_velocity_x((dir==RIGHT)?(+JUMP_UP_VX):(-JUMP_UP_VX));
164   physic.set_velocity_y(JUMP_UP_VY);
165   state = JUMP_UP;
166 }
167
168 void
169 Yeti::be_angry()
170 {
171   //turn around
172   dir = (dir==RIGHT)?LEFT:RIGHT;
173
174   sprite->set_action((dir==RIGHT)?"stand-right":"stand-left");
175   physic.set_velocity_x(0);
176   physic.set_velocity_y(0);
177   if (hit_points < INITIAL_HITPOINTS) summon_snowball();
178   stomp_count = 0;
179   state = BE_ANGRY;
180   state_timer.start(STOMP_WAIT);
181 }
182
183 void
184 Yeti::summon_snowball()
185 {
186   Sector::current()->add_object(new BouncingSnowball(Vector(get_pos().x+(dir == RIGHT ? 64 : -64), get_pos().y), dir));
187 }
188
189 bool
190 Yeti::collision_squished(GameObject& object)
191 {
192   kill_squished(object);
193
194   return true;
195 }
196
197 void
198 Yeti::kill_squished(GameObject& object)
199 {
200   Player* player = dynamic_cast<Player*>(&object);
201   if (player) {
202     player->bounce(*this);
203     take_hit(*player);
204   }
205 }
206
207 void Yeti::take_hit(Player& )
208 {
209   if(safe_timer.started())
210     return;
211
212   sound_manager->play("sounds/yeti_roar.wav");
213   hit_points--;
214
215   if(hit_points <= 0) {
216     // We're dead
217     physic.enable_gravity(true);
218     physic.set_velocity_x(0);
219     physic.set_velocity_y(0);
220
221     state = SQUISHED;
222     state_timer.start(SQUISH_TIME);
223     set_group(COLGROUP_MOVING_ONLY_STATIC);
224     sprite->set_action("dead");
225
226     if (countMe) Sector::current()->get_level()->stats.badguys++;
227
228     if(dead_script != "") {
229       std::istringstream stream(dead_script);
230       Sector::current()->run_script(stream, "Yeti - dead-script");
231     }
232   }
233   else {
234     safe_timer.start(SAFE_TIME);
235   }
236 }
237
238 void
239 Yeti::kill_fall()
240 {
241   // shooting bullets or being invincible won't work :)
242   take_hit(*get_nearest_player()); // FIXME: debug only(?)
243 }
244
245 void
246 Yeti::write(lisp::Writer& writer)
247 {
248   writer.start_list("yeti");
249
250   writer.write_float("x", start_position.x);
251   writer.write_float("y", start_position.y);
252
253   writer.end_list("yeti");
254 }
255
256 void
257 Yeti::drop_stalactite()
258 {
259   // make a stalactite falling down and shake camera a bit
260   Sector::current()->camera->shake(.1f, 0, 10);
261
262   YetiStalactite* nearest = 0;
263   float dist = FLT_MAX;
264
265   Player* player = this->get_nearest_player();
266   if (!player) return;
267
268   Sector* sector = Sector::current();
269   for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
270       i != sector->gameobjects.end(); ++i) {
271     YetiStalactite* stalactite = dynamic_cast<YetiStalactite*> (*i);
272     if(stalactite && stalactite->is_hanging()) {
273       float sdist
274         = fabsf(stalactite->get_pos().x - player->get_pos().x);
275       if(sdist < dist) {
276         nearest = stalactite;
277         dist = sdist;
278       }
279     }
280   }
281
282   if(nearest)
283     nearest->start_shaking();
284 }
285
286 void
287 Yeti::collision_solid(const CollisionHit& hit)
288 {
289   if(hit.top || hit.bottom) {
290     // hit floor or roof
291     physic.set_velocity_y(0);
292     switch (state) {
293       case JUMP_DOWN:
294         run();
295         break;
296       case RUN:
297         break;
298       case JUMP_UP:
299         break;
300       case BE_ANGRY:
301         // we just landed
302         if(!state_timer.started()) {
303           sprite->set_action((dir==RIGHT)?"stand-right":"stand-left");
304           stomp_count++;
305           drop_stalactite();
306
307           // go to other side after 3 jumps
308           if(stomp_count == 3) {
309             jump_down();
310           } else {
311             // jump again
312             state_timer.start(STOMP_WAIT);
313           }
314         }
315         break;
316       case SQUISHED:
317         break;
318     }
319   } else if(hit.left || hit.right) {
320     // hit wall
321     jump_up();
322   }
323 }
324
325 IMPLEMENT_FACTORY(Yeti, "yeti")