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