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