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