- read gravity has real, instead of int
[supertux.git] / src / special.cpp
1 //  $Id$
2 //
3 //  SuperTux -  A Jump'n Run
4 //  Copyright (C) 2003 Tobias Glaesser <tobi.web@gmx.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  02111-1307, USA.
19
20 #include <cassert>
21 #include <iostream>
22
23 #include "SDL.h"
24
25 #include "defines.h"
26 #include "special.h"
27 #include "camera.h"
28 #include "gameloop.h"
29 #include "screen/screen.h"
30 #include "sound.h"
31 #include "scene.h"
32 #include "globals.h"
33 #include "player.h"
34 #include "sector.h"
35 #include "sprite_manager.h"
36 #include "resources.h"
37
38 Sprite* img_firebullet;
39 Sprite* img_icebullet;
40 Sprite* img_star;
41 Sprite* img_growup;
42 Sprite* img_iceflower;
43 Sprite* img_fireflower;
44 Sprite* img_1up;
45
46 #define GROWUP_SPEED 1.0f
47
48 #define BULLET_STARTING_YM 0
49 #define BULLET_XM 6
50
51 Bullet::Bullet(const Vector& pos, float xm, int dir, int kind_)
52 {
53   life_count = 3;
54   base.width = 4;
55   base.height = 4;
56   
57   if (kind == ICE_BULLET)
58     life_count = 6; //ice-bullets get "extra lives" for bumping off walls
59
60   if (dir == RIGHT)
61     {
62       base.x = pos.x + 32;
63       physic.set_velocity_x(BULLET_XM + xm);
64     }
65   else
66     {
67       base.x = pos.x;
68       physic.set_velocity_x(-BULLET_XM + xm);
69     }
70
71   base.y = pos.y;
72   physic.set_velocity_y(-BULLET_STARTING_YM);
73   old_base = base;
74   kind = kind_;
75 }
76
77 void
78 Bullet::action(float elapsed_time)
79 {
80   elapsed_time *= 0.5f;
81
82   float old_y = base.y;
83
84   physic.apply(elapsed_time, base.x, base.y);
85   collision_swept_object_map(&old_base,&base);
86       
87   if (issolid(base.x+2, base.y + 4) || issolid(base.x+2, base.y))
88     {
89       base.y  = old_y;
90       physic.set_velocity_y(-physic.get_velocity_y());
91       life_count -= 1;
92     }
93
94   if(kind == FIRE_BULLET)
95     // @not framerate independant :-/
96     physic.set_velocity_y(physic.get_velocity_y() - 0.5 * elapsed_time);
97   if(physic.get_velocity_y() > 9)
98     physic.set_velocity_y(9);
99   else if(physic.get_velocity_y() < -9)
100     physic.set_velocity_y(-9);
101
102   float scroll_x =
103     Sector::current()->camera->get_translation().x;
104   float scroll_y =
105     Sector::current()->camera->get_translation().y;
106   if (base.x < scroll_x ||
107       base.x > scroll_x + screen->w ||
108       base.y < scroll_y ||
109       base.y > scroll_y + screen->h ||
110       life_count <= 0)
111     {
112       remove_me();
113     }
114   if (issolid(base.x + 4, base.y + 2) || 
115       issolid(base.x, base.y + 2))
116      {
117        if (kind == FIRE_BULLET)
118          remove_me();
119        else if (kind == ICE_BULLET)
120          {
121            physic.set_velocity_x(-physic.get_velocity_x());
122            //physic.set_velocity_y(-physic.get_velocity_y());
123          }
124      }
125 }
126
127 void 
128 Bullet::draw(DrawingContext& context)
129 {
130   Sprite* sprite = kind == FIRE_BULLET ? img_firebullet : img_icebullet;
131  
132   sprite->draw(context, Vector(base.x, base.y), LAYER_OBJECTS);
133 }
134
135 void
136 Bullet::collision(const MovingObject& , int)
137 {
138   // later
139 }
140
141 void
142 Bullet::collision(int c_object)
143 {
144   if(c_object == CO_BADGUY) {
145     remove_me();
146   }
147 }
148
149 //---------------------------------------------------------------------------
150
151 Upgrade::Upgrade(const Vector& pos, Direction dir_, UpgradeKind kind_)
152 {
153   kind = kind_;
154   dir = dir_;
155
156   base.width = 32;
157   base.height = 0;
158   base.x = pos.x;
159   base.y = pos.y;
160   old_base = base;
161
162   physic.reset();
163   physic.enable_gravity(false);
164
165   if(kind == UPGRADE_1UP || kind == UPGRADE_HERRING) {
166     physic.set_velocity(dir == LEFT ? -1 : 1, 4);
167     physic.enable_gravity(true);
168     base.height = 32;
169   } else if (kind == UPGRADE_ICEFLOWER || kind == UPGRADE_FIREFLOWER) {
170     // nothing
171   } else if (kind == UPGRADE_GROWUP) {
172     physic.set_velocity(dir == LEFT ? -GROWUP_SPEED : GROWUP_SPEED, 0);
173   } else {
174     physic.set_velocity(dir == LEFT ? -2 : 2, 0);
175   }
176 }
177
178 Upgrade::~Upgrade()
179 {
180 }
181
182 void
183 Upgrade::action(float elapsed_time)
184 {
185   if (kind == UPGRADE_ICEFLOWER || kind == UPGRADE_FIREFLOWER
186       || kind == UPGRADE_GROWUP) {
187     if (base.height < 32) {
188       /* Rise up! */
189       base.height = base.height + 0.7 * elapsed_time;
190       if(base.height > 32)
191         base.height = 32;
192
193       return;
194     }
195   }
196
197   /* Away from the screen? Kill it! */
198   float scroll_x =
199     Sector::current()->camera->get_translation().x;
200   float scroll_y =                                                        
201     Sector::current()->camera->get_translation().y;
202   
203   if(base.x < scroll_x - X_OFFSCREEN_DISTANCE ||
204       base.x > scroll_x + screen->w + X_OFFSCREEN_DISTANCE ||
205       base.y < scroll_y - Y_OFFSCREEN_DISTANCE ||
206       base.y > scroll_y + screen->h + Y_OFFSCREEN_DISTANCE)
207     {
208     remove_me();
209     return;
210     }
211
212   /* Move around? */
213   physic.apply(elapsed_time, base.x, base.y);
214   if(kind == UPGRADE_GROWUP) {
215     collision_swept_object_map(&old_base, &base);
216   }
217
218   // fall down?
219   if(kind == UPGRADE_GROWUP || kind == UPGRADE_HERRING) {
220     // falling?
221     if(physic.get_velocity_y() != 0) {
222       if(issolid(base.x, base.y + base.height)) {
223         base.y = int(base.y / 32) * 32;
224         old_base = base;                         
225         if(kind == UPGRADE_GROWUP) {
226           physic.enable_gravity(false);
227           physic.set_velocity(dir == LEFT ? -GROWUP_SPEED : GROWUP_SPEED, 0);
228         } else if(kind == UPGRADE_HERRING) {
229           physic.set_velocity(dir == LEFT ? -2 : 2, 3);
230         }
231       }
232     } else {
233       if((physic.get_velocity_x() < 0
234             && !issolid(base.x+base.width, base.y + base.height))
235         || (physic.get_velocity_x() > 0
236             && !issolid(base.x, base.y + base.height))) {
237         physic.enable_gravity(true);
238       }
239     }
240   }
241
242   // horizontal bounce?
243   if(kind == UPGRADE_GROWUP || kind == UPGRADE_HERRING) {
244     if (  (physic.get_velocity_x() < 0
245           && issolid(base.x, (int) base.y + base.height/2)) 
246         ||  (physic.get_velocity_x() > 0
247           && issolid(base.x + base.width, (int) base.y + base.height/2))) {
248         physic.set_velocity(-physic.get_velocity_x(),physic.get_velocity_y());
249         dir = dir == LEFT ? RIGHT : LEFT;
250     }
251   }
252 }
253
254 void
255 Upgrade::draw(DrawingContext& context)
256 {
257   Sprite* sprite;
258   switch(kind) {
259     case UPGRADE_GROWUP: sprite = img_growup; break;
260     case UPGRADE_ICEFLOWER: sprite = img_iceflower; break;
261     case UPGRADE_FIREFLOWER: sprite = img_fireflower; break;
262     case UPGRADE_HERRING: sprite = img_star; break;
263     case UPGRADE_1UP: sprite = img_1up; break;
264     default:
265       assert(!"wrong type in Powerup::draw()");
266   }
267
268   if(base.height < 32) // still raising up?
269     sprite->draw(context, Vector(base.x, base.y + (32 - base.height)),
270         LAYER_TILES - 10);
271   else
272     sprite->draw(context, Vector(base.x, base.y), LAYER_OBJECTS);
273 }
274
275 void
276 Upgrade::bump(Player* player)
277 {
278   // these can't be bumped
279   if(kind != UPGRADE_GROWUP)
280     return;
281
282   sound_manager->play_sound(sounds[SND_BUMP_UPGRADE], Vector(base.x, base.y));
283   
284   // determine new direction
285   Direction old_dir = dir;
286   if (player->base.x + player->base.width/2 > base.x + base.width/2)
287     dir = LEFT;
288   else
289     dir = RIGHT;
290
291   // do a little jump and change direction (if necessary)
292   if (dir != old_dir)
293     physic.set_velocity(-physic.get_velocity_x(), 3);
294   else
295     physic.set_velocity_y(3);
296
297   physic.enable_gravity(true);
298 }
299
300 void
301 Upgrade::collision(const MovingObject& , int)
302 {
303   // later
304 }
305
306 void
307 Upgrade::collision(void* p_c_object, int c_object, CollisionType type)
308 {
309   Player* pplayer = NULL;
310
311   if(type == COLLISION_BUMP) {
312     if(c_object == CO_PLAYER)
313       pplayer = (Player*) p_c_object;
314     bump(pplayer);
315     return;
316   }
317
318   switch (c_object)
319     {
320     case CO_PLAYER:
321       /* Remove the upgrade: */
322
323       /* p_c_object is CO_PLAYER, so assign it to pplayer */
324       pplayer = (Player*) p_c_object;
325
326       /* Affect the player: */
327
328       if (kind == UPGRADE_GROWUP)
329         {
330           sound_manager->play_sound(sounds[SND_EXCELLENT]);
331           pplayer->grow(true);
332         }
333       else if (kind == UPGRADE_FIREFLOWER)
334         {
335           sound_manager->play_sound(sounds[SND_COFFEE]);
336           pplayer->grow(true);
337           pplayer->got_power = pplayer->FIRE_POWER;
338         }
339       else if (kind == UPGRADE_ICEFLOWER)
340         {
341           sound_manager->play_sound(sounds[SND_COFFEE]);
342           pplayer->grow(true);
343           pplayer->got_power = pplayer->ICE_POWER;
344         }
345       else if (kind == UPGRADE_FIREFLOWER)
346         {
347           sound_manager->play_sound(sounds[SND_COFFEE]);
348           pplayer->grow(true);
349           pplayer->got_power = pplayer->FIRE_POWER;
350         }
351       else if (kind == UPGRADE_HERRING)
352         {
353           sound_manager->play_sound(sounds[SND_HERRING]);
354           pplayer->invincible_timer.start(TUX_INVINCIBLE_TIME);
355           Sector::current()->play_music(HERRING_MUSIC);
356         }
357       else if (kind == UPGRADE_1UP)
358         {
359           if(player_status.lives < MAX_LIVES) {
360             player_status.lives++;
361             sound_manager->play_sound(sounds[SND_LIFEUP]);
362           }
363         }
364
365       remove_me();
366       return;
367     }
368 }
369
370 void load_special_gfx()
371 {
372   img_growup    = sprite_manager->load("egg");
373   img_iceflower = sprite_manager->load("iceflower");
374   img_fireflower = sprite_manager->load("fireflower");
375   img_star      = sprite_manager->load("star");
376   img_1up       = sprite_manager->load("1up");
377
378   img_firebullet = sprite_manager->load("firebullet");
379   img_icebullet  = sprite_manager->load("icebullet");
380 }
381
382 void free_special_gfx()
383 {
384 }
385