- More work on scripting interface
[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
21 #include <config.h>
22
23 #include <float.h>
24 #include "yeti.h"
25 #include "object/camera.h"
26 #include "yeti_stalactite.h"
27 #include "bouncing_snowball.h"
28
29 static const float JUMP_VEL1 = 250;
30 static const float JUMP_VEL2 = 700;
31 static const float RUN_SPEED = 350;
32 static const float JUMP_TIME = 1.6;
33 static const float ANGRY_JUMP_WAIT = .5;
34 static const int INITIAL_HITPOINTS = 3;
35 static const int INITIAL_BULLET_HP = 10;
36
37 Yeti::Yeti(const lisp::Lisp& reader)
38 {
39   reader.get("x", start_position.x);
40   reader.get("y", start_position.y);
41   bbox.set_size(80, 120);
42   sprite = sprite_manager->create("yeti");
43   state = INIT;
44   side = LEFT;
45   hitpoints = INITIAL_HITPOINTS;
46   bullet_hitpoints = INITIAL_BULLET_HP;
47   sound_manager->preload_sound("yeti_gna");
48   sound_manager->preload_sound("yeti_roar");  
49 }
50
51 Yeti::~Yeti()
52 {
53   Mix_FreeChunk(sound_gna);
54 }
55
56 void
57 Yeti::active_action(float elapsed_time)
58 {
59   switch(state) {
60     case INIT:
61       break;
62     case GO_RIGHT:
63       physic.set_velocity_x(RUN_SPEED);
64       if(timer.check())
65         physic.set_velocity_y(JUMP_VEL2);
66       break;
67     case GO_LEFT:
68       physic.set_velocity_x(-RUN_SPEED);
69       if(timer.check())
70         physic.set_velocity_y(JUMP_VEL2);
71       break;
72     case ANGRY_JUMPING:
73       if(timer.check()) {
74         // jump
75         sound_manager->play_sound("yeti_gna");
76         physic.set_velocity_y(JUMP_VEL1);
77       }
78       break;
79     default:
80       break;
81   }
82
83   movement = physic.get_movement(elapsed_time);
84 }
85
86 void
87 Yeti::go_right()
88 {
89   // jump and move right
90   physic.set_velocity_y(JUMP_VEL1);
91   physic.set_velocity_x(RUN_SPEED);
92   state = GO_RIGHT;
93   timer.start(JUMP_TIME);
94 }
95
96 void
97 Yeti::go_left()
98 {
99   physic.set_velocity_y(JUMP_VEL1);
100   physic.set_velocity_x(-RUN_SPEED);
101   state = GO_LEFT;
102   timer.start(JUMP_TIME);
103 }
104
105 void
106 Yeti::angry_jumping()
107 {
108   jumpcount = 0;
109   timer.start(ANGRY_JUMP_WAIT);
110   state = ANGRY_JUMPING;
111   physic.set_velocity_x(0);
112 }
113
114 void
115 Yeti::summon_snowball()
116 {
117   Sector::current()->add_object(new BouncingSnowball(get_pos().x+(side == LEFT ? 64 : -64), get_pos().y, (side == LEFT ? RIGHT : LEFT)));
118 }
119
120 HitResponse
121 Yeti::collision_player(Player& player, const CollisionHit& hit)
122 {
123   if(player.is_invincible()) {
124     kill_fall();
125     return ABORT_MOVE;
126   }
127   if(hit.normal.y > .9) {
128     hitpoints--;
129     bullet_hitpoints--;
130     sound_manager->play_sound("yeti_roar");    
131     if(collision_squished(player))
132       return ABORT_MOVE;
133     else if (hitpoints <= 0) {
134       bullet_hitpoints = 0;
135       player.kill(Player::SHRINK);
136       return FORCE_MOVE;
137     }
138   }
139   player.kill(Player::SHRINK);
140   return FORCE_MOVE;
141 }
142
143 bool
144 Yeti::collision_squished(Player& player)
145 {
146   bool result = false;
147   player.bounce(*this);
148   if (hitpoints <= 0) {
149     bullet_hitpoints = 0;
150     //sprite->set_action("dead"); 
151     kill_squished(player);
152     result = true;
153   }
154   return result;
155 }
156
157 void
158 Yeti::write(lisp::Writer& )
159 {
160 }
161
162 void
163 Yeti::drop_stalactite()
164 {
165   YetiStalactite* nearest = 0;
166   float dist = FLT_MAX;
167
168   Sector* sector = Sector::current();
169   for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
170       i != sector->gameobjects.end(); ++i) {
171     YetiStalactite* stalactite = dynamic_cast<YetiStalactite*> (*i);
172     if(stalactite && stalactite->is_hanging()) {
173       float sdist 
174         = fabsf(stalactite->get_pos().x - sector->player->get_pos().x);
175       if(sdist < dist) {
176         nearest = stalactite;
177         dist = sdist;
178       }
179     }
180   }
181
182   if(nearest)
183     nearest->start_shaking();
184 }
185
186 HitResponse
187 Yeti::collision_solid(GameObject& , const CollisionHit& hit)
188 {
189   if(fabsf(hit.normal.y) > .5) { // hit floor or roof?
190     physic.set_velocity_y(0);
191     if(state == INIT) {
192       go_right();
193     } else if(state == GO_LEFT && !timer.started()) {
194       side = LEFT;
195       summon_snowball();
196       angry_jumping();
197     } else if(state == GO_RIGHT && !timer.started()) {
198       side = RIGHT;
199       summon_snowball();
200       angry_jumping();
201     } else if(state == ANGRY_JUMPING) {
202       if(!timer.started()) {
203         // we just landed
204         jumpcount++;
205         // make a stalactite falling down and shake camera a bit
206         Sector::current()->camera->shake(.1, 0, 10);
207         drop_stalactite();
208         
209         // go to other side after 3 jumps
210         if(jumpcount == 3) {
211           if(side == LEFT)
212             go_right();
213           else
214             go_left();
215         } else {
216           // jump again
217           timer.start(ANGRY_JUMP_WAIT);
218         }
219       }
220     }
221   }
222   
223   return CONTINUE;
224 }
225
226 void
227 Yeti::kill_fall()
228 {
229   sound_manager->play_sound("yeti_roar");  
230   bullet_hitpoints--;
231   if (bullet_hitpoints <= 0) {
232     sound_manager->play_sound("fall", this,
233                               Sector::current()->player->get_pos());
234     physic.set_velocity_y(0);
235     physic.enable_gravity(true);
236     set_state(STATE_FALLING);
237   }
238 }
239
240 IMPLEMENT_FACTORY(Yeti, "yeti")