fix for bad hit structure
[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
31 #include "sector.hpp"
32 #include "object/player.hpp"
33 #include "object/gameobjs.hpp"
34 #include "object/camera.hpp"
35 #include "object/background.hpp"
36 #include "object/gradient.hpp"
37 #include "object/particlesystem.hpp"
38 #include "object/particlesystem_interactive.hpp"
39 #include "object/tilemap.hpp"
40 #include "lisp/parser.hpp"
41 #include "lisp/lisp.hpp"
42 #include "lisp/writer.hpp"
43 #include "lisp/list_iterator.hpp"
44 #include "tile.hpp"
45 #include "audio/sound_manager.hpp"
46 #include "game_session.hpp"
47 #include "resources.hpp"
48 #include "statistics.hpp"
49 #include "object_factory.hpp"
50 #include "collision.hpp"
51 #include "spawn_point.hpp"
52 #include "math/rect.hpp"
53 #include "math/aatriangle.hpp"
54 #include "object/coin.hpp"
55 #include "object/block.hpp"
56 #include "object/invisible_block.hpp"
57 #include "object/bullet.hpp"
58 #include "object/text_object.hpp"
59 #include "badguy/jumpy.hpp"
60 #include "trigger/sequence_trigger.hpp"
61 #include "player_status.hpp"
62 #include "scripting/squirrel_util.hpp"
63 #include "script_interface.hpp"
64 #include "log.hpp"
65
66 Sector* Sector::_current = 0;
67
68 bool Sector::show_collrects = false;
69 bool Sector::draw_solids_only = false;
70
71 Sector::Sector(Level* parent)
72   : level(parent), currentmusic(LEVEL_MUSIC), gravity(10),
73     player(0), solids(0), camera(0)
74 {
75   add_object(new Player(player_status));
76   add_object(new DisplayEffect());
77   add_object(new TextObject());
78
79   // create a new squirrel table for the sector
80   using namespace Scripting;
81
82   sq_collectgarbage(global_vm);
83
84   sq_newtable(global_vm);
85   sq_pushroottable(global_vm);
86   if(SQ_FAILED(sq_setdelegate(global_vm, -2)))
87     throw Scripting::SquirrelError(global_vm, "Couldn't set sector_table delegate");
88
89   sq_resetobject(&sector_table);
90   if(SQ_FAILED(sq_getstackobj(global_vm, -1, &sector_table)))
91     throw Scripting::SquirrelError(global_vm, "Couldn't get sector table");
92   sq_addref(global_vm, &sector_table);
93   sq_pop(global_vm, 1);
94 }
95
96 Sector::~Sector()
97 {
98   using namespace Scripting;
99   
100   deactivate();
101
102   for(ScriptList::iterator i = scripts.begin();
103       i != scripts.end(); ++i) {
104     HSQOBJECT& object = *i;
105     sq_release(global_vm, &object);
106   }
107   sq_release(global_vm, &sector_table);
108   sq_collectgarbage(global_vm);
109  
110   update_game_objects();
111   assert(gameobjects_new.size() == 0);
112
113   for(GameObjects::iterator i = gameobjects.begin();
114       i != gameobjects.end(); ++i) {
115     GameObject* object = *i;
116     before_object_remove(object);
117     object->unref();
118   }
119
120   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
121       ++i)
122     delete *i;
123 }
124
125 Level*
126 Sector::get_level()
127 {
128   return level;
129 }
130
131 GameObject*
132 Sector::parse_object(const std::string& name, const lisp::Lisp& reader)
133 {
134   if(name == "camera") {
135     Camera* camera = new Camera(this);
136     camera->parse(reader);
137     return camera;
138   } else if(name == "particles-snow") {
139     SnowParticleSystem* partsys = new SnowParticleSystem();
140     partsys->parse(reader);
141     return partsys;
142   } else if(name == "particles-rain") {
143     RainParticleSystem* partsys = new RainParticleSystem();
144     partsys->parse(reader);
145     return partsys;
146   } else if(name == "particles-comets") {
147     CometParticleSystem* partsys = new CometParticleSystem();
148     partsys->parse(reader);
149     return partsys;
150   } else if(name == "particles-ghosts") {
151     GhostParticleSystem* partsys = new GhostParticleSystem();
152     partsys->parse(reader);
153     return partsys;
154   } else if(name == "particles-clouds") {
155     CloudParticleSystem* partsys = new CloudParticleSystem();
156     partsys->parse(reader);
157     return partsys;
158   } else if(name == "money") { // for compatibility with old maps
159     return new Jumpy(reader);
160   } 
161
162   try {
163     return create_object(name, reader);
164   } catch(std::exception& e) {
165     log_warning << e.what() << "" << std::endl;
166   }
167   
168   return 0;
169 }
170
171 void
172 Sector::parse(const lisp::Lisp& sector)
173 {  
174   lisp::ListIterator iter(&sector);
175   while(iter.next()) {
176     const std::string& token = iter.item();
177     if(token == "name") {
178       iter.value()->get(name);
179     } else if(token == "gravity") {
180       iter.value()->get(gravity);
181     } else if(token == "music") {
182       iter.value()->get(music);
183     } else if(token == "spawnpoint") {
184       SpawnPoint* sp = new SpawnPoint(iter.lisp());
185       spawnpoints.push_back(sp);
186     } else if(token == "init-script") {
187       iter.value()->get(init_script);
188     } else {
189       GameObject* object = parse_object(token, *(iter.lisp()));
190       if(object) {
191         add_object(object);
192       }
193     }
194   }
195
196   update_game_objects();
197
198   if(!solids)
199     throw std::runtime_error("sector does not contain a solid tile layer.");
200
201   fix_old_tiles();
202   if(!camera) {
203     log_warning << "sector '" << name << "' does not contain a camera." << std::endl;
204     update_game_objects();
205     add_object(new Camera(this));
206   }
207
208   update_game_objects();
209 }
210
211 void
212 Sector::parse_old_format(const lisp::Lisp& reader)
213 {
214   name = "main";
215   reader.get("gravity", gravity);
216
217   std::string backgroundimage;
218   reader.get("background", backgroundimage);
219   float bgspeed = .5;
220   reader.get("bkgd_speed", bgspeed);
221   bgspeed /= 100;
222
223   Color bkgd_top, bkgd_bottom;
224   int r = 0, g = 0, b = 128;
225   reader.get("bkgd_red_top", r);
226   reader.get("bkgd_green_top",  g);
227   reader.get("bkgd_blue_top",  b);
228   bkgd_top.red = static_cast<float> (r) / 255.0f;
229   bkgd_top.green = static_cast<float> (g) / 255.0f;
230   bkgd_top.blue = static_cast<float> (b) / 255.0f;
231   
232   reader.get("bkgd_red_bottom",  r);
233   reader.get("bkgd_green_bottom", g);
234   reader.get("bkgd_blue_bottom", b);
235   bkgd_bottom.red = static_cast<float> (r) / 255.0f;
236   bkgd_bottom.green = static_cast<float> (g) / 255.0f;
237   bkgd_bottom.blue = static_cast<float> (b) / 255.0f;
238   
239   if(backgroundimage != "") {
240     Background* background = new Background();
241     background->set_image(
242             std::string("images/background/") + backgroundimage, bgspeed);
243     add_object(background);
244   } else {
245     Gradient* gradient = new Gradient();
246     gradient->set_gradient(bkgd_top, bkgd_bottom);
247     add_object(gradient);
248   }
249
250   std::string particlesystem;
251   reader.get("particle_system", particlesystem);
252   if(particlesystem == "clouds")
253     add_object(new CloudParticleSystem());
254   else if(particlesystem == "snow")
255     add_object(new SnowParticleSystem());
256   else if(particlesystem == "rain")
257     add_object(new RainParticleSystem());
258
259   Vector startpos(100, 170);
260   reader.get("start_pos_x", startpos.x);
261   reader.get("start_pos_y", startpos.y);
262
263   SpawnPoint* spawn = new SpawnPoint;
264   spawn->pos = startpos;
265   spawn->name = "main";
266   spawnpoints.push_back(spawn);
267
268   music = "chipdisko.ogg";
269   reader.get("music", music);
270   music = "music/" + music;
271
272   int width = 30, height = 15;
273   reader.get("width", width);
274   reader.get("height", height);
275   
276   std::vector<unsigned int> tiles;
277   if(reader.get_vector("interactive-tm", tiles)
278       || reader.get_vector("tilemap", tiles)) {
279     TileMap* tilemap = new TileMap();
280     tilemap->set(width, height, tiles, LAYER_TILES, true);
281     add_object(tilemap);
282   }
283
284   if(reader.get_vector("background-tm", tiles)) {
285     TileMap* tilemap = new TileMap();
286     tilemap->set(width, height, tiles, LAYER_BACKGROUNDTILES, false);
287     add_object(tilemap);
288   }
289
290   if(reader.get_vector("foreground-tm", tiles)) {
291     TileMap* tilemap = new TileMap();
292     tilemap->set(width, height, tiles, LAYER_FOREGROUNDTILES, false);
293     add_object(tilemap);
294   }
295
296   // read reset-points (now spawn-points)
297   const lisp::Lisp* resetpoints = reader.get_lisp("reset-points");
298   if(resetpoints) {
299     lisp::ListIterator iter(resetpoints);
300     while(iter.next()) {
301       if(iter.item() == "point") {
302         Vector sp_pos;
303         if(reader.get("x", sp_pos.x) && reader.get("y", sp_pos.y))
304           {
305           SpawnPoint* sp = new SpawnPoint;
306           sp->name = "main";
307           sp->pos = sp_pos;
308           spawnpoints.push_back(sp);
309           }
310       } else {
311         log_warning << "Unknown token '" << iter.item() << "' in reset-points." << std::endl;
312       }
313     }
314   }
315
316   // read objects
317   const lisp::Lisp* objects = reader.get_lisp("objects");
318   if(objects) {
319     lisp::ListIterator iter(objects);
320     while(iter.next()) {
321       GameObject* object = parse_object(iter.item(), *(iter.lisp()));
322       if(object) {
323         add_object(object);
324       } else {
325         log_warning << "Unknown object '" << iter.item() << "' in level." << std::endl;
326       }
327     }
328   }
329
330   // add a camera
331   Camera* camera = new Camera(this);
332   add_object(camera);
333
334   update_game_objects();
335
336   if(solids == 0)
337     throw std::runtime_error("sector does not contain a solid tile layer.");
338
339   fix_old_tiles();
340   update_game_objects();
341 }
342
343 void
344 Sector::fix_old_tiles()
345 {
346   // hack for now...
347   for(size_t x=0; x < solids->get_width(); ++x) {
348     for(size_t y=0; y < solids->get_height(); ++y) {
349       const Tile* tile = solids->get_tile(x, y);
350       Vector pos(x*32, y*32);
351       
352       if(tile->getID() == 112) {
353         add_object(new InvisibleBlock(pos));
354         solids->change(x, y, 0);
355       } else if(tile->getAttributes() & Tile::COIN) {
356         add_object(new Coin(pos));
357         solids->change(x, y, 0);
358       } else if(tile->getAttributes() & Tile::FULLBOX) {
359         add_object(new BonusBlock(pos, tile->getData()));
360         solids->change(x, y, 0);
361       } else if(tile->getAttributes() & Tile::BRICK) {
362         add_object(new Brick(pos, tile->getData()));
363         solids->change(x, y, 0);
364       } else if(tile->getAttributes() & Tile::GOAL) {
365         std::string sequence = tile->getData() == 0 ? "endsequence" : "stoptux";
366         add_object(new SequenceTrigger(pos, sequence));
367         solids->change(x, y, 0);
368       }
369     }
370   }
371 }
372
373 void
374 Sector::write(lisp::Writer& writer)
375 {
376   writer.write_string("name", name);
377   writer.write_float("gravity", gravity);
378   writer.write_string("music", music);
379
380   // write spawnpoints
381   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
382       ++i) {
383     SpawnPoint* spawn = *i;
384     writer.start_list("spawn-points");
385     writer.write_string("name", spawn->name);
386     writer.write_float("x", spawn->pos.x);
387     writer.write_float("y", spawn->pos.y);
388     writer.end_list("spawn-points");
389   }
390
391   // write objects
392   for(GameObjects::iterator i = gameobjects.begin();
393       i != gameobjects.end(); ++i) {
394     Serializable* serializable = dynamic_cast<Serializable*> (*i);
395     if(serializable)
396       serializable->write(writer);
397   }
398 }
399
400 HSQUIRRELVM
401 Sector::run_script(std::istream& in, const std::string& sourcename)
402 {
403   using namespace Scripting;
404
405   // garbage collect thread list
406   for(ScriptList::iterator i = scripts.begin();
407       i != scripts.end(); ) {
408     HSQOBJECT& object = *i;
409     HSQUIRRELVM vm = object_to_vm(object);
410
411     if(sq_getvmstate(vm) != SQ_VMSTATE_SUSPENDED) {
412       sq_release(global_vm, &object);
413       i = scripts.erase(i);
414       continue;
415     }
416     
417     ++i;
418   }
419   
420   HSQOBJECT object = create_thread(global_vm);
421   scripts.push_back(object);
422
423   HSQUIRRELVM vm = object_to_vm(object);
424
425   // set sector_table as roottable for the thread
426   sq_pushobject(vm, sector_table);
427   sq_setroottable(vm);
428
429   compile_and_run(vm, in, sourcename);
430
431   return vm;
432 }
433
434 void
435 Sector::add_object(GameObject* object)
436 {
437   // make sure the object isn't already in the list
438 #ifdef DEBUG
439   for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end();
440       ++i) {
441     if(*i == object) {
442       assert("object already added to sector" == 0);
443     }
444   }
445   for(GameObjects::iterator i = gameobjects_new.begin();
446       i != gameobjects_new.end(); ++i) {
447     if(*i == object) {
448       assert("object already added to sector" == 0);
449     }
450   }
451 #endif
452
453   object->ref();
454   gameobjects_new.push_back(object);
455 }
456
457 void
458 Sector::activate(const std::string& spawnpoint)
459 {
460   SpawnPoint* sp = 0;
461   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
462       ++i) {
463     if((*i)->name == spawnpoint) {
464       sp = *i;
465       break;
466     }
467   }                                                                           
468   if(!sp) {
469     log_warning << "Spawnpoint '" << spawnpoint << "' not found." << std::endl;
470     if(spawnpoint != "main") {
471       activate("main");
472     } else {
473       activate(Vector(0, 0));
474     }
475   } else {
476     activate(sp->pos);
477   }
478 }
479
480 void
481 Sector::activate(const Vector& player_pos)
482 {
483   if(_current != this) {
484     if(_current != NULL)
485       _current->deactivate();
486     _current = this;
487
488     // register sectortable as sector in scripting
489     HSQUIRRELVM vm = Scripting::global_vm;
490     sq_pushroottable(vm);
491     sq_pushstring(vm, "sector", -1);
492     sq_pushobject(vm, sector_table);
493     if(SQ_FAILED(sq_createslot(vm, -3)))
494       throw Scripting::SquirrelError(vm, "Couldn't set sector in roottable");
495     sq_pop(vm, 1);
496
497     for(GameObjects::iterator i = gameobjects.begin();
498         i != gameobjects.end(); ++i) {
499       GameObject* object = *i;
500
501       try_expose(object);
502     }
503   }
504
505   player->move(player_pos);
506   camera->reset(player->get_pos());
507   update_game_objects();
508
509   // Run init script
510   if(init_script != "") {
511     std::istringstream in(init_script);
512     run_script(in, std::string("Sector(") + name + ") - init");
513   }
514 }
515
516 void
517 Sector::deactivate()
518 {
519   if(_current != this)
520     return;
521
522   // remove sector entry from global vm
523   HSQUIRRELVM vm = Scripting::global_vm;
524   sq_pushroottable(vm);
525   sq_pushstring(vm, "sector", -1);
526   if(SQ_FAILED(sq_deleteslot(vm, -2, SQFalse)))
527     throw Scripting::SquirrelError(vm, "Couldn't unset sector in roottable");
528   sq_pop(vm, 1);
529   
530   for(GameObjects::iterator i = gameobjects.begin();
531       i != gameobjects.end(); ++i) {
532     GameObject* object = *i;
533     
534     try_unexpose(object);
535   }
536
537   _current = NULL;
538 }
539
540 Rect
541 Sector::get_active_region()
542 {
543   return Rect(
544     camera->get_translation() - Vector(1600, 1200),
545     camera->get_translation() + Vector(1600, 1200));
546 }
547
548 void
549 Sector::update(float elapsed_time)
550 {
551   player->check_bounds(camera);
552
553   /* update objects */
554   for(GameObjects::iterator i = gameobjects.begin();
555           i != gameobjects.end(); ++i) {
556     GameObject* object = *i;
557     if(!object->is_valid())
558       continue;
559     
560     object->update(elapsed_time);
561   }
562   
563   /* Handle all possible collisions. */
564   handle_collisions();
565   update_game_objects();
566 }
567
568 void
569 Sector::update_game_objects()
570 {
571   /** cleanup marked objects */
572   for(std::vector<Bullet*>::iterator i = bullets.begin();
573       i != bullets.end(); /* nothing */) {
574     Bullet* bullet = *i;
575     if(bullet->is_valid()) {
576       ++i;
577       continue;
578     }
579
580     i = bullets.erase(i);
581   }
582   for(MovingObjects::iterator i = moving_objects.begin();
583       i != moving_objects.end(); /* nothing */) {
584     MovingObject* moving_object = *i;
585     if(moving_object->is_valid()) {
586       ++i;
587       continue;
588     }
589
590     i = moving_objects.erase(i);
591   }
592   for(std::vector<GameObject*>::iterator i = gameobjects.begin();
593       i != gameobjects.end(); /* nothing */) {
594     GameObject* object = *i;
595     
596     if(object->is_valid()) {
597       ++i;
598       continue;
599     }
600
601     before_object_remove(object);
602     
603     object->unref();
604     i = gameobjects.erase(i);
605   }
606
607   /* add newly created objects */
608   for(std::vector<GameObject*>::iterator i = gameobjects_new.begin();
609       i != gameobjects_new.end(); ++i)
610   {
611     GameObject* object = *i;
612
613     before_object_add(object);
614     
615     gameobjects.push_back(object);
616   }
617   gameobjects_new.clear();
618 }
619
620 bool
621 Sector::before_object_add(GameObject* object)
622 {
623   Bullet* bullet = dynamic_cast<Bullet*> (object);
624   if(bullet)
625     bullets.push_back(bullet);
626
627   MovingObject* movingobject = dynamic_cast<MovingObject*> (object);
628   if(movingobject) {
629     moving_objects.push_back(movingobject);
630   }
631   
632   TileMap* tilemap = dynamic_cast<TileMap*> (object);
633   if(tilemap && tilemap->is_solid()) {
634     if(solids == 0) {
635       solids = tilemap;
636     } else {
637       log_warning << "Another solid tilemaps added. Ignoring" << std::endl;
638     }
639   }
640
641   Camera* camera = dynamic_cast<Camera*> (object);
642   if(camera) {
643     if(this->camera != 0) {
644       log_warning << "Multiple cameras added. Ignoring" << std::endl;
645       return false;
646     }
647     this->camera = camera;
648   }
649
650   Player* player = dynamic_cast<Player*> (object);
651   if(player) {
652     if(this->player != 0) {
653       log_warning << "Multiple players added. Ignoring" << std::endl;
654       return false;
655     }
656     this->player = player;
657   }
658
659   if(_current == this) {
660     try_expose(object);
661   }
662   
663   return true;
664 }
665
666 void
667 Sector::try_expose(GameObject* object)
668 {
669   ScriptInterface* interface = dynamic_cast<ScriptInterface*> (object);
670   if(interface != NULL) {
671     HSQUIRRELVM vm = Scripting::global_vm;
672     sq_pushobject(vm, sector_table);
673     interface->expose(vm, -1);
674     sq_pop(vm, 1);
675   }
676 }
677
678 void
679 Sector::before_object_remove(GameObject* object)
680 {
681   if(_current == this)
682     try_unexpose(object);
683 }
684
685 void
686 Sector::try_unexpose(GameObject* object)
687 {
688   ScriptInterface* interface = dynamic_cast<ScriptInterface*> (object);
689   if(interface != NULL) {
690     HSQUIRRELVM vm = Scripting::global_vm;
691     SQInteger oldtop = sq_gettop(vm);
692     sq_pushobject(vm, sector_table);
693     try {
694       interface->unexpose(vm, -1);
695     } catch(std::exception& e) {
696       log_warning << "Couldn't unregister object: " << e.what() << std::endl;
697     }
698     sq_settop(vm, oldtop);
699   }
700
701
702 void
703 Sector::draw(DrawingContext& context)
704 {
705   context.push_transform();
706   context.set_translation(camera->get_translation());
707
708   for(GameObjects::iterator i = gameobjects.begin();
709       i != gameobjects.end(); ++i) {
710     GameObject* object = *i; 
711     if(!object->is_valid())
712       continue;
713
714     if (draw_solids_only)
715     {
716       TileMap* tm = dynamic_cast<TileMap*>(object);
717       if (tm && !tm->is_solid())
718         continue;
719     }
720
721     object->draw(context);
722   }
723
724   if(show_collrects) {
725     Color col(0.2, 0.2, 0.2, 0.7);
726     for(MovingObjects::iterator i = moving_objects.begin();
727             i != moving_objects.end(); ++i) {
728       MovingObject* object = *i;
729       const Rect& rect = object->get_bbox();
730
731       context.draw_filled_rect(rect, col, LAYER_FOREGROUND1 + 10);
732     }
733   }
734
735   context.pop_transform();
736 }
737
738 /*-------------------------------------------------------------------------
739  * Collision Detection 
740  *-------------------------------------------------------------------------*/
741
742 static const float SHIFT_DELTA = 7.0f;
743
744 /** r1 is supposed to be moving, r2 a solid object */
745 void check_collisions(collision::Constraints* constraints,
746                       const Vector& movement, const Rect& r1, const Rect& r2,
747                       GameObject* object = NULL, MovingObject* other = NULL)
748 {
749   if(!collision::intersects(r1, r2))
750     return;
751
752   // calculate intersection
753   float itop = r1.get_bottom() - r2.get_top();
754   float ibottom = r2.get_bottom() - r1.get_top();
755   float ileft = r1.get_right() - r2.get_left();
756   float iright = r2.get_right() - r1.get_left();
757
758   if(fabsf(movement.y) > fabsf(movement.x)) {
759     if(ileft < SHIFT_DELTA) {
760       constraints->right = std::min(constraints->right, r2.get_left());
761       return;
762     } else if(iright < SHIFT_DELTA) {
763       constraints->left = std::max(constraints->left, r2.get_right());
764       return;
765     }
766   } else {
767     // shiftout bottom/top
768     if(itop < SHIFT_DELTA) {
769       constraints->bottom = std::min(constraints->bottom, r2.get_top());
770       return;
771     } else if(ibottom < SHIFT_DELTA) {
772       constraints->top = std::max(constraints->top, r2.get_bottom());
773       return;
774     }
775   }
776
777   if(other != NULL) {
778     CollisionHit dummy;
779     HitResponse response = other->collision(*object, dummy);
780     if(response == PASSTHROUGH)
781       return;
782     if(other->get_movement() != Vector(0, 0)) {
783       // TODO what todo when we collide with 2 moving objects?!?
784       constraints->ground_movement = other->get_movement();
785     }
786   }
787
788   float vert_penetration = std::min(itop, ibottom);
789   float horiz_penetration = std::min(ileft, iright);
790   if(vert_penetration < horiz_penetration) {
791     if(itop < ibottom) {
792       constraints->bottom = std::min(constraints->bottom, r2.get_top());
793       constraints->hit.bottom = true;
794     } else {
795       constraints->top = std::max(constraints->top, r2.get_bottom());
796       constraints->hit.top = true;
797     }
798   } else {
799     if(ileft < iright) {
800       constraints->right = std::min(constraints->right, r2.get_left());
801       constraints->hit.right = true;
802     } else {
803       constraints->left = std::max(constraints->left, r2.get_right());
804       constraints->hit.left = true;
805     }
806   }
807 }
808
809 static const float DELTA = .001;
810
811 void
812 Sector::collision_tilemap(collision::Constraints* constraints,
813                           const Vector& movement, const Rect& dest) const
814 {
815   // calculate rectangle where the object will move
816   float x1 = dest.get_left();
817   float x2 = dest.get_right();
818   float y1 = dest.get_top();
819   float y2 = dest.get_bottom();
820
821   // test with all tiles in this rectangle
822   int starttilex = int(x1) / 32;
823   int starttiley = int(y1) / 32;
824   int max_x = int(x2);
825   int max_y = int(y2+1);
826
827   for(int x = starttilex; x*32 < max_x; ++x) {
828     for(int y = starttiley; y*32 < max_y; ++y) {
829       const Tile* tile = solids->get_tile(x, y);
830       if(!tile)
831         continue;
832       // skip non-solid tiles
833       if((tile->getAttributes() & Tile::SOLID) == 0)
834         continue;
835       // only handle unisolid when the player is falling down and when he was
836       // above the tile before
837       if(tile->getAttributes() & Tile::UNISOLID) {
838         if(movement.y <= 0 || dest.get_bottom() - movement.y - SHIFT_DELTA > y*32)
839           continue;
840       }
841
842       if(tile->getAttributes() & Tile::SLOPE) { // slope tile
843         AATriangle triangle;
844         Vector p1(x*32, y*32);
845         Vector p2((x+1)*32, (y+1)*32);
846         triangle = AATriangle(p1, p2, tile->getData());
847
848         collision::rectangle_aatriangle(constraints, dest, triangle);
849       } else { // normal rectangular tile
850         Rect rect(x*32, y*32, (x+1)*32, (y+1)*32);
851         check_collisions(constraints, movement, dest, rect);
852       }
853     }
854   }
855 }
856
857 uint32_t
858 Sector::collision_tile_attributes(const Rect& dest) const
859 {
860   float x1 = dest.p1.x;
861   float y1 = dest.p1.y;
862   float x2 = dest.p2.x;
863   float y2 = dest.p2.y;
864
865   // test with all tiles in this rectangle
866   int starttilex = int(x1) / 32;
867   int starttiley = int(y1) / 32;
868   int max_x = int(x2);
869   int max_y = int(y2);
870
871   uint32_t result = 0;
872   for(int x = starttilex; x*32 < max_x; ++x) {
873     for(int y = starttiley; y*32 < max_y; ++y) {
874       const Tile* tile = solids->get_tile(x, y);
875       if(!tile)
876         continue;
877       result |= tile->getAttributes();
878     }
879   }
880
881   return result;
882 }
883
884 /** fills in CollisionHit and Normal vector of 2 intersecting rectangle */
885 static void get_hit_normal(const Rect& r1, const Rect& r2, CollisionHit& hit,
886                            Vector& normal)
887 {
888   float itop = r1.get_bottom() - r2.get_top();
889   float ibottom = r2.get_bottom() - r1.get_top();
890   float ileft = r1.get_right() - r2.get_left();
891   float iright = r2.get_right() - r1.get_left();
892
893   float vert_penetration = std::min(itop, ibottom);
894   float horiz_penetration = std::min(ileft, iright);
895   if(vert_penetration < horiz_penetration) {
896     if(itop < ibottom) {
897       hit.bottom = true;
898       normal.y = vert_penetration;
899     } else {
900       hit.top = true;
901       normal.y = -vert_penetration;
902     }
903   } else {
904     if(ileft < iright) {
905       hit.right = true;
906       normal.x = horiz_penetration;
907     } else {
908       hit.left = true;
909       normal.x = -horiz_penetration;
910     }
911   }
912 }
913
914 void
915 Sector::collision_object(MovingObject* object1, MovingObject* object2) const
916 {
917   using namespace collision;
918   
919   const Rect& r1 = object1->dest;
920   const Rect& r2 = object2->dest;
921
922   CollisionHit hit;
923   if(intersects(object1->dest, object2->dest)) {
924     Vector normal;
925     get_hit_normal(r1, r2, hit, normal);
926
927     HitResponse response1 = object1->collision(*object2, hit);
928     std::swap(hit.left, hit.right);
929     std::swap(hit.top, hit.bottom);
930     HitResponse response2 = object2->collision(*object1, hit);
931     if(response1 == CONTINUE || response2 == CONTINUE) {
932       normal *= (0.5 + DELTA);
933       object1->dest.move(-normal);
934       object2->dest.move(normal);
935     }
936   }
937 }
938
939 void
940 Sector::collision_static(collision::Constraints* constraints,
941                          const Vector& movement, const Rect& dest,
942                          GameObject& object)
943 {
944   collision_tilemap(constraints, movement, dest);
945
946   // collision with other (static) objects
947   for(MovingObjects::iterator i = moving_objects.begin();
948       i != moving_objects.end(); ++i) {
949     MovingObject* moving_object = *i;
950     if(moving_object->get_group() != COLGROUP_STATIC
951         || !moving_object->is_valid())
952       continue;
953   
954     check_collisions(constraints, movement, dest, moving_object->dest,
955         &object, moving_object);
956   }
957 }
958
959 void
960 Sector::handle_collisions()
961 {
962   using namespace collision;
963   
964   // calculate destination positions of the objects
965   for(MovingObjects::iterator i = moving_objects.begin();
966       i != moving_objects.end(); ++i) {
967     MovingObject* moving_object = *i;
968
969     moving_object->dest = moving_object->get_bbox();
970     moving_object->dest.move(moving_object->get_movement());
971   }
972     
973   // part1: COLGROUP_MOVING vs COLGROUP_STATIC and tilemap
974   for(MovingObjects::iterator i = moving_objects.begin();
975       i != moving_objects.end(); ++i) {
976     MovingObject* moving_object = *i;
977     if((moving_object->get_group() != COLGROUP_MOVING
978           && moving_object->get_group() != COLGROUP_MOVING_ONLY_STATIC)
979         || !moving_object->is_valid())
980       continue;
981
982     Constraints constraints;
983     Vector movement = moving_object->get_movement();
984     Rect& dest = moving_object->dest;
985     float owidth = moving_object->get_bbox().get_width();
986     float oheight = moving_object->get_bbox().get_height();
987
988     for(int i = 0; i < 2; ++i) {
989       collision_static(&constraints, Vector(0, movement.y), dest, *moving_object);
990       if(!constraints.has_constraints())
991         break;
992
993       // apply calculated horizontal constraints
994       if(constraints.bottom < INFINITY) {
995         float height = constraints.bottom - constraints.top;
996         if(height < oheight) {
997           // we're crushed, but ignore this for now, we'll get this again
998           // later if we're really crushed or things will solve itself when
999           // looking at the vertical constraints
1000         }
1001         dest.p2.y = constraints.bottom - DELTA;
1002         dest.p1.y = dest.p2.y - oheight;
1003       } else if(constraints.top > -INFINITY) {
1004         dest.p1.y = constraints.top + DELTA;
1005         dest.p2.y = dest.p1.y + oheight;
1006       }
1007     }
1008     if(constraints.has_constraints()) {
1009       if(constraints.hit.bottom) {
1010         dest.move(constraints.ground_movement);
1011       }
1012       if(constraints.hit.top || constraints.hit.bottom) {
1013         constraints.hit.left = false;
1014         constraints.hit.right = false;
1015           moving_object->collision_solid(constraints.hit);
1016       }
1017     }
1018
1019     constraints = Constraints();
1020     for(int i = 0; i < 2; ++i) {
1021       collision_static(&constraints, movement, dest, *moving_object);
1022       if(!constraints.has_constraints())
1023         break;
1024
1025       // apply calculated vertical constraints
1026       if(constraints.right < INFINITY) {
1027         float width = constraints.right - constraints.left;
1028         if(width + SHIFT_DELTA < owidth) {
1029           printf("Object %p crushed horizontally... L:%f R:%f\n", moving_object,
1030               constraints.left, constraints.right);
1031           CollisionHit h;
1032           h.left = true;
1033           h.right = true;
1034           h.crush = true;
1035           moving_object->collision_solid(h);
1036         } else {
1037           dest.p2.x = constraints.right - DELTA;
1038           dest.p1.x = dest.p2.x - owidth;
1039         }
1040       } else if(constraints.left > -INFINITY) {
1041         dest.p1.x = constraints.left + DELTA;
1042         dest.p2.x = dest.p1.x + owidth;
1043       }
1044     }
1045    
1046     if(constraints.has_constraints()) {
1047       if( constraints.hit.left || constraints.hit.right 
1048           || constraints.hit.top || constraints.hit.bottom 
1049           || constraints.hit.crush )
1050         moving_object->collision_solid(constraints.hit);
1051       //else printf("Wayne?\n");
1052     }    
1053     
1054     // an extra pass to make sure we're not crushed horizontally
1055     constraints = Constraints();
1056     collision_static(&constraints, movement, dest, *moving_object);
1057     if(constraints.bottom < INFINITY) {
1058       float height = constraints.bottom - constraints.top;
1059       if(height + SHIFT_DELTA < oheight) {
1060         printf("Object %p crushed vertically...\n", moving_object);
1061         CollisionHit h;
1062         h.top = true;
1063         h.bottom = true;
1064         h.crush = true;
1065         moving_object->collision_solid(h); 
1066       }
1067     }
1068   }
1069
1070   // part2: COLGROUP_MOVING vs tile attributes
1071   for(MovingObjects::iterator i = moving_objects.begin();
1072       i != moving_objects.end(); ++i) {
1073     MovingObject* moving_object = *i;
1074     if((moving_object->get_group() != COLGROUP_MOVING
1075           && moving_object->get_group() != COLGROUP_MOVING_ONLY_STATIC)
1076         || !moving_object->is_valid())
1077       continue;
1078
1079     uint32_t tile_attributes = collision_tile_attributes(moving_object->dest);
1080     if(tile_attributes > Tile::FIRST_INTERESTING_FLAG) {
1081       moving_object->collision_tile(tile_attributes);
1082     }
1083   }
1084
1085   // part2.5: COLGROUP_MOVING vs COLGROUP_TOUCHABLE
1086   for(MovingObjects::iterator i = moving_objects.begin();
1087       i != moving_objects.end(); ++i) {
1088     MovingObject* moving_object = *i;
1089     if(moving_object->get_group() != COLGROUP_MOVING
1090         || !moving_object->is_valid())
1091       continue;
1092
1093     for(MovingObjects::iterator i2 = moving_objects.begin();
1094         i2 != moving_objects.end(); ++i2) {
1095       MovingObject* moving_object_2 = *i2;
1096       if(moving_object_2->get_group() != COLGROUP_TOUCHABLE
1097          || !moving_object_2->is_valid())
1098         continue;
1099
1100       if(intersects(moving_object->dest, moving_object_2->dest)) {
1101         Vector normal;
1102         CollisionHit hit;
1103         get_hit_normal(moving_object->dest, moving_object_2->dest,
1104                        hit, normal);
1105         moving_object->collision(*moving_object_2, hit);
1106         moving_object_2->collision(*moving_object, hit);
1107       }
1108     } 
1109   }
1110
1111   // part3: COLGROUP_MOVING vs COLGROUP_MOVING
1112   for(MovingObjects::iterator i = moving_objects.begin();
1113       i != moving_objects.end(); ++i) {
1114     MovingObject* moving_object = *i;
1115
1116     if(moving_object->get_group() != COLGROUP_MOVING
1117         || !moving_object->is_valid())
1118       continue;
1119
1120     for(MovingObjects::iterator i2 = i+1;
1121         i2 != moving_objects.end(); ++i2) {
1122       MovingObject* moving_object_2 = *i2;
1123       if(moving_object_2->get_group() != COLGROUP_MOVING
1124          || !moving_object_2->is_valid())
1125         continue;
1126
1127       collision_object(moving_object, moving_object_2);
1128     }    
1129   }
1130
1131   // apply object movement
1132   for(MovingObjects::iterator i = moving_objects.begin();
1133       i != moving_objects.end(); ++i) {
1134     MovingObject* moving_object = *i;
1135
1136     moving_object->bbox = moving_object->dest;
1137     moving_object->movement = Vector(0, 0);
1138   }
1139 }
1140
1141 bool
1142 Sector::is_free_space(const Rect& rect) const
1143 {
1144   using namespace collision;
1145   
1146   // test with all tiles in this rectangle
1147   int starttilex = int(rect.p1.x) / 32;
1148   int starttiley = int(rect.p1.y) / 32;
1149   int max_x = int(rect.p2.x);
1150   int max_y = int(rect.p2.y);
1151
1152   for(int x = starttilex; x*32 <= max_x; ++x) {
1153     for(int y = starttiley; y*32 <= max_y; ++y) {
1154       const Tile* tile = solids->get_tile(x, y);
1155       if(!tile)
1156         continue;
1157       if(tile->getAttributes() & Tile::SOLID)
1158         return false;
1159     }
1160   }
1161
1162   for(MovingObjects::const_iterator i = moving_objects.begin();
1163       i != moving_objects.end(); ++i) {
1164     const MovingObject* moving_object = *i;
1165     if(moving_object->get_group() != COLGROUP_STATIC
1166         || !moving_object->is_valid())
1167       continue;
1168
1169     if(intersects(rect, moving_object->get_bbox()))
1170       return false;
1171   }
1172
1173   return true;
1174 }
1175
1176 bool
1177 Sector::add_bullet(const Vector& pos, float xm, Direction dir)
1178 {
1179   // TODO remove this function and move these checks elsewhere...
1180
1181   Bullet* new_bullet = 0;
1182   if((int)bullets.size() >= player_status->max_fire_bullets)
1183     return false;
1184   new_bullet = new Bullet(pos, xm, dir);
1185   add_object(new_bullet);
1186
1187   sound_manager->play("sounds/shoot.wav");
1188
1189   return true;
1190 }
1191
1192 bool
1193 Sector::add_smoke_cloud(const Vector& pos)
1194 {
1195   add_object(new SmokeCloud(pos));
1196   return true;
1197 }
1198
1199 void
1200 Sector::play_music(MusicType type)
1201 {
1202   currentmusic = type;
1203   switch(currentmusic) {
1204     case LEVEL_MUSIC:
1205       sound_manager->play_music(music);
1206       break;
1207     case HERRING_MUSIC:
1208       sound_manager->play_music("music/salcon.ogg");
1209       break;
1210     case HERRING_WARNING_MUSIC:
1211       sound_manager->stop_music(TUX_INVINCIBLE_TIME_WARNING);
1212       break;
1213     default:
1214       sound_manager->play_music("");
1215       break;
1216   }
1217 }
1218
1219 MusicType
1220 Sector::get_music_type()
1221 {
1222   return currentmusic;
1223 }
1224
1225 int
1226 Sector::get_total_badguys()
1227 {
1228   int total_badguys = 0;
1229   for(GameObjects::iterator i = gameobjects.begin();
1230       i != gameobjects.end(); ++i) {
1231     BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
1232     if (badguy && badguy->countMe)
1233       total_badguys++;
1234   }
1235
1236   return total_badguys;
1237 }
1238
1239 bool
1240 Sector::inside(const Rect& rect) const
1241 {
1242   if(rect.p1.x > solids->get_width() * 32 
1243       || rect.p1.y > solids->get_height() * 32
1244       || rect.p2.x < 0)
1245     return false;
1246
1247   return true;
1248 }