Added a Jump 'n Bump like way to show statistics.
[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 "scene.h"
31 #include "app/globals.h"
32 #include "player.h"
33 #include "sector.h"
34 #include "special/sprite_manager.h"
35 #include "resources.h"
36
37 Sprite* img_firebullet;
38 Sprite* img_icebullet;
39 Sprite* img_star;
40 Sprite* img_growup;
41 Sprite* img_iceflower;
42 Sprite* img_fireflower;
43 Sprite* img_1up;
44
45 #define GROWUP_SPEED 1.0f
46
47 #define BULLET_STARTING_YM 0
48 #define BULLET_XM 6
49
50 Bullet::Bullet(const Vector& pos, float xm, int dir, int kind_)
51 {
52   life_count = 3;
53   base.width = 4;
54   base.height = 4;
55   
56   if (kind == ICE_BULLET)
57     life_count = 6; //ice-bullets get "extra lives" for bumping off walls
58
59   if (dir == RIGHT)
60     {
61       base.x = pos.x + 32;
62       physic.set_velocity_x(BULLET_XM + xm);
63     }
64   else
65     {
66       base.x = pos.x;
67       physic.set_velocity_x(-BULLET_XM + xm);
68     }
69
70   base.y = pos.y;
71   physic.set_velocity_y(-BULLET_STARTING_YM);
72   old_base = base;
73   kind = kind_;
74 }
75
76 void
77 Bullet::action(float elapsed_time)
78 {
79   elapsed_time *= 0.5f;
80
81   float old_y = base.y;
82
83   physic.apply(elapsed_time, base.x, base.y,  Sector::current()->gravity);
84   collision_swept_object_map(&old_base,&base);
85       
86   if (issolid(base.x+2, base.y + 4) || issolid(base.x+2, base.y))
87     {
88       base.y  = old_y;
89       physic.set_velocity_y(-physic.get_velocity_y());
90       life_count -= 1;
91     }
92
93   if(kind == FIRE_BULLET)
94     // @not framerate independant :-/
95     physic.set_velocity_y(physic.get_velocity_y() - 0.5 * elapsed_time);
96   if(physic.get_velocity_y() > 9)
97     physic.set_velocity_y(9);
98   else if(physic.get_velocity_y() < -9)
99     physic.set_velocity_y(-9);
100
101   float scroll_x =
102     Sector::current()->camera->get_translation().x;
103   float scroll_y =
104     Sector::current()->camera->get_translation().y;
105   if (base.x < scroll_x ||
106       base.x > scroll_x + screen->w ||
107 //      base.y < scroll_y ||
108       base.y > scroll_y + screen->h ||
109       life_count <= 0)
110     {
111       remove_me();
112     }
113   if (issolid(base.x + 4, base.y + 2) || 
114       issolid(base.x, base.y + 2))
115      {
116        if (kind == FIRE_BULLET)
117          remove_me();
118        else if (kind == ICE_BULLET)
119          {
120            physic.set_velocity_x(-physic.get_velocity_x());
121            //physic.set_velocity_y(-physic.get_velocity_y());
122          }
123      }
124 }
125
126 void 
127 Bullet::draw(DrawingContext& context)
128 {
129   Sprite* sprite = kind == FIRE_BULLET ? img_firebullet : img_icebullet;
130  
131   sprite->draw(context, Vector(base.x, base.y), LAYER_OBJECTS);
132 }
133
134 void
135 Bullet::collision(const MovingObject& , int)
136 {
137   // later
138 }
139
140 void
141 Bullet::collision(int c_object)
142 {
143   if(c_object == CO_BADGUY) {
144     remove_me();
145   }
146 }
147
148 //---------------------------------------------------------------------------
149
150 Upgrade::Upgrade(const Vector& pos, Direction dir_, UpgradeKind kind_)
151 {
152   kind = kind_;
153   dir = dir_;
154
155   base.width = 32;
156   base.height = 0;
157   base.x = pos.x;
158   base.y = pos.y;
159   old_base = base;
160
161   physic.reset();
162   physic.enable_gravity(false);
163
164   if(kind == UPGRADE_1UP || kind == UPGRADE_STAR) {
165     physic.set_velocity(dir == LEFT ? -1 : 1, 4);
166     physic.enable_gravity(true);
167     base.height = 32;
168   } else if (kind == UPGRADE_ICEFLOWER || kind == UPGRADE_FIREFLOWER) {
169     // nothing
170   } else if (kind == UPGRADE_GROWUP) {
171     physic.set_velocity(dir == LEFT ? -GROWUP_SPEED : GROWUP_SPEED, 0);
172   } else {
173     physic.set_velocity(dir == LEFT ? -2 : 2, 0);
174   }
175 }
176
177 Upgrade::~Upgrade()
178 {
179 }
180
181 void
182 Upgrade::action(float elapsed_time)
183 {
184   if (kind == UPGRADE_ICEFLOWER || kind == UPGRADE_FIREFLOWER
185       || kind == UPGRADE_GROWUP) {
186     if (base.height < 32) {
187       /* Rise up! */
188       base.height = base.height + 0.7 * elapsed_time;
189       if(base.height > 32)
190         base.height = 32;
191
192       return;
193     }
194   }
195
196   /* Away from the screen? Kill it! */
197   float scroll_x =
198     Sector::current()->camera->get_translation().x;
199   float scroll_y =                                                        
200     Sector::current()->camera->get_translation().y;
201   
202   if(base.x < scroll_x - X_OFFSCREEN_DISTANCE ||
203       base.x > scroll_x + screen->w + X_OFFSCREEN_DISTANCE ||
204       base.y < scroll_y - Y_OFFSCREEN_DISTANCE ||
205       base.y > scroll_y + screen->h + Y_OFFSCREEN_DISTANCE)
206     {
207     remove_me();
208     return;
209     }
210
211   /* Move around? */
212   physic.apply(elapsed_time, base.x, base.y, Sector::current()->gravity);
213   if(kind == UPGRADE_GROWUP || kind == UPGRADE_STAR) {
214     collision_swept_object_map(&old_base, &base);
215   }
216
217   // fall down?
218   if(kind == UPGRADE_GROWUP || kind == UPGRADE_STAR) {
219     // falling?
220     if(physic.get_velocity_y() != 0) {
221       if(issolid(base.x, base.y + base.height)) {
222         base.y = int(base.y / 32) * 32;
223         old_base = base;                         
224         if(kind == UPGRADE_GROWUP) {
225           physic.enable_gravity(false);
226           physic.set_velocity(dir == LEFT ? -GROWUP_SPEED : GROWUP_SPEED, 0);
227         } else if(kind == UPGRADE_STAR) {
228           physic.set_velocity(dir == LEFT ? -2 : 2, 3);
229         }
230       }
231     } else {
232       if((physic.get_velocity_x() < 0
233             && !issolid(base.x+base.width, base.y + base.height))
234         || (physic.get_velocity_x() > 0
235             && !issolid(base.x, base.y + base.height))) {
236         physic.enable_gravity(true);
237       }
238     }
239   }
240
241   // horizontal bounce?
242   if(kind == UPGRADE_GROWUP || kind == UPGRADE_STAR) {
243     if (  (physic.get_velocity_x() < 0
244           && issolid(base.x, (int) base.y + base.height/2)) 
245         ||  (physic.get_velocity_x() > 0
246           && issolid(base.x + base.width, (int) base.y + base.height/2))) {
247         physic.set_velocity(-physic.get_velocity_x(),physic.get_velocity_y());
248         dir = dir == LEFT ? RIGHT : LEFT;
249     }
250   }
251 }
252
253 void
254 Upgrade::draw(DrawingContext& context)
255 {
256   Sprite* sprite;
257   switch(kind) {
258     case UPGRADE_GROWUP: sprite = img_growup; break;
259     case UPGRADE_ICEFLOWER: sprite = img_iceflower; break;
260     case UPGRADE_FIREFLOWER: sprite = img_fireflower; break;
261     case UPGRADE_STAR: sprite = img_star; break;
262     case UPGRADE_1UP: sprite = img_1up; break;
263     default:
264       assert(!"wrong type in Powerup::draw()");
265       sprite = NULL;            // added by neoneurone, g++ likes this !
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   SoundManager::get()->play_sound(IDToSound(SND_BUMP_UPGRADE), Vector(base.x, base.y), Sector::current()->player->get_pos());
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           SoundManager::get()->play_sound(IDToSound(SND_EXCELLENT));
331           pplayer->grow(true);
332         }
333       else if (kind == UPGRADE_FIREFLOWER)
334         {
335           SoundManager::get()->play_sound(IDToSound(SND_COFFEE));
336           pplayer->grow(true);
337           pplayer->got_power = pplayer->FIRE_POWER;
338         }
339       else if (kind == UPGRADE_ICEFLOWER)
340         {
341           SoundManager::get()->play_sound(IDToSound(SND_COFFEE));
342           pplayer->grow(true);
343           pplayer->got_power = pplayer->ICE_POWER;
344         }
345       else if (kind == UPGRADE_FIREFLOWER)
346         {
347           SoundManager::get()->play_sound(IDToSound(SND_COFFEE));
348           pplayer->grow(true);
349           pplayer->got_power = pplayer->FIRE_POWER;
350         }
351       else if (kind == UPGRADE_STAR)
352         {
353           SoundManager::get()->play_sound(IDToSound(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             SoundManager::get()->play_sound(IDToSound(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