3 // SuperTux - A Jump'n Run
4 // Copyright (C) 2006 Matthias Braun <matze@braunis.de>
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.
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.
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.
31 #include "player_status.hpp"
32 #include "object/gameobjs.hpp"
33 #include "object/camera.hpp"
34 #include "object/background.hpp"
35 #include "object/gradient.hpp"
36 #include "object/particlesystem.hpp"
37 #include "object/particlesystem_interactive.hpp"
38 #include "object/tilemap.hpp"
39 #include "lisp/parser.hpp"
40 #include "lisp/lisp.hpp"
41 #include "lisp/writer.hpp"
42 #include "lisp/list_iterator.hpp"
44 #include "audio/sound_manager.hpp"
45 #include "game_session.hpp"
46 #include "resources.hpp"
47 #include "statistics.hpp"
48 #include "collision_grid.hpp"
49 #include "collision_grid_iterator.hpp"
50 #include "object_factory.hpp"
51 #include "collision.hpp"
52 #include "spawn_point.hpp"
53 #include "math/rect.hpp"
54 #include "math/aatriangle.hpp"
55 #include "object/coin.hpp"
56 #include "object/block.hpp"
57 #include "object/invisible_block.hpp"
58 #include "object/bullet.hpp"
59 #include "object/text_object.hpp"
60 #include "badguy/jumpy.hpp"
61 #include "trigger/sequence_trigger.hpp"
62 #include "player_status.hpp"
63 #include "script_manager.hpp"
64 #include "scripting/wrapper_util.hpp"
65 #include "script_interface.hpp"
68 Sector* Sector::_current = 0;
70 bool Sector::show_collrects = false;
73 : currentmusic(LEVEL_MUSIC), gravity(10),
74 player(0), solids(0), camera(0)
76 add_object(new Player(player_status));
77 add_object(new DisplayEffect());
78 add_object(new TextObject());
81 grid.reset(new CollisionGrid(32000, 32000));
84 script_manager.reset(new ScriptManager(ScriptManager::instance));
86 // create a new squirrel table for the sector
87 HSQUIRRELVM vm = ScriptManager::instance->get_vm();
91 if(SQ_FAILED(sq_setdelegate(vm, -2)))
92 throw Scripting::SquirrelError(vm, "Couldn't set sector_table delegate");
94 sq_resetobject(§or_table);
95 if(SQ_FAILED(sq_getstackobj(vm, -1, §or_table)))
96 throw Scripting::SquirrelError(vm, "Couldn't get sector table");
97 sq_addref(vm, §or_table);
105 script_manager.reset(NULL);
106 sq_release(ScriptManager::instance->get_vm(), §or_table);
108 update_game_objects();
109 assert(gameobjects_new.size() == 0);
111 for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end();
113 before_object_remove(*i);
117 for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
123 Sector::parse_object(const std::string& name, const lisp::Lisp& reader)
125 if(name == "camera") {
126 Camera* camera = new Camera(this);
127 camera->parse(reader);
129 } else if(name == "particles-snow") {
130 SnowParticleSystem* partsys = new SnowParticleSystem();
131 partsys->parse(reader);
133 } else if(name == "particles-rain") {
134 RainParticleSystem* partsys = new RainParticleSystem();
135 partsys->parse(reader);
137 } else if(name == "particles-comets") {
138 CometParticleSystem* partsys = new CometParticleSystem();
139 partsys->parse(reader);
141 } else if(name == "particles-ghosts") {
142 GhostParticleSystem* partsys = new GhostParticleSystem();
143 partsys->parse(reader);
145 } else if(name == "particles-clouds") {
146 CloudParticleSystem* partsys = new CloudParticleSystem();
147 partsys->parse(reader);
149 } else if(name == "money") { // for compatibility with old maps
150 return new Jumpy(reader);
154 return create_object(name, reader);
155 } catch(std::exception& e) {
156 log_warning << e.what() << "" << std::endl;
163 Sector::parse(const lisp::Lisp& sector)
165 lisp::ListIterator iter(§or);
167 const std::string& token = iter.item();
168 if(token == "name") {
169 iter.value()->get(name);
170 } else if(token == "gravity") {
171 iter.value()->get(gravity);
172 } else if(token == "music") {
173 iter.value()->get(music);
174 } else if(token == "spawnpoint") {
175 SpawnPoint* sp = new SpawnPoint(iter.lisp());
176 spawnpoints.push_back(sp);
177 } else if(token == "init-script") {
178 iter.value()->get(init_script);
180 GameObject* object = parse_object(token, *(iter.lisp()));
187 update_game_objects();
190 throw std::runtime_error("sector does not contain a solid tile layer.");
194 log_warning << "sector '" << name << "' does not contain a camera." << std::endl;
195 update_game_objects();
196 add_object(new Camera(this));
199 update_game_objects();
203 Sector::parse_old_format(const lisp::Lisp& reader)
206 reader.get("gravity", gravity);
208 std::string backgroundimage;
209 reader.get("background", backgroundimage);
211 reader.get("bkgd_speed", bgspeed);
214 Color bkgd_top, bkgd_bottom;
215 int r = 0, g = 0, b = 128;
216 reader.get("bkgd_red_top", r);
217 reader.get("bkgd_green_top", g);
218 reader.get("bkgd_blue_top", b);
219 bkgd_top.red = static_cast<float> (r) / 255.0f;
220 bkgd_top.green = static_cast<float> (g) / 255.0f;
221 bkgd_top.blue = static_cast<float> (b) / 255.0f;
223 reader.get("bkgd_red_bottom", r);
224 reader.get("bkgd_green_bottom", g);
225 reader.get("bkgd_blue_bottom", b);
226 bkgd_bottom.red = static_cast<float> (r) / 255.0f;
227 bkgd_bottom.green = static_cast<float> (g) / 255.0f;
228 bkgd_bottom.blue = static_cast<float> (b) / 255.0f;
230 if(backgroundimage != "") {
231 Background* background = new Background();
232 background->set_image(
233 std::string("images/background/") + backgroundimage, bgspeed);
234 add_object(background);
236 Gradient* gradient = new Gradient();
237 gradient->set_gradient(bkgd_top, bkgd_bottom);
238 add_object(gradient);
241 std::string particlesystem;
242 reader.get("particle_system", particlesystem);
243 if(particlesystem == "clouds")
244 add_object(new CloudParticleSystem());
245 else if(particlesystem == "snow")
246 add_object(new SnowParticleSystem());
247 else if(particlesystem == "rain")
248 add_object(new RainParticleSystem());
250 Vector startpos(100, 170);
251 reader.get("start_pos_x", startpos.x);
252 reader.get("start_pos_y", startpos.y);
254 SpawnPoint* spawn = new SpawnPoint;
255 spawn->pos = startpos;
256 spawn->name = "main";
257 spawnpoints.push_back(spawn);
259 music = "chipdisko.ogg";
260 reader.get("music", music);
261 music = "music/" + music;
263 int width = 30, height = 15;
264 reader.get("width", width);
265 reader.get("height", height);
267 std::vector<unsigned int> tiles;
268 if(reader.get_vector("interactive-tm", tiles)
269 || reader.get_vector("tilemap", tiles)) {
270 TileMap* tilemap = new TileMap();
271 tilemap->set(width, height, tiles, LAYER_TILES, true);
275 if(reader.get_vector("background-tm", tiles)) {
276 TileMap* tilemap = new TileMap();
277 tilemap->set(width, height, tiles, LAYER_BACKGROUNDTILES, false);
281 if(reader.get_vector("foreground-tm", tiles)) {
282 TileMap* tilemap = new TileMap();
283 tilemap->set(width, height, tiles, LAYER_FOREGROUNDTILES, false);
287 // read reset-points (now spawn-points)
288 const lisp::Lisp* resetpoints = reader.get_lisp("reset-points");
290 lisp::ListIterator iter(resetpoints);
292 if(iter.item() == "point") {
294 if(reader.get("x", sp_pos.x) && reader.get("y", sp_pos.y))
296 SpawnPoint* sp = new SpawnPoint;
299 spawnpoints.push_back(sp);
302 log_warning << "Unknown token '" << iter.item() << "' in reset-points." << std::endl;
308 const lisp::Lisp* objects = reader.get_lisp("objects");
310 lisp::ListIterator iter(objects);
312 GameObject* object = parse_object(iter.item(), *(iter.lisp()));
316 log_warning << "Unknown object '" << iter.item() << "' in level." << std::endl;
322 Camera* camera = new Camera(this);
325 update_game_objects();
328 throw std::runtime_error("sector does not contain a solid tile layer.");
331 update_game_objects();
335 Sector::fix_old_tiles()
338 for(size_t x=0; x < solids->get_width(); ++x) {
339 for(size_t y=0; y < solids->get_height(); ++y) {
340 const Tile* tile = solids->get_tile(x, y);
341 Vector pos(x*32, y*32);
343 if(tile->getID() == 112) {
344 add_object(new InvisibleBlock(pos));
345 solids->change(x, y, 0);
346 } else if(tile->getAttributes() & Tile::COIN) {
347 add_object(new Coin(pos));
348 solids->change(x, y, 0);
349 } else if(tile->getAttributes() & Tile::FULLBOX) {
350 add_object(new BonusBlock(pos, tile->getData()));
351 solids->change(x, y, 0);
352 } else if(tile->getAttributes() & Tile::BRICK) {
353 add_object(new Brick(pos, tile->getData()));
354 solids->change(x, y, 0);
355 } else if(tile->getAttributes() & Tile::GOAL) {
356 std::string sequence = tile->getData() == 0 ? "endsequence" : "stoptux";
357 add_object(new SequenceTrigger(pos, sequence));
358 solids->change(x, y, 0);
365 Sector::write(lisp::Writer& writer)
367 writer.write_string("name", name);
368 writer.write_float("gravity", gravity);
369 writer.write_string("music", music);
372 for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
374 SpawnPoint* spawn = *i;
375 writer.start_list("spawn-points");
376 writer.write_string("name", spawn->name);
377 writer.write_float("x", spawn->pos.x);
378 writer.write_float("y", spawn->pos.y);
379 writer.end_list("spawn-points");
383 for(GameObjects::iterator i = gameobjects.begin();
384 i != gameobjects.end(); ++i) {
385 Serializable* serializable = dynamic_cast<Serializable*> (*i);
387 serializable->write(writer);
392 Sector::run_script(std::istream& in, const std::string& sourcename)
394 // create new thread and keep a weakref
395 HSQUIRRELVM vm = script_manager->create_thread();
397 // set sector_table as roottable for the thread
398 sq_pushobject(vm, sector_table);
401 Scripting::compile_and_run(vm, in, sourcename);
407 Sector::add_object(GameObject* object)
409 // make sure the object isn't already in the list
411 for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end();
414 assert("object already added to sector" == 0);
417 for(GameObjects::iterator i = gameobjects_new.begin();
418 i != gameobjects_new.end(); ++i) {
420 assert("object already added to sector" == 0);
425 gameobjects_new.push_back(object);
429 Sector::activate(const std::string& spawnpoint)
432 for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
434 if((*i)->name == spawnpoint) {
440 log_warning << "Spawnpoint '" << spawnpoint << "' not found." << std::endl;
441 if(spawnpoint != "main") {
444 activate(Vector(0, 0));
452 Sector::activate(const Vector& player_pos)
454 if(_current != this) {
456 _current->deactivate();
459 // register sectortable as current_sector in scripting
460 HSQUIRRELVM vm = ScriptManager::instance->get_vm();
461 sq_pushroottable(vm);
462 sq_pushstring(vm, "sector", -1);
463 sq_pushobject(vm, sector_table);
464 if(SQ_FAILED(sq_createslot(vm, -3)))
465 throw Scripting::SquirrelError(vm, "Couldn't set sector in roottable");
468 for(GameObjects::iterator i = gameobjects.begin();
469 i != gameobjects.end(); ++i) {
470 GameObject* object = *i;
476 player->move(player_pos);
477 camera->reset(player->get_pos());
478 update_game_objects();
481 if(init_script != "") {
482 std::istringstream in(init_script);
483 run_script(in, std::string("Sector(") + name + ") - init");
493 // remove sector entry from global vm
494 HSQUIRRELVM vm = ScriptManager::instance->get_vm();
495 sq_pushroottable(vm);
496 sq_pushstring(vm, "sector", -1);
497 if(SQ_FAILED(sq_deleteslot(vm, -2, SQFalse)))
498 throw Scripting::SquirrelError(vm, "Couldn't unset sector in roottable");
501 for(GameObjects::iterator i = gameobjects.begin();
502 i != gameobjects.end(); ++i) {
503 GameObject* object = *i;
505 try_unexpose(object);
512 Sector::get_active_region()
515 camera->get_translation() - Vector(1600, 1200),
516 camera->get_translation() + Vector(1600, 1200));
520 Sector::update(float elapsed_time)
522 script_manager->update();
524 player->check_bounds(camera);
527 CollisionGridIterator iter(*grid, get_active_region());
528 while(MovingObject* object = iter.next()) {
529 if(!object->is_valid())
532 object->update(elapsed_time);
536 for(GameObjects::iterator i = gameobjects.begin();
537 i != gameobjects.end(); ++i) {
538 GameObject* object = *i;
539 if(!object->is_valid())
542 object->update(elapsed_time);
546 /* Handle all possible collisions. */
548 update_game_objects();
552 Sector::update_game_objects()
554 /** cleanup marked objects */
555 for(std::vector<Bullet*>::iterator i = bullets.begin();
556 i != bullets.end(); /* nothing */) {
558 if(bullet->is_valid()) {
563 i = bullets.erase(i);
565 for(MovingObjects::iterator i = moving_objects.begin();
566 i != moving_objects.end(); /* nothing */) {
567 MovingObject* moving_object = *i;
568 if(moving_object->is_valid()) {
574 grid->remove_object(moving_object);
577 i = moving_objects.erase(i);
579 for(std::vector<GameObject*>::iterator i = gameobjects.begin();
580 i != gameobjects.end(); /* nothing */) {
581 GameObject* object = *i;
583 if(object->is_valid()) {
588 before_object_remove(object);
591 i = gameobjects.erase(i);
594 /* add newly created objects */
595 for(std::vector<GameObject*>::iterator i = gameobjects_new.begin();
596 i != gameobjects_new.end(); ++i)
598 GameObject* object = *i;
600 before_object_add(object);
602 gameobjects.push_back(object);
604 gameobjects_new.clear();
608 Sector::before_object_add(GameObject* object)
610 Bullet* bullet = dynamic_cast<Bullet*> (object);
612 bullets.push_back(bullet);
614 MovingObject* movingobject = dynamic_cast<MovingObject*> (object);
616 moving_objects.push_back(movingobject);
618 grid->add_object(movingobject);
622 TileMap* tilemap = dynamic_cast<TileMap*> (object);
623 if(tilemap && tilemap->is_solid()) {
627 log_warning << "Another solid tilemaps added. Ignoring" << std::endl;
631 Camera* camera = dynamic_cast<Camera*> (object);
633 if(this->camera != 0) {
634 log_warning << "Multiple cameras added. Ignoring" << std::endl;
637 this->camera = camera;
640 Player* player = dynamic_cast<Player*> (object);
642 if(this->player != 0) {
643 log_warning << "Multiple players added. Ignoring" << std::endl;
646 this->player = player;
649 if(_current == this) {
657 Sector::try_expose(GameObject* object)
659 ScriptInterface* interface = dynamic_cast<ScriptInterface*> (object);
660 if(interface != NULL) {
661 HSQUIRRELVM vm = script_manager->get_vm();
662 sq_pushobject(vm, sector_table);
663 interface->expose(vm, -1);
669 Sector::before_object_remove(GameObject* object)
672 try_unexpose(object);
676 Sector::try_unexpose(GameObject* object)
678 ScriptInterface* interface = dynamic_cast<ScriptInterface*> (object);
679 if(interface != NULL) {
680 HSQUIRRELVM vm = script_manager->get_vm();
681 int oldtop = sq_gettop(vm);
682 sq_pushobject(vm, sector_table);
684 interface->unexpose(vm, -1);
685 } catch(std::exception& e) {
686 log_warning << "Couldn't unregister object: " << e.what() << std::endl;
688 sq_settop(vm, oldtop);
693 Sector::draw(DrawingContext& context)
695 context.push_transform();
696 context.set_translation(camera->get_translation());
698 for(GameObjects::iterator i = gameobjects.begin();
699 i != gameobjects.end(); ++i) {
700 GameObject* object = *i;
701 if(!object->is_valid())
704 object->draw(context);
708 Color col(0.2, 0.2, 0.2, 0.7);
709 for(MovingObjects::iterator i = moving_objects.begin();
710 i != moving_objects.end(); ++i) {
711 MovingObject* object = *i;
712 const Rect& rect = object->get_bbox();
714 context.draw_filled_rect(rect, col, LAYER_FOREGROUND1 + 10);
718 context.pop_transform();
721 static const float DELTA = .001;
724 Sector::collision_tilemap(const Rect& dest, const Vector& movement,
725 CollisionHit& hit) const
727 // calculate rectangle where the object will move
728 float x1 = dest.get_left();
729 float x2 = dest.get_right();
730 float y1 = dest.get_top();
731 float y2 = dest.get_bottom();
733 // test with all tiles in this rectangle
734 int starttilex = int(x1) / 32;
735 int starttiley = int(y1) / 32;
736 int max_x = int(x2 + (1 - DELTA));
737 int max_y = int(y2 + (1 - DELTA));
739 CollisionHit temphit;
740 for(int x = starttilex; x*32 < max_x; ++x) {
741 for(int y = starttiley; y*32 < max_y; ++y) {
742 const Tile* tile = solids->get_tile(x, y);
745 // skip non-solid tiles
746 if(tile->getAttributes() == 0)
748 // only handle unisolid when the player is falling down and when he was
749 // above the tile before
750 if(tile->getAttributes() & Tile::UNISOLID) {
751 if(movement.y < 0 || dest.get_top() - movement.y > y*32)
755 if(tile->getAttributes() & Tile::SLOPE) { // slope tile
757 Vector p1(x*32, y*32);
758 Vector p2((x+1)*32, (y+1)*32);
759 triangle = AATriangle(p1, p2, tile->getData());
761 if(Collision::rectangle_aatriangle(temphit, dest, movement,
763 if(temphit.time > hit.time && (tile->getAttributes() & Tile::SOLID)) {
767 } else { // normal rectangular tile
768 Rect rect(x*32, y*32, (x+1)*32, (y+1)*32);
769 if(Collision::rectangle_rectangle(temphit, dest, movement, rect)) {
770 if(temphit.time > hit.time && (tile->getAttributes() & Tile::SOLID)) {
780 Sector::collision_tile_attributes(const Rect& dest) const
782 /** XXX This function doesn't work correctly as it will check all tiles
783 * in the bounding box of the object movement, this might include tiles
784 * that have actually never been touched by the object
785 * (though this only occures for very fast objects...)
789 // calculate rectangle where the object will move
791 if(object->get_movement().x >= 0) {
792 x1 = object->get_bbox().p1.x;
793 x2 = object->get_bbox().p2.x + object->get_movement().x;
795 x1 = object->get_bbox().p1.x + object->get_movement().x;
796 x2 = object->get_bbox().p2.x;
799 if(object->get_movement().y >= 0) {
800 y1 = object->get_bbox().p1.y;
801 y2 = object->get_bbox().p2.y + object->get_movement().y;
803 y1 = object->get_bbox().p1.y + object->get_movement().y;
804 y2 = object->get_bbox().p2.y;
807 float x1 = dest.p1.x;
808 float y1 = dest.p1.y;
809 float x2 = dest.p2.x;
810 float y2 = dest.p2.y;
812 // test with all tiles in this rectangle
813 int starttilex = int(x1) / 32;
814 int starttiley = int(y1) / 32;
819 for(int x = starttilex; x*32 < max_x; ++x) {
820 for(int y = starttiley; y*32 < max_y; ++y) {
821 const Tile* tile = solids->get_tile(x, y);
824 result |= tile->getAttributes();
832 Sector::collision_object(MovingObject* object1, MovingObject* object2) const
836 Vector movement = object1->get_movement() - object2->get_movement();
837 if(Collision::rectangle_rectangle(hit, object1->dest, movement, object2->dest)) {
838 HitResponse response1 = object1->collision(*object2, hit);
840 HitResponse response2 = object2->collision(*object1, hit);
842 if(response1 != CONTINUE) {
843 if(response1 == ABORT_MOVE)
844 object1->dest = object1->get_bbox();
845 if(response2 == CONTINUE)
846 object2->dest.move(hit.normal * (hit.depth + DELTA));
847 } else if(response2 != CONTINUE) {
848 if(response2 == ABORT_MOVE)
849 object2->dest = object2->get_bbox();
850 if(response1 == CONTINUE)
851 object1->dest.move(-hit.normal * (hit.depth + DELTA));
853 object1->dest.move(-hit.normal * (hit.depth/2 + DELTA));
854 object2->dest.move(hit.normal * (hit.depth/2 + DELTA));
860 Sector::collision_static(MovingObject* object, const Vector& movement)
862 GameObject* collided_with = solids;
866 collision_tilemap(object->dest, movement, hit);
868 // collision with other (static) objects
869 CollisionHit temphit;
870 for(MovingObjects::iterator i2 = moving_objects.begin();
871 i2 != moving_objects.end(); ++i2) {
872 MovingObject* moving_object_2 = *i2;
873 if(moving_object_2->get_group() != COLGROUP_STATIC
874 || !moving_object_2->is_valid())
877 Rect dest = moving_object_2->dest;
880 = movement - moving_object_2->get_movement();
882 if(Collision::rectangle_rectangle(temphit, object->dest, rel_movement, dest)
883 && temphit.time > hit.time) {
885 collided_with = moving_object_2;
892 HitResponse response = object->collision(*collided_with, hit);
894 if(collided_with != solids) {
895 MovingObject* moving_object = (MovingObject*) collided_with;
896 HitResponse other_response = moving_object->collision(*object, hit);
897 if(other_response == ABORT_MOVE) {
898 moving_object->dest = moving_object->get_bbox();
899 } else if(other_response == FORCE_MOVE) {
900 // the static object "wins" move tux out of the collision
901 object->dest.move(-hit.normal * (hit.depth + DELTA));
903 } else if(other_response == PASS_MOVEMENT) {
904 object->dest.move(moving_object->get_movement());
905 //object->movement += moving_object->get_movement();
909 if(response == CONTINUE) {
910 object->dest.move(-hit.normal * (hit.depth + DELTA));
912 } else if(response == ABORT_MOVE) {
913 object->dest = object->get_bbox();
922 Sector::handle_collisions()
924 // calculate destination positions of the objects
925 for(MovingObjects::iterator i = moving_objects.begin();
926 i != moving_objects.end(); ++i) {
927 MovingObject* moving_object = *i;
929 moving_object->dest = moving_object->get_bbox();
930 moving_object->dest.move(moving_object->get_movement());
933 // part1: COLGROUP_MOVING vs COLGROUP_STATIC and tilemap
934 // we do this up to 4 times and have to sort all results for the smallest
935 // one before we can continue here
936 for(MovingObjects::iterator i = moving_objects.begin();
937 i != moving_objects.end(); ++i) {
938 MovingObject* moving_object = *i;
939 if((moving_object->get_group() != COLGROUP_MOVING
940 && moving_object->get_group() != COLGROUP_MOVING_ONLY_STATIC)
941 || !moving_object->is_valid())
944 Vector movement = moving_object->get_movement();
946 // test if x or y movement is dominant
947 if(fabsf(moving_object->get_movement().x) < fabsf(moving_object->get_movement().y)) {
949 // test in x direction first, then y direction
950 moving_object->dest.move(Vector(0, -movement.y));
951 for(int i = 0; i < 2; ++i) {
952 bool res = collision_static(moving_object, Vector(movement.x, 0));
956 moving_object->dest.move(Vector(0, movement.y));
957 for(int i = 0; i < 2; ++i) {
958 bool res = collision_static(moving_object, Vector(0, movement.y));
965 // test in y direction first, then x direction
966 moving_object->dest.move(Vector(-movement.x, 0));
967 for(int i = 0; i < 2; ++i) {
968 bool res = collision_static(moving_object, Vector(0, movement.y));
972 moving_object->dest.move(Vector(movement.x, 0));
973 for(int i = 0; i < 2; ++i) {
974 bool res = collision_static(moving_object, Vector(movement.x, 0));
981 // part2: COLGROUP_MOVING vs tile attributes
982 for(MovingObjects::iterator i = moving_objects.begin();
983 i != moving_objects.end(); ++i) {
984 MovingObject* moving_object = *i;
985 if((moving_object->get_group() != COLGROUP_MOVING
986 && moving_object->get_group() != COLGROUP_MOVING_ONLY_STATIC)
987 || !moving_object->is_valid())
990 uint32_t tile_attributes = collision_tile_attributes(moving_object->dest);
991 if(tile_attributes > Tile::FIRST_INTERESTING_FLAG) {
992 moving_object->collision_tile(tile_attributes);
996 // part2.5: COLGROUP_MOVING vs COLGROUP_TOUCHABLE
997 for(MovingObjects::iterator i = moving_objects.begin();
998 i != moving_objects.end(); ++i) {
999 MovingObject* moving_object = *i;
1000 if(moving_object->get_group() != COLGROUP_MOVING
1001 || !moving_object->is_valid())
1004 for(MovingObjects::iterator i2 = moving_objects.begin();
1005 i2 != moving_objects.end(); ++i2) {
1006 MovingObject* moving_object_2 = *i2;
1007 if(moving_object_2->get_group() != COLGROUP_TOUCHABLE
1008 || !moving_object_2->is_valid())
1011 collision_object(moving_object, moving_object_2);
1015 // part3: COLGROUP_MOVING vs COLGROUP_MOVING
1016 for(MovingObjects::iterator i = moving_objects.begin();
1017 i != moving_objects.end(); ++i) {
1018 MovingObject* moving_object = *i;
1020 if(moving_object->get_group() != COLGROUP_MOVING
1021 || !moving_object->is_valid())
1024 for(MovingObjects::iterator i2 = i+1;
1025 i2 != moving_objects.end(); ++i2) {
1026 MovingObject* moving_object_2 = *i2;
1027 if(moving_object_2->get_group() != COLGROUP_MOVING
1028 || !moving_object_2->is_valid())
1031 collision_object(moving_object, moving_object_2);
1035 // apply object movement
1036 for(MovingObjects::iterator i = moving_objects.begin();
1037 i != moving_objects.end(); ++i) {
1038 MovingObject* moving_object = *i;
1040 moving_object->bbox = moving_object->dest;
1041 moving_object->movement = Vector(0, 0);
1046 Sector::is_free_space(const Rect& rect) const
1048 // test with all tiles in this rectangle
1049 int starttilex = int(rect.p1.x) / 32;
1050 int starttiley = int(rect.p1.y) / 32;
1051 int max_x = int(rect.p2.x);
1052 int max_y = int(rect.p2.y);
1054 for(int x = starttilex; x*32 < max_x; ++x) {
1055 for(int y = starttiley; y*32 < max_y; ++y) {
1056 const Tile* tile = solids->get_tile(x, y);
1059 if(tile->getAttributes() & Tile::SOLID)
1064 for(MovingObjects::const_iterator i = moving_objects.begin();
1065 i != moving_objects.end(); ++i) {
1066 const MovingObject* moving_object = *i;
1067 if(moving_object->get_group() != COLGROUP_STATIC
1068 || !moving_object->is_valid())
1071 if(Collision::intersects(rect, moving_object->get_bbox()))
1079 Sector::add_bullet(const Vector& pos, float xm, Direction dir)
1081 // TODO remove this function and move these checks elsewhere...
1082 static const size_t MAX_FIRE_BULLETS = 2;
1083 static const size_t MAX_ICE_BULLETS = 1;
1085 Bullet* new_bullet = 0;
1086 if(player_status->bonus == FIRE_BONUS) {
1087 if(bullets.size() > MAX_FIRE_BULLETS-1)
1089 new_bullet = new Bullet(pos, xm, dir, FIRE_BULLET);
1090 } else if(player_status->bonus == ICE_BONUS) {
1091 if(bullets.size() > MAX_ICE_BULLETS-1)
1093 new_bullet = new Bullet(pos, xm, dir, ICE_BULLET);
1097 add_object(new_bullet);
1099 sound_manager->play("sounds/shoot.wav");
1105 Sector::add_smoke_cloud(const Vector& pos)
1107 add_object(new SmokeCloud(pos));
1112 Sector::add_floating_text(const Vector& pos, const std::string& text)
1114 add_object(new FloatingText(pos, text));
1118 Sector::play_music(MusicType type)
1120 currentmusic = type;
1121 switch(currentmusic) {
1123 sound_manager->play_music(music);
1126 sound_manager->play_music("music/salcon.ogg");
1128 case HERRING_WARNING_MUSIC:
1129 sound_manager->stop_music(TUX_INVINCIBLE_TIME_WARNING);
1132 sound_manager->play_music("");
1138 Sector::get_music_type()
1140 return currentmusic;
1144 Sector::get_total_badguys()
1146 int total_badguys = 0;
1147 for(GameObjects::iterator i = gameobjects.begin();
1148 i != gameobjects.end(); ++i) {
1149 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
1150 if (badguy && badguy->countMe)
1154 return total_badguys;
1158 Sector::inside(const Rect& rect) const
1160 if(rect.p1.x > solids->get_width() * 32
1161 || rect.p1.y > solids->get_height() * 32
1162 || rect.p2.x < 0 || rect.p2.y < 0)