Let us hit yeti with bullets
[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& )
183 {
184   if(safe_timer.started())
185     return;
186
187   sound_manager->play("sounds/yeti_roar.wav");
188   hit_points--;
189
190   if(hit_points <= 0) {
191     // We're dead
192     physic.enable_gravity(true);
193     physic.set_velocity_x(0);
194     physic.set_velocity_y(0);
195     
196     state = SQUISHED;
197     state_timer.start(SQUISH_TIME);
198     set_group(COLGROUP_MOVING_ONLY_STATIC);
199     sprite->set_action("dead");
200
201     if (countMe) Sector::current()->get_level()->stats.badguys++;
202
203     // start script
204     if(dead_script != "") {
205       std::istringstream stream(dead_script);
206       Sector::current()->run_script(stream, "Yeti - dead-script");
207     }
208   }
209   else {
210     safe_timer.start(SAFE_TIME);
211   }
212 }
213
214 void
215 Yeti::kill_fall()
216 {
217   // shooting bullets or being invincible won't work :)
218   take_hit(*get_nearest_player()); // FIXME: debug only(?)
219 }
220
221 void
222 Yeti::write(lisp::Writer& writer)
223 {
224   writer.start_list("yeti");
225
226   writer.write_float("x", start_position.x);
227   writer.write_float("y", start_position.y);
228
229   if(dead_script != "") {
230     writer.write_string("dead-script", dead_script);
231   }
232
233   writer.end_list("yeti");
234 }
235
236 void
237 Yeti::drop_stalactite()
238 {
239   // make a stalactite falling down and shake camera a bit
240   Sector::current()->camera->shake(.1, 0, 10);
241
242   YetiStalactite* nearest = 0;
243   float dist = FLT_MAX;
244
245   Player* player = this->get_nearest_player();
246   if (!player) return;
247
248   Sector* sector = Sector::current();
249   for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
250       i != sector->gameobjects.end(); ++i) {
251     YetiStalactite* stalactite = dynamic_cast<YetiStalactite*> (*i);
252     if(stalactite && stalactite->is_hanging()) {
253       float sdist 
254         = fabsf(stalactite->get_pos().x - player->get_pos().x);
255       if(sdist < dist) {
256         nearest = stalactite;
257         dist = sdist;
258       }
259     }
260   }
261
262   if(nearest)
263     nearest->start_shaking();
264 }
265
266 HitResponse
267 Yeti::collision_solid(GameObject& , const CollisionHit& hit)
268 {
269   if(fabsf(hit.normal.y) > .5) { 
270     // hit floor or roof
271     physic.set_velocity_y(0);
272     switch (state) {
273       case JUMP_DOWN:
274         run();
275         break;
276       case RUN:
277         break;
278       case JUMP_UP:
279         break;
280       case BE_ANGRY:
281         // we just landed
282         if(!state_timer.started()) {
283           sprite->set_action((dir==RIGHT)?"stand-right":"stand-left");
284           stomp_count++;
285           drop_stalactite();
286
287           // go to other side after 3 jumps
288           if(stomp_count == 3) {
289             jump_down();
290           } else {
291             // jump again
292             state_timer.start(STOMP_WAIT);
293           }
294         }
295         break;
296       case SQUISHED:
297         break;
298     }
299   } else 
300   if(fabsf(hit.normal.x) > .5) {
301     // hit wall
302     jump_up();
303   }
304
305   return CONTINUE;
306 }
307
308 IMPLEMENT_FACTORY(Yeti, "yeti")