- fixed bug reported by Frank van der Loo
[supertux.git] / src / sector.cpp
1 //  $Id$
2 //
3 //  SuperTux -  A Jump'n Run
4 //  Copyright (C) 2004 Matthias Braun <matze@braunis.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 <memory>
21 #include <algorithm>
22 #include <stdexcept>
23 #include <iostream>
24 #include <fstream>
25 #include <stdexcept>
26
27 #include "app/globals.h"
28 #include "sector.h"
29 #include "utils/lispreader.h"
30 #include "badguy.h"
31 #include "special.h"
32 #include "gameobjs.h"
33 #include "camera.h"
34 #include "background.h"
35 #include "particlesystem.h"
36 #include "tile.h"
37 #include "tilemap.h"
38 #include "audio/sound_manager.h"
39 #include "gameloop.h"
40 #include "resources.h"
41 #include "interactive_object.h"
42 #include "door.h"
43 #include "statistics.h"
44
45 Sector* Sector::_current = 0;
46
47 Sector::Sector()
48   : gravity(10), player(0), solids(0), background(0), camera(0),
49     end_sequence_animation_type(NONE_ENDSEQ_ANIM), currentmusic(LEVEL_MUSIC)
50 {
51   song_title = "Mortimers_chipdisko.mod";
52   player = new Player();
53   add_object(player);
54 }
55
56 Sector::~Sector()
57 {
58   for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end();
59       ++i)
60     delete *i;
61
62   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
63       ++i)
64     delete *i;
65     
66   if(_current == this)
67     _current = 0;
68 }
69
70 void
71 Sector::parse(LispReader& lispreader)
72 {
73   _current = this;
74   
75   for(lisp_object_t* cur = lispreader.get_lisp(); !lisp_nil_p(cur);
76       cur = lisp_cdr(cur)) {
77     std::string token = lisp_symbol(lisp_car(lisp_car(cur)));
78     // FIXME: doesn't handle empty data
79     lisp_object_t* data = lisp_car(lisp_cdr(lisp_car(cur)));
80     LispReader reader(lisp_cdr(lisp_car(cur)));
81
82     if(token == "name") {
83       name = lisp_string(data);
84     } else if(token == "gravity") {
85       gravity = lisp_real(data);
86     } else if(token == "music") {
87       song_title = lisp_string(data);
88       load_music();
89     } else if(token == "end-sequence-animation") {
90       std::string end_seq_anim = lisp_string(data);
91       if(end_seq_anim == "fireworks")
92         end_sequence_animation_type = FIREWORKS_ENDSEQ_ANIM;
93     } else if(token == "camera") {
94       if(camera) {
95         std::cerr << "Warning: More than 1 camera defined in sector.\n";
96         continue;
97       }
98       camera = new Camera(this);
99       camera->read(reader);
100       add_object(camera);
101     } else if(token == "background") {
102       background = new Background(reader);
103       add_object(background);
104     } else if(token == "spawn-points") {
105       SpawnPoint* sp = new SpawnPoint;
106       reader.read_string("name", sp->name);
107       reader.read_float("x", sp->pos.x);
108       reader.read_float("y", sp->pos.y);
109       spawnpoints.push_back(sp);
110     } else if(token == "tilemap") {
111       TileMap* tilemap = new TileMap(reader);
112       add_object(tilemap);
113
114       if(tilemap->is_solid()) {
115         if(solids) {
116           std::cerr << "Warning multiple solid tilemaps in sector.\n";
117           continue;
118         }
119         solids = tilemap;
120       }
121     } else if(badguykind_from_string(token) != BAD_INVALID) {
122       add_object(new BadGuy(badguykind_from_string(token), reader));
123     } else if(token == "trampoline") {
124       add_object(new Trampoline(reader));
125     } else if(token == "flying-platform") {
126       add_object(new FlyingPlatform(reader));
127     } else if(token == "particles-snow") {
128       SnowParticleSystem* partsys = new SnowParticleSystem();
129       partsys->parse(reader);
130       add_object(partsys);
131     } else if(token == "particles-clouds") {
132       CloudParticleSystem* partsys = new CloudParticleSystem();
133       partsys->parse(reader);
134       add_object(partsys);
135     } else if(token == "door") {
136       add_object(new Door(reader));
137     } else {
138       std::cerr << "Unknown object type '" << token << "'.\n";
139     }
140   }
141
142   if(!camera) {
143     std::cerr << "sector '" << name << "' does not contain a camera.\n";
144     camera = new Camera(this);
145     add_object(camera);
146   }
147   if(!solids)
148     throw std::runtime_error("sector does not contain a solid tile layer.");
149 }
150
151 void
152 Sector::parse_old_format(LispReader& reader)
153 {
154   _current = this;
155   
156   name = "main";
157   reader.read_float("gravity", gravity);
158
159   std::string backgroundimage;
160   reader.read_string("background", backgroundimage);
161   float bgspeed = .5;
162   reader.read_float("bkgd_speed", bgspeed);
163   bgspeed /= 100;
164
165   Color bkgd_top, bkgd_bottom;
166   int r = 0, g = 0, b = 128;
167   reader.read_int("bkgd_red_top", r);
168   reader.read_int("bkgd_green_top",  g);
169   reader.read_int("bkgd_blue_top",  b);
170   bkgd_top.red = r;
171   bkgd_top.green = g;
172   bkgd_top.blue = b;
173   
174   reader.read_int("bkgd_red_bottom",  r);
175   reader.read_int("bkgd_green_bottom", g);
176   reader.read_int("bkgd_blue_bottom", b);
177   bkgd_bottom.red = r;
178   bkgd_bottom.green = g;
179   bkgd_bottom.blue = b;
180   
181   if(backgroundimage != "") {
182     background = new Background;
183     background->set_image(backgroundimage, bgspeed);
184     add_object(background);
185   } else {
186     background = new Background;
187     background->set_gradient(bkgd_top, bkgd_bottom);
188     add_object(background);
189   }
190
191   std::string end_seq_anim;
192   reader.read_string("end-sequence-animation", end_seq_anim);
193   if(end_seq_anim == "fireworks")
194     end_sequence_animation_type = FIREWORKS_ENDSEQ_ANIM;
195 //  else
196 //    end_sequence_animation = NONE_ENDSEQ_ANIM;
197
198   std::string particlesystem;
199   reader.read_string("particle_system", particlesystem);
200   if(particlesystem == "clouds")
201     add_object(new CloudParticleSystem());
202   else if(particlesystem == "snow")
203     add_object(new SnowParticleSystem());
204
205   Vector startpos(100, 170);
206   reader.read_float("start_pos_x", startpos.x);
207   reader.read_float("start_pos_y", startpos.y);
208
209   SpawnPoint* spawn = new SpawnPoint;
210   spawn->pos = startpos;
211   spawn->name = "main";
212   spawnpoints.push_back(spawn);
213
214   song_title = "Mortimers_chipdisko.mod";
215   reader.read_string("music", song_title);
216   load_music();
217
218   int width, height = 15;
219   reader.read_int("width", width);
220   reader.read_int("height", height);
221   
222   std::vector<unsigned int> tiles;
223   if(reader.read_int_vector("interactive-tm", tiles)
224       || reader.read_int_vector("tilemap", tiles)) {
225     TileMap* tilemap = new TileMap();
226     tilemap->set(width, height, tiles, LAYER_TILES, true);
227     solids = tilemap;
228     add_object(tilemap);
229   }
230
231   if(reader.read_int_vector("background-tm", tiles)) {
232     TileMap* tilemap = new TileMap();
233     tilemap->set(width, height, tiles, LAYER_BACKGROUNDTILES, false);
234     add_object(tilemap);
235   }
236
237   if(reader.read_int_vector("foreground-tm", tiles)) {
238     TileMap* tilemap = new TileMap();
239     tilemap->set(width, height, tiles, LAYER_FOREGROUNDTILES, false);
240     add_object(tilemap);
241   }
242
243   // read reset-points (now spawn-points)
244   {
245     lisp_object_t* cur = 0;
246     if(reader.read_lisp("reset-points", cur)) {
247       while(!lisp_nil_p(cur)) {
248         lisp_object_t* data = lisp_car(cur);
249         LispReader reader(lisp_cdr(data));
250
251         Vector sp_pos;
252         if(reader.read_float("x", sp_pos.x) && reader.read_float("y", sp_pos.y))
253           {
254           SpawnPoint* sp = new SpawnPoint;
255           sp->name = "main";
256           sp->pos = sp_pos;
257           spawnpoints.push_back(sp);
258           }
259                                                              
260         cur = lisp_cdr(cur);
261       }
262     }
263   }
264
265   // read objects
266   {
267     lisp_object_t* cur = 0;
268     if(reader.read_lisp("objects", cur)) {
269       while(!lisp_nil_p(cur)) {
270         lisp_object_t* data = lisp_car(cur);
271         std::string object_type = lisp_symbol(lisp_car(data));
272                                                                                 
273         LispReader reader(lisp_cdr(data));
274                                                                                 
275         if(object_type == "trampoline") {
276           add_object(new Trampoline(reader));
277         }
278         else if(object_type == "flying-platform") {
279           add_object(new FlyingPlatform(reader));
280         }
281         else {
282           BadGuyKind kind = badguykind_from_string(object_type);
283           add_object(new BadGuy(kind, reader));
284         }
285                                                                                 
286         cur = lisp_cdr(cur);
287       }
288     }
289   }
290
291   // add a camera
292   camera = new Camera(this);
293   add_object(camera);
294 }
295
296 void
297 Sector::write(LispWriter& writer)
298 {
299   writer.write_string("name", name);
300   writer.write_float("gravity", gravity);
301   writer.write_string("music", song_title);
302
303   // write spawnpoints
304   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
305       ++i) {
306     SpawnPoint* spawn = *i;
307     writer.start_list("spawn-points");
308     writer.write_string("name", spawn->name);
309     writer.write_float("x", spawn->pos.x);
310     writer.write_float("y", spawn->pos.y);
311     writer.end_list("spawn-points");
312   }
313
314   // write objects
315   for(GameObjects::iterator i = gameobjects.begin();
316       i != gameobjects.end(); ++i) {
317     Serializable* serializable = dynamic_cast<Serializable*> (*i);
318     if(serializable)
319       serializable->write(writer);
320   }
321 }
322
323 void
324 Sector::do_vertical_flip()
325 {
326   for(GameObjects::iterator i = gameobjects_new.begin(); i != gameobjects_new.end(); ++i)
327     {
328     TileMap* tilemap = dynamic_cast<TileMap*> (*i);
329     if(tilemap)
330       {
331       tilemap->do_vertical_flip();
332       }
333
334     BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
335     if(badguy)
336       badguy->start_position.y = solids->get_height()*32 - badguy->start_position.y - 32;
337     Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
338     if(trampoline)
339       trampoline->base.y = solids->get_height()*32 - trampoline->base.y - 32;
340     FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (*i);
341     if(flying_platform)
342       flying_platform->base.y = solids->get_height()*32 - flying_platform->base.y - 32;
343     Door* door = dynamic_cast<Door*> (*i);
344     if(door)
345       door->set_area(door->get_area().x, solids->get_height()*32 - door->get_area().y - 32);
346     }
347
348   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
349       ++i) {
350     SpawnPoint* spawn = *i;
351     spawn->pos.y = solids->get_height()*32 - spawn->pos.y - 32;
352   }
353 }
354
355 void
356 Sector::add_object(GameObject* object)
357 {
358   gameobjects_new.push_back(object);
359 }
360
361 void
362 Sector::activate(const std::string& spawnpoint)
363 {
364   _current = this;
365
366   // Apply bonuses from former levels
367   switch (player_status.bonus)
368     {
369     case PlayerStatus::NO_BONUS:
370       break;
371                                                                                 
372     case PlayerStatus::FLOWER_BONUS:
373       player->got_power = Player::FIRE_POWER;  // FIXME: add ice power to here
374       // fall through
375                                                                                 
376     case PlayerStatus::GROWUP_BONUS:
377       player->grow(false);
378       break;
379     }
380
381   SpawnPoint* sp = 0;
382   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
383       ++i) {
384     if((*i)->name == spawnpoint) {
385       sp = *i;
386       break;
387     }
388   }
389   if(!sp) {
390     std::cerr << "Spawnpoint '" << spawnpoint << "' not found.\n";
391   } else {
392     player->move(sp->pos);
393   }
394
395   camera->reset(Vector(player->base.x, player->base.y));
396 }
397
398 Vector
399 Sector::get_best_spawn_point(Vector pos)
400 {
401 Vector best_reset_point = Vector(-1,-1);
402
403 for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
404       ++i) {
405   if((*i)->name != "main")
406     continue;
407   if((*i)->pos.x > best_reset_point.x && (*i)->pos.x < pos.x)
408     best_reset_point = (*i)->pos;
409   }
410
411 return best_reset_point;
412 }
413
414 void
415 Sector::action(float elapsed_time)
416 {
417   player->check_bounds(camera);
418                                                                                 
419   /* update objects (don't use iterators here, because the list might change
420    * during the iteration)
421    */
422   for(size_t i = 0; i < gameobjects.size(); ++i)
423     if(gameobjects[i]->is_valid())
424       gameobjects[i]->action(elapsed_time);
425                                                                                 
426   /* Handle all possible collisions. */
427   collision_handler();
428                                                                                 
429   update_game_objects();
430 }
431
432 void
433 Sector::update_game_objects()
434 {
435   /** cleanup marked objects */
436   for(std::vector<GameObject*>::iterator i = gameobjects.begin();
437       i != gameobjects.end(); /* nothing */) {
438     if((*i)->is_valid() == false) {
439       BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
440       if(badguy) {
441         badguys.erase(std::remove(badguys.begin(), badguys.end(), badguy),
442             badguys.end());
443       }
444       Bullet* bullet = dynamic_cast<Bullet*> (*i);
445       if(bullet) {
446         bullets.erase(
447             std::remove(bullets.begin(), bullets.end(), bullet),
448             bullets.end());
449       }
450       InteractiveObject* interactive_object =
451           dynamic_cast<InteractiveObject*> (*i);
452       if(interactive_object) {
453         interactive_objects.erase(
454             std::remove(interactive_objects.begin(), interactive_objects.end(),
455                 interactive_object), interactive_objects.end());
456       }
457       Upgrade* upgrade = dynamic_cast<Upgrade*> (*i);
458       if(upgrade) {
459         upgrades.erase(
460             std::remove(upgrades.begin(), upgrades.end(), upgrade),
461             upgrades.end());
462       }
463       Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
464       if(trampoline) {
465         trampolines.erase(
466             std::remove(trampolines.begin(), trampolines.end(), trampoline),
467             trampolines.end());
468       }
469       FlyingPlatform* flying_platform= dynamic_cast<FlyingPlatform*> (*i);
470       if(flying_platform) {
471         flying_platforms.erase(
472             std::remove(flying_platforms.begin(), flying_platforms.end(), flying_platform),
473             flying_platforms.end());
474       }
475       SmokeCloud* smoke_cloud = dynamic_cast<SmokeCloud*> (*i);
476       if(smoke_cloud) {
477         smoke_clouds.erase(
478             std::remove(smoke_clouds.begin(), smoke_clouds.end(), smoke_cloud),
479             smoke_clouds.end());
480       }
481       Particles* particle = dynamic_cast<Particles*> (*i);
482       if(particle) {
483         particles.erase(
484             std::remove(particles.begin(), particles.end(), particle),
485             particles.end());
486       }
487
488       delete *i;
489       i = gameobjects.erase(i);
490     } else {
491       ++i;
492     }
493   }
494
495   /* add newly created objects */
496   for(std::vector<GameObject*>::iterator i = gameobjects_new.begin();
497       i != gameobjects_new.end(); ++i)
498   {
499           BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
500           if(badguy)
501             badguys.push_back(badguy);
502           Bullet* bullet = dynamic_cast<Bullet*> (*i);
503           if(bullet)
504             bullets.push_back(bullet);
505           Upgrade* upgrade = dynamic_cast<Upgrade*> (*i);
506           if(upgrade)
507             upgrades.push_back(upgrade);
508           Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
509           if(trampoline)
510             trampolines.push_back(trampoline);
511           FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (*i);
512           if(flying_platform)
513             flying_platforms.push_back(flying_platform);
514           InteractiveObject* interactive_object 
515               = dynamic_cast<InteractiveObject*> (*i);
516           if(interactive_object)
517             interactive_objects.push_back(interactive_object);
518           SmokeCloud* smoke_cloud = dynamic_cast<SmokeCloud*> (*i);
519           if(smoke_cloud)
520             smoke_clouds.push_back(smoke_cloud);
521           Particles* particle = dynamic_cast<Particles*> (*i);
522           if(particle)
523             particles.push_back(particle);
524
525           gameobjects.push_back(*i);
526   }
527   gameobjects_new.clear();
528 }
529
530 void
531 Sector::draw(DrawingContext& context)
532 {
533   context.push_transform();
534   context.set_translation(camera->get_translation());
535   
536   for(GameObjects::iterator i = gameobjects.begin();
537       i != gameobjects.end(); ++i) {
538     if( (*i)->is_valid() )
539       (*i)->draw(context);
540   }
541
542   context.pop_transform();
543 }
544
545 void
546 Sector::collision_handler()
547 {
548   // CO_BULLET & CO_BADGUY check
549   for(unsigned int i = 0; i < bullets.size(); ++i)
550     {
551       for (BadGuys::iterator j = badguys.begin(); j != badguys.end(); ++j)
552         {
553           if((*j)->dying != DYING_NOT)
554             continue;
555                                                                                 
556           if(rectcollision(bullets[i]->base, (*j)->base))
557             {
558               // We have detected a collision and now call the
559               // collision functions of the collided objects.
560               (*j)->collision(bullets[i], CO_BULLET, COLLISION_NORMAL);
561               bullets[i]->collision(CO_BADGUY);
562               break; // bullet is invalid now, so break
563             }
564         }
565     }
566                                                                                 
567   /* CO_BADGUY & CO_BADGUY check */
568   for (BadGuys::iterator i = badguys.begin(); i != badguys.end(); ++i)
569     {
570       if((*i)->dying != DYING_NOT)
571         continue;
572                                                                                 
573       BadGuys::iterator j = i;
574       ++j;
575       for (; j != badguys.end(); ++j)
576         {
577           if(j == i || (*j)->dying != DYING_NOT)
578             continue;
579                                                                                 
580           if(rectcollision((*i)->base, (*j)->base))
581             {
582               // We have detected a collision and now call the
583               // collision functions of the collided objects.
584               (*j)->collision(*i, CO_BADGUY);
585               (*i)->collision(*j, CO_BADGUY);
586             }
587         }
588     }
589   if(player->dying != DYING_NOT) return;
590                                                                                 
591   // CO_BADGUY & CO_PLAYER check
592   for (BadGuys::iterator i = badguys.begin(); i != badguys.end(); ++i)
593     {
594       if((*i)->dying != DYING_NOT)
595         continue;
596                                                                                 
597       if(rectcollision_offset((*i)->base, player->base, 0, 0))
598         {
599           // We have detected a collision and now call the collision
600           // functions of the collided objects.
601           if (player->previous_base.y < player->base.y &&
602               player->previous_base.y + player->previous_base.height
603               < (*i)->base.y + (*i)->base.height/2
604               && !player->invincible_timer.started())
605             {
606               (*i)->collision(player, CO_PLAYER, COLLISION_SQUISH);
607             }
608           else
609             {
610               player->collision(*i, CO_BADGUY);
611               (*i)->collision(player, CO_PLAYER, COLLISION_NORMAL);
612             }
613         }
614     }
615                                                                                 
616   // CO_UPGRADE & CO_PLAYER check
617   for(unsigned int i = 0; i < upgrades.size(); ++i)
618     {
619       if(rectcollision(upgrades[i]->base, player->base))
620         {
621           // We have detected a collision and now call the collision
622           // functions of the collided objects.
623           upgrades[i]->collision(player, CO_PLAYER, COLLISION_NORMAL);
624         }
625     }
626                                                                                 
627   // CO_TRAMPOLINE & (CO_PLAYER or CO_BADGUY)
628   for (Trampolines::iterator i = trampolines.begin(); i != trampolines.end(); ++i)
629   {
630     if (rectcollision((*i)->base, player->base))
631     {
632       if (player->previous_base.y < player->base.y &&
633           player->previous_base.y + player->previous_base.height
634           < (*i)->base.y + (*i)->base.height/2)
635       {
636         (*i)->collision(player, CO_PLAYER, COLLISION_SQUISH);
637       }
638       else if (player->previous_base.y <= player->base.y)
639       {
640         player->collision(*i, CO_TRAMPOLINE);
641         (*i)->collision(player, CO_PLAYER, COLLISION_NORMAL);
642       }
643     }
644   }
645                                                                                 
646   // CO_FLYING_PLATFORM & (CO_PLAYER or CO_BADGUY)
647   for (FlyingPlatforms::iterator i = flying_platforms.begin(); i != flying_platforms.end(); ++i)
648   {
649     if (rectcollision((*i)->base, player->base))
650     {
651       if (player->previous_base.y < player->base.y &&
652           player->previous_base.y + player->previous_base.height
653           < (*i)->base.y + (*i)->base.height/2)
654       {
655         (*i)->collision(player, CO_PLAYER, COLLISION_SQUISH);
656         player->collision(*i, CO_FLYING_PLATFORM);
657       }
658 /*      else if (player->previous_base.y <= player->base.y)
659       {
660       }*/
661     }
662   }
663 }
664
665 void
666 Sector::add_score(const Vector& pos, int s)
667 {
668   global_stats.add_points(SCORE_STAT, s);
669                                                                                 
670   add_object(new FloatingText(pos, s));
671 }
672                                                                                 
673 void
674 Sector::add_bouncy_distro(const Vector& pos)
675 {
676   add_object(new BouncyDistro(pos));
677 }
678                                                                                 
679 void
680 Sector::add_broken_brick(const Vector& pos, Tile* tile)
681 {
682   add_broken_brick_piece(pos, Vector(-1, -4), tile);
683   add_broken_brick_piece(pos + Vector(0, 16), Vector(-1.5, -3), tile);
684                                                                                 
685   add_broken_brick_piece(pos + Vector(16, 0), Vector(1, -4), tile);
686   add_broken_brick_piece(pos + Vector(16, 16), Vector(1.5, -3), tile);
687 }
688                                                                                 
689 void
690 Sector::add_broken_brick_piece(const Vector& pos, const Vector& movement,
691     Tile* tile)
692 {
693   add_object(new BrokenBrick(tile, pos, movement));
694 }
695                                                                                 
696 void
697 Sector::add_bouncy_brick(const Vector& pos)
698 {
699   add_object(new BouncyBrick(pos));
700 }
701
702 BadGuy*
703 Sector::add_bad_guy(float x, float y, BadGuyKind kind)
704 {
705   BadGuy* badguy = new BadGuy(kind, x, y);
706   add_object(badguy);
707   return badguy;
708 }
709                                                                                 
710 void
711 Sector::add_upgrade(const Vector& pos, Direction dir, UpgradeKind kind)
712 {
713   add_object(new Upgrade(pos, dir, kind));
714 }
715                                                                                 
716 bool
717 Sector::add_bullet(const Vector& pos, float xm, Direction dir)
718 {
719   if(player->got_power == Player::FIRE_POWER)
720     {
721     if(bullets.size() > MAX_FIRE_BULLETS-1)
722       return false;
723     }
724   else if(player->got_power == Player::ICE_POWER)
725     {
726     if(bullets.size() > MAX_ICE_BULLETS-1)
727       return false;
728     }
729                                                                                 
730   Bullet* new_bullet = 0;
731   if(player->got_power == Player::FIRE_POWER)
732     new_bullet = new Bullet(pos, xm, dir, FIRE_BULLET);
733   else if(player->got_power == Player::ICE_POWER)
734     new_bullet = new Bullet(pos, xm, dir, ICE_BULLET);
735   else
736     throw std::runtime_error("wrong bullet type.");
737   add_object(new_bullet);
738
739   SoundManager::get()->play_sound(IDToSound(SND_SHOOT));
740                                                                                 
741   return true;
742 }
743
744 bool
745 Sector::add_smoke_cloud(const Vector& pos)
746 {
747   add_object(new SmokeCloud(pos));
748   return true;
749 }
750
751 bool
752 Sector::add_particles(const Vector& epicenter, const Vector& velocity, const Vector& acceleration, int number, Color color, int size, int life_time)
753 {
754   add_object(new Particles(epicenter, velocity, acceleration, number, color, size, life_time));
755   return true;
756 }
757
758 /* Break a brick: */
759 bool
760 Sector::trybreakbrick(const Vector& pos, bool small)
761 {
762   Tile* tile = solids->get_tile_at(pos);
763   if (!tile)
764   {
765     char errmsg[64];
766     sprintf(errmsg, "Invalid tile at %i,%i", (int)((pos.x+1)/32*32), (int)((pos.y+1)/32*32));
767     throw SuperTuxException(errmsg, __FILE__, __LINE__);
768   }
769
770   if (tile->attributes & Tile::BRICK)
771     {
772       if (tile->data > 0)
773         {
774           /* Get a distro from it: */
775           add_bouncy_distro(
776               Vector(((int)(pos.x + 1) / 32) * 32, (int)(pos.y / 32) * 32));
777                                                                                 
778           // TODO: don't handle this in a global way but per-tile...
779           if (!counting_distros)
780             {
781               counting_distros = true;
782               distro_counter = 5;
783             }
784           else
785             {
786               distro_counter--;
787             }
788                                                                                 
789           if (distro_counter <= 0)
790             {
791               counting_distros = false;
792               solids->change_at(pos, tile->next_tile);
793             }
794
795           SoundManager::get()->play_sound(IDToSound(SND_DISTRO));
796           global_stats.add_points(SCORE_STAT, SCORE_DISTRO);
797           global_stats.add_points(COINS_COLLECTED_STAT, 1);
798           player_status.distros++;
799           return true;
800         }
801       else if (!small)
802         {
803           /* Get rid of it: */
804           solids->change_at(pos, tile->next_tile);
805                                                                                 
806           /* Replace it with broken bits: */
807           add_broken_brick(Vector(
808                                  ((int)(pos.x + 1) / 32) * 32,
809                                  (int)(pos.y / 32) * 32), tile);
810                                                                                 
811           /* Get some score: */
812           SoundManager::get()->play_sound(IDToSound(SND_BRICK));
813           global_stats.add_points(SCORE_STAT, SCORE_BRICK);
814                                                                                 
815           return true;
816         }
817     }
818                                                                                 
819   return false;
820 }
821                                                                                 
822 /* Empty a box: */
823 void
824 Sector::tryemptybox(const Vector& pos, Direction col_side)
825 {
826   Tile* tile = solids->get_tile_at(pos);
827   if (!tile)
828   {
829     char errmsg[64];
830     sprintf(errmsg, "Invalid tile at %i,%i", (int)((pos.x+1)/32*32), (int)((pos.y+1)/32*32));
831     throw SuperTuxException(errmsg, __FILE__, __LINE__);
832   }
833
834
835   if (!(tile->attributes & Tile::FULLBOX))
836     return;
837                                                                                 
838   // according to the collision side, set the upgrade direction
839   if(col_side == LEFT)
840     col_side = RIGHT;
841   else
842     col_side = LEFT;
843                                                                                 
844   int posx = ((int)(pos.x+1) / 32) * 32;
845   int posy = (int)(pos.y/32) * 32 - 32;
846   switch(tile->data)
847     {
848     case 1: // Box with a distro!
849       add_bouncy_distro(Vector(posx, posy));
850       SoundManager::get()->play_sound(IDToSound(SND_DISTRO));
851       global_stats.add_points(SCORE_STAT, SCORE_DISTRO);
852       global_stats.add_points(COINS_COLLECTED_STAT, 1);
853       player_status.distros++;
854       break;
855                                                                                 
856     case 2: // Add a fire flower upgrade!
857       if (player->size == SMALL)     /* Tux is small, add mints! */
858         add_upgrade(Vector(posx, posy), col_side, UPGRADE_GROWUP);
859       else     /* Tux is big, add a fireflower: */
860         add_upgrade(Vector(posx, posy), col_side, UPGRADE_FIREFLOWER);
861       SoundManager::get()->play_sound(IDToSound(SND_UPGRADE));
862       break;
863                                                                                 
864     case 5: // Add an ice flower upgrade!
865       if (player->size == SMALL)     /* Tux is small, add mints! */
866         add_upgrade(Vector(posx, posy), col_side, UPGRADE_GROWUP);
867       else     /* Tux is big, add an iceflower: */
868         add_upgrade(Vector(posx, posy), col_side, UPGRADE_ICEFLOWER);
869       SoundManager::get()->play_sound(IDToSound(SND_UPGRADE));
870       break;
871                                                                                 
872     case 3: // Add a golden herring
873       add_upgrade(Vector(posx, posy), col_side, UPGRADE_STAR);
874       break;
875                                                                                 
876     case 4: // Add a 1up extra
877       add_upgrade(Vector(posx, posy), col_side, UPGRADE_1UP);
878       break;
879     default:
880       break;
881     }
882                                                                                 
883   /* Empty the box: */
884   solids->change_at(pos, tile->next_tile);
885 }
886                                                                                 
887 /* Try to grab a distro: */
888 void
889 Sector::trygrabdistro(const Vector& pos, int bounciness)
890 {
891   Tile* tile = solids->get_tile_at(pos);
892   if (!tile)
893   {
894     /*char errmsg[64];
895     sprintf(errmsg, "Invalid tile at %i,%i", (int)((pos.x+1)/32*32), (int)((pos.y+1)/32*32));
896     throw SuperTuxException(errmsg, __FILE__, __LINE__); */
897     
898     //Bad tiles (i.e. tiles that are not defined in supertux.stgt but appear in the map) are changed to ID 0 (blank tile)
899     std::cout << "Warning: Undefined tile at " <<(int)pos.x/32 << "/" << (int)pos.y/32 << " (ID: " << (int)solids->get_tile_id_at(pos).id << ")" << std::endl;
900     solids->change_at(pos,0);
901     tile = solids->get_tile_at(pos);
902   }
903
904
905   if (!(tile->attributes & Tile::COIN))
906     return;
907
908   solids->change_at(pos, tile->next_tile);
909   SoundManager::get()->play_sound(IDToSound(SND_DISTRO));
910
911   if (bounciness == BOUNCE)
912     {
913       add_bouncy_distro(Vector(((int)(pos.x + 1) / 32) * 32,
914                               (int)(pos.y / 32) * 32));
915     }
916                                                                             
917   global_stats.add_points(SCORE_STAT, SCORE_DISTRO);
918   global_stats.add_points(COINS_COLLECTED_STAT, 1);
919   player_status.distros++;
920
921 }
922                                                                                 
923 /* Try to bump a bad guy from below: */
924 void
925 Sector::trybumpbadguy(const Vector& pos)
926 {
927   // Bad guys:
928   for (BadGuys::iterator i = badguys.begin(); i != badguys.end(); ++i)
929     {
930       if ((*i)->base.x >= pos.x - 32 && (*i)->base.x <= pos.x + 32 &&
931           (*i)->base.y >= pos.y - 16 && (*i)->base.y <= pos.y + 16)
932         {
933           (*i)->collision(player, CO_PLAYER, COLLISION_BUMP);
934         }
935     }
936                                                                                 
937   // Upgrades:
938   for (unsigned int i = 0; i < upgrades.size(); i++)
939     {
940       if (upgrades[i]->base.height == 32 &&
941           upgrades[i]->base.x >= pos.x - 32 && upgrades[i]->base.x <= pos.x + 32 &&
942           upgrades[i]->base.y >= pos.y - 16 && upgrades[i]->base.y <= pos.y + 16)
943         {
944           upgrades[i]->collision(player, CO_PLAYER, COLLISION_BUMP);
945         }
946     }
947 }
948
949 void
950 Sector::load_music()
951 {
952   char* song_path;
953   char* song_subtitle;
954                                                                                 
955   level_song = SoundManager::get()->load_music(datadir + "/music/" + song_title);
956                                                                                 
957   song_path = (char *) malloc(sizeof(char) * datadir.length() +
958                               strlen(song_title.c_str()) + 8 + 5);
959   song_subtitle = strdup(song_title.c_str());
960   strcpy(strstr(song_subtitle, "."), "\0");
961   sprintf(song_path, "%s/music/%s-fast%s", datadir.c_str(),
962           song_subtitle, strstr(song_title.c_str(), "."));
963   if(!SoundManager::get()->exists_music(song_path)) {
964     level_song_fast = level_song;
965   } else {
966     level_song_fast = SoundManager::get()->load_music(song_path);
967   }
968   free(song_subtitle);
969   free(song_path);
970 }
971
972 void
973 Sector::play_music(int type)
974 {
975   currentmusic = type;
976   switch(currentmusic) {
977     case HURRYUP_MUSIC:
978       SoundManager::get()->play_music(level_song_fast);
979       break;
980     case LEVEL_MUSIC:
981       SoundManager::get()->play_music(level_song);
982       break;
983     case HERRING_MUSIC:
984       SoundManager::get()->play_music(herring_song);
985       break;
986     default:
987       SoundManager::get()->halt_music();
988       break;
989   }
990 }
991
992 int
993 Sector::get_music_type()
994 {
995   return currentmusic;
996 }
997
998 int
999 Sector::get_total_badguys()
1000 {
1001   int total_badguys = 0;
1002   for(GameObjects::iterator i = gameobjects_new.begin(); i != gameobjects_new.end(); ++i)
1003     {
1004     BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
1005     if(badguy)
1006       total_badguys++;
1007     }
1008   return total_badguys;
1009 }