Badguys rely on new (dummy-)function get_nearest_player. /
[supertux.git] / src / badguy / yeti.cpp
1 //  $Id$
2 // 
3 //  SuperTux
4 //  Copyright (C) 2005 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
19 //  02111-1307, USA.
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 #include "scripting/script_interpreter.hpp"
31
32 static const float JUMP_VEL1 = 250;
33 static const float JUMP_VEL2 = 700;
34 static const float RUN_SPEED = 350;
35 static const float JUMP_TIME = 1.6;
36 static const float ANGRY_JUMP_WAIT = .5;
37 /// the time we are safe when tux just hit us
38 static const float SAFE_TIME = .5;
39 static const int INITIAL_HITPOINTS = 3;
40
41 Yeti::Yeti(const lisp::Lisp& reader)
42 {
43   reader.get("x", start_position.x);
44   reader.get("y", start_position.y);
45   bbox.set_size(80, 120);
46   sprite = sprite_manager->create("images/creatures/yeti/yeti.sprite");
47   sprite->set_action("right");
48   state = INIT;
49   side = LEFT;
50 #if 0
51   sound_manager->preload_sound("yeti_gna");
52   sound_manager->preload_sound("yeti_roar");
53 #endif
54   hit_points = INITIAL_HITPOINTS;
55   reader.get("dead-script", dead_script);
56   countMe = false;
57 }
58
59 Yeti::~Yeti()
60 {
61 }
62
63 void
64 Yeti::draw(DrawingContext& context)
65 {
66   // we blink when we are safe
67   if(safe_timer.started() && size_t(game_time*40)%2)
68     return;
69
70   BadGuy::draw(context);
71 }
72
73 void
74 Yeti::active_update(float elapsed_time)
75 {
76   switch(state) {
77     case INIT:
78       break;
79     case GO_RIGHT:
80       physic.set_velocity_x(RUN_SPEED);
81       if(timer.check())
82         physic.set_velocity_y(JUMP_VEL2);
83       break;
84     case GO_LEFT:
85       physic.set_velocity_x(-RUN_SPEED);
86       if(timer.check())
87         physic.set_velocity_y(JUMP_VEL2);
88       break;
89     case ANGRY_JUMPING:
90       if(timer.check()) {
91         // jump
92         sound_manager->play("sounds/yeti_gna.wav");
93         physic.set_velocity_y(JUMP_VEL1);
94         if (side == LEFT)  // on the left, facing Tux who is on the right
95           sprite->set_action("jump-right");
96         else
97           sprite->set_action("jump-left");
98       }
99       break;
100     default:
101       break;
102   }
103
104   movement = physic.get_movement(elapsed_time);
105 }
106
107 void
108 Yeti::go_right()
109 {
110   // jump and move right
111   sprite->set_action("right");
112   physic.set_velocity_y(JUMP_VEL1);
113   physic.set_velocity_x(RUN_SPEED);
114   state = GO_RIGHT;
115   timer.start(JUMP_TIME);
116 }
117
118 void
119 Yeti::go_left()
120 {
121   sprite->set_action("left");
122   physic.set_velocity_y(JUMP_VEL1);
123   physic.set_velocity_x(-RUN_SPEED);
124   state = GO_LEFT;
125   timer.start(JUMP_TIME);
126 }
127
128 void
129 Yeti::angry_jumping()
130 {
131   jumpcount = 0;
132   timer.start(ANGRY_JUMP_WAIT);
133   state = ANGRY_JUMPING;
134   physic.set_velocity_x(0);
135 }
136
137 void
138 Yeti::summon_snowball()
139 {
140   Sector::current()->add_object(new BouncingSnowball(get_pos().x+(side == LEFT ? 64 : -64), get_pos().y, (side == LEFT ? RIGHT : LEFT)));
141 }
142
143 bool
144 Yeti::collision_squished(Player& player)
145 {
146   if(safe_timer.started())
147     return true;
148
149   player.bounce(*this);
150   sound_manager->play("sounds/yeti_roar.wav");
151   hit_points--;
152   if(hit_points <= 0) {
153     sprite->set_action("dead");
154     kill_squished(player);
155
156     // start script
157     if(dead_script != "") {
158       ScriptInterpreter::add_script_object(Sector::current(),
159           "Yeti - dead-script", dead_script);
160     }
161   } else {
162     safe_timer.start(SAFE_TIME);
163   }
164   
165   return true;
166 }
167
168 void
169 Yeti::kill_fall()
170 {
171   // shooting bullets or being invincible won't work :)
172 }
173
174 void
175 Yeti::write(lisp::Writer& writer)
176 {
177   writer.start_list("yeti");
178
179   writer.write_float("x", start_position.x);
180   writer.write_float("y", start_position.y);
181
182   if(dead_script != "") {
183     writer.write_string("dead-script", dead_script);
184   }
185
186   writer.end_list("yeti");
187 }
188
189 void
190 Yeti::drop_stalactite()
191 {
192   YetiStalactite* nearest = 0;
193   float dist = FLT_MAX;
194
195   Player* player = this->get_nearest_player();
196   if (!player) return;
197
198   Sector* sector = Sector::current();
199   for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
200       i != sector->gameobjects.end(); ++i) {
201     YetiStalactite* stalactite = dynamic_cast<YetiStalactite*> (*i);
202     if(stalactite && stalactite->is_hanging()) {
203       float sdist 
204         = fabsf(stalactite->get_pos().x - player->get_pos().x);
205       if(sdist < dist) {
206         nearest = stalactite;
207         dist = sdist;
208       }
209     }
210   }
211
212   if(nearest)
213     nearest->start_shaking();
214 }
215
216 HitResponse
217 Yeti::collision_solid(GameObject& , const CollisionHit& hit)
218 {
219   if(fabsf(hit.normal.y) > .5) { // hit floor or roof?
220     physic.set_velocity_y(0);
221     if(state == INIT) {
222       go_right();
223     } else if(state == GO_LEFT && !timer.started()) {
224       side = LEFT;
225       summon_snowball();
226       sprite->set_action("stand-right");
227       angry_jumping();
228     } else if(state == GO_RIGHT && !timer.started()) {
229       side = RIGHT;
230       summon_snowball();
231       sprite->set_action("stand-left");
232       angry_jumping();
233     } else if(state == ANGRY_JUMPING) {
234       if(!timer.started()) {
235         // we just landed
236         if (side == LEFT)  // standing on the left, facing Tux who is on the right
237               sprite->set_action("stand-right");
238             else
239               sprite->set_action("stand-left");
240         jumpcount++;
241         // make a stalactite falling down and shake camera a bit
242         Sector::current()->camera->shake(.1, 0, 10);
243         drop_stalactite();
244         
245         // go to other side after 3 jumps
246         if(jumpcount == 3) {
247           if(side == LEFT)
248             go_right();
249           else
250             go_left();
251         } else {
252           // jump again
253           timer.start(ANGRY_JUMP_WAIT);
254         }
255       }
256     }
257   }
258   
259   return CONTINUE;
260 }
261
262 IMPLEMENT_FACTORY(Yeti, "yeti")