Don't report collisions if nothing got hit
[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     HitResponse response2 = object2->collision(*object1, hit);
929     if(response1 == CONTINUE || response2 == CONTINUE) {
930       normal *= (0.5 + DELTA);
931       object1->dest.move(-normal);
932       object2->dest.move(normal);
933     }
934   }
935 }
936
937 void
938 Sector::collision_static(collision::Constraints* constraints,
939                          const Vector& movement, const Rect& dest,
940                          GameObject& object)
941 {
942   collision_tilemap(constraints, movement, dest);
943
944   // collision with other (static) objects
945   for(MovingObjects::iterator i = moving_objects.begin();
946       i != moving_objects.end(); ++i) {
947     MovingObject* moving_object = *i;
948     if(moving_object->get_group() != COLGROUP_STATIC
949         || !moving_object->is_valid())
950       continue;
951   
952     check_collisions(constraints, movement, dest, moving_object->dest,
953         &object, moving_object);
954   }
955 }
956
957 void
958 Sector::handle_collisions()
959 {
960   using namespace collision;
961   
962   // calculate destination positions of the objects
963   for(MovingObjects::iterator i = moving_objects.begin();
964       i != moving_objects.end(); ++i) {
965     MovingObject* moving_object = *i;
966
967     moving_object->dest = moving_object->get_bbox();
968     moving_object->dest.move(moving_object->get_movement());
969   }
970     
971   // part1: COLGROUP_MOVING vs COLGROUP_STATIC and tilemap
972   for(MovingObjects::iterator i = moving_objects.begin();
973       i != moving_objects.end(); ++i) {
974     MovingObject* moving_object = *i;
975     if((moving_object->get_group() != COLGROUP_MOVING
976           && moving_object->get_group() != COLGROUP_MOVING_ONLY_STATIC)
977         || !moving_object->is_valid())
978       continue;
979
980     Constraints constraints;
981     Vector movement = moving_object->get_movement();
982     Rect& dest = moving_object->dest;
983     float owidth = moving_object->get_bbox().get_width();
984     float oheight = moving_object->get_bbox().get_height();
985
986     for(int i = 0; i < 2; ++i) {
987       collision_static(&constraints, Vector(0, movement.y), dest, *moving_object);
988       if(!constraints.has_constraints())
989         break;
990
991       // apply calculated horizontal constraints
992       if(constraints.bottom < INFINITY) {
993         float height = constraints.bottom - constraints.top;
994         if(height < oheight) {
995           // we're crushed, but ignore this for now, we'll get this again
996           // later if we're really crushed or things will solve itself when
997           // looking at the vertical constraints
998         }
999         dest.p2.y = constraints.bottom - DELTA;
1000         dest.p1.y = dest.p2.y - oheight;
1001       } else if(constraints.top > -INFINITY) {
1002         dest.p1.y = constraints.top + DELTA;
1003         dest.p2.y = dest.p1.y + oheight;
1004       }
1005     }
1006     if(constraints.has_constraints()) {
1007       if(constraints.hit.bottom) {
1008         dest.move(constraints.ground_movement);
1009       }
1010       if(constraints.hit.top || constraints.hit.bottom) {
1011         constraints.hit.left = false;
1012         constraints.hit.right = false;
1013           moving_object->collision_solid(constraints.hit);
1014       }
1015     }
1016
1017     constraints = Constraints();
1018     for(int i = 0; i < 2; ++i) {
1019       collision_static(&constraints, movement, dest, *moving_object);
1020       if(!constraints.has_constraints())
1021         break;
1022
1023       // apply calculated vertical constraints
1024       if(constraints.right < INFINITY) {
1025         float width = constraints.right - constraints.left;
1026         if(width + SHIFT_DELTA < owidth) {
1027           printf("Object %p crushed horizontally... L:%f R:%f\n", moving_object,
1028               constraints.left, constraints.right);
1029           CollisionHit h;
1030           h.left = true;
1031           h.right = true;
1032           h.crush = true;
1033           moving_object->collision_solid(h);
1034         } else {
1035           dest.p2.x = constraints.right - DELTA;
1036           dest.p1.x = dest.p2.x - owidth;
1037         }
1038       } else if(constraints.left > -INFINITY) {
1039         dest.p1.x = constraints.left + DELTA;
1040         dest.p2.x = dest.p1.x + owidth;
1041       }
1042     }
1043    
1044     if(constraints.has_constraints()) {
1045       if( constraints.hit.left || constraints.hit.right 
1046           || constraints.hit.top || constraints.hit.bottom 
1047           || constraints.hit.crush )
1048         moving_object->collision_solid(constraints.hit);
1049       //else printf("Wayne?\n");
1050     }    
1051     
1052     // an extra pass to make sure we're not crushed horizontally
1053     constraints = Constraints();
1054     collision_static(&constraints, movement, dest, *moving_object);
1055     if(constraints.bottom < INFINITY) {
1056       float height = constraints.bottom - constraints.top;
1057       if(height + SHIFT_DELTA < oheight) {
1058         printf("Object %p crushed vertically...\n", moving_object);
1059         CollisionHit h;
1060         h.top = true;
1061         h.bottom = true;
1062         h.crush = true;
1063         moving_object->collision_solid(h); 
1064       }
1065     }
1066   }
1067
1068   // part2: COLGROUP_MOVING vs tile attributes
1069   for(MovingObjects::iterator i = moving_objects.begin();
1070       i != moving_objects.end(); ++i) {
1071     MovingObject* moving_object = *i;
1072     if((moving_object->get_group() != COLGROUP_MOVING
1073           && moving_object->get_group() != COLGROUP_MOVING_ONLY_STATIC)
1074         || !moving_object->is_valid())
1075       continue;
1076
1077     uint32_t tile_attributes = collision_tile_attributes(moving_object->dest);
1078     if(tile_attributes > Tile::FIRST_INTERESTING_FLAG) {
1079       moving_object->collision_tile(tile_attributes);
1080     }
1081   }
1082
1083   // part2.5: COLGROUP_MOVING vs COLGROUP_TOUCHABLE
1084   for(MovingObjects::iterator i = moving_objects.begin();
1085       i != moving_objects.end(); ++i) {
1086     MovingObject* moving_object = *i;
1087     if(moving_object->get_group() != COLGROUP_MOVING
1088         || !moving_object->is_valid())
1089       continue;
1090
1091     for(MovingObjects::iterator i2 = moving_objects.begin();
1092         i2 != moving_objects.end(); ++i2) {
1093       MovingObject* moving_object_2 = *i2;
1094       if(moving_object_2->get_group() != COLGROUP_TOUCHABLE
1095          || !moving_object_2->is_valid())
1096         continue;
1097
1098       if(intersects(moving_object->dest, moving_object_2->dest)) {
1099         Vector normal;
1100         CollisionHit hit;
1101         get_hit_normal(moving_object->dest, moving_object_2->dest,
1102                        hit, normal);
1103         moving_object->collision(*moving_object_2, hit);
1104         moving_object_2->collision(*moving_object, hit);
1105       }
1106     } 
1107   }
1108
1109   // part3: COLGROUP_MOVING vs COLGROUP_MOVING
1110   for(MovingObjects::iterator i = moving_objects.begin();
1111       i != moving_objects.end(); ++i) {
1112     MovingObject* moving_object = *i;
1113
1114     if(moving_object->get_group() != COLGROUP_MOVING
1115         || !moving_object->is_valid())
1116       continue;
1117
1118     for(MovingObjects::iterator i2 = i+1;
1119         i2 != moving_objects.end(); ++i2) {
1120       MovingObject* moving_object_2 = *i2;
1121       if(moving_object_2->get_group() != COLGROUP_MOVING
1122          || !moving_object_2->is_valid())
1123         continue;
1124
1125       collision_object(moving_object, moving_object_2);
1126     }    
1127   }
1128
1129   // apply object movement
1130   for(MovingObjects::iterator i = moving_objects.begin();
1131       i != moving_objects.end(); ++i) {
1132     MovingObject* moving_object = *i;
1133
1134     moving_object->bbox = moving_object->dest;
1135     moving_object->movement = Vector(0, 0);
1136   }
1137 }
1138
1139 bool
1140 Sector::is_free_space(const Rect& rect) const
1141 {
1142   using namespace collision;
1143   
1144   // test with all tiles in this rectangle
1145   int starttilex = int(rect.p1.x) / 32;
1146   int starttiley = int(rect.p1.y) / 32;
1147   int max_x = int(rect.p2.x);
1148   int max_y = int(rect.p2.y);
1149
1150   for(int x = starttilex; x*32 <= max_x; ++x) {
1151     for(int y = starttiley; y*32 <= max_y; ++y) {
1152       const Tile* tile = solids->get_tile(x, y);
1153       if(!tile)
1154         continue;
1155       if(tile->getAttributes() & Tile::SOLID)
1156         return false;
1157     }
1158   }
1159
1160   for(MovingObjects::const_iterator i = moving_objects.begin();
1161       i != moving_objects.end(); ++i) {
1162     const MovingObject* moving_object = *i;
1163     if(moving_object->get_group() != COLGROUP_STATIC
1164         || !moving_object->is_valid())
1165       continue;
1166
1167     if(intersects(rect, moving_object->get_bbox()))
1168       return false;
1169   }
1170
1171   return true;
1172 }
1173
1174 bool
1175 Sector::add_bullet(const Vector& pos, float xm, Direction dir)
1176 {
1177   // TODO remove this function and move these checks elsewhere...
1178
1179   Bullet* new_bullet = 0;
1180   if((int)bullets.size() >= player_status->max_fire_bullets)
1181     return false;
1182   new_bullet = new Bullet(pos, xm, dir);
1183   add_object(new_bullet);
1184
1185   sound_manager->play("sounds/shoot.wav");
1186
1187   return true;
1188 }
1189
1190 bool
1191 Sector::add_smoke_cloud(const Vector& pos)
1192 {
1193   add_object(new SmokeCloud(pos));
1194   return true;
1195 }
1196
1197 void
1198 Sector::play_music(MusicType type)
1199 {
1200   currentmusic = type;
1201   switch(currentmusic) {
1202     case LEVEL_MUSIC:
1203       sound_manager->play_music(music);
1204       break;
1205     case HERRING_MUSIC:
1206       sound_manager->play_music("music/salcon.ogg");
1207       break;
1208     case HERRING_WARNING_MUSIC:
1209       sound_manager->stop_music(TUX_INVINCIBLE_TIME_WARNING);
1210       break;
1211     default:
1212       sound_manager->play_music("");
1213       break;
1214   }
1215 }
1216
1217 MusicType
1218 Sector::get_music_type()
1219 {
1220   return currentmusic;
1221 }
1222
1223 int
1224 Sector::get_total_badguys()
1225 {
1226   int total_badguys = 0;
1227   for(GameObjects::iterator i = gameobjects.begin();
1228       i != gameobjects.end(); ++i) {
1229     BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
1230     if (badguy && badguy->countMe)
1231       total_badguys++;
1232   }
1233
1234   return total_badguys;
1235 }
1236
1237 bool
1238 Sector::inside(const Rect& rect) const
1239 {
1240   if(rect.p1.x > solids->get_width() * 32 
1241       || rect.p1.y > solids->get_height() * 32
1242       || rect.p2.x < 0)
1243     return false;
1244
1245   return true;
1246 }