Massive copyright update. I'm sorry if I'm crediting Matze for something he didn...
[supertux.git] / src / badguy / yeti.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 #include <config.h>
21
22 #include <float.h>
23 #include <sstream>
24 #include <memory>
25 #include "yeti.hpp"
26 #include "object/camera.hpp"
27 #include "yeti_stalactite.hpp"
28 #include "bouncing_snowball.hpp"
29 #include "game_session.hpp"
30
31 static const float JUMP_VEL1 = 250;
32 static const float JUMP_VEL2 = 700;
33 static const float RUN_SPEED = 350;
34 static const float JUMP_TIME = 1.6;
35 static const float ANGRY_JUMP_WAIT = .5;
36 /// the time we are safe when tux just hit us
37 static const float SAFE_TIME = .5;
38 static const int INITIAL_HITPOINTS = 3;
39
40 Yeti::Yeti(const lisp::Lisp& reader)
41 {
42   reader.get("x", start_position.x);
43   reader.get("y", start_position.y);
44   bbox.set_size(90, 106);
45   sprite = sprite_manager->create("images/creatures/yeti/yeti.sprite");
46   sprite->set_action("right");
47   state = INIT;
48   side = LEFT;
49 #if 0
50   sound_manager->preload_sound("yeti_gna");
51   sound_manager->preload_sound("yeti_roar");
52 #endif
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::draw(DrawingContext& context)
64 {
65   // we blink when we are safe
66   if(safe_timer.started() && size_t(game_time*40)%2)
67     return;
68
69   BadGuy::draw(context);
70 }
71
72 void
73 Yeti::active_update(float elapsed_time)
74 {
75   switch(state) {
76     case INIT:
77       break;
78     case GO_RIGHT:
79       physic.set_velocity_x(RUN_SPEED);
80       if(timer.check())
81         physic.set_velocity_y(JUMP_VEL2);
82       break;
83     case GO_LEFT:
84       physic.set_velocity_x(-RUN_SPEED);
85       if(timer.check())
86         physic.set_velocity_y(JUMP_VEL2);
87       break;
88     case ANGRY_JUMPING:
89       if(timer.check()) {
90         // jump
91         sound_manager->play("sounds/yeti_gna.wav");
92         physic.set_velocity_y(JUMP_VEL1);
93         if (side == LEFT)  // on the left, facing Tux who is on the right
94           sprite->set_action("jump-right");
95         else
96           sprite->set_action("jump-left");
97       }
98       break;
99     default:
100       break;
101   }
102
103   movement = physic.get_movement(elapsed_time);
104 }
105
106 void
107 Yeti::go_right()
108 {
109   // jump and move right
110   sprite->set_action("right");
111   physic.set_velocity_y(JUMP_VEL1);
112   physic.set_velocity_x(RUN_SPEED);
113   state = GO_RIGHT;
114   timer.start(JUMP_TIME);
115 }
116
117 void
118 Yeti::go_left()
119 {
120   sprite->set_action("left");
121   physic.set_velocity_y(JUMP_VEL1);
122   physic.set_velocity_x(-RUN_SPEED);
123   state = GO_LEFT;
124   timer.start(JUMP_TIME);
125 }
126
127 void
128 Yeti::angry_jumping()
129 {
130   jumpcount = 0;
131   timer.start(ANGRY_JUMP_WAIT);
132   state = ANGRY_JUMPING;
133   physic.set_velocity_x(0);
134 }
135
136 void
137 Yeti::summon_snowball()
138 {
139   Sector::current()->add_object(new BouncingSnowball(get_pos().x+(side == LEFT ? 64 : -64), get_pos().y, (side == LEFT ? RIGHT : LEFT)));
140 }
141
142 bool
143 Yeti::collision_squished(Player& player)
144 {
145   if(safe_timer.started())
146     return true;
147
148   player.bounce(*this);
149   sound_manager->play("sounds/yeti_roar.wav");
150   hit_points--;
151   if(hit_points <= 0) {
152     sprite->set_action("dead");
153     kill_squished(player);
154
155     // start script
156     if(dead_script != "") {
157       std::istringstream stream(dead_script);
158       Sector::current()->run_script(stream, "Yeti - dead-script");
159     }
160   } else {
161     safe_timer.start(SAFE_TIME);
162   }
163   
164   return true;
165 }
166
167 void
168 Yeti::kill_fall()
169 {
170   // shooting bullets or being invincible won't work :)
171 }
172
173 void
174 Yeti::write(lisp::Writer& writer)
175 {
176   writer.start_list("yeti");
177
178   writer.write_float("x", start_position.x);
179   writer.write_float("y", start_position.y);
180
181   if(dead_script != "") {
182     writer.write_string("dead-script", dead_script);
183   }
184
185   writer.end_list("yeti");
186 }
187
188 void
189 Yeti::drop_stalactite()
190 {
191   YetiStalactite* nearest = 0;
192   float dist = FLT_MAX;
193
194   Player* player = this->get_nearest_player();
195   if (!player) return;
196
197   Sector* sector = Sector::current();
198   for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
199       i != sector->gameobjects.end(); ++i) {
200     YetiStalactite* stalactite = dynamic_cast<YetiStalactite*> (*i);
201     if(stalactite && stalactite->is_hanging()) {
202       float sdist 
203         = fabsf(stalactite->get_pos().x - player->get_pos().x);
204       if(sdist < dist) {
205         nearest = stalactite;
206         dist = sdist;
207       }
208     }
209   }
210
211   if(nearest)
212     nearest->start_shaking();
213 }
214
215 HitResponse
216 Yeti::collision_solid(GameObject& , const CollisionHit& hit)
217 {
218   if(fabsf(hit.normal.y) > .5) { // hit floor or roof?
219     physic.set_velocity_y(0);
220     if(state == INIT) {
221       go_right();
222     } else if(state == GO_LEFT && !timer.started()) {
223       side = LEFT;
224       summon_snowball();
225       sprite->set_action("stand-right");
226       angry_jumping();
227     } else if(state == GO_RIGHT && !timer.started()) {
228       side = RIGHT;
229       summon_snowball();
230       sprite->set_action("stand-left");
231       angry_jumping();
232     } else if(state == ANGRY_JUMPING) {
233       if(!timer.started()) {
234         // we just landed
235         if (side == LEFT)  // standing on the left, facing Tux who is on the right
236               sprite->set_action("stand-right");
237             else
238               sprite->set_action("stand-left");
239         jumpcount++;
240         // make a stalactite falling down and shake camera a bit
241         Sector::current()->camera->shake(.1, 0, 10);
242         drop_stalactite();
243         
244         // go to other side after 3 jumps
245         if(jumpcount == 3) {
246           if(side == LEFT)
247             go_right();
248           else
249             go_left();
250         } else {
251           // jump again
252           timer.start(ANGRY_JUMP_WAIT);
253         }
254       }
255     }
256   }
257   
258   return CONTINUE;
259 }
260
261 IMPLEMENT_FACTORY(Yeti, "yeti")