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