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