converted all sound files to .ogg (to avoid problems with different sampling rates...
[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 #include <config.h>
20
21 #include <memory>
22 #include <algorithm>
23 #include <stdexcept>
24 #include <iostream>
25 #include <fstream>
26 #include <sstream>
27 #include <stdexcept>
28
29 #include "sector.hpp"
30 #include "player_status.hpp"
31 #include "object/gameobjs.hpp"
32 #include "object/camera.hpp"
33 #include "object/background.hpp"
34 #include "object/particlesystem.hpp"
35 #include "object/particlesystem_interactive.hpp"
36 #include "object/tilemap.hpp"
37 #include "lisp/parser.hpp"
38 #include "lisp/lisp.hpp"
39 #include "lisp/writer.hpp"
40 #include "lisp/list_iterator.hpp"
41 #include "tile.hpp"
42 #include "audio/sound_manager.hpp"
43 #include "game_session.hpp"
44 #include "resources.hpp"
45 #include "statistics.hpp"
46 #include "collision_grid.hpp"
47 #include "collision_grid_iterator.hpp"
48 #include "object_factory.hpp"
49 #include "collision.hpp"
50 #include "spawn_point.hpp"
51 #include "math/rect.hpp"
52 #include "math/aatriangle.hpp"
53 #include "object/coin.hpp"
54 #include "object/block.hpp"
55 #include "object/invisible_block.hpp"
56 #include "object/bullet.hpp"
57 #include "object/text_object.hpp"
58 #include "badguy/jumpy.hpp"
59 #include "badguy/spike.hpp"
60 #include "trigger/sequence_trigger.hpp"
61 #include "player_status.hpp"
62 #include "scripting/script_interpreter.hpp"
63 #include "scripting/sound.hpp"
64 #include "scripting/scripted_object.hpp"
65 #include "scripting/text.hpp"
66
67 //#define USE_GRID
68
69 Sector* Sector::_current = 0;
70
71 Sector::Sector()
72   : gravity(10), player(0), solids(0), camera(0),
73     currentmusic(LEVEL_MUSIC)
74 {
75   song_title = "chipdisko.ogg";
76   player = new Player(&player_status);
77   add_object(player);
78
79 #ifdef USE_GRID
80   grid = new CollisionGrid(32000, 32000);
81 #else
82   grid = 0;
83 #endif
84 }
85
86 Sector::~Sector()
87 {
88   update_game_objects();
89   assert(gameobjects_new.size() == 0);
90
91   delete grid;
92
93   for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end();
94       ++i) {
95     delete *i;
96   }
97
98   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
99       ++i)
100     delete *i;
101     
102   if(_current == this)
103     _current = 0;
104 }
105
106 GameObject*
107 Sector::parse_object(const std::string& name, const lisp::Lisp& reader)
108 {
109   if(name == "camera") {
110     Camera* camera = new Camera(this);
111     camera->parse(reader);
112     return camera;
113   } else if(name == "particles-snow") {
114     SnowParticleSystem* partsys = new SnowParticleSystem();
115     partsys->parse(reader);
116     return partsys;
117   } else if(name == "particles-rain") {
118     RainParticleSystem* partsys = new RainParticleSystem();
119     partsys->parse(reader);
120     return partsys;
121   } else if(name == "particles-comets") {
122     CometParticleSystem* partsys = new CometParticleSystem();
123     partsys->parse(reader);
124     return partsys;
125   } else if(name == "particles-ghosts") {
126     GhostParticleSystem* partsys = new GhostParticleSystem();
127     partsys->parse(reader);
128     return partsys;
129   } else if(name == "particles-clouds") {
130     CloudParticleSystem* partsys = new CloudParticleSystem();
131     partsys->parse(reader);
132     return partsys;
133   } else if(name == "money") { // for compatibility with old maps
134     return new Jumpy(reader);
135   } 
136
137   try {
138     return create_object(name, reader);
139   } catch(std::exception& e) {
140     std::cerr << e.what() << "\n";
141   }
142   
143   return 0;
144 }
145
146 void
147 Sector::parse(const lisp::Lisp& sector)
148 {
149   _current = this;
150   
151   lisp::ListIterator iter(&sector);
152   while(iter.next()) {
153     const std::string& token = iter.item();
154     if(token == "name") {
155       iter.value()->get(name);
156     } else if(token == "gravity") {
157       iter.value()->get(gravity);
158     } else if(token == "music") {
159       iter.value()->get(song_title);
160     } else if(token == "spawnpoint") {
161       SpawnPoint* sp = new SpawnPoint(iter.lisp());
162       spawnpoints.push_back(sp);
163     } else if(token == "init-script") {
164       iter.value()->get(init_script);
165     } else {
166       GameObject* object = parse_object(token, *(iter.lisp()));
167       if(object) {
168         add_object(object);
169       }
170     }
171   }
172
173   update_game_objects();
174   fix_old_tiles();
175   if(!camera) {
176     std::cerr << "sector '" << name << "' does not contain a camera.\n";
177     update_game_objects();
178     add_object(new Camera(this));
179   }
180   if(!solids)
181     throw std::runtime_error("sector does not contain a solid tile layer.");
182
183   update_game_objects();
184 }
185
186 void
187 Sector::parse_old_format(const lisp::Lisp& reader)
188 {
189   _current = this;
190   
191   name = "main";
192   reader.get("gravity", gravity);
193
194   std::string backgroundimage;
195   reader.get("background", backgroundimage);
196   float bgspeed = .5;
197   reader.get("bkgd_speed", bgspeed);
198   bgspeed /= 100;
199
200   Color bkgd_top, bkgd_bottom;
201   int r = 0, g = 0, b = 128;
202   reader.get("bkgd_red_top", r);
203   reader.get("bkgd_green_top",  g);
204   reader.get("bkgd_blue_top",  b);
205   bkgd_top.red = r;
206   bkgd_top.green = g;
207   bkgd_top.blue = b;
208   
209   reader.get("bkgd_red_bottom",  r);
210   reader.get("bkgd_green_bottom", g);
211   reader.get("bkgd_blue_bottom", b);
212   bkgd_bottom.red = r;
213   bkgd_bottom.green = g;
214   bkgd_bottom.blue = b;
215   
216   if(backgroundimage != "") {
217     Background* background = new Background;
218     background->set_image(backgroundimage, bgspeed);
219     add_object(background);
220   } else {
221     Background* background = new Background;
222     background->set_gradient(bkgd_top, bkgd_bottom);
223     add_object(background);
224   }
225
226   std::string particlesystem;
227   reader.get("particle_system", particlesystem);
228   if(particlesystem == "clouds")
229     add_object(new CloudParticleSystem());
230   else if(particlesystem == "snow")
231     add_object(new SnowParticleSystem());
232   else if(particlesystem == "rain")
233     add_object(new RainParticleSystem());
234
235   Vector startpos(100, 170);
236   reader.get("start_pos_x", startpos.x);
237   reader.get("start_pos_y", startpos.y);
238
239   SpawnPoint* spawn = new SpawnPoint;
240   spawn->pos = startpos;
241   spawn->name = "main";
242   spawnpoints.push_back(spawn);
243
244   song_title = "chipdisko.ogg";
245   reader.get("music", song_title);
246
247   int width, height = 15;
248   reader.get("width", width);
249   reader.get("height", height);
250   
251   std::vector<unsigned int> tiles;
252   if(reader.get_vector("interactive-tm", tiles)
253       || reader.get_vector("tilemap", tiles)) {
254     TileMap* tilemap = new TileMap();
255     tilemap->set(width, height, tiles, LAYER_TILES, true);
256     add_object(tilemap);
257   }
258
259   if(reader.get_vector("background-tm", tiles)) {
260     TileMap* tilemap = new TileMap();
261     tilemap->set(width, height, tiles, LAYER_BACKGROUNDTILES, false);
262     add_object(tilemap);
263   }
264
265   if(reader.get_vector("foreground-tm", tiles)) {
266     TileMap* tilemap = new TileMap();
267     tilemap->set(width, height, tiles, LAYER_FOREGROUNDTILES, false);
268     add_object(tilemap);
269   }
270
271   // read reset-points (now spawn-points)
272   const lisp::Lisp* resetpoints = reader.get_lisp("reset-points");
273   if(resetpoints) {
274     lisp::ListIterator iter(resetpoints);
275     while(iter.next()) {
276       if(iter.item() == "point") {
277         Vector sp_pos;
278         if(reader.get("x", sp_pos.x) && reader.get("y", sp_pos.y))
279           {
280           SpawnPoint* sp = new SpawnPoint;
281           sp->name = "main";
282           sp->pos = sp_pos;
283           spawnpoints.push_back(sp);
284           }
285       } else {
286         std::cerr << "Unknown token '" << iter.item() << "' in reset-points.\n";
287       }
288     }
289   }
290
291   // read objects
292   const lisp::Lisp* objects = reader.get_lisp("objects");
293   if(objects) {
294     lisp::ListIterator iter(objects);
295     while(iter.next()) {
296       GameObject* object = parse_object(iter.item(), *(iter.lisp()));
297       if(object) {
298         add_object(object);
299       } else {
300         std::cerr << "Unknown object '" << iter.item() << "' in level.\n";
301       }
302     }
303   }
304
305   // add a camera
306   Camera* camera = new Camera(this);
307   add_object(camera);
308
309   update_game_objects();
310   fix_old_tiles();
311   update_game_objects();
312   if(solids == 0)
313     throw std::runtime_error("sector does not contain a solid tile layer.");  
314 }
315
316 void
317 Sector::fix_old_tiles()
318 {
319   // hack for now...
320   for(size_t x=0; x < solids->get_width(); ++x) {
321     for(size_t y=0; y < solids->get_height(); ++y) {
322       const Tile* tile = solids->get_tile(x, y);
323       Vector pos(x*32, y*32);
324       
325       if(tile->getID() == 112) {
326         add_object(new InvisibleBlock(pos));
327         solids->change(x, y, 0);
328       } else if(tile->getID() == 295) {
329         add_object(new Spike(pos, Spike::NORTH));
330         solids->change(x, y, 0);
331       } else if(tile->getID() == 296) {
332         add_object(new Spike(pos, Spike::EAST));
333         solids->change(x, y, 0);
334       } else if(tile->getID() == 297) {
335         add_object(new Spike(pos, Spike::SOUTH));
336         solids->change(x, y, 0);
337       } else if(tile->getID() == 298) {
338         add_object(new Spike(pos, Spike::WEST));
339         solids->change(x, y, 0);
340       } else if(tile->getAttributes() & Tile::COIN) {
341         add_object(new Coin(pos));
342         solids->change(x, y, 0);
343       } else if(tile->getAttributes() & Tile::FULLBOX) {
344         add_object(new BonusBlock(pos, tile->getData()));
345         solids->change(x, y, 0);
346       } else if(tile->getAttributes() & Tile::BRICK) {
347         add_object(new Brick(pos, tile->getData()));
348         solids->change(x, y, 0);
349       } else if(tile->getAttributes() & Tile::GOAL) {
350         std::string sequence = tile->getData() == 0 ? "endsequence" : "stoptux";
351         add_object(new SequenceTrigger(pos, sequence));
352         solids->change(x, y, 0);
353       }
354     }                                                   
355   }
356 }
357
358 void
359 Sector::write(lisp::Writer& writer)
360 {
361   writer.write_string("name", name);
362   writer.write_float("gravity", gravity);
363   writer.write_string("music", song_title);
364
365   // write spawnpoints
366   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
367       ++i) {
368     SpawnPoint* spawn = *i;
369     writer.start_list("spawn-points");
370     writer.write_string("name", spawn->name);
371     writer.write_float("x", spawn->pos.x);
372     writer.write_float("y", spawn->pos.y);
373     writer.end_list("spawn-points");
374   }
375
376   // write objects
377   for(GameObjects::iterator i = gameobjects.begin();
378       i != gameobjects.end(); ++i) {
379     Serializable* serializable = dynamic_cast<Serializable*> (*i);
380     if(serializable)
381       serializable->write(writer);
382   }
383 }
384
385 void
386 Sector::add_object(GameObject* object)
387 {
388   // make sure the object isn't already in the list
389 #ifdef DEBUG
390   for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end();
391       ++i) {
392     if(*i == object) {
393       assert("object already added to sector" == 0);
394     }
395   }
396   for(GameObjects::iterator i = gameobjects_new.begin();
397       i != gameobjects_new.end(); ++i) {
398     if(*i == object) {
399       assert("object already added to sector" == 0);
400     }
401   }
402 #endif
403
404   gameobjects_new.push_back(object);
405 }
406
407 void
408 Sector::activate(const std::string& spawnpoint)
409 {
410   SpawnPoint* sp = 0;
411   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
412       ++i) {
413     if((*i)->name == spawnpoint) {
414       sp = *i;
415       break;
416     }
417   }                                                                           
418   if(!sp) {
419     std::cerr << "Spawnpoint '" << spawnpoint << "' not found.\n";
420     if(spawnpoint != "main") {
421       activate("main");
422     } else {
423       activate(Vector(0, 0));
424     }
425   } else {
426     activate(sp->pos);
427   }
428
429   // Run init script
430   if(init_script != "") {
431     ScriptInterpreter::add_script_object(this,
432         std::string("Sector(") + name + ") - init", init_script);
433   }
434 }
435
436 void
437 Sector::activate(const Vector& player_pos)
438 {
439   _current = this;
440
441   player->move(player_pos);
442   camera->reset(player->get_pos());
443 }
444
445 Rect
446 Sector::get_active_region()
447 {
448   return Rect(
449     camera->get_translation() - Vector(1600, 1200),
450     camera->get_translation() + Vector(1600, 1200));
451 }
452
453 void
454 Sector::update(float elapsed_time)
455 {
456   player->check_bounds(camera);
457
458 #if 0
459   CollisionGridIterator iter(*grid, get_active_region());
460   while(MovingObject* object = iter.next()) {
461     if(!object->is_valid())
462       continue;
463
464     object->update(elapsed_time);
465   }
466 #else
467   /* update objects */
468   for(GameObjects::iterator i = gameobjects.begin();
469           i != gameobjects.end(); ++i) {
470     GameObject* object = *i;
471     if(!object->is_valid())
472       continue;
473     
474     object->update(elapsed_time);
475   }
476 #endif
477   
478   /* Handle all possible collisions. */
479   collision_handler();                                                                              
480   update_game_objects();
481 }
482
483 void
484 Sector::update_game_objects()
485 {
486   /** cleanup marked objects */
487   for(std::vector<GameObject*>::iterator i = gameobjects.begin();
488       i != gameobjects.end(); /* nothing */) {
489     GameObject* object = *i;
490     
491     if(object->is_valid()) {
492       ++i;
493       continue;
494     }
495     
496     Bullet* bullet = dynamic_cast<Bullet*> (object);
497     if(bullet) {
498       bullets.erase(
499           std::remove(bullets.begin(), bullets.end(), bullet),
500           bullets.end());
501     }
502 #ifdef USE_GRID
503     MovingObject* movingobject = dynamic_cast<MovingObject*> (object);
504     if(movingobject) {
505       grid->remove_object(movingobject);
506     }
507 #endif
508     delete *i;
509     i = gameobjects.erase(i);
510   }
511
512   /* add newly created objects */
513   for(std::vector<GameObject*>::iterator i = gameobjects_new.begin();
514       i != gameobjects_new.end(); ++i)
515   {
516     GameObject* object = *i;
517     
518     Bullet* bullet = dynamic_cast<Bullet*> (object);
519     if(bullet)
520       bullets.push_back(bullet);
521
522 #ifdef USE_GRID
523     MovingObject* movingobject = dynamic_cast<MovingObject*> (object);
524     if(movingobject)
525       grid->add_object(movingobject);
526 #endif
527     
528     TileMap* tilemap = dynamic_cast<TileMap*> (object);
529     if(tilemap && tilemap->is_solid()) {
530       if(solids == 0) {
531         solids = tilemap;
532       } else {
533         std::cerr << "Another solid tilemaps added. Ignoring.";
534       }
535     }
536
537     Camera* camera = dynamic_cast<Camera*> (object);
538     if(camera) {
539       if(this->camera != 0) {
540         std::cerr << "Warning: Multiple cameras added. Ignoring.";
541         continue;
542       }
543       this->camera = camera;
544     }
545
546     gameobjects.push_back(object);
547   }
548   gameobjects_new.clear();
549 }
550
551 void
552 Sector::draw(DrawingContext& context)
553 {
554   context.push_transform();
555   context.set_translation(camera->get_translation());
556
557 #if 0
558   CollisionGridIterator iter(*grid, get_active_region());
559   while(MovingObject* object = iter.next()) {
560     if(!object->is_valid())
561       continue;
562
563     object->draw(context);
564   }
565 #else
566   for(GameObjects::iterator i = gameobjects.begin();
567       i != gameobjects.end(); ++i) {
568     GameObject* object = *i; 
569     if(!object->is_valid())
570       continue;
571     
572     object->draw(context);
573   }
574 #endif
575
576   context.pop_transform();
577 }
578
579 static const float DELTA = .001;
580
581 void
582 Sector::collision_tilemap(MovingObject* object, int depth)
583 {
584   if(depth >= 4) {
585 #ifdef DEBUG
586     std::cout << "Max collision depth reached.\n";
587 #endif
588     object->movement = Vector(0, 0);
589     return;
590   }
591
592   // calculate rectangle where the object will move
593   float x1, x2;
594   if(object->get_movement().x >= 0) {
595     x1 = object->get_pos().x;
596     x2 = object->get_bbox().p2.x + object->get_movement().x;
597   } else {
598     x1 = object->get_pos().x + object->get_movement().x;
599     x2 = object->get_bbox().p2.x;
600   }
601   float y1, y2;
602   if(object->get_movement().y >= 0) {
603     y1 = object->get_pos().y;
604     y2 = object->get_bbox().p2.y + object->get_movement().y;
605   } else {
606     y1 = object->get_pos().y + object->get_movement().y;
607     y2 = object->get_bbox().p2.y;
608   }
609
610   // test with all tiles in this rectangle
611   int starttilex = int(x1-1) / 32;
612   int starttiley = int(y1-1) / 32;
613   int max_x = int(x2+1);
614   int max_y = int(y2+1);
615
616   CollisionHit temphit, hit;
617   Rect dest = object->get_bbox();
618   dest.move(object->movement);
619   hit.time = -1; // represents an invalid value
620   for(int x = starttilex; x*32 < max_x; ++x) {
621     for(int y = starttiley; y*32 < max_y; ++y) {
622       const Tile* tile = solids->get_tile(x, y);
623       if(!tile)
624         continue;
625       // skip non-solid tiles
626       if(!(tile->getAttributes() & Tile::SOLID))
627         continue;
628       // only handle unisolid when the player is falling down and when he was
629       // above the tile before
630       if(tile->getAttributes() & Tile::UNISOLID) {
631         if(object->movement.y < 0 || object->get_bbox().p2.y > y*32)
632           continue;
633       }
634
635       if(tile->getAttributes() & Tile::SLOPE) { // slope tile
636         AATriangle triangle;
637         Vector p1(x*32, y*32);
638         Vector p2((x+1)*32, (y+1)*32);
639         triangle = AATriangle(p1, p2, tile->getData());
640
641         if(Collision::rectangle_aatriangle(temphit, dest, object->movement,
642               triangle)) {
643           if(temphit.time > hit.time)
644             hit = temphit;
645         }
646       } else { // normal rectangular tile
647         Rect rect(x*32, y*32, (x+1)*32, (y+1)*32);
648         if(Collision::rectangle_rectangle(temphit, dest,
649               object->movement, rect)) {
650           if(temphit.time > hit.time)
651             hit = temphit;
652         }
653       }
654     }
655   }
656
657   // did we collide at all?
658   if(hit.time < 0)
659     return;
660  
661   // call collision function
662   HitResponse response = object->collision(*solids, hit);
663   if(response == ABORT_MOVE) {
664     object->movement = Vector(0, 0);
665     return;
666   }
667   if(response == FORCE_MOVE) {
668       return;
669   }
670   // move out of collision and try again
671   object->movement += hit.normal * (hit.depth + DELTA);
672   collision_tilemap(object, depth+1);
673 }
674
675 void
676 Sector::collision_object(MovingObject* object1, MovingObject* object2)
677 {
678   CollisionHit hit;
679   Rect dest1 = object1->get_bbox();
680   dest1.move(object1->get_movement());
681   Rect dest2 = object2->get_bbox();
682   dest2.move(object2->get_movement());
683
684   Vector movement = object1->get_movement() - object2->get_movement();
685   if(Collision::rectangle_rectangle(hit, dest1, movement, dest2)) {
686     HitResponse response1 = object1->collision(*object2, hit);
687     hit.normal *= -1;
688     HitResponse response2 = object2->collision(*object1, hit);
689
690     if(response1 != CONTINUE) {
691       if(response1 == ABORT_MOVE)
692         object1->movement = Vector(0, 0);
693       if(response2 == CONTINUE)
694         object2->movement += hit.normal * (hit.depth + DELTA);
695     } else if(response2 != CONTINUE) {
696       if(response2 == ABORT_MOVE)
697         object2->movement = Vector(0, 0);
698       if(response1 == CONTINUE)
699         object1->movement += -hit.normal * (hit.depth + DELTA);
700     } else {
701       object1->movement += -hit.normal * (hit.depth/2 + DELTA);
702       object2->movement += hit.normal * (hit.depth/2 + DELTA);
703     }
704   }
705 }
706
707 void
708 Sector::collision_handler()
709 {
710 #ifdef USE_GRID
711   grid->check_collisions();
712 #else
713   for(std::vector<GameObject*>::iterator i = gameobjects.begin();
714       i != gameobjects.end(); ++i) {
715     GameObject* gameobject = *i;
716     if(!gameobject->is_valid())
717       continue;
718     MovingObject* movingobject = dynamic_cast<MovingObject*> (gameobject);
719     if(!movingobject)
720       continue;
721     if(movingobject->get_flags() & GameObject::FLAG_NO_COLLDET) {
722       movingobject->bbox.move(movingobject->movement);
723       movingobject->movement = Vector(0, 0);
724       continue;
725     }
726
727     // collision with tilemap
728     if(! (movingobject->movement == Vector(0, 0)))
729       collision_tilemap(movingobject, 0);
730
731     // collision with other objects
732     for(std::vector<GameObject*>::iterator i2 = i+1;
733         i2 != gameobjects.end(); ++i2) {
734       GameObject* other_object = *i2;
735       if(!other_object->is_valid() 
736           || other_object->get_flags() & GameObject::FLAG_NO_COLLDET)
737         continue;
738       MovingObject* movingobject2 = dynamic_cast<MovingObject*> (other_object);
739       if(!movingobject2)
740         continue;
741
742       collision_object(movingobject, movingobject2);
743     }
744
745     movingobject->bbox.move(movingobject->get_movement());
746     movingobject->movement = Vector(0, 0);
747   }
748 #endif
749 }
750
751 bool
752 Sector::add_bullet(const Vector& pos, float xm, Direction dir)
753 {
754   // TODO remove this function and move these checks elsewhere...
755   static const size_t MAX_FIRE_BULLETS = 2;
756   static const size_t MAX_ICE_BULLETS = 1;
757
758   Bullet* new_bullet = 0;
759   if(player_status.bonus == FIRE_BONUS) {
760     if(bullets.size() > MAX_FIRE_BULLETS-1)
761       return false;
762     new_bullet = new Bullet(pos, xm, dir, FIRE_BULLET);
763   } else if(player_status.bonus == ICE_BONUS) {
764     if(bullets.size() > MAX_ICE_BULLETS-1)
765       return false;
766     new_bullet = new Bullet(pos, xm, dir, ICE_BULLET);
767   } else {
768     return false;
769   }
770   add_object(new_bullet);
771
772   sound_manager->play("sounds/shoot.ogg");
773
774   return true;
775 }
776
777 bool
778 Sector::add_smoke_cloud(const Vector& pos)
779 {
780   add_object(new SmokeCloud(pos));
781   return true;
782 }
783
784 void
785 Sector::add_floating_text(const Vector& pos, const std::string& text)
786 {
787   add_object(new FloatingText(pos, text));
788 }
789
790 void
791 Sector::play_music(MusicType type)
792 {
793   currentmusic = type;
794   switch(currentmusic) {
795     case LEVEL_MUSIC:
796       sound_manager->play_music(std::string("music/") + song_title);
797       break;
798     case HERRING_MUSIC:
799       sound_manager->play_music("music/salcon.ogg");
800       break;
801     default:
802       sound_manager->play_music("");
803       break;
804   }
805 }
806
807 MusicType
808 Sector::get_music_type()
809 {
810   return currentmusic;
811 }
812
813 int
814 Sector::get_total_badguys()
815 {
816   int total_badguys = 0;
817   for(GameObjects::iterator i = gameobjects.begin();
818       i != gameobjects.end(); ++i) {
819     BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
820     if (badguy && badguy->countMe)
821       total_badguys++;
822   }
823
824   return total_badguys;
825 }
826
827 bool
828 Sector::inside(const Rect& rect) const
829 {
830   if(rect.p1.x > solids->get_width() * 32 
831       || rect.p1.y > solids->get_height() * 32
832       || rect.p2.x < 0 || rect.p2.y < 0)
833     return false;
834
835   return true;
836 }