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