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