gave the Yeti hitpoints and Matze's angry roar :)
[supertux.git] / src / badguy / yeti.cpp
1 #include <config.h>
2
3 #include <float.h>
4 #include "yeti.h"
5 #include "object/camera.h"
6 #include "yeti_stalactite.h"
7
8 static const float JUMP_VEL1 = 250;
9 static const float JUMP_VEL2 = 700;
10 static const float RUN_SPEED = 300;
11 static const float JUMP_TIME = 1.6;
12 static const float ANGRY_JUMP_WAIT = .5;
13 static const float STUN_TIME = 2;
14 static const int INITIAL_HITPOINTS = 3;
15
16 Yeti::Yeti(const lisp::Lisp& reader)
17 {
18   reader.get("x", start_position.x);
19   reader.get("y", start_position.y);
20   bbox.set_size(80, 120);
21   sprite = sprite_manager->create("yeti");
22   state = INIT;
23   side = LEFT;
24   hitpoints = INITIAL_HITPOINTS;
25   sound_gna = SoundManager::get()->load_sound(
26       get_resource_filename("sounds/yeti_gna.wav"));
27   sound_roar = SoundManager::get()->load_sound(
28       get_resource_filename("sounds/yeti_roar.wav"));
29   jump_time_left = 0.0f;
30 }
31
32 Yeti::~Yeti()
33 {
34   Mix_FreeChunk(sound_gna);
35 }
36
37 void
38 Yeti::active_action(float elapsed_time)
39 {
40   switch(state) {
41     case INIT:
42       break;
43     case GO_RIGHT:
44       physic.set_velocity_x(RUN_SPEED);
45       if(jump_timer.check())
46         physic.set_velocity_y(JUMP_VEL2);
47       break;
48     case GO_LEFT:
49       physic.set_velocity_x(-RUN_SPEED);
50       if(jump_timer.check())
51         physic.set_velocity_y(JUMP_VEL2);
52       break;
53     case ANGRY_JUMPING:
54       if(jump_timer.check()) {
55         // jump
56         SoundManager::get()->play_sound(sound_gna);
57         physic.set_velocity_y(JUMP_VEL1);
58       }
59       break;
60
61     case STUNNED:
62         if (stun_timer.check()) {
63             go_right();
64         }
65       
66     default:
67       break;
68   }
69
70   movement = physic.get_movement(elapsed_time);
71 }
72
73 void
74 Yeti::go_right()
75 {
76   // jump and move right
77   physic.set_velocity_y(JUMP_VEL1);
78   physic.set_velocity_x(RUN_SPEED);
79   state = GO_RIGHT;
80   jump_timer.start(JUMP_TIME);
81 }
82
83 void
84 Yeti::go_left()
85 {
86   physic.set_velocity_y(JUMP_VEL1);
87   physic.set_velocity_x(-RUN_SPEED);
88   state = GO_LEFT;
89   jump_timer.start(JUMP_TIME);
90 }
91
92 void
93 Yeti::angry_jumping()
94 {
95   jumpcount = 0;
96   jump_timer.start(ANGRY_JUMP_WAIT);
97   state = ANGRY_JUMPING;
98   physic.set_velocity_x(0);
99 }
100
101 void
102 Yeti::stun()
103 {
104   physic.set_acceleration(0.0f, 0.0f);
105   physic.set_velocity(0.0f, 0.0f);
106   jump_time_left = jump_timer.get_timeleft();
107   jump_timer.stop();
108   stun_timer.start(STUN_TIME);
109   state = STUNNED;
110 }
111
112 HitResponse
113 Yeti::collision_player(Player& player, const CollisionHit& hit)
114 {     
115   if(player.is_invincible()) {
116     kill_fall();
117     return ABORT_MOVE;
118   } 
119   if(hit.normal.y > .9) {
120     //TODO: fix inaccuracy (tux sometimes dies even if badguy was hit)
121     //      give badguys some invincible time (prevent them from being hit multiple times)
122     hitpoints--;
123     if(collision_squished(player))
124       return ABORT_MOVE;
125     else if (hitpoints <= 0) {
126       player.kill(Player::SHRINK);
127       return FORCE_MOVE;
128     }
129   }
130
131   if (state == STUNNED)
132     return ABORT_MOVE;
133
134   player.kill(Player::SHRINK);
135   return FORCE_MOVE;
136 }
137
138 HitResponse
139 Yeti::collision_badguy(BadGuy& badguy, const CollisionHit&)
140 {
141   YetiStalactite* yeti_stal = dynamic_cast<YetiStalactite*>(&badguy);
142
143   if (state == STUNNED && yeti_stal && yeti_stal->is_harmful())
144   {
145     kill_fall();
146   }
147
148   return FORCE_MOVE;
149
150   
151
152 bool
153 Yeti::collision_squished(Player& player)
154 {
155   // we don't use the player object, even though it was given to us
156   (void)player;
157
158   // stun yeti
159   stun();
160
161   return true;
162 }
163
164 void
165 Yeti::write(lisp::Writer& )
166 {
167 }
168
169 void
170 Yeti::drop_stalactite()
171 {
172   YetiStalactite* nearest = 0;
173   float dist = FLT_MAX;
174
175   Sector* sector = Sector::current();
176   for(Sector::GameObjects::iterator i = sector->gameobjects.begin();
177       i != sector->gameobjects.end(); ++i) {
178     YetiStalactite* stalactite = dynamic_cast<YetiStalactite*> (*i);
179     if(stalactite && stalactite->is_hanging()) {
180       float sdist 
181         = fabsf(stalactite->get_pos().x - sector->player->get_pos().x);
182       if(sdist < dist) {
183         nearest = stalactite;
184         dist = sdist;
185       }
186     }
187   }
188
189   if(nearest)
190     nearest->start_shaking();
191 }
192
193 HitResponse
194 Yeti::collision_solid(GameObject& , const CollisionHit& hit)
195 {
196   if(fabsf(hit.normal.y) > .5) { // hit floor or roof?
197     physic.set_velocity_y(0);
198     if(state == INIT) {
199       go_right();
200     } else if(state == GO_LEFT && !jump_timer.started()) {
201       side = LEFT;
202       angry_jumping();
203     } else if(state == GO_RIGHT && !jump_timer.started()) {
204       side = RIGHT;
205       angry_jumping();
206     } else if(state == ANGRY_JUMPING) {
207       if(!jump_timer.started()) {
208         // we just landed
209         jumpcount++;
210         // make a stalactite falling down and shake camera a bit
211         Sector::current()->camera->shake(.1, 0, 10);
212         drop_stalactite();
213         
214         // go to other side after 3 jumps
215         if(jumpcount == 3) {
216           if(side == LEFT)
217             go_right();
218           else
219             go_left();
220         } else {
221           // jump again
222           jump_timer.start(ANGRY_JUMP_WAIT);
223         }
224       }
225     }
226   }
227   
228   return CONTINUE;
229 }
230
231 void
232 Yeti::kill_fall()
233 {
234   SoundManager::get()->play_sound(sound_roar);
235   hitpoints--;
236   if (hitpoints <= 0) {
237     SoundManager::get()->play_sound(IDToSound(SND_FALL), this,
238        Sector::current()->player->get_pos());
239     physic.set_velocity_y(0);
240     physic.enable_gravity(true);
241     set_state(STATE_FALLING);
242   }
243 }
244
245 IMPLEMENT_FACTORY(Yeti, "yeti")