bonusblock can now contain custom MovingObjects, added possibility to execute script...
[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.h"
26 #include "object/camera.h"
27 #include "yeti_stalactite.h"
28 #include "bouncing_snowball.h"
29 #include "game_session.h"
30 #include "scripting/script_interpreter.h"
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("yeti");
47   sprite->set_action("right");
48   state = INIT;
49   side = LEFT;
50   sound_manager->preload_sound("yeti_gna");
51   sound_manager->preload_sound("yeti_roar");
52   hit_points = INITIAL_HITPOINTS;
53   reader.get("dead-script", dead_script);
54   countMe = false;
55 }
56
57 Yeti::~Yeti()
58 {
59 }
60
61 void
62 Yeti::draw(DrawingContext& context)
63 {
64   // we blink when we are safe
65   if(safe_timer.started() && size_t(global_time*40)%2)
66     return;
67
68   BadGuy::draw(context);
69 }
70
71 void
72 Yeti::active_update(float elapsed_time)
73 {
74   switch(state) {
75     case INIT:
76       break;
77     case GO_RIGHT:
78       physic.set_velocity_x(RUN_SPEED);
79       if(timer.check())
80         physic.set_velocity_y(JUMP_VEL2);
81       break;
82     case GO_LEFT:
83       physic.set_velocity_x(-RUN_SPEED);
84       if(timer.check())
85         physic.set_velocity_y(JUMP_VEL2);
86       break;
87     case ANGRY_JUMPING:
88       if(timer.check()) {
89         // jump
90         sound_manager->play_sound("yeti_gna");
91         physic.set_velocity_y(JUMP_VEL1);
92       }
93       break;
94     default:
95       break;
96   }
97
98   movement = physic.get_movement(elapsed_time);
99 }
100
101 void
102 Yeti::go_right()
103 {
104   // jump and move right
105   physic.set_velocity_y(JUMP_VEL1);
106   physic.set_velocity_x(RUN_SPEED);
107   state = GO_RIGHT;
108   timer.start(JUMP_TIME);
109 }
110
111 void
112 Yeti::go_left()
113 {
114   physic.set_velocity_y(JUMP_VEL1);
115   physic.set_velocity_x(-RUN_SPEED);
116   state = GO_LEFT;
117   timer.start(JUMP_TIME);
118 }
119
120 void
121 Yeti::angry_jumping()
122 {
123   jumpcount = 0;
124   timer.start(ANGRY_JUMP_WAIT);
125   state = ANGRY_JUMPING;
126   physic.set_velocity_x(0);
127 }
128
129 void
130 Yeti::summon_snowball()
131 {
132   Sector::current()->add_object(new BouncingSnowball(get_pos().x+(side == LEFT ? 64 : -64), get_pos().y, (side == LEFT ? RIGHT : LEFT)));
133 }
134
135 bool
136 Yeti::collision_squished(Player& player)
137 {
138   if(safe_timer.started())
139     return true;
140
141   player.bounce(*this);
142   sound_manager->play_sound("yeti_roar");
143   hit_points--;
144   if(hit_points <= 0) {
145     sprite->set_action("dead");
146     kill_squished(player);
147
148     // start script
149     if(dead_script != "") {
150       ScriptInterpreter::add_script_object(Sector::current(),
151           "Yeti - dead-script", dead_script);
152     }
153   } else {
154     safe_timer.start(SAFE_TIME);
155   }
156   
157   return true;
158 }
159
160 void
161 Yeti::kill_fall()
162 {
163   // shooting bullets or being invincible won't work :)
164 }
165
166 void
167 Yeti::write(lisp::Writer& )
168 {
169 }
170
171 void
172 Yeti::drop_stalactite()
173 {
174   YetiStalactite* nearest = 0;
175   float dist = FLT_MAX;
176
177   Sector* sector = Sector::current();
178   for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
179       i != sector->gameobjects.end(); ++i) {
180     YetiStalactite* stalactite = dynamic_cast<YetiStalactite*> (*i);
181     if(stalactite && stalactite->is_hanging()) {
182       float sdist 
183         = fabsf(stalactite->get_pos().x - sector->player->get_pos().x);
184       if(sdist < dist) {
185         nearest = stalactite;
186         dist = sdist;
187       }
188     }
189   }
190
191   if(nearest)
192     nearest->start_shaking();
193 }
194
195 HitResponse
196 Yeti::collision_solid(GameObject& , const CollisionHit& hit)
197 {
198   if(fabsf(hit.normal.y) > .5) { // hit floor or roof?
199     physic.set_velocity_y(0);
200     if(state == INIT) {
201       go_right();
202     } else if(state == GO_LEFT && !timer.started()) {
203       side = LEFT;
204       summon_snowball();
205       sprite->set_action("right");
206       angry_jumping();
207     } else if(state == GO_RIGHT && !timer.started()) {
208       side = RIGHT;
209       summon_snowball();
210       sprite->set_action("left");
211       angry_jumping();
212     } else if(state == ANGRY_JUMPING) {
213       if(!timer.started()) {
214         // we just landed
215         jumpcount++;
216         // make a stalactite falling down and shake camera a bit
217         Sector::current()->camera->shake(.1, 0, 10);
218         drop_stalactite();
219         
220         // go to other side after 3 jumps
221         if(jumpcount == 3) {
222           if(side == LEFT)
223             go_right();
224           else
225             go_left();
226         } else {
227           // jump again
228           timer.start(ANGRY_JUMP_WAIT);
229         }
230       }
231     }
232   }
233   
234   return CONTINUE;
235 }
236
237 IMPLEMENT_FACTORY(Yeti, "yeti")