Split particlesystem_interactive.?pp into separate files
[supertux.git] / src / supertux / sector.cpp
1 //  SuperTux -  A Jump'n Run
2 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3 //
4 //  This program is free software: you can redistribute it and/or modify
5 //  it under the terms of the GNU General Public License as published by
6 //  the Free Software Foundation, either version 3 of the License, or
7 //  (at your option) any later version.
8 //
9 //  This program is distributed in the hope that it will be useful,
10 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
11 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 //  GNU General Public License for more details.
13 //
14 //  You should have received a copy of the GNU General Public License
15 //  along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
17 #include "supertux/sector.hpp"
18
19 #include <algorithm>
20 #include <math.h>
21
22 #include "audio/sound_manager.hpp"
23 #include "badguy/jumpy.hpp"
24 #include "lisp/list_iterator.hpp"
25 #include "math/aatriangle.hpp"
26 #include "object/background.hpp"
27 #include "object/bonus_block.hpp"
28 #include "object/brick.hpp"
29 #include "object/bullet.hpp"
30 #include "object/camera.hpp"
31 #include "object/cloud_particle_system.hpp"
32 #include "object/coin.hpp"
33 #include "object/comet_particle_system.hpp"
34 #include "object/display_effect.hpp"
35 #include "object/ghost_particle_system.hpp"
36 #include "object/gradient.hpp"
37 #include "object/invisible_block.hpp"
38 #include "object/particlesystem.hpp"
39 #include "object/particlesystem_interactive.hpp"
40 #include "object/player.hpp"
41 #include "object/portable.hpp"
42 #include "object/pulsing_light.hpp"
43 #include "object/rain_particle_system.hpp"
44 #include "object/smoke_cloud.hpp"
45 #include "object/snow_particle_system.hpp"
46 #include "object/text_object.hpp"
47 #include "object/tilemap.hpp"
48 #include "physfs/ifile_stream.hpp"
49 #include "scripting/squirrel_util.hpp"
50 #include "supertux/collision.hpp"
51 #include "supertux/constants.hpp"
52 #include "supertux/globals.hpp"
53 #include "supertux/level.hpp"
54 #include "supertux/object_factory.hpp"
55 #include "supertux/spawn_point.hpp"
56 #include "supertux/tile.hpp"
57 #include "trigger/sequence_trigger.hpp"
58 #include "util/file_system.hpp"
59
60 Sector* Sector::_current = 0;
61
62 bool Sector::show_collrects = false;
63 bool Sector::draw_solids_only = false;
64
65 Sector::Sector(Level* parent) :
66   level(parent), 
67   name(),
68   bullets(),
69   init_script(),
70   gameobjects_new(),
71   currentmusic(LEVEL_MUSIC),
72   sector_table(),
73   scripts(),
74   ambient_light( 1.0f, 1.0f, 1.0f, 1.0f ), 
75   gameobjects(),
76   moving_objects(),
77   spawnpoints(),
78   portables(),
79   music(),
80   gravity(10.0), 
81   player(0), 
82   solid_tilemaps(),
83   camera(0), 
84   effect(0)
85 {
86   add_object(new Player(player_status, "Tux"));
87   add_object(new DisplayEffect("Effect"));
88   add_object(new TextObject("Text"));
89
90   sound_manager->preload("sounds/shoot.wav");
91
92   // create a new squirrel table for the sector
93   using namespace Scripting;
94
95   sq_collectgarbage(global_vm);
96
97   sq_newtable(global_vm);
98   sq_pushroottable(global_vm);
99   if(SQ_FAILED(sq_setdelegate(global_vm, -2)))
100     throw Scripting::SquirrelError(global_vm, "Couldn't set sector_table delegate");
101
102   sq_resetobject(&sector_table);
103   if(SQ_FAILED(sq_getstackobj(global_vm, -1, &sector_table)))
104     throw Scripting::SquirrelError(global_vm, "Couldn't get sector table");
105   sq_addref(global_vm, &sector_table);
106   sq_pop(global_vm, 1);
107 }
108
109 Sector::~Sector()
110 {
111   using namespace Scripting;
112
113   deactivate();
114
115   for(ScriptList::iterator i = scripts.begin();
116       i != scripts.end(); ++i) {
117     HSQOBJECT& object = *i;
118     sq_release(global_vm, &object);
119   }
120   sq_release(global_vm, &sector_table);
121   sq_collectgarbage(global_vm);
122
123   update_game_objects();
124   assert(gameobjects_new.size() == 0);
125
126   for(GameObjects::iterator i = gameobjects.begin();
127       i != gameobjects.end(); ++i) {
128     GameObject* object = *i;
129     before_object_remove(object);
130     object->unref();
131   }
132
133   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
134       ++i)
135     delete *i;
136 }
137
138 Level*
139 Sector::get_level()
140 {
141   return level;
142 }
143
144 GameObject*
145 Sector::parse_object(const std::string& name, const Reader& reader)
146 {
147   if(name == "camera") {
148     Camera* camera = new Camera(this, "Camera");
149     camera->parse(reader);
150     return camera;
151   } else if(name == "particles-snow") {
152     SnowParticleSystem* partsys = new SnowParticleSystem();
153     partsys->parse(reader);
154     return partsys;
155   } else if(name == "particles-rain") {
156     RainParticleSystem* partsys = new RainParticleSystem();
157     partsys->parse(reader);
158     return partsys;
159   } else if(name == "particles-comets") {
160     CometParticleSystem* partsys = new CometParticleSystem();
161     partsys->parse(reader);
162     return partsys;
163   } else if(name == "particles-ghosts") {
164     GhostParticleSystem* partsys = new GhostParticleSystem();
165     partsys->parse(reader);
166     return partsys;
167   } else if(name == "particles-clouds") {
168     CloudParticleSystem* partsys = new CloudParticleSystem();
169     partsys->parse(reader);
170     return partsys;
171   } else if(name == "money") { // for compatibility with old maps
172     return new Jumpy(reader);
173   } else {
174     try {
175       return create_object(name, reader);
176     } catch(std::exception& e) {
177       log_warning << e.what() << "" << std::endl;
178       return 0;
179     }
180   }
181 }
182
183 void
184 Sector::parse(const Reader& sector)
185 {
186   bool has_background = false;
187   lisp::ListIterator iter(&sector);
188   while(iter.next()) {
189     const std::string& token = iter.item();
190     if(token == "name") {
191       iter.value()->get(name);
192     } else if(token == "gravity") {
193       iter.value()->get(gravity);
194     } else if(token == "music") {
195       iter.value()->get(music);
196     } else if(token == "spawnpoint") {
197       SpawnPoint* sp = new SpawnPoint(*iter.lisp());
198       spawnpoints.push_back(sp);
199     } else if(token == "init-script") {
200       iter.value()->get(init_script);
201     } else if(token == "ambient-light") {
202       std::vector<float> vColor;
203       sector.get( "ambient-light", vColor );
204       if(vColor.size() < 3) {
205         log_warning << "(ambient-light) requires a color as argument" << std::endl;
206       } else {
207         ambient_light = Color( vColor );
208       }
209     } else {
210       GameObject* object = parse_object(token, *(iter.lisp()));
211       if(object) {
212         if(dynamic_cast<Background *>(object)) {
213           has_background = true;
214         } else if(dynamic_cast<Gradient *>(object)) {
215           has_background = true;
216         }
217         add_object(object);
218       }
219     }
220   }
221
222   if(!has_background) {
223     Gradient* gradient = new Gradient();
224     gradient->set_gradient(Color(0.3, 0.4, 0.75), Color(1, 1, 1));
225     add_object(gradient);
226   }
227
228   update_game_objects();
229
230   if(solid_tilemaps.size() < 1) log_warning << "sector '" << name << "' does not contain a solid tile layer." << std::endl;
231
232   fix_old_tiles();
233   if(!camera) {
234     log_warning << "sector '" << name << "' does not contain a camera." << std::endl;
235     update_game_objects();
236     add_object(new Camera(this, "Camera"));
237   }
238
239   update_game_objects();
240 }
241
242 void
243 Sector::parse_old_format(const Reader& reader)
244 {
245   name = "main";
246   reader.get("gravity", gravity);
247
248   std::string backgroundimage;
249   if (reader.get("background", backgroundimage) && (backgroundimage != "")) {
250     if (backgroundimage == "arctis.png") backgroundimage = "arctis.jpg";
251     if (backgroundimage == "arctis2.jpg") backgroundimage = "arctis.jpg";
252     if (backgroundimage == "ocean.png") backgroundimage = "ocean.jpg";
253     backgroundimage = "images/background/" + backgroundimage;
254     if (!PHYSFS_exists(backgroundimage.c_str())) {
255       log_warning << "Background image \"" << backgroundimage << "\" not found. Ignoring." << std::endl;
256       backgroundimage = "";
257     }
258   }
259
260   float bgspeed = .5;
261   reader.get("bkgd_speed", bgspeed);
262   bgspeed /= 100;
263
264   Color bkgd_top, bkgd_bottom;
265   int r = 0, g = 0, b = 128;
266   reader.get("bkgd_red_top", r);
267   reader.get("bkgd_green_top",  g);
268   reader.get("bkgd_blue_top",  b);
269   bkgd_top.red = static_cast<float> (r) / 255.0f;
270   bkgd_top.green = static_cast<float> (g) / 255.0f;
271   bkgd_top.blue = static_cast<float> (b) / 255.0f;
272
273   reader.get("bkgd_red_bottom",  r);
274   reader.get("bkgd_green_bottom", g);
275   reader.get("bkgd_blue_bottom", b);
276   bkgd_bottom.red = static_cast<float> (r) / 255.0f;
277   bkgd_bottom.green = static_cast<float> (g) / 255.0f;
278   bkgd_bottom.blue = static_cast<float> (b) / 255.0f;
279
280   if(backgroundimage != "") {
281     Background* background = new Background();
282     background->set_image(backgroundimage, bgspeed);
283     add_object(background);
284   } else {
285     Gradient* gradient = new Gradient();
286     gradient->set_gradient(bkgd_top, bkgd_bottom);
287     add_object(gradient);
288   }
289
290   std::string particlesystem;
291   reader.get("particle_system", particlesystem);
292   if(particlesystem == "clouds")
293     add_object(new CloudParticleSystem());
294   else if(particlesystem == "snow")
295     add_object(new SnowParticleSystem());
296   else if(particlesystem == "rain")
297     add_object(new RainParticleSystem());
298
299   Vector startpos(100, 170);
300   reader.get("start_pos_x", startpos.x);
301   reader.get("start_pos_y", startpos.y);
302
303   SpawnPoint* spawn = new SpawnPoint;
304   spawn->pos = startpos;
305   spawn->name = "main";
306   spawnpoints.push_back(spawn);
307
308   music = "chipdisko.ogg";
309   // skip reading music filename. It's all .ogg now, anyway
310   /*
311     reader.get("music", music);
312   */
313   music = "music/" + music;
314
315   int width = 30, height = 15;
316   reader.get("width", width);
317   reader.get("height", height);
318
319   std::vector<unsigned int> tiles;
320   if(reader.get("interactive-tm", tiles)
321      || reader.get("tilemap", tiles)) {
322     TileMap* tilemap = new TileMap(level->get_tileset());
323     tilemap->set(width, height, tiles, LAYER_TILES, true);
324
325     // replace tile id 112 (old invisible tile) with 1311 (new invisible tile)
326     for(size_t x=0; x < tilemap->get_width(); ++x) {
327       for(size_t y=0; y < tilemap->get_height(); ++y) {
328         uint32_t id = tilemap->get_tile_id(x, y);
329         if(id == 112)
330           tilemap->change(x, y, 1311);
331       }
332     }
333
334     if (height < 19) tilemap->resize(width, 19);
335     add_object(tilemap);
336   }
337
338   if(reader.get("background-tm", tiles)) {
339     TileMap* tilemap = new TileMap(level->get_tileset());
340     tilemap->set(width, height, tiles, LAYER_BACKGROUNDTILES, false);
341     if (height < 19) tilemap->resize(width, 19);
342     add_object(tilemap);
343   }
344
345   if(reader.get("foreground-tm", tiles)) {
346     TileMap* tilemap = new TileMap(level->get_tileset());
347     tilemap->set(width, height, tiles, LAYER_FOREGROUNDTILES, false);
348
349     // fill additional space in foreground with tiles of ID 2035 (lightmap/black)
350     if (height < 19) tilemap->resize(width, 19, 2035);
351
352     add_object(tilemap);
353   }
354
355   // read reset-points (now spawn-points)
356   const lisp::Lisp* resetpoints = reader.get_lisp("reset-points");
357   if(resetpoints) {
358     lisp::ListIterator iter(resetpoints);
359     while(iter.next()) {
360       if(iter.item() == "point") {
361         Vector sp_pos;
362         if(reader.get("x", sp_pos.x) && reader.get("y", sp_pos.y))
363         {
364           SpawnPoint* sp = new SpawnPoint;
365           sp->name = "main";
366           sp->pos = sp_pos;
367           spawnpoints.push_back(sp);
368         }
369       } else {
370         log_warning << "Unknown token '" << iter.item() << "' in reset-points." << std::endl;
371       }
372     }
373   }
374
375   // read objects
376   const lisp::Lisp* objects = reader.get_lisp("objects");
377   if(objects) {
378     lisp::ListIterator iter(objects);
379     while(iter.next()) {
380       GameObject* object = parse_object(iter.item(), *(iter.lisp()));
381       if(object) {
382         add_object(object);
383       } else {
384         log_warning << "Unknown object '" << iter.item() << "' in level." << std::endl;
385       }
386     }
387   }
388
389   // add a camera
390   Camera* camera = new Camera(this, "Camera");
391   add_object(camera);
392
393   update_game_objects();
394
395   if(solid_tilemaps.size() < 1) log_warning << "sector '" << name << "' does not contain a solid tile layer." << std::endl;
396
397   fix_old_tiles();
398   update_game_objects();
399 }
400
401 void
402 Sector::fix_old_tiles()
403 {
404   for(std::list<TileMap*>::iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
405     TileMap* solids = *i;
406     for(size_t x=0; x < solids->get_width(); ++x) {
407       for(size_t y=0; y < solids->get_height(); ++y) {
408         uint32_t    id   = solids->get_tile_id(x, y);
409         const Tile *tile = solids->get_tile(x, y);
410         Vector pos(solids->get_x_offset() + x*32, solids->get_y_offset() + y*32);
411
412         if(id == 112) {
413           add_object(new InvisibleBlock(pos));
414           solids->change(x, y, 0);
415         } else if(tile->getAttributes() & Tile::COIN) {
416           add_object(new Coin(pos));
417           solids->change(x, y, 0);
418         } else if(tile->getAttributes() & Tile::FULLBOX) {
419           add_object(new BonusBlock(pos, tile->getData()));
420           solids->change(x, y, 0);
421         } else if(tile->getAttributes() & Tile::BRICK) {
422           add_object(new Brick(pos, tile->getData()));
423           solids->change(x, y, 0);
424         } else if(tile->getAttributes() & Tile::GOAL) {
425           std::string sequence = tile->getData() == 0 ? "endsequence" : "stoptux";
426           add_object(new SequenceTrigger(pos, sequence));
427           solids->change(x, y, 0);
428         }
429       }
430     }
431   }
432
433   // add lights for special tiles
434   for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end(); i++) {
435     TileMap* tm = dynamic_cast<TileMap*>(*i);
436     if (!tm) continue;
437     for(size_t x=0; x < tm->get_width(); ++x) {
438       for(size_t y=0; y < tm->get_height(); ++y) {
439         uint32_t id = tm->get_tile_id(x, y);
440         Vector pos(tm->get_x_offset() + x*32, tm->get_y_offset() + y*32);
441         Vector center(pos.x + 16, pos.y + 16);
442
443         // torch
444         if (id == 1517) {
445           float pseudo_rnd = (float)((int)pos.x % 10) / 10;
446           add_object(new PulsingLight(center, 1.0f + pseudo_rnd, 0.9f, 1.0f, Color(1.0f, 1.0f, 0.6f, 1.0f)));
447         }
448         // lava or lavaflow
449         if ((id == 173) || (id == 1700) || (id == 1705) || (id == 1706)) {
450           // space lights a bit
451           if ((((tm->get_tile_id(x-1, y)) != tm->get_tile_id(x,y))
452                && (tm->get_tile_id(x, y-1) != tm->get_tile_id(x,y)))
453               || ((x % 3 == 0) && (y % 3 == 0))) {
454             float pseudo_rnd = (float)((int)pos.x % 10) / 10;
455             add_object(new PulsingLight(center, 1.0f + pseudo_rnd, 0.8f, 1.0f, Color(1.0f, 0.3f, 0.0f, 1.0f)));
456           }
457         }
458
459       }
460     }
461   }
462
463 }
464
465 HSQUIRRELVM
466 Sector::run_script(std::istream& in, const std::string& sourcename)
467 {
468   using namespace Scripting;
469
470   // garbage collect thread list
471   for(ScriptList::iterator i = scripts.begin();
472       i != scripts.end(); ) {
473     HSQOBJECT& object = *i;
474     HSQUIRRELVM vm = object_to_vm(object);
475
476     if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) {
477       sq_release(global_vm, &object);
478       i = scripts.erase(i);
479       continue;
480     }
481
482     ++i;
483   }
484
485   HSQOBJECT object = create_thread(global_vm);
486   scripts.push_back(object);
487
488   HSQUIRRELVM vm = object_to_vm(object);
489
490   // set sector_table as roottable for the thread
491   sq_pushobject(vm, sector_table);
492   sq_setroottable(vm);
493
494   try {
495     compile_and_run(vm, in, "Sector " + name + " - " + sourcename);
496   } catch(std::exception& e) {
497     log_warning << "Error running script: " << e.what() << std::endl;
498   }
499
500   return vm;
501 }
502
503 void
504 Sector::add_object(GameObject* object)
505 {
506   // make sure the object isn't already in the list
507 #ifdef DEBUG
508   for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end();
509       ++i) {
510     if(*i == object) {
511       assert("object already added to sector" == 0);
512     }
513   }
514   for(GameObjects::iterator i = gameobjects_new.begin();
515       i != gameobjects_new.end(); ++i) {
516     if(*i == object) {
517       assert("object already added to sector" == 0);
518     }
519   }
520 #endif
521
522   object->ref();
523   gameobjects_new.push_back(object);
524 }
525
526 void
527 Sector::activate(const std::string& spawnpoint)
528 {
529   SpawnPoint* sp = 0;
530   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
531       ++i) {
532     if((*i)->name == spawnpoint) {
533       sp = *i;
534       break;
535     }
536   }
537   if(!sp) {
538     log_warning << "Spawnpoint '" << spawnpoint << "' not found." << std::endl;
539     if(spawnpoint != "main") {
540       activate("main");
541     } else {
542       activate(Vector(0, 0));
543     }
544   } else {
545     activate(sp->pos);
546   }
547 }
548
549 void
550 Sector::activate(const Vector& player_pos)
551 {
552   if(_current != this) {
553     if(_current != NULL)
554       _current->deactivate();
555     _current = this;
556
557     // register sectortable as sector in scripting
558     HSQUIRRELVM vm = Scripting::global_vm;
559     sq_pushroottable(vm);
560     sq_pushstring(vm, "sector", -1);
561     sq_pushobject(vm, sector_table);
562     if(SQ_FAILED(sq_createslot(vm, -3)))
563       throw Scripting::SquirrelError(vm, "Couldn't set sector in roottable");
564     sq_pop(vm, 1);
565
566     for(GameObjects::iterator i = gameobjects.begin();
567         i != gameobjects.end(); ++i) {
568       GameObject* object = *i;
569
570       try_expose(object);
571     }
572   }
573   try_expose_me();
574
575   // spawn smalltux below spawnpoint
576   if (!player->is_big()) {
577     player->move(player_pos + Vector(0,32));
578   } else {
579     player->move(player_pos);
580   }
581
582   // spawning tux in the ground would kill him
583   if(!is_free_of_tiles(player->get_bbox())) {
584     log_warning << "Tried spawning Tux in solid matter. Compensating." << std::endl;
585     Vector npos = player->get_bbox().p1;
586     npos.y-=32;
587     player->move(npos);
588   }
589
590   camera->reset(player->get_pos());
591   update_game_objects();
592
593   //Run default.nut just before init script
594   //Check to see if it's in a levelset (info file)
595   std::string basedir = FileSystem::dirname(get_level()->filename);
596   if(PHYSFS_exists((basedir + "/info").c_str())) {
597     try {
598       IFileStream in(basedir + "/default.nut");
599       run_script(in, "default.nut");
600     } catch(std::exception& ) {
601       // doesn't exist or erroneous; do nothing
602     }
603   }
604
605   // Run init script
606   if(init_script != "") {
607     std::istringstream in(init_script);
608     run_script(in, "init-script");
609   }
610 }
611
612 void
613 Sector::deactivate()
614 {
615   if(_current != this)
616     return;
617
618   // remove sector entry from global vm
619   HSQUIRRELVM vm = Scripting::global_vm;
620   sq_pushroottable(vm);
621   sq_pushstring(vm, "sector", -1);
622   if(SQ_FAILED(sq_deleteslot(vm, -2, SQFalse)))
623     throw Scripting::SquirrelError(vm, "Couldn't unset sector in roottable");
624   sq_pop(vm, 1);
625
626   for(GameObjects::iterator i = gameobjects.begin();
627       i != gameobjects.end(); ++i) {
628     GameObject* object = *i;
629
630     try_unexpose(object);
631   }
632
633   try_unexpose_me();
634   _current = NULL;
635 }
636
637 Rect
638 Sector::get_active_region()
639 {
640   return Rect(
641     camera->get_translation() - Vector(1600, 1200),
642     camera->get_translation() + Vector(1600, 1200) + Vector(SCREEN_WIDTH,SCREEN_HEIGHT));
643 }
644
645 void
646 Sector::update(float elapsed_time)
647 {
648   player->check_bounds(camera);
649
650   /* update objects */
651   for(GameObjects::iterator i = gameobjects.begin();
652       i != gameobjects.end(); ++i) {
653     GameObject* object = *i;
654     if(!object->is_valid())
655       continue;
656
657     object->update(elapsed_time);
658   }
659
660   /* Handle all possible collisions. */
661   handle_collisions();
662   update_game_objects();
663 }
664
665 void
666 Sector::update_game_objects()
667 {
668   /** cleanup marked objects */
669   for(std::vector<GameObject*>::iterator i = gameobjects.begin();
670       i != gameobjects.end(); /* nothing */) {
671     GameObject* object = *i;
672
673     if(object->is_valid()) {
674       ++i;
675       continue;
676     }
677
678     before_object_remove(object);
679
680     object->unref();
681     i = gameobjects.erase(i);
682   }
683
684   /* add newly created objects */
685   for(std::vector<GameObject*>::iterator i = gameobjects_new.begin();
686       i != gameobjects_new.end(); ++i)
687   {
688     GameObject* object = *i;
689
690     before_object_add(object);
691
692     gameobjects.push_back(object);
693   }
694   gameobjects_new.clear();
695
696   /* update solid_tilemaps list */
697   //FIXME: this could be more efficient
698   solid_tilemaps.clear();
699   for(std::vector<GameObject*>::iterator i = gameobjects.begin();
700       i != gameobjects.end(); ++i)
701   {
702     TileMap* tm = dynamic_cast<TileMap*>(*i);
703     if (!tm) continue;
704     if (tm->is_solid()) solid_tilemaps.push_back(tm);
705   }
706
707 }
708
709 bool
710 Sector::before_object_add(GameObject* object)
711 {
712   Bullet* bullet = dynamic_cast<Bullet*> (object);
713   if(bullet != NULL) {
714     bullets.push_back(bullet);
715   }
716
717   MovingObject* movingobject = dynamic_cast<MovingObject*> (object);
718   if(movingobject != NULL) {
719     moving_objects.push_back(movingobject);
720   }
721
722   Portable* portable = dynamic_cast<Portable*> (object);
723   if(portable != NULL) {
724     portables.push_back(portable);
725   }
726
727   TileMap* tilemap = dynamic_cast<TileMap*> (object);
728   if(tilemap != NULL && tilemap->is_solid()) {
729     solid_tilemaps.push_back(tilemap);
730   }
731
732   Camera* camera = dynamic_cast<Camera*> (object);
733   if(camera != NULL) {
734     if(this->camera != 0) {
735       log_warning << "Multiple cameras added. Ignoring" << std::endl;
736       return false;
737     }
738     this->camera = camera;
739   }
740
741   Player* player = dynamic_cast<Player*> (object);
742   if(player != NULL) {
743     if(this->player != 0) {
744       log_warning << "Multiple players added. Ignoring" << std::endl;
745       return false;
746     }
747     this->player = player;
748   }
749
750   DisplayEffect* effect = dynamic_cast<DisplayEffect*> (object);
751   if(effect != NULL) {
752     if(this->effect != 0) {
753       log_warning << "Multiple DisplayEffects added. Ignoring" << std::endl;
754       return false;
755     }
756     this->effect = effect;
757   }
758
759   if(_current == this) {
760     try_expose(object);
761   }
762
763   return true;
764 }
765
766 void
767 Sector::try_expose(GameObject* object)
768 {
769   ScriptInterface* interface = dynamic_cast<ScriptInterface*> (object);
770   if(interface != NULL) {
771     HSQUIRRELVM vm = Scripting::global_vm;
772     sq_pushobject(vm, sector_table);
773     interface->expose(vm, -1);
774     sq_pop(vm, 1);
775   }
776 }
777
778 void
779 Sector::try_expose_me()
780 {
781   HSQUIRRELVM vm = Scripting::global_vm;
782   sq_pushobject(vm, sector_table);
783   Scripting::SSector* interface = static_cast<Scripting::SSector*> (this);
784   expose_object(vm, -1, interface, "settings", false);
785   sq_pop(vm, 1);
786 }
787
788 void
789 Sector::before_object_remove(GameObject* object)
790 {
791   Portable* portable = dynamic_cast<Portable*> (object);
792   if(portable != NULL) {
793     portables.erase(std::find(portables.begin(), portables.end(), portable));
794   }
795   Bullet* bullet = dynamic_cast<Bullet*> (object);
796   if(bullet != NULL) {
797     bullets.erase(std::find(bullets.begin(), bullets.end(), bullet));
798   }
799   MovingObject* moving_object = dynamic_cast<MovingObject*> (object);
800   if(moving_object != NULL) {
801     moving_objects.erase(
802       std::find(moving_objects.begin(), moving_objects.end(), moving_object));
803   }
804
805   if(_current == this)
806     try_unexpose(object);
807 }
808
809 void
810 Sector::try_unexpose(GameObject* object)
811 {
812   ScriptInterface* interface = dynamic_cast<ScriptInterface*> (object);
813   if(interface != NULL) {
814     HSQUIRRELVM vm = Scripting::global_vm;
815     SQInteger oldtop = sq_gettop(vm);
816     sq_pushobject(vm, sector_table);
817     try {
818       interface->unexpose(vm, -1);
819     } catch(std::exception& e) {
820       log_warning << "Couldn't unregister object: " << e.what() << std::endl;
821     }
822     sq_settop(vm, oldtop);
823   }
824 }
825
826 void
827 Sector::try_unexpose_me()
828 {
829   HSQUIRRELVM vm = Scripting::global_vm;
830   SQInteger oldtop = sq_gettop(vm);
831   sq_pushobject(vm, sector_table);
832   try {
833     Scripting::unexpose_object(vm, -1, "settings");
834   } catch(std::exception& e) {
835     log_warning << "Couldn't unregister object: " << e.what() << std::endl;
836   }
837   sq_settop(vm, oldtop);
838 }
839 void
840 Sector::draw(DrawingContext& context)
841 {
842   context.set_ambient_color( ambient_light );
843   context.push_transform();
844   context.set_translation(camera->get_translation());
845
846   for(GameObjects::iterator i = gameobjects.begin();
847       i != gameobjects.end(); ++i) {
848     GameObject* object = *i;
849     if(!object->is_valid())
850       continue;
851
852     if (draw_solids_only)
853     {
854       TileMap* tm = dynamic_cast<TileMap*>(object);
855       if (tm && !tm->is_solid())
856         continue;
857     }
858
859     object->draw(context);
860   }
861
862   if(show_collrects) {
863     Color col(0.2f, 0.2f, 0.2f, 0.7f);
864     for(MovingObjects::iterator i = moving_objects.begin();
865         i != moving_objects.end(); ++i) {
866       MovingObject* object = *i;
867       const Rect& rect = object->get_bbox();
868
869       context.draw_filled_rect(rect, col, LAYER_FOREGROUND1 + 10);
870     }
871   }
872
873   context.pop_transform();
874 }
875
876 /*-------------------------------------------------------------------------
877  * Collision Detection
878  *-------------------------------------------------------------------------*/
879
880 /** r1 is supposed to be moving, r2 a solid object */
881 void check_collisions(collision::Constraints* constraints,
882                       const Vector& movement, const Rect& r1, const Rect& r2,
883                       GameObject* object = NULL, MovingObject* other = NULL, const Vector& addl_ground_movement = Vector(0,0))
884 {
885   if(!collision::intersects(r1, r2))
886     return;
887
888   MovingObject *moving_object = dynamic_cast<MovingObject*> (object);
889   CollisionHit dummy;
890   if(other != NULL && !other->collides(*object, dummy))
891     return;
892   if(moving_object != NULL && !moving_object->collides(*other, dummy))
893     return;
894
895   // calculate intersection
896   float itop    = r1.get_bottom() - r2.get_top();
897   float ibottom = r2.get_bottom() - r1.get_top();
898   float ileft   = r1.get_right() - r2.get_left();
899   float iright  = r2.get_right() - r1.get_left();
900
901   if(fabsf(movement.y) > fabsf(movement.x)) {
902     if(ileft < SHIFT_DELTA) {
903       constraints->right = std::min(constraints->right, r2.get_left());
904       return;
905     } else if(iright < SHIFT_DELTA) {
906       constraints->left = std::max(constraints->left, r2.get_right());
907       return;
908     }
909   } else {
910     // shiftout bottom/top
911     if(itop < SHIFT_DELTA) {
912       constraints->bottom = std::min(constraints->bottom, r2.get_top());
913       return;
914     } else if(ibottom < SHIFT_DELTA) {
915       constraints->top = std::max(constraints->top, r2.get_bottom());
916       return;
917     }
918   }
919
920   constraints->ground_movement += addl_ground_movement;
921   if(other != NULL) {
922     HitResponse response = other->collision(*object, dummy);
923     if(response == PASSTHROUGH)
924       return;
925
926     if(other->get_movement() != Vector(0, 0)) {
927       // TODO what todo when we collide with 2 moving objects?!?
928       constraints->ground_movement = other->get_movement();
929     }
930   }
931
932   float vert_penetration = std::min(itop, ibottom);
933   float horiz_penetration = std::min(ileft, iright);
934   if(vert_penetration < horiz_penetration) {
935     if(itop < ibottom) {
936       constraints->bottom = std::min(constraints->bottom, r2.get_top());
937       constraints->hit.bottom = true;
938     } else {
939       constraints->top = std::max(constraints->top, r2.get_bottom());
940       constraints->hit.top = true;
941     }
942   } else {
943     if(ileft < iright) {
944       constraints->right = std::min(constraints->right, r2.get_left());
945       constraints->hit.right = true;
946     } else {
947       constraints->left = std::max(constraints->left, r2.get_right());
948       constraints->hit.left = true;
949     }
950   }
951 }
952
953 void
954 Sector::collision_tilemap(collision::Constraints* constraints,
955                           const Vector& movement, const Rect& dest) const
956 {
957   // calculate rectangle where the object will move
958   float x1 = dest.get_left();
959   float x2 = dest.get_right();
960   float y1 = dest.get_top();
961   float y2 = dest.get_bottom();
962
963   for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
964     TileMap* solids = *i;
965
966     // test with all tiles in this rectangle
967     int starttilex = int(x1 - solids->get_x_offset()) / 32;
968     int starttiley = int(y1 - solids->get_y_offset()) / 32;
969     int max_x = int(x2 - solids->get_x_offset());
970     int max_y = int(y2+1 - solids->get_y_offset());
971
972     for(int x = starttilex; x*32 < max_x; ++x) {
973       for(int y = starttiley; y*32 < max_y; ++y) {
974         const Tile* tile = solids->get_tile(x, y);
975         if(!tile)
976           continue;
977         // skip non-solid tiles
978         if((tile->getAttributes() & Tile::SOLID) == 0)
979           continue;
980         // only handle unisolid when the player is falling down and when he was
981         // above the tile before
982         if(tile->getAttributes() & Tile::UNISOLID) {
983           if(movement.y <= 0 || dest.get_bottom() - movement.y - SHIFT_DELTA > y*32)
984             continue;
985         }
986
987         if(tile->getAttributes() & Tile::SLOPE) { // slope tile
988           AATriangle triangle;
989           Vector p1(x*32 + solids->get_x_offset(), y*32 + solids->get_y_offset());
990           Vector p2((x+1)*32 + solids->get_x_offset(), (y+1)*32 + solids->get_y_offset());
991           triangle = AATriangle(p1, p2, tile->getData());
992
993           collision::rectangle_aatriangle(constraints, dest, triangle, solids->get_movement());
994         } else { // normal rectangular tile
995           Rect rect(x*32 + solids->get_x_offset(), y*32 + solids->get_y_offset(), (x+1)*32 + solids->get_x_offset(), (y+1)*32 + solids->get_y_offset());
996           check_collisions(constraints, movement, dest, rect, NULL, NULL, solids->get_movement());
997         }
998       }
999     }
1000   }
1001 }
1002
1003 uint32_t
1004 Sector::collision_tile_attributes(const Rect& dest) const
1005 {
1006   float x1 = dest.p1.x;
1007   float y1 = dest.p1.y;
1008   float x2 = dest.p2.x;
1009   float y2 = dest.p2.y + SHIFT_DELTA;
1010
1011   uint32_t result = 0;
1012   for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
1013     TileMap* solids = *i;
1014
1015     // test with all tiles in this rectangle
1016     int starttilex = int(x1 - solids->get_x_offset()) / 32;
1017     int starttiley = int(y1 - solids->get_y_offset()) / 32;
1018     int max_x = int(x2 - solids->get_x_offset());
1019     int max_y = int(y2+1 - solids->get_y_offset());
1020
1021     for(int x = starttilex; x*32 < max_x; ++x) {
1022       for(int y = starttiley; y*32 < max_y; ++y) {
1023         const Tile* tile = solids->get_tile(x, y);
1024         if(!tile)
1025           continue;
1026         result |= tile->getAttributes();
1027       }
1028     }
1029   }
1030
1031   return result;
1032 }
1033
1034 /** fills in CollisionHit and Normal vector of 2 intersecting rectangle */
1035 static void get_hit_normal(const Rect& r1, const Rect& r2, CollisionHit& hit,
1036                            Vector& normal)
1037 {
1038   float itop = r1.get_bottom() - r2.get_top();
1039   float ibottom = r2.get_bottom() - r1.get_top();
1040   float ileft = r1.get_right() - r2.get_left();
1041   float iright = r2.get_right() - r1.get_left();
1042
1043   float vert_penetration = std::min(itop, ibottom);
1044   float horiz_penetration = std::min(ileft, iright);
1045   if(vert_penetration < horiz_penetration) {
1046     if(itop < ibottom) {
1047       hit.bottom = true;
1048       normal.y = vert_penetration;
1049     } else {
1050       hit.top = true;
1051       normal.y = -vert_penetration;
1052     }
1053   } else {
1054     if(ileft < iright) {
1055       hit.right = true;
1056       normal.x = horiz_penetration;
1057     } else {
1058       hit.left = true;
1059       normal.x = -horiz_penetration;
1060     }
1061   }
1062 }
1063
1064 void
1065 Sector::collision_object(MovingObject* object1, MovingObject* object2) const
1066 {
1067   using namespace collision;
1068
1069   const Rect& r1 = object1->dest;
1070   const Rect& r2 = object2->dest;
1071
1072   CollisionHit hit;
1073   if(intersects(object1->dest, object2->dest)) {
1074     Vector normal;
1075     get_hit_normal(r1, r2, hit, normal);
1076
1077     if(!object1->collides(*object2, hit))
1078       return;
1079     std::swap(hit.left, hit.right);
1080     std::swap(hit.top, hit.bottom);
1081     if(!object2->collides(*object1, hit))
1082       return;
1083     std::swap(hit.left, hit.right);
1084     std::swap(hit.top, hit.bottom);
1085
1086     HitResponse response1 = object1->collision(*object2, hit);
1087     std::swap(hit.left, hit.right);
1088     std::swap(hit.top, hit.bottom);
1089     HitResponse response2 = object2->collision(*object1, hit);
1090     if(response1 == CONTINUE && response2 == CONTINUE) {
1091       normal *= (0.5 + DELTA);
1092       object1->dest.move(-normal);
1093       object2->dest.move(normal);
1094     } else if (response1 == CONTINUE && response2 == FORCE_MOVE) {
1095       normal *= (1 + DELTA);
1096       object1->dest.move(-normal);
1097     } else if (response1 == FORCE_MOVE && response2 == CONTINUE) {
1098       normal *= (1 + DELTA);
1099       object2->dest.move(normal);
1100     }
1101   }
1102 }
1103
1104 void
1105 Sector::collision_static(collision::Constraints* constraints,
1106                          const Vector& movement, const Rect& dest,
1107                          GameObject& object)
1108 {
1109   collision_tilemap(constraints, movement, dest);
1110
1111   // collision with other (static) objects
1112   for(MovingObjects::iterator i = moving_objects.begin();
1113       i != moving_objects.end(); ++i) {
1114     MovingObject* moving_object = *i;
1115     if(moving_object->get_group() != COLGROUP_STATIC
1116        && moving_object->get_group() != COLGROUP_MOVING_STATIC)
1117       continue;
1118     if(!moving_object->is_valid())
1119       continue;
1120
1121     if(moving_object != &object)
1122       check_collisions(constraints, movement, dest, moving_object->bbox,
1123                        &object, moving_object);
1124   }
1125 }
1126
1127 void
1128 Sector::collision_static_constrains(MovingObject& object)
1129 {
1130   using namespace collision;
1131   float infinity = (std::numeric_limits<float>::has_infinity ? std::numeric_limits<float>::infinity() : std::numeric_limits<float>::max());
1132
1133   Constraints constraints;
1134   Vector movement = object.get_movement();
1135   Rect& dest = object.dest;
1136   float owidth = object.get_bbox().get_width();
1137   float oheight = object.get_bbox().get_height();
1138
1139   for(int i = 0; i < 2; ++i) {
1140     collision_static(&constraints, Vector(0, movement.y), dest, object);
1141     if(!constraints.has_constraints())
1142       break;
1143
1144     // apply calculated horizontal constraints
1145     if(constraints.bottom < infinity) {
1146       float height = constraints.bottom - constraints.top;
1147       if(height < oheight) {
1148         // we're crushed, but ignore this for now, we'll get this again
1149         // later if we're really crushed or things will solve itself when
1150         // looking at the vertical constraints
1151       }
1152       dest.p2.y = constraints.bottom - DELTA;
1153       dest.p1.y = dest.p2.y - oheight;
1154     } else if(constraints.top > -infinity) {
1155       dest.p1.y = constraints.top + DELTA;
1156       dest.p2.y = dest.p1.y + oheight;
1157     }
1158   }
1159   if(constraints.has_constraints()) {
1160     if(constraints.hit.bottom) {
1161       dest.move(constraints.ground_movement);
1162     }
1163     if(constraints.hit.top || constraints.hit.bottom) {
1164       constraints.hit.left = false;
1165       constraints.hit.right = false;
1166       object.collision_solid(constraints.hit);
1167     }
1168   }
1169
1170   constraints = Constraints();
1171   for(int i = 0; i < 2; ++i) {
1172     collision_static(&constraints, movement, dest, object);
1173     if(!constraints.has_constraints())
1174       break;
1175
1176     // apply calculated vertical constraints
1177     if(constraints.right < infinity) {
1178       float width = constraints.right - constraints.left;
1179       if(width + SHIFT_DELTA < owidth) {
1180 #if 0
1181         printf("Object %p crushed horizontally... L:%f R:%f\n", &object,
1182                constraints.left, constraints.right);
1183 #endif
1184         CollisionHit h;
1185         h.left = true;
1186         h.right = true;
1187         h.crush = true;
1188         object.collision_solid(h);
1189       } else {
1190         dest.p2.x = constraints.right - DELTA;
1191         dest.p1.x = dest.p2.x - owidth;
1192       }
1193     } else if(constraints.left > -infinity) {
1194       dest.p1.x = constraints.left + DELTA;
1195       dest.p2.x = dest.p1.x + owidth;
1196     }
1197   }
1198
1199   if(constraints.has_constraints()) {
1200     if( constraints.hit.left || constraints.hit.right
1201         || constraints.hit.top || constraints.hit.bottom
1202         || constraints.hit.crush )
1203       object.collision_solid(constraints.hit);
1204   }
1205
1206   // an extra pass to make sure we're not crushed horizontally
1207   constraints = Constraints();
1208   collision_static(&constraints, movement, dest, object);
1209   if(constraints.bottom < infinity) {
1210     float height = constraints.bottom - constraints.top;
1211     if(height + SHIFT_DELTA < oheight) {
1212 #if 0
1213       printf("Object %p crushed vertically...\n", &object);
1214 #endif
1215       CollisionHit h;
1216       h.top = true;
1217       h.bottom = true;
1218       h.crush = true;
1219       object.collision_solid(h);
1220     }
1221   }
1222 }
1223
1224 namespace {
1225 const float MAX_SPEED = 16.0f;
1226 }
1227
1228 void
1229 Sector::handle_collisions()
1230 {
1231   using namespace collision;
1232
1233   // calculate destination positions of the objects
1234   for(MovingObjects::iterator i = moving_objects.begin();
1235       i != moving_objects.end(); ++i) {
1236     MovingObject* moving_object = *i;
1237     Vector mov = moving_object->get_movement();
1238
1239     // make sure movement is never faster than MAX_SPEED. Norm is pretty fat, so two addl. checks are done before.
1240     if (((mov.x > MAX_SPEED * M_SQRT1_2) || (mov.y > MAX_SPEED * M_SQRT1_2)) && (mov.norm() > MAX_SPEED)) {
1241       moving_object->movement = mov.unit() * MAX_SPEED;
1242       //log_debug << "Temporarily reduced object's speed of " << mov.norm() << " to " << moving_object->movement.norm() << "." << std::endl;
1243     }
1244
1245     moving_object->dest = moving_object->get_bbox();
1246     moving_object->dest.move(moving_object->get_movement());
1247   }
1248
1249   // part1: COLGROUP_MOVING vs COLGROUP_STATIC and tilemap
1250   for(MovingObjects::iterator i = moving_objects.begin();
1251       i != moving_objects.end(); ++i) {
1252     MovingObject* moving_object = *i;
1253     if((moving_object->get_group() != COLGROUP_MOVING
1254         && moving_object->get_group() != COLGROUP_MOVING_STATIC
1255         && moving_object->get_group() != COLGROUP_MOVING_ONLY_STATIC)
1256        || !moving_object->is_valid())
1257       continue;
1258
1259     collision_static_constrains(*moving_object);
1260   }
1261
1262   // part2: COLGROUP_MOVING vs tile attributes
1263   for(MovingObjects::iterator i = moving_objects.begin();
1264       i != moving_objects.end(); ++i) {
1265     MovingObject* moving_object = *i;
1266     if((moving_object->get_group() != COLGROUP_MOVING
1267         && moving_object->get_group() != COLGROUP_MOVING_STATIC
1268         && moving_object->get_group() != COLGROUP_MOVING_ONLY_STATIC)
1269        || !moving_object->is_valid())
1270       continue;
1271
1272     uint32_t tile_attributes = collision_tile_attributes(moving_object->dest);
1273     if(tile_attributes > Tile::FIRST_INTERESTING_FLAG) {
1274       moving_object->collision_tile(tile_attributes);
1275     }
1276   }
1277
1278   // part2.5: COLGROUP_MOVING vs COLGROUP_TOUCHABLE
1279   for(MovingObjects::iterator i = moving_objects.begin();
1280       i != moving_objects.end(); ++i) {
1281     MovingObject* moving_object = *i;
1282     if((moving_object->get_group() != COLGROUP_MOVING
1283         && moving_object->get_group() != COLGROUP_MOVING_STATIC)
1284        || !moving_object->is_valid())
1285       continue;
1286
1287     for(MovingObjects::iterator i2 = moving_objects.begin();
1288         i2 != moving_objects.end(); ++i2) {
1289       MovingObject* moving_object_2 = *i2;
1290       if(moving_object_2->get_group() != COLGROUP_TOUCHABLE
1291          || !moving_object_2->is_valid())
1292         continue;
1293
1294       if(intersects(moving_object->dest, moving_object_2->dest)) {
1295         Vector normal;
1296         CollisionHit hit;
1297         get_hit_normal(moving_object->dest, moving_object_2->dest,
1298                        hit, normal);
1299         if(!moving_object->collides(*moving_object_2, hit))
1300           continue;
1301         if(!moving_object_2->collides(*moving_object, hit))
1302           continue;
1303
1304         moving_object->collision(*moving_object_2, hit);
1305         moving_object_2->collision(*moving_object, hit);
1306       }
1307     }
1308   }
1309
1310   // part3: COLGROUP_MOVING vs COLGROUP_MOVING
1311   for(MovingObjects::iterator i = moving_objects.begin();
1312       i != moving_objects.end(); ++i) {
1313     MovingObject* moving_object = *i;
1314
1315     if((moving_object->get_group() != COLGROUP_MOVING
1316         && moving_object->get_group() != COLGROUP_MOVING_STATIC)
1317        || !moving_object->is_valid())
1318       continue;
1319
1320     for(MovingObjects::iterator i2 = i+1;
1321         i2 != moving_objects.end(); ++i2) {
1322       MovingObject* moving_object_2 = *i2;
1323       if((moving_object_2->get_group() != COLGROUP_MOVING
1324           && moving_object_2->get_group() != COLGROUP_MOVING_STATIC)
1325          || !moving_object_2->is_valid())
1326         continue;
1327
1328       collision_object(moving_object, moving_object_2);
1329     }
1330   }
1331
1332   // apply object movement
1333   for(MovingObjects::iterator i = moving_objects.begin();
1334       i != moving_objects.end(); ++i) {
1335     MovingObject* moving_object = *i;
1336
1337     moving_object->bbox = moving_object->dest;
1338     moving_object->movement = Vector(0, 0);
1339   }
1340 }
1341
1342 bool
1343 Sector::is_free_of_tiles(const Rect& rect, const bool ignoreUnisolid) const
1344 {
1345   using namespace collision;
1346
1347   for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
1348     TileMap* solids = *i;
1349
1350     // test with all tiles in this rectangle
1351     int starttilex = int(rect.p1.x - solids->get_x_offset()) / 32;
1352     int starttiley = int(rect.p1.y - solids->get_y_offset()) / 32;
1353     int max_x = int(rect.p2.x - solids->get_x_offset());
1354     int max_y = int(rect.p2.y - solids->get_y_offset());
1355
1356     for(int x = starttilex; x*32 <= max_x; ++x) {
1357       for(int y = starttiley; y*32 <= max_y; ++y) {
1358         const Tile* tile = solids->get_tile(x, y);
1359         if(!tile) continue;
1360         if(tile->getAttributes() & Tile::SLOPE) {
1361           AATriangle triangle;
1362           Vector p1(x*32 + solids->get_x_offset(), y*32 + solids->get_y_offset());
1363           Vector p2((x+1)*32 + solids->get_x_offset(), (y+1)*32 + solids->get_y_offset());
1364           triangle = AATriangle(p1, p2, tile->getData());
1365           Constraints constraints;
1366           if(collision::rectangle_aatriangle(&constraints, rect, triangle) && (!ignoreUnisolid || !(tile->getAttributes() & Tile::UNISOLID))) return false;
1367         }
1368         if((tile->getAttributes() & Tile::SOLID) && (!ignoreUnisolid || !(tile->getAttributes() & Tile::UNISOLID))) return false;
1369       }
1370     }
1371   }
1372
1373   return true;
1374 }
1375
1376 bool
1377 Sector::is_free_of_statics(const Rect& rect, const MovingObject* ignore_object, const bool ignoreUnisolid) const
1378 {
1379   using namespace collision;
1380
1381   if (!is_free_of_tiles(rect, ignoreUnisolid)) return false;
1382
1383   for(MovingObjects::const_iterator i = moving_objects.begin();
1384       i != moving_objects.end(); ++i) {
1385     const MovingObject* moving_object = *i;
1386     if (moving_object == ignore_object) continue;
1387     if (!moving_object->is_valid()) continue;
1388     if (moving_object->get_group() == COLGROUP_STATIC) {
1389       if(intersects(rect, moving_object->get_bbox())) return false;
1390     }
1391   }
1392
1393   return true;
1394 }
1395
1396 bool
1397 Sector::is_free_of_movingstatics(const Rect& rect, const MovingObject* ignore_object) const
1398 {
1399   using namespace collision;
1400
1401   if (!is_free_of_tiles(rect)) return false;
1402
1403   for(MovingObjects::const_iterator i = moving_objects.begin();
1404       i != moving_objects.end(); ++i) {
1405     const MovingObject* moving_object = *i;
1406     if (moving_object == ignore_object) continue;
1407     if (!moving_object->is_valid()) continue;
1408     if ((moving_object->get_group() == COLGROUP_MOVING)
1409         || (moving_object->get_group() == COLGROUP_MOVING_STATIC)
1410         || (moving_object->get_group() == COLGROUP_STATIC)) {
1411       if(intersects(rect, moving_object->get_bbox())) return false;
1412     }
1413   }
1414
1415   return true;
1416 }
1417
1418 bool
1419 Sector::add_bullet(const Vector& pos, float xm, Direction dir)
1420 {
1421   // TODO remove this function and move these checks elsewhere...
1422
1423   Bullet* new_bullet = 0;
1424   if((player_status->bonus == FIRE_BONUS &&
1425       (int)bullets.size() >= player_status->max_fire_bullets) ||
1426      (player_status->bonus == ICE_BONUS &&
1427       (int)bullets.size() >= player_status->max_ice_bullets))
1428     return false;
1429   new_bullet = new Bullet(pos, xm, dir, player_status->bonus);
1430   add_object(new_bullet);
1431
1432   sound_manager->play("sounds/shoot.wav");
1433
1434   return true;
1435 }
1436
1437 bool
1438 Sector::add_smoke_cloud(const Vector& pos)
1439 {
1440   add_object(new SmokeCloud(pos));
1441   return true;
1442 }
1443
1444 void
1445 Sector::play_music(MusicType type)
1446 {
1447   currentmusic = type;
1448   switch(currentmusic) {
1449     case LEVEL_MUSIC:
1450       sound_manager->play_music(music);
1451       break;
1452     case HERRING_MUSIC:
1453       sound_manager->play_music("music/invincible.music");
1454       break;
1455     case HERRING_WARNING_MUSIC:
1456       sound_manager->stop_music(TUX_INVINCIBLE_TIME_WARNING);
1457       break;
1458     default:
1459       sound_manager->play_music("");
1460       break;
1461   }
1462 }
1463
1464 MusicType
1465 Sector::get_music_type()
1466 {
1467   return currentmusic;
1468 }
1469
1470 int
1471 Sector::get_total_badguys()
1472 {
1473   int total_badguys = 0;
1474   for(GameObjects::iterator i = gameobjects.begin();
1475       i != gameobjects.end(); ++i) {
1476     BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
1477     if (badguy && badguy->countMe)
1478       total_badguys++;
1479   }
1480
1481   return total_badguys;
1482 }
1483
1484 bool
1485 Sector::inside(const Rect& rect) const
1486 {
1487   for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
1488     TileMap* solids = *i;
1489     bool horizontally = ((rect.p2.x >= 0 + solids->get_x_offset()) && (rect.p1.x <= solids->get_width() * 32 + solids->get_x_offset()));
1490     bool vertically = (rect.p1.y <= solids->get_height() * 32 + solids->get_y_offset());
1491
1492     if (horizontally && vertically)
1493       return true;
1494   }
1495   return false;
1496 }
1497
1498 float
1499 Sector::get_width() const
1500 {
1501   float width = 0;
1502   for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin();
1503       i != solid_tilemaps.end(); i++) {
1504     TileMap* solids = *i;
1505     if ((solids->get_width() * 32 + solids->get_x_offset()) > width) {
1506       width = solids->get_width() * 32 + solids->get_x_offset();
1507     }
1508   }
1509
1510   return width;
1511 }
1512
1513 float
1514 Sector::get_height() const
1515 {
1516   float height = 0;
1517   for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin();
1518       i != solid_tilemaps.end(); i++) {
1519     TileMap* solids = *i;
1520     if ((solids->get_height() * 32 + solids->get_y_offset()) > height) {
1521       height = solids->get_height() * 32 + solids->get_y_offset();
1522     }
1523   }
1524
1525   return height;
1526 }
1527
1528 void
1529 Sector::change_solid_tiles(uint32_t old_tile_id, uint32_t new_tile_id)
1530 {
1531   for(std::list<TileMap*>::const_iterator i = solid_tilemaps.begin(); i != solid_tilemaps.end(); i++) {
1532     TileMap* solids = *i;
1533     solids->change_all(old_tile_id, new_tile_id);
1534   }
1535 }
1536
1537 void
1538 Sector::set_ambient_light(float red, float green, float blue)
1539 {
1540   ambient_light.red = red;
1541   ambient_light.green = green;
1542   ambient_light.blue = blue;
1543 }
1544
1545 float
1546 Sector::get_ambient_red()
1547 {
1548   return ambient_light.red;
1549 }
1550
1551 float
1552 Sector::get_ambient_green()
1553 {
1554   return ambient_light.green;
1555 }
1556
1557 float
1558 Sector::get_ambient_blue()
1559 {
1560   return ambient_light.blue;
1561 }
1562
1563 void
1564 Sector::set_gravity(float gravity)
1565 {
1566   log_warning << "Changing a Sector's gravitational constant might have unforeseen side-effects" << std::endl;
1567   this->gravity = gravity;
1568 }
1569
1570 float
1571 Sector::get_gravity() const
1572 {
1573   return gravity;
1574 }
1575
1576 /* EOF */