use spike flag again and don't replace spikes with objects
[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->getAttributes() & Tile::COIN) {
329         add_object(new Coin(pos));
330         solids->change(x, y, 0);
331       } else if(tile->getAttributes() & Tile::FULLBOX) {
332         add_object(new BonusBlock(pos, tile->getData()));
333         solids->change(x, y, 0);
334       } else if(tile->getAttributes() & Tile::BRICK) {
335         add_object(new Brick(pos, tile->getData()));
336         solids->change(x, y, 0);
337       } else if(tile->getAttributes() & Tile::GOAL) {
338         std::string sequence = tile->getData() == 0 ? "endsequence" : "stoptux";
339         add_object(new SequenceTrigger(pos, sequence));
340         solids->change(x, y, 0);
341       }
342     }                                                   
343   }
344 }
345
346 void
347 Sector::write(lisp::Writer& writer)
348 {
349   writer.write_string("name", name);
350   writer.write_float("gravity", gravity);
351   writer.write_string("music", song_title);
352
353   // write spawnpoints
354   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
355       ++i) {
356     SpawnPoint* spawn = *i;
357     writer.start_list("spawn-points");
358     writer.write_string("name", spawn->name);
359     writer.write_float("x", spawn->pos.x);
360     writer.write_float("y", spawn->pos.y);
361     writer.end_list("spawn-points");
362   }
363
364   // write objects
365   for(GameObjects::iterator i = gameobjects.begin();
366       i != gameobjects.end(); ++i) {
367     Serializable* serializable = dynamic_cast<Serializable*> (*i);
368     if(serializable)
369       serializable->write(writer);
370   }
371 }
372
373 void
374 Sector::add_object(GameObject* object)
375 {
376   // make sure the object isn't already in the list
377 #ifdef DEBUG
378   for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end();
379       ++i) {
380     if(*i == object) {
381       assert("object already added to sector" == 0);
382     }
383   }
384   for(GameObjects::iterator i = gameobjects_new.begin();
385       i != gameobjects_new.end(); ++i) {
386     if(*i == object) {
387       assert("object already added to sector" == 0);
388     }
389   }
390 #endif
391
392   gameobjects_new.push_back(object);
393 }
394
395 void
396 Sector::activate(const std::string& spawnpoint)
397 {
398   SpawnPoint* sp = 0;
399   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
400       ++i) {
401     if((*i)->name == spawnpoint) {
402       sp = *i;
403       break;
404     }
405   }                                                                           
406   if(!sp) {
407     std::cerr << "Spawnpoint '" << spawnpoint << "' not found.\n";
408     if(spawnpoint != "main") {
409       activate("main");
410     } else {
411       activate(Vector(0, 0));
412     }
413   } else {
414     activate(sp->pos);
415   }
416
417   // Run init script
418   if(init_script != "") {
419     ScriptInterpreter::add_script_object(this,
420         std::string("Sector(") + name + ") - init", init_script);
421   }
422 }
423
424 void
425 Sector::activate(const Vector& player_pos)
426 {
427   _current = this;
428
429   player->move(player_pos);
430   camera->reset(player->get_pos());
431 }
432
433 Rect
434 Sector::get_active_region()
435 {
436   return Rect(
437     camera->get_translation() - Vector(1600, 1200),
438     camera->get_translation() + Vector(1600, 1200));
439 }
440
441 void
442 Sector::update(float elapsed_time)
443 {
444   player->check_bounds(camera);
445
446 #if 0
447   CollisionGridIterator iter(*grid, get_active_region());
448   while(MovingObject* object = iter.next()) {
449     if(!object->is_valid())
450       continue;
451
452     object->update(elapsed_time);
453   }
454 #else
455   /* update objects */
456   for(GameObjects::iterator i = gameobjects.begin();
457           i != gameobjects.end(); ++i) {
458     GameObject* object = *i;
459     if(!object->is_valid())
460       continue;
461     
462     object->update(elapsed_time);
463   }
464 #endif
465   
466   /* Handle all possible collisions. */
467   collision_handler();                                                                              
468   update_game_objects();
469 }
470
471 void
472 Sector::update_game_objects()
473 {
474   /** cleanup marked objects */
475   for(std::vector<GameObject*>::iterator i = gameobjects.begin();
476       i != gameobjects.end(); /* nothing */) {
477     GameObject* object = *i;
478     
479     if(object->is_valid()) {
480       ++i;
481       continue;
482     }
483     
484     Bullet* bullet = dynamic_cast<Bullet*> (object);
485     if(bullet) {
486       bullets.erase(
487           std::remove(bullets.begin(), bullets.end(), bullet),
488           bullets.end());
489     }
490 #ifdef USE_GRID
491     MovingObject* movingobject = dynamic_cast<MovingObject*> (object);
492     if(movingobject) {
493       grid->remove_object(movingobject);
494     }
495 #endif
496     delete *i;
497     i = gameobjects.erase(i);
498   }
499
500   /* add newly created objects */
501   for(std::vector<GameObject*>::iterator i = gameobjects_new.begin();
502       i != gameobjects_new.end(); ++i)
503   {
504     GameObject* object = *i;
505     
506     Bullet* bullet = dynamic_cast<Bullet*> (object);
507     if(bullet)
508       bullets.push_back(bullet);
509
510 #ifdef USE_GRID
511     MovingObject* movingobject = dynamic_cast<MovingObject*> (object);
512     if(movingobject)
513       grid->add_object(movingobject);
514 #endif
515     
516     TileMap* tilemap = dynamic_cast<TileMap*> (object);
517     if(tilemap && tilemap->is_solid()) {
518       if(solids == 0) {
519         solids = tilemap;
520       } else {
521         std::cerr << "Another solid tilemaps added. Ignoring.";
522       }
523     }
524
525     Camera* camera = dynamic_cast<Camera*> (object);
526     if(camera) {
527       if(this->camera != 0) {
528         std::cerr << "Warning: Multiple cameras added. Ignoring.";
529         continue;
530       }
531       this->camera = camera;
532     }
533
534     gameobjects.push_back(object);
535   }
536   gameobjects_new.clear();
537 }
538
539 void
540 Sector::draw(DrawingContext& context)
541 {
542   context.push_transform();
543   context.set_translation(camera->get_translation());
544
545 #if 0
546   CollisionGridIterator iter(*grid, get_active_region());
547   while(MovingObject* object = iter.next()) {
548     if(!object->is_valid())
549       continue;
550
551     object->draw(context);
552   }
553 #else
554   for(GameObjects::iterator i = gameobjects.begin();
555       i != gameobjects.end(); ++i) {
556     GameObject* object = *i; 
557     if(!object->is_valid())
558       continue;
559     
560     object->draw(context);
561   }
562 #endif
563
564   context.pop_transform();
565 }
566
567 static const float DELTA = .001;
568
569 void
570 Sector::collision_tilemap(MovingObject* object, int depth)
571 {
572   if(depth >= 4) {
573 #ifdef DEBUG
574     std::cout << "Max collision depth reached.\n";
575 #endif
576     object->movement = Vector(0, 0);
577     return;
578   }
579
580   // calculate rectangle where the object will move
581   float x1, x2;
582   if(object->get_movement().x >= 0) {
583     x1 = object->get_pos().x;
584     x2 = object->get_bbox().p2.x + object->get_movement().x;
585   } else {
586     x1 = object->get_pos().x + object->get_movement().x;
587     x2 = object->get_bbox().p2.x;
588   }
589   float y1, y2;
590   if(object->get_movement().y >= 0) {
591     y1 = object->get_pos().y;
592     y2 = object->get_bbox().p2.y + object->get_movement().y;
593   } else {
594     y1 = object->get_pos().y + object->get_movement().y;
595     y2 = object->get_bbox().p2.y;
596   }
597
598   // test with all tiles in this rectangle
599   int starttilex = int(x1-1) / 32;
600   int starttiley = int(y1-1) / 32;
601   int max_x = int(x2+1);
602   int max_y = int(y2+1);
603
604   TilemapCollisionHit temphit, hit;
605   Rect dest = object->get_bbox();
606   dest.move(object->movement);
607   hit.tileflags = 0;
608   hit.time = -1; // represents an invalid value
609   for(int x = starttilex; x*32 < max_x; ++x) {
610     for(int y = starttiley; y*32 < max_y; ++y) {
611       const Tile* tile = solids->get_tile(x, y);
612       if(!tile)
613         continue;
614       // skip non-solid tiles
615       if(tile->getAttributes() == 0)
616         continue;
617       // only handle unisolid when the player is falling down and when he was
618       // above the tile before
619       if(tile->getAttributes() & Tile::UNISOLID) {
620         if(object->movement.y < 0 || object->get_bbox().p2.y > y*32)
621           continue;
622       }
623
624       if(tile->getAttributes() & Tile::SLOPE) { // slope tile
625         AATriangle triangle;
626         Vector p1(x*32, y*32);
627         Vector p2((x+1)*32, (y+1)*32);
628         triangle = AATriangle(p1, p2, tile->getData());
629
630         if(Collision::rectangle_aatriangle(temphit, dest, object->movement,
631               triangle)) {
632           hit.tileflags |= tile->getAttributes();
633           if(temphit.time > hit.time) {
634             temphit.tileflags = hit.tileflags;
635             hit = temphit;
636           }
637         }
638       } else { // normal rectangular tile
639         Rect rect(x*32, y*32, (x+1)*32, (y+1)*32);
640         if(Collision::rectangle_rectangle(temphit, dest,
641               object->movement, rect)) {
642           hit.tileflags |= tile->getAttributes();
643           if(temphit.time > hit.time) {
644             temphit.tileflags = hit.tileflags;
645             hit = temphit;
646           }
647         }
648       }
649     }
650   }
651
652   // did we collide at all?
653   if(hit.time < 0)
654     return;
655  
656   // call collision function
657   HitResponse response = object->collision(*solids, hit);
658   if(response == ABORT_MOVE) {
659     object->movement = Vector(0, 0);
660     return;
661   }
662   if(response == FORCE_MOVE) {
663       return;
664   }
665   // move out of collision and try again
666   object->movement += hit.normal * (hit.depth + DELTA);
667   collision_tilemap(object, depth+1);
668 }
669
670 void
671 Sector::collision_object(MovingObject* object1, MovingObject* object2)
672 {
673   CollisionHit hit;
674   Rect dest1 = object1->get_bbox();
675   dest1.move(object1->get_movement());
676   Rect dest2 = object2->get_bbox();
677   dest2.move(object2->get_movement());
678
679   Vector movement = object1->get_movement() - object2->get_movement();
680   if(Collision::rectangle_rectangle(hit, dest1, movement, dest2)) {
681     HitResponse response1 = object1->collision(*object2, hit);
682     hit.normal *= -1;
683     HitResponse response2 = object2->collision(*object1, hit);
684
685     if(response1 != CONTINUE) {
686       if(response1 == ABORT_MOVE)
687         object1->movement = Vector(0, 0);
688       if(response2 == CONTINUE)
689         object2->movement += hit.normal * (hit.depth + DELTA);
690     } else if(response2 != CONTINUE) {
691       if(response2 == ABORT_MOVE)
692         object2->movement = Vector(0, 0);
693       if(response1 == CONTINUE)
694         object1->movement += -hit.normal * (hit.depth + DELTA);
695     } else {
696       object1->movement += -hit.normal * (hit.depth/2 + DELTA);
697       object2->movement += hit.normal * (hit.depth/2 + DELTA);
698     }
699   }
700 }
701
702 void
703 Sector::collision_handler()
704 {
705 #ifdef USE_GRID
706   grid->check_collisions();
707 #else
708   for(std::vector<GameObject*>::iterator i = gameobjects.begin();
709       i != gameobjects.end(); ++i) {
710     GameObject* gameobject = *i;
711     if(!gameobject->is_valid())
712       continue;
713     MovingObject* movingobject = dynamic_cast<MovingObject*> (gameobject);
714     if(!movingobject)
715       continue;
716     if(movingobject->get_flags() & GameObject::FLAG_NO_COLLDET) {
717       movingobject->bbox.move(movingobject->movement);
718       movingobject->movement = Vector(0, 0);
719       continue;
720     }
721
722     // collision with tilemap
723     if(! (movingobject->movement == Vector(0, 0)))
724       collision_tilemap(movingobject, 0);
725
726     // collision with other objects
727     for(std::vector<GameObject*>::iterator i2 = i+1;
728         i2 != gameobjects.end(); ++i2) {
729       GameObject* other_object = *i2;
730       if(!other_object->is_valid() 
731           || other_object->get_flags() & GameObject::FLAG_NO_COLLDET)
732         continue;
733       MovingObject* movingobject2 = dynamic_cast<MovingObject*> (other_object);
734       if(!movingobject2)
735         continue;
736
737       collision_object(movingobject, movingobject2);
738     }
739
740     movingobject->bbox.move(movingobject->get_movement());
741     movingobject->movement = Vector(0, 0);
742   }
743 #endif
744 }
745
746 bool
747 Sector::add_bullet(const Vector& pos, float xm, Direction dir)
748 {
749   // TODO remove this function and move these checks elsewhere...
750   static const size_t MAX_FIRE_BULLETS = 2;
751   static const size_t MAX_ICE_BULLETS = 1;
752
753   Bullet* new_bullet = 0;
754   if(player_status.bonus == FIRE_BONUS) {
755     if(bullets.size() > MAX_FIRE_BULLETS-1)
756       return false;
757     new_bullet = new Bullet(pos, xm, dir, FIRE_BULLET);
758   } else if(player_status.bonus == ICE_BONUS) {
759     if(bullets.size() > MAX_ICE_BULLETS-1)
760       return false;
761     new_bullet = new Bullet(pos, xm, dir, ICE_BULLET);
762   } else {
763     return false;
764   }
765   add_object(new_bullet);
766
767   sound_manager->play("sounds/shoot.wav");
768
769   return true;
770 }
771
772 bool
773 Sector::add_smoke_cloud(const Vector& pos)
774 {
775   add_object(new SmokeCloud(pos));
776   return true;
777 }
778
779 void
780 Sector::add_floating_text(const Vector& pos, const std::string& text)
781 {
782   add_object(new FloatingText(pos, text));
783 }
784
785 void
786 Sector::play_music(MusicType type)
787 {
788   currentmusic = type;
789   switch(currentmusic) {
790     case LEVEL_MUSIC:
791       sound_manager->play_music(std::string("music/") + song_title);
792       break;
793     case HERRING_MUSIC:
794       sound_manager->play_music("music/salcon.ogg");
795       break;
796     default:
797       sound_manager->play_music("");
798       break;
799   }
800 }
801
802 MusicType
803 Sector::get_music_type()
804 {
805   return currentmusic;
806 }
807
808 int
809 Sector::get_total_badguys()
810 {
811   int total_badguys = 0;
812   for(GameObjects::iterator i = gameobjects.begin();
813       i != gameobjects.end(); ++i) {
814     BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
815     if (badguy && badguy->countMe)
816       total_badguys++;
817   }
818
819   return total_badguys;
820 }
821
822 bool
823 Sector::inside(const Rect& rect) const
824 {
825   if(rect.p1.x > solids->get_width() * 32 
826       || rect.p1.y > solids->get_height() * 32
827       || rect.p2.x < 0 || rect.p2.y < 0)
828     return false;
829
830   return true;
831 }