52337be44e83eddc810c402ea22fadbd30485630
[supertux.git] / src / world.cpp
1 //
2 // C Implementation: world
3 //
4 // Description:
5 //
6 //
7 // Author: Tobias Glaesser <tobi.web@gmx.de>, (C) 2004
8 //
9 // Copyright: See COPYING file that comes with this distribution
10 //
11 //
12
13 #include <stdlib.h>
14 #include <string.h>
15 #include "globals.h"
16 #include "scene.h"
17 #include "screen.h"
18 #include "defines.h"
19 #include "world.h"
20 #include "tile.h"
21
22 texture_type img_distro[4];
23
24 World world;
25
26 World::World()
27 {
28   
29 }
30
31 void
32 World::arrays_free(void)
33 {
34   bad_guys.clear();
35   bouncy_distros.clear();
36   broken_bricks.clear();
37   bouncy_bricks.clear();
38   floating_scores.clear();
39   upgrades.clear();
40   bullets.clear();
41   std::vector<ParticleSystem*>::iterator i;
42   for(i = particle_systems.begin(); i != particle_systems.end(); ++i) {
43     delete *i;
44   }
45   particle_systems.clear();
46 }
47
48 void
49 World::draw()
50 {
51   /* (Bouncy bricks): */
52   for (unsigned int i = 0; i < bouncy_bricks.size(); ++i)
53     bouncy_brick_draw(&bouncy_bricks[i]);
54
55   for (unsigned int i = 0; i < bad_guys.size(); ++i)
56     bad_guys[i].draw();
57
58   tux.draw();
59
60   for (unsigned int i = 0; i < bullets.size(); ++i)
61     bullet_draw(&bullets[i]);
62
63   for (unsigned int i = 0; i < floating_scores.size(); ++i)
64     floating_score_draw(&floating_scores[i]);
65
66   for (unsigned int i = 0; i < upgrades.size(); ++i)
67     upgrade_draw(&upgrades[i]);
68
69   for (unsigned int i = 0; i < bouncy_distros.size(); ++i)
70     bouncy_distro_draw(&bouncy_distros[i]);
71 }
72
73 void
74 World::action()
75 {
76   /* Handle bouncy distros: */
77   for (unsigned int i = 0; i < bouncy_distros.size(); i++)
78     bouncy_distro_action(&bouncy_distros[i]);
79
80   /* Handle broken bricks: */
81   for (unsigned int i = 0; i < broken_bricks.size(); i++)
82     broken_brick_action(&broken_bricks[i]);
83
84   /* Handle distro counting: */
85   if (counting_distros)
86     {
87       distro_counter--;
88
89       if (distro_counter <= 0)
90         counting_distros = -1;
91     }
92
93   // Handle all kinds of game objects
94   for (unsigned int i = 0; i < bouncy_bricks.size(); i++)
95     bouncy_brick_action(&bouncy_bricks[i]);
96   
97   for (unsigned int i = 0; i < floating_scores.size(); i++)
98     floating_score_action(&floating_scores[i]);
99
100   for (unsigned int i = 0; i < bullets.size(); ++i)
101     bullet_action(&bullets[i]);
102   
103   for (unsigned int i = 0; i < upgrades.size(); i++)
104     upgrade_action(&upgrades[i]);
105
106   for (unsigned int i = 0; i < bad_guys.size(); i++)
107     bad_guys[i].action();
108 }
109
110 void
111 World::add_score(float x, float y, int s)
112 {
113   score += s;
114
115   floating_score_type new_floating_score;
116   floating_score_init(&new_floating_score,x,y,s);
117   floating_scores.push_back(new_floating_score);
118 }
119
120 void
121 World::add_bouncy_distro(float x, float y)
122 {
123   bouncy_distro_type new_bouncy_distro;
124   bouncy_distro_init(&new_bouncy_distro,x,y);
125   bouncy_distros.push_back(new_bouncy_distro);
126 }
127
128 void
129 World::add_broken_brick(float x, float y)
130 {
131   add_broken_brick_piece(x, y, -1, -4);
132   add_broken_brick_piece(x, y + 16, -1.5, -3);
133
134   add_broken_brick_piece(x + 16, y, 1, -4);
135   add_broken_brick_piece(x + 16, y + 16, 1.5, -3);
136 }
137
138 void
139 World::add_broken_brick_piece(float x, float y, float xm, float ym)
140 {
141   broken_brick_type new_broken_brick;
142   broken_brick_init(&new_broken_brick,x,y,xm,ym);
143   broken_bricks.push_back(new_broken_brick);
144 }
145
146 void
147 World::add_bouncy_brick(float x, float y)
148 {
149   bouncy_brick_type new_bouncy_brick;
150   bouncy_brick_init(&new_bouncy_brick,x,y);
151   bouncy_bricks.push_back(new_bouncy_brick);
152 }
153
154 void
155 World::add_bad_guy(float x, float y, BadGuyKind kind)
156 {
157   bad_guys.push_back(BadGuy());
158   BadGuy& new_bad_guy = bad_guys.back();
159   
160   new_bad_guy.init(x,y,kind);
161 }
162
163 void
164 World::add_upgrade(float x, float y, int dir, int kind)
165 {
166   upgrade_type new_upgrade;
167   upgrade_init(&new_upgrade,x,y,dir,kind);
168   upgrades.push_back(new_upgrade);
169 }
170
171 void 
172 World::add_bullet(float x, float y, float xm, int dir)
173 {
174   bullet_type new_bullet;
175   bullet_init(&new_bullet,x,y,xm,dir);
176   bullets.push_back(new_bullet);
177   
178   play_sound(sounds[SND_SHOOT], SOUND_CENTER_SPEAKER);
179 }
180
181
182
183 void bouncy_distro_init(bouncy_distro_type* pbouncy_distro, float x, float y)
184 {
185   pbouncy_distro->base.x = x;
186   pbouncy_distro->base.y = y;
187   pbouncy_distro->base.ym = -2;
188 }
189
190 void bouncy_distro_action(bouncy_distro_type* pbouncy_distro)
191 {
192   pbouncy_distro->base.y = pbouncy_distro->base.y + pbouncy_distro->base.ym * frame_ratio;
193
194   pbouncy_distro->base.ym += 0.1 * frame_ratio;
195
196   if (pbouncy_distro->base.ym >= 0)
197     world.bouncy_distros.erase(static_cast<std::vector<bouncy_distro_type>::iterator>(pbouncy_distro));
198 }
199
200 void bouncy_distro_draw(bouncy_distro_type* pbouncy_distro)
201 {
202   texture_draw(&img_distro[0],
203                pbouncy_distro->base.x - scroll_x,
204                pbouncy_distro->base.y);
205 }
206
207 void broken_brick_init(broken_brick_type* pbroken_brick, float x, float y, float xm, float ym)
208 {
209   pbroken_brick->base.x = x;
210   pbroken_brick->base.y = y;
211   pbroken_brick->base.xm = xm;
212   pbroken_brick->base.ym = ym;
213   timer_init(&pbroken_brick->timer, true);
214   timer_start(&pbroken_brick->timer,200);
215 }
216
217 void broken_brick_action(broken_brick_type* pbroken_brick)
218 {
219   pbroken_brick->base.x = pbroken_brick->base.x + pbroken_brick->base.xm * frame_ratio;
220   pbroken_brick->base.y = pbroken_brick->base.y + pbroken_brick->base.ym * frame_ratio;
221
222   if (!timer_check(&pbroken_brick->timer))
223     world.broken_bricks.erase(static_cast<std::vector<broken_brick_type>::iterator>(pbroken_brick));
224 }
225
226 void broken_brick_draw(broken_brick_type* pbroken_brick)
227 {
228   SDL_Rect src, dest;
229   src.x = rand() % 16;
230   src.y = rand() % 16;
231   src.w = 16;
232   src.h = 16;
233
234   dest.x = (int)(pbroken_brick->base.x - scroll_x);
235   dest.y = (int)pbroken_brick->base.y;
236   dest.w = 16;
237   dest.h = 16;
238
239   texture_draw_part(&img_brick[0],src.x,src.y,dest.x,dest.y,dest.w,dest.h);
240 }
241
242 void bouncy_brick_init(bouncy_brick_type* pbouncy_brick, float x, float y)
243 {
244   pbouncy_brick->base.x   = x;
245   pbouncy_brick->base.y   = y;
246   pbouncy_brick->offset   = 0;
247   pbouncy_brick->offset_m = -BOUNCY_BRICK_SPEED;
248   pbouncy_brick->shape    = GameSession::current()->get_level()->gettileid(x, y);
249 }
250
251 void bouncy_brick_action(bouncy_brick_type* pbouncy_brick)
252 {
253
254   pbouncy_brick->offset = (pbouncy_brick->offset +
255                            pbouncy_brick->offset_m * frame_ratio);
256
257   /* Go back down? */
258
259   if (pbouncy_brick->offset < -BOUNCY_BRICK_MAX_OFFSET)
260     pbouncy_brick->offset_m = BOUNCY_BRICK_SPEED;
261
262
263   /* Stop bouncing? */
264
265   if (pbouncy_brick->offset >= 0)
266     world.bouncy_bricks.erase(static_cast<std::vector<bouncy_brick_type>::iterator>(pbouncy_brick));
267 }
268
269 void bouncy_brick_draw(bouncy_brick_type* pbouncy_brick)
270 {
271   int s;
272   SDL_Rect dest;
273   
274   if (pbouncy_brick->base.x >= scroll_x - 32 &&
275       pbouncy_brick->base.x <= scroll_x + screen->w)
276     {
277       dest.x = (int)(pbouncy_brick->base.x - scroll_x);
278       dest.y = (int)pbouncy_brick->base.y;
279       dest.w = 32;
280       dest.h = 32;
281
282       Level* plevel = GameSession::current()->get_level();
283
284       // FIXME: overdrawing hack to clean the tile from the screen to
285       // paint it later at on offseted position
286       if(plevel->bkgd_image[0] == '\0')
287         {
288           fillrect(pbouncy_brick->base.x - scroll_x, pbouncy_brick->base.y,
289                    32,32, 
290                    plevel->bkgd_red, plevel->bkgd_green, plevel->bkgd_blue, 0);
291         }
292       else
293         {
294           s = (int)scroll_x / 30;
295           texture_draw_part(&img_bkgd,dest.x + s,dest.y,dest.x,dest.y,dest.w,dest.h);
296         }
297
298       drawshape(pbouncy_brick->base.x - scroll_x,
299                 pbouncy_brick->base.y + pbouncy_brick->offset,
300                 pbouncy_brick->shape);
301     }
302 }
303
304 void floating_score_init(floating_score_type* pfloating_score, float x, float y, int s)
305 {
306   pfloating_score->base.x = x;
307   pfloating_score->base.y = y - 16;
308   timer_init(&pfloating_score->timer,true);
309   timer_start(&pfloating_score->timer,1000);
310   pfloating_score->value = s;
311 }
312
313 void floating_score_action(floating_score_type* pfloating_score)
314 {
315   pfloating_score->base.y = pfloating_score->base.y - 2 * frame_ratio;
316
317   if(!timer_check(&pfloating_score->timer))
318     world.floating_scores.erase(static_cast<std::vector<floating_score_type>::iterator>(pfloating_score));
319 }
320
321 void floating_score_draw(floating_score_type* pfloating_score)
322 {
323   char str[10];
324   sprintf(str, "%d", pfloating_score->value);
325   text_draw(&gold_text, str, (int)pfloating_score->base.x + 16 - strlen(str) * 8, (int)pfloating_score->base.y, 1);
326 }
327
328 /* Break a brick: */
329 void trybreakbrick(float x, float y, bool small)
330 {
331   Level* plevel = GameSession::current()->get_level();
332   
333   Tile* tile = gettile(x, y);
334   if (tile->brick)
335     {
336       if (tile->data > 0)
337         {
338           /* Get a distro from it: */
339           world.add_bouncy_distro(((int)(x + 1) / 32) * 32,
340                                   (int)(y / 32) * 32);
341
342           if (!counting_distros)
343             {
344               counting_distros = true;
345               distro_counter = 50;
346             }
347
348           if (distro_counter <= 0)
349             plevel->change(x, y, TM_IA, tile->next_tile);
350
351           play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
352           score = score + SCORE_DISTRO;
353           distros++;
354         }
355       else if (!small)
356         {
357           /* Get rid of it: */
358           plevel->change(x, y, TM_IA, tile->next_tile);
359           
360           /* Replace it with broken bits: */
361           world.add_broken_brick(((int)(x + 1) / 32) * 32,
362                            (int)(y / 32) * 32);
363           
364           /* Get some score: */
365           play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
366           score = score + SCORE_BRICK;
367         }
368     }
369 }
370
371 /* Empty a box: */
372 void tryemptybox(float x, float y, int col_side)
373 {
374   Level* plevel = GameSession::current()->get_level();
375
376   Tile* tile = gettile(x,y);
377   if (!tile->fullbox)
378     return;
379
380   // according to the collision side, set the upgrade direction
381   if(col_side == LEFT)
382     col_side = RIGHT;
383   else
384     col_side = LEFT;
385
386   switch(tile->data)
387     {
388     case 1: //'A':      /* Box with a distro! */
389       world.add_bouncy_distro(((int)(x + 1) / 32) * 32, (int)(y / 32) * 32 - 32);
390       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
391       score = score + SCORE_DISTRO;
392       distros++;
393       break;
394
395     case 2: // 'B':      /* Add an upgrade! */
396       if (tux.size == SMALL)     /* Tux is small, add mints! */
397         world.add_upgrade((int)((x + 1) / 32) * 32, (int)(y / 32) * 32 - 32, col_side, UPGRADE_MINTS);
398       else     /* Tux is big, add coffee: */
399         world.add_upgrade((int)((x + 1) / 32) * 32, (int)(y / 32) * 32 - 32, col_side, UPGRADE_COFFEE);
400       play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER);
401       break;
402
403     case 3:// '!':     /* Add a golden herring */
404       world.add_upgrade((int)((x + 1) / 32) * 32, (int)(y / 32) * 32 - 32, col_side, UPGRADE_HERRING);
405       break;
406     default:
407       break;
408     }
409
410   /* Empty the box: */
411   plevel->change(x, y, TM_IA, tile->next_tile);
412 }
413
414 /* Try to grab a distro: */
415 void trygrabdistro(float x, float y, int bounciness)
416 {
417   Level* plevel = GameSession::current()->get_level();
418   Tile* tile = gettile(x, y);
419   if (tile && tile->distro)
420     {
421       plevel->change(x, y, TM_IA, tile->next_tile);
422       play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
423
424       if (bounciness == BOUNCE)
425         {
426           world.add_bouncy_distro(((int)(x + 1) / 32) * 32,
427                                   (int)(y / 32) * 32);
428         }
429
430       score = score + SCORE_DISTRO;
431       distros++;
432     }
433 }
434
435 /* Try to bump a bad guy from below: */
436 void trybumpbadguy(float x, float y)
437 {
438   /* Bad guys: */
439   for (unsigned int i = 0; i < world.bad_guys.size(); i++)
440     {
441       if (world.bad_guys[i].base.x >= x - 32 && world.bad_guys[i].base.x <= x + 32 &&
442           world.bad_guys[i].base.y >= y - 16 && world.bad_guys[i].base.y <= y + 16)
443         {
444           world.bad_guys[i].collision(&tux, CO_PLAYER, COLLISION_BUMP);
445         }
446     }
447
448
449   /* Upgrades: */
450   for (unsigned int i = 0; i < world.upgrades.size(); i++)
451     {
452       if (world.upgrades[i].base.height == 32 &&
453           world.upgrades[i].base.x >= x - 32 && world.upgrades[i].base.x <= x + 32 &&
454           world.upgrades[i].base.y >= y - 16 && world.upgrades[i].base.y <= y + 16)
455         {
456           world.upgrades[i].base.xm = -world.upgrades[i].base.xm;
457           world.upgrades[i].base.ym = -8;
458           play_sound(sounds[SND_BUMP_UPGRADE], SOUND_CENTER_SPEAKER);
459         }
460     }
461 }
462
463 /* EOF */
464