8787bb91c41992f0a3ef8cbbc2c416a0391a7b4a
[supertux.git] / src / gameobjs.cpp
1 //  $Id$
2 // 
3 //  SuperTux
4 //  Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
5 //  Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 // 
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 //  02111-1307, USA.
21 #include <algorithm>
22 #include <iostream>
23 #include "world.h"
24 #include "tile.h"
25 #include "gameloop.h"
26 #include "gameobjs.h"
27 #include "sprite_manager.h"
28 #include "resources.h"
29 #include "level.h"
30
31 void
32 BouncyDistro::init(float x, float y)
33 {
34   base.x = x;
35   base.y = y;
36   base.ym = -2;
37 }
38
39 void
40 BouncyDistro::action(double frame_ratio)
41 {
42   base.y = base.y + base.ym * frame_ratio;
43
44   base.ym += 0.1 * frame_ratio;
45
46   if (base.ym >= 0)
47     {
48       std::vector<BouncyDistro*>::iterator i
49         = std::find(World::current()->bouncy_distros.begin(), 
50                     World::current()->bouncy_distros.end(), 
51                     this);
52       if (i != World::current()->bouncy_distros.end())
53         World::current()->bouncy_distros.erase(i);
54     }
55 }
56
57 void
58 BouncyDistro::draw()
59 {
60   img_distro[0]->draw(base.x - scroll_x,
61                       base.y - scroll_y);
62 }
63
64
65 void
66 BrokenBrick::init(Tile* tile_, float x, float y, float xm, float ym)
67 {
68   tile    = tile_;
69   base.x  = x;
70   base.y  = y;
71   base.xm = xm;
72   base.ym = ym;
73
74   timer.init(true);
75   timer.start(200);
76 }
77
78 void
79 BrokenBrick::action(double frame_ratio)
80 {
81   base.x = base.x + base.xm * frame_ratio;
82   base.y = base.y + base.ym * frame_ratio;
83
84   if (!timer.check())
85     {
86       std::vector<BrokenBrick*>::iterator i
87         = std::find(World::current()->broken_bricks.begin(), 
88                     World::current()->broken_bricks.end(), 
89                     this);
90       if (i != World::current()->broken_bricks.end())
91         World::current()->broken_bricks.erase(i);
92     }
93 }
94
95 void
96 BrokenBrick::draw()
97 {
98   SDL_Rect src, dest;
99   src.x = rand() % 16;
100   src.y = rand() % 16;
101   src.w = 16;
102   src.h = 16;
103
104   dest.x = (int)(base.x - scroll_x);
105   dest.y = (int)(base.y  - scroll_y);
106   dest.w = 16;
107   dest.h = 16;
108   
109   if (tile->images.size() > 0)
110     tile->images[0]->draw_part(src.x,src.y,dest.x,dest.y,dest.w,dest.h);
111 }
112
113 void
114 BouncyBrick::init(float x, float y)
115 {
116   base.x   = x;
117   base.y   = y;
118   offset   = 0;
119   offset_m = -BOUNCY_BRICK_SPEED;
120   shape    = World::current()->get_level()->gettileid(x, y);
121 }
122
123 void
124 BouncyBrick::action(double frame_ratio)
125 {
126   offset = (offset + offset_m * frame_ratio);
127
128   /* Go back down? */
129   if (offset < -BOUNCY_BRICK_MAX_OFFSET)
130     offset_m = BOUNCY_BRICK_SPEED;
131
132
133   /* Stop bouncing? */
134   if (offset >= 0)
135     {
136       std::vector<BouncyBrick*>::iterator i
137         = std::find(World::current()->bouncy_bricks.begin(), 
138                     World::current()->bouncy_bricks.end(), 
139                     this);
140       if (i != World::current()->bouncy_bricks.end())
141         World::current()->bouncy_bricks.erase(i);
142     }
143 }
144
145 void
146 BouncyBrick::draw()
147 {
148   SDL_Rect dest;
149   
150   if (base.x >= scroll_x - 32 &&
151       base.x <= scroll_x + screen->w)
152     {
153       dest.x = (int)(base.x - scroll_x);
154       dest.y = (int)(base.y - scroll_y);
155       dest.w = 32;
156       dest.h = 32;
157
158       Level* plevel = World::current()->get_level();
159
160       // FIXME: overdrawing hack to clean the tile from the screen to
161       // paint it later at on offseted position
162       if(plevel->img_bkgd)
163         {
164           fillrect(base.x - scroll_x, base.y - scroll_y,
165                    32,32, 
166                    plevel->bkgd_top.red, plevel->bkgd_top.green, plevel->bkgd_top.blue, 0);
167 // FIXME: doesn't respect the gradient, futhermore is this necessary at all??
168         }
169       else
170         {
171           int s = ((int)scroll_x / 2)%640;
172           plevel->img_bkgd->draw_part(dest.x + s, dest.y, 
173                                       dest.x, dest.y,dest.w,dest.h);
174         }
175
176       Tile::draw(base.x - scroll_x,
177                  base.y - scroll_y + offset,
178                  shape);
179     }
180 }
181
182 void
183 FloatingScore::init(float x, float y, int s)
184 {
185   base.x = x;
186   base.y = y - 16;
187   timer.init(true);
188   timer.start(1000);
189   value = s;
190 }
191
192 void
193 FloatingScore::action(double frame_ratio)
194 {
195   base.y = base.y - 2 * frame_ratio;
196
197   if(!timer.check())
198     {
199       std::vector<FloatingScore*>::iterator i
200         = std::find(World::current()->floating_scores.begin(), 
201                     World::current()->floating_scores.end(), 
202                     this);
203       if (i != World::current()->floating_scores.end())
204         World::current()->floating_scores.erase(i);
205     }
206 }
207
208 void
209 FloatingScore::draw()
210 {
211   char str[10];
212   sprintf(str, "%d", value);
213   gold_text->draw(str, (int)base.x + 16 - strlen(str) * 8, (int)base.y, 1);
214 }
215
216 /* Trampoline */
217
218 #define TRAMPOLINE_FRAMES 4
219 Sprite *img_trampoline[TRAMPOLINE_FRAMES];
220
221 void load_object_gfx()
222 {
223   char sprite_name[16];
224
225   for (int i = 0; i < TRAMPOLINE_FRAMES; i++)
226   {
227     sprintf(sprite_name, "trampoline-%i", i+1);
228     img_trampoline[i] = sprite_manager->load(sprite_name);
229   }
230 }
231
232 void
233 Trampoline::init(float x, float y)
234 {
235   base.x = x;
236   base.y = y;
237   base.width = 32;
238   base.height = 32;
239
240   frame = 0;
241   mode = M_NORMAL;
242   physic.reset();
243 }
244
245 void
246 Trampoline::draw()
247 {
248   img_trampoline[frame]->draw((int)base.x, (int)base.y);
249
250   frame = 0;
251
252   if (debug_mode)
253     fillrect(base.x - scroll_x, base.y - scroll_y, base.width, base.height, 75, 75, 0, 150);
254 }
255
256 void
257 Trampoline::action(double frame_ratio)
258 {
259   // TODO: Remove if we're too far off the screen
260
261   // Falling
262   if (mode != M_HELD)
263   {
264     if (issolid(base.x + base.width/2, base.y + base.height))
265     {
266       base.y = int((base.y + base.height)/32) * 32 - base.height;
267
268       physic.enable_gravity(false);
269       physic.set_velocity_y(0.0f);
270
271       physic.set_velocity_x(0);
272     }
273     else
274     {
275       physic.enable_gravity(true);
276     }
277   }
278   else // Player is carrying us around
279   {
280     /* FIXME: The trampoline object shouldn't know about pplayer objects. */
281     /* If we're holding the iceblock */
282     Player& tux = *World::current()->get_tux();
283     Direction dir = tux.dir;
284
285     if(dir == RIGHT)
286     {
287       base.x = tux.base.x + 16;
288       base.y = tux.base.y + tux.base.height/1.5 - base.height;
289     }
290     else /* facing left */
291     {
292       base.x = tux.base.x - 16;
293       base.y = tux.base.y + tux.base.height/1.5 - base.height;
294     }
295
296     if(collision_object_map(base))
297     {
298       base.x = tux.base.x;
299       base.y = tux.base.y + tux.base.height/1.5 - base.height;
300     }
301   }
302
303   physic.apply(frame_ratio, base.x, base.y);
304   collision_swept_object_map(&old_base, &base);
305 }
306
307 void
308 Trampoline::collision(void *p_c_object, int c_object, CollisionType type)
309 {
310   Player* pplayer_c = NULL;
311   switch (c_object)
312   {
313     case CO_PLAYER:
314       pplayer_c = (Player*) p_c_object;
315
316       if (type == COLLISION_NORMAL)
317       {
318         // Pick up if HELD (done in Player)
319       }
320
321       else if (type == COLLISION_SQUISH)
322       {
323         int squish_amount = (32 - (int)pplayer_c->base.y % 32);
324
325         if (squish_amount < 24)
326           frame = 3;
327         else if (squish_amount < 28)
328           frame = 2;
329         else if (squish_amount < 30)
330           frame = 1;
331         else
332           frame = 0;
333
334         if (squish_amount < 20)
335           pplayer_c->physic.set_velocity_y(power);
336         else if (pplayer_c->physic.get_velocity_y() < 0)
337           pplayer_c->physic.set_velocity_y(-squish_amount/32);
338       }
339
340       break;
341
342     default:
343       break;
344     
345   }
346 }
347
348
349 /* Object Manager */
350 //---------------------------------------------------------------------------
351
352 ObjectManager::ObjectManager()
353 {
354   std::string filename = datadir + "/images/tilesets/supertux.stbg";
355   load_badguys(filename);
356 }
357
358 ObjectManager::~ObjectManager()
359 {
360   for(std::vector<BadGuy*>::iterator i = badguys.begin(); i != badguys.end(); ++i) {
361     delete *i;                                                                  
362   }
363 }
364
365 void ObjectManager::load_badguys(std::string filename)
366 {
367 /*
368   lisp_object_t* root_obj = lisp_read_from_file(filename);
369
370   if (!root_obj)
371     st_abort("Couldn't load file", filename);
372
373   if (strcmp(lisp_symbol(lisp_car(root_obj)), "supertux-badguys") == 0)
374     {
375       lisp_object_t* cur = lisp_cdr(root_obj);
376
377       while(!lisp_nil_p(cur))
378         {
379           lisp_object_t* element = lisp_car(cur);
380
381           if (strcmp(lisp_symbol(lisp_car(element)), "badguy") == 0)
382             {
383               
384              
385               Tile* tile = new Tile;
386               tile->id      = -1;
387               tile->solid   = false;
388               tile->brick   = false;
389               tile->ice     = false;
390               tile->water   = false;
391               tile->fullbox = false;
392               tile->distro  = false;
393               tile->goal    = false;
394               tile->data    = 0;
395               tile->next_tile  = 0;
396               tile->anim_speed = 25;
397
398               LispReader reader(lisp_cdr(element));
399               assert(reader.read_int("id",  &tile->id));
400               reader.read_bool("solid",     &tile->solid);
401               reader.read_bool("brick",     &tile->brick);
402               reader.read_bool("ice",       &tile->ice);
403               reader.read_bool("water",     &tile->water);
404               reader.read_bool("fullbox",   &tile->fullbox);
405               reader.read_bool("distro",    &tile->distro);
406               reader.read_bool("goal",      &tile->goal);
407               reader.read_int("data",       &tile->data);
408               reader.read_int("anim-speed", &tile->anim_speed);
409               reader.read_int("next-tile",  &tile->next_tile);
410               reader.read_string_vector("images",  &tile->filenames);
411               reader.read_string_vector("editor-images", &tile->editor_filenames);
412
413               for(std::vector<std::string>::iterator it = tile->
414                   filenames.begin();
415                   it != tile->filenames.end();
416                   ++it)
417                 {
418                   Surface* cur_image;
419                   tile->images.push_back(cur_image);
420                   tile->images[tile->images.size()-1] = new Surface(
421                                datadir +  "/images/tilesets/" + (*it),
422                                USE_ALPHA);
423                 }
424               for(std::vector<std::string>::iterator it = tile->editor_filenames.begin();
425                   it != tile->editor_filenames.end();
426                   ++it)
427                 {
428                   Surface* cur_image;
429                   tile->editor_images.push_back(cur_image);
430                   tile->editor_images[tile->editor_images.size()-1] = new Surface(
431                                datadir + "/images/tilesets/" + (*it),
432                                USE_ALPHA);
433                 }
434                 
435               if (tile->id + tileset_id >= int(tiles.size())
436                  )
437                 tiles.resize(tile->id + tileset_id+1);
438
439               tiles[tile->id + tileset_id] = tile;
440             }
441           else if (strcmp(lisp_symbol(lisp_car(element)), "tileset") == 0)
442             {
443               LispReader reader(lisp_cdr(element));
444               std::string filename;
445               reader.read_string("file",  &filename);
446               filename = datadir + "/images/tilesets/" + filename;
447               load_tileset(filename);
448             }
449           else if (strcmp(lisp_symbol(lisp_car(element)), "tilegroup") == 0)
450             {
451               TileGroup new_;
452               LispReader reader(lisp_cdr(element));
453               reader.read_string("name",  &new_.name);
454               reader.read_int_vector("tiles", &new_.tiles);           
455               if(!tilegroups_)
456                 tilegroups_ = new std::set<TileGroup>;
457               tilegroups_->insert(new_).first;
458             }
459           else if (strcmp(lisp_symbol(lisp_car(element)), "properties") == 0)
460             {
461               LispReader reader(lisp_cdr(element));
462               reader.read_int("id",  &tileset_id);
463               tileset_id *= 1000;
464             }
465           else
466             {
467               puts("Unhandled symbol");
468             }
469
470           cur = lisp_cdr(cur);
471         }
472     }
473   else
474     {
475       assert(0);
476     }
477
478   lisp_free(root_obj);
479 */
480 }
481
482 void ObjectManager::draw_bg()
483 {
484 /*
485   for (unsigned int i = 0; i < bouncy_bricks.size(); ++i)
486     bouncy_bricks[i]->draw();
487
488   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
489     (*i)->draw();
490
491   for (Trampolines::iterator i = trampolines.begin(); i != trampolines.end(); ++i)
492     (*i)->draw();
493 */
494 }
495
496 void ObjectManager::draw_fg()
497 {
498 /*
499   for (unsigned int i = 0; i < bullets.size(); ++i)
500     bullets[i].draw();
501
502   for (unsigned int i = 0; i < floating_scores.size(); ++i)
503     floating_scores[i]->draw();
504
505   for (unsigned int i = 0; i < upgrades.size(); ++i)
506     upgrades[i].draw();
507
508   for (unsigned int i = 0; i < bouncy_distros.size(); ++i)
509     bouncy_distros[i]->draw();
510
511   for (unsigned int i = 0; i < broken_bricks.size(); ++i)
512     broken_bricks[i]->draw();
513 */
514 }
515
516 void ObjectManager::actions()
517 {
518 /*
519   for (unsigned int i = 0; i < bouncy_distros.size(); i++)
520     bouncy_distros[i]->action(frame_ratio);
521
522   for (unsigned int i = 0; i < broken_bricks.size(); i++)
523     broken_bricks[i]->action(frame_ratio);
524
525   // Handle all kinds of game objects
526   for (unsigned int i = 0; i < bouncy_bricks.size(); i++)
527     bouncy_bricks[i]->action(frame_ratio);
528   
529   for (unsigned int i = 0; i < floating_scores.size(); i++)
530     floating_scores[i]->action(frame_ratio);
531
532   for (unsigned int i = 0; i < bullets.size(); ++i)
533     bullets[i].action(frame_ratio);
534   
535   for (unsigned int i = 0; i < upgrades.size(); i++)
536     upgrades[i].action(frame_ratio);
537
538   for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
539     (*i)->action(frame_ratio);
540
541   for (Trampolines::iterator i = trampolines.begin(); i != trampolines.end(); ++i)
542      (*i)->action(frame_ratio);
543 */
544 }
545
546 /* EOF */
547