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