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