701a82c7473115097f8c82209d36fa239518ca9c
[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 "video/screen.h"
30 #include "audio/sound.h"
31 #include "scene.h"
32 #include "app/globals.h"
33 #include "player.h"
34 #include "sector.h"
35 #include "special/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,  Sector::current()->gravity);
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, Sector::current()->gravity);
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       sprite = NULL;            // added by neoneurone, g++ likes this !
267   }
268
269   if(base.height < 32) // still raising up?
270     sprite->draw(context, Vector(base.x, base.y + (32 - base.height)),
271         LAYER_TILES - 10);
272   else
273     sprite->draw(context, Vector(base.x, base.y), LAYER_OBJECTS);
274 }
275
276 void
277 Upgrade::bump(Player* player)
278 {
279   // these can't be bumped
280   if(kind != UPGRADE_GROWUP)
281     return;
282
283   sound_manager->play_sound(sounds[SND_BUMP_UPGRADE], Vector(base.x, base.y), Sector::current()->player->get_pos());
284   
285   // determine new direction
286   Direction old_dir = dir;
287   if (player->base.x + player->base.width/2 > base.x + base.width/2)
288     dir = LEFT;
289   else
290     dir = RIGHT;
291
292   // do a little jump and change direction (if necessary)
293   if (dir != old_dir)
294     physic.set_velocity(-physic.get_velocity_x(), 3);
295   else
296     physic.set_velocity_y(3);
297
298   physic.enable_gravity(true);
299 }
300
301 void
302 Upgrade::collision(const MovingObject& , int)
303 {
304   // later
305 }
306
307 void
308 Upgrade::collision(void* p_c_object, int c_object, CollisionType type)
309 {
310   Player* pplayer = NULL;
311
312   if(type == COLLISION_BUMP) {
313     if(c_object == CO_PLAYER)
314       pplayer = (Player*) p_c_object;
315     bump(pplayer);
316     return;
317   }
318
319   switch (c_object)
320     {
321     case CO_PLAYER:
322       /* Remove the upgrade: */
323
324       /* p_c_object is CO_PLAYER, so assign it to pplayer */
325       pplayer = (Player*) p_c_object;
326
327       /* Affect the player: */
328
329       if (kind == UPGRADE_GROWUP)
330         {
331           sound_manager->play_sound(sounds[SND_EXCELLENT]);
332           pplayer->grow(true);
333         }
334       else if (kind == UPGRADE_FIREFLOWER)
335         {
336           sound_manager->play_sound(sounds[SND_COFFEE]);
337           pplayer->grow(true);
338           pplayer->got_power = pplayer->FIRE_POWER;
339         }
340       else if (kind == UPGRADE_ICEFLOWER)
341         {
342           sound_manager->play_sound(sounds[SND_COFFEE]);
343           pplayer->grow(true);
344           pplayer->got_power = pplayer->ICE_POWER;
345         }
346       else if (kind == UPGRADE_FIREFLOWER)
347         {
348           sound_manager->play_sound(sounds[SND_COFFEE]);
349           pplayer->grow(true);
350           pplayer->got_power = pplayer->FIRE_POWER;
351         }
352       else if (kind == UPGRADE_HERRING)
353         {
354           sound_manager->play_sound(sounds[SND_HERRING]);
355           pplayer->invincible_timer.start(TUX_INVINCIBLE_TIME);
356           Sector::current()->play_music(HERRING_MUSIC);
357         }
358       else if (kind == UPGRADE_1UP)
359         {
360           if(player_status.lives < MAX_LIVES) {
361             player_status.lives++;
362             sound_manager->play_sound(sounds[SND_LIFEUP]);
363           }
364         }
365
366       remove_me();
367       return;
368     }
369 }
370
371 void load_special_gfx()
372 {
373   img_growup    = sprite_manager->load("egg");
374   img_iceflower = sprite_manager->load("iceflower");
375   img_fireflower = sprite_manager->load("fireflower");
376   img_star      = sprite_manager->load("star");
377   img_1up       = sprite_manager->load("1up");
378
379   img_firebullet = sprite_manager->load("firebullet");
380   img_icebullet  = sprite_manager->load("icebullet");
381 }
382
383 void free_special_gfx()
384 {
385 }
386