0e03e760633a67ad9f64dab7a6e828710564bc05
[supertux.git] / src / sector.cpp
1 //  $Id$
2 //
3 //  SuperTux -  A Jump'n Run
4 //  Copyright (C) 2004 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
20 #include <config.h>
21
22 #include <memory>
23 #include <algorithm>
24 #include <stdexcept>
25 #include <iostream>
26 #include <fstream>
27 #include <stdexcept>
28
29 #include "app/globals.h"
30 #include "sector.h"
31 #include "utils/lispreader.h"
32 #include "gameobjs.h"
33 #include "camera.h"
34 #include "background.h"
35 #include "particlesystem.h"
36 #include "tile.h"
37 #include "tilemap.h"
38 #include "audio/sound_manager.h"
39 #include "gameloop.h"
40 #include "resources.h"
41 #include "statistics.h"
42 #include "special/collision.h"
43 #include "math/rectangle.h"
44 #include "math/aatriangle.h"
45 #include "object/coin.h"
46 #include "object/block.h"
47 #include "object/platform.h"
48 #include "trigger/door.h"
49 #include "object/bullet.h"
50 #include "badguy/jumpy.h"
51 #include "badguy/snowball.h"
52 #include "badguy/bouncing_snowball.h"
53 #include "badguy/flame.h"
54 #include "badguy/mriceblock.h"
55 #include "badguy/mrbomb.h"
56 #include "trigger/sequence_trigger.h"
57
58 Sector* Sector::_current = 0;
59
60 Sector::Sector()
61   : gravity(10), player(0), solids(0), background(0), camera(0),
62     currentmusic(LEVEL_MUSIC)
63 {
64   song_title = "Mortimers_chipdisko.mod";
65   player = new Player();
66   add_object(player);
67 }
68
69 Sector::~Sector()
70 {
71   for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end();
72       ++i) {
73     delete *i;
74   }
75
76   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
77       ++i)
78     delete *i;
79     
80   if(_current == this)
81     _current = 0;
82 }
83
84 Sector *Sector::create(const std::string& name, size_t width, size_t height)
85 {
86   Sector *sector = new Sector;
87   sector->name = name;
88   TileMap *background = new TileMap(LAYER_BACKGROUNDTILES, false, width, height);
89   TileMap *interactive = new TileMap(LAYER_TILES, true, width, height);
90   TileMap *foreground = new TileMap(LAYER_FOREGROUNDTILES, false, width, height);
91   sector->add_object(background);
92   sector->add_object(interactive);
93   sector->add_object(foreground);
94   sector->solids = interactive;
95   sector->camera = new Camera(sector);
96   sector->add_object(sector->camera);
97   sector->update_game_objects();
98   return sector;
99 }
100
101 GameObject*
102 Sector::parseObject(const std::string& name, LispReader& reader)
103 {
104   if(name == "background") {
105     background = new Background(reader);
106     return background;
107   } else if(name == "camera") {
108     if(camera) {
109       std::cerr << "Warning: More than 1 camera defined in sector.\n";
110       return 0;
111     }
112     camera = new Camera(this);
113     camera->read(reader);
114     return camera;
115   } else if(name == "tilemap") {
116     TileMap* tilemap = new TileMap(reader);
117
118     if(tilemap->is_solid()) {
119       if(solids) {
120         std::cerr << "Warning multiple solid tilemaps in sector.\n";
121         return 0;
122       }
123       solids = tilemap;
124     }
125     return tilemap;
126   } else if(name == "particles-snow") {
127     SnowParticleSystem* partsys = new SnowParticleSystem();
128     partsys->parse(reader);
129     return partsys;
130   } else if(name == "particles-clouds") {
131     CloudParticleSystem* partsys = new CloudParticleSystem();
132     partsys->parse(reader);
133     return partsys;
134   } else if(name == "door") {
135     return new Door(reader);
136   } else if(name == "platform") {
137     return new Platform(reader);
138   } else if(name == "jumpy" || name == "money") {
139     return new Jumpy(reader);
140   } else if(name == "snowball") {
141     return new SnowBall(reader);
142   } else if(name == "bouncingsnowball") {
143     return new BouncingSnowball(reader);
144   } else if(name == "flame") {
145     return new Flame(reader);
146   } else if(name == "mriceblock") {
147     return new MrIceBlock(reader);
148   } else if(name == "mrbomb") {
149     return new MrBomb(reader);
150   }
151 #if 0
152     else if(badguykind_from_string(name) != BAD_INVALID) {
153       return new BadGuy(badguykind_from_string(name), reader);
154     } else if(name == "trampoline") {
155       return new Trampoline(reader);
156     } else if(name == "flying-platform") {
157       return new FlyingPlatform(reader);
158 #endif
159
160    std::cerr << "Unknown object type '" << name << "'.\n";
161    return 0;
162 }
163
164 void
165 Sector::parse(LispReader& lispreader)
166 {
167   _current = this;
168   
169   for(lisp_object_t* cur = lispreader.get_lisp(); !lisp_nil_p(cur);
170       cur = lisp_cdr(cur)) {
171     std::string token = lisp_symbol(lisp_car(lisp_car(cur)));
172     // FIXME: doesn't handle empty data
173     lisp_object_t* data = lisp_car(lisp_cdr(lisp_car(cur)));
174     LispReader reader(lisp_cdr(lisp_car(cur)));
175
176     if(token == "name") {
177       name = lisp_string(data);
178     } else if(token == "gravity") {
179       gravity = lisp_real(data);
180     } else if(token == "music") {
181       song_title = lisp_string(data);
182       load_music();
183     } else if(token == "spawn-points") {
184       SpawnPoint* sp = new SpawnPoint;
185       reader.read_string("name", sp->name);
186       reader.read_float("x", sp->pos.x);
187       reader.read_float("y", sp->pos.y);
188       spawnpoints.push_back(sp);
189     } else {
190       GameObject* object = parseObject(token, reader);
191       if(object) {
192         add_object(object);
193       }
194     }
195   }
196
197   if(!camera) {
198     std::cerr << "sector '" << name << "' does not contain a camera.\n";
199     camera = new Camera(this);
200     add_object(camera);
201   }
202   if(!solids)
203     throw std::runtime_error("sector does not contain a solid tile layer.");
204 }
205
206 void
207 Sector::parse_old_format(LispReader& reader)
208 {
209   _current = this;
210   
211   name = "main";
212   reader.read_float("gravity", gravity);
213
214   std::string backgroundimage;
215   reader.read_string("background", backgroundimage);
216   float bgspeed = .5;
217   reader.read_float("bkgd_speed", bgspeed);
218   bgspeed /= 100;
219
220   Color bkgd_top, bkgd_bottom;
221   int r = 0, g = 0, b = 128;
222   reader.read_int("bkgd_red_top", r);
223   reader.read_int("bkgd_green_top",  g);
224   reader.read_int("bkgd_blue_top",  b);
225   bkgd_top.red = r;
226   bkgd_top.green = g;
227   bkgd_top.blue = b;
228   
229   reader.read_int("bkgd_red_bottom",  r);
230   reader.read_int("bkgd_green_bottom", g);
231   reader.read_int("bkgd_blue_bottom", b);
232   bkgd_bottom.red = r;
233   bkgd_bottom.green = g;
234   bkgd_bottom.blue = b;
235   
236   if(backgroundimage != "") {
237     background = new Background;
238     background->set_image(backgroundimage, bgspeed);
239     add_object(background);
240   } else {
241     background = new Background;
242     background->set_gradient(bkgd_top, bkgd_bottom);
243     add_object(background);
244   }
245
246   std::string particlesystem;
247   reader.read_string("particle_system", particlesystem);
248   if(particlesystem == "clouds")
249     add_object(new CloudParticleSystem());
250   else if(particlesystem == "snow")
251     add_object(new SnowParticleSystem());
252
253   Vector startpos(100, 170);
254   reader.read_float("start_pos_x", startpos.x);
255   reader.read_float("start_pos_y", startpos.y);
256
257   SpawnPoint* spawn = new SpawnPoint;
258   spawn->pos = startpos;
259   spawn->name = "main";
260   spawnpoints.push_back(spawn);
261
262   song_title = "Mortimers_chipdisko.mod";
263   reader.read_string("music", song_title);
264   load_music();
265
266   int width, height = 15;
267   reader.read_int("width", width);
268   reader.read_int("height", height);
269   
270   std::vector<unsigned int> tiles;
271   if(reader.read_int_vector("interactive-tm", tiles)
272       || reader.read_int_vector("tilemap", tiles)) {
273     TileMap* tilemap = new TileMap();
274     tilemap->set(width, height, tiles, LAYER_TILES, true);
275     solids = tilemap;
276     add_object(tilemap);
277
278     // hack for now...
279     for(size_t x=0; x < solids->get_width(); ++x) {
280       for(size_t y=0; y < solids->get_height(); ++y) {
281         const Tile* tile = solids->get_tile(x, y);
282
283         if(tile->attributes & Tile::COIN) {
284           Coin* coin = new Coin(Vector(x*32, y*32));
285           add_object(coin);
286           solids->change(x, y, 0);
287         } else if(tile->attributes & Tile::FULLBOX) {
288           BonusBlock* block = new BonusBlock(Vector(x*32, y*32), tile->data);
289           add_object(block);
290           solids->change(x, y, 0);
291         } else if(tile->attributes & Tile::BRICK) {
292           Brick* brick = new Brick(Vector(x*32, y*32), tile->data);
293           add_object(brick);
294           solids->change(x, y, 0);
295         } else if(tile->attributes & Tile::GOAL) {
296           SequenceTrigger* trigger = new SequenceTrigger(Vector(x*32, y*32),
297               "endsequence");
298           add_object(trigger);
299           solids->change(x, y, 0);
300         }
301       }                                                   
302     }
303   }
304
305   if(reader.read_int_vector("background-tm", tiles)) {
306     TileMap* tilemap = new TileMap();
307     tilemap->set(width, height, tiles, LAYER_BACKGROUNDTILES, false);
308     add_object(tilemap);
309   }
310
311   if(reader.read_int_vector("foreground-tm", tiles)) {
312     TileMap* tilemap = new TileMap();
313     tilemap->set(width, height, tiles, LAYER_FOREGROUNDTILES, false);
314     add_object(tilemap);
315   }
316
317   // read reset-points (now spawn-points)
318   {
319     lisp_object_t* cur = 0;
320     if(reader.read_lisp("reset-points", cur)) {
321       while(!lisp_nil_p(cur)) {
322         lisp_object_t* data = lisp_car(cur);
323         LispReader reader(lisp_cdr(data));
324
325         Vector sp_pos;
326         if(reader.read_float("x", sp_pos.x) && reader.read_float("y", sp_pos.y))
327           {
328           SpawnPoint* sp = new SpawnPoint;
329           sp->name = "main";
330           sp->pos = sp_pos;
331           spawnpoints.push_back(sp);
332           }
333                                                              
334         cur = lisp_cdr(cur);
335       }
336     }
337   }
338
339   // read objects
340   {
341     lisp_object_t* cur = 0;
342     if(reader.read_lisp("objects", cur)) {
343       while(!lisp_nil_p(cur)) {
344         lisp_object_t* data = lisp_car(cur);
345         std::string object_type = lisp_symbol(lisp_car(data));
346                                                                                 
347         LispReader reader(lisp_cdr(data));
348
349         GameObject* object = parseObject(object_type, reader);
350         if(object) {
351           add_object(object);
352         } else {
353           std::cerr << "Unknown object '" << object_type << "' in level.\n";
354         }
355                                                                                
356         cur = lisp_cdr(cur);
357       }
358     }
359   }
360
361   // add a camera
362   camera = new Camera(this);
363   add_object(camera);
364 }
365
366 void
367 Sector::write(LispWriter& writer)
368 {
369   writer.write_string("name", name);
370   writer.write_float("gravity", gravity);
371   writer.write_string("music", song_title);
372
373   // write spawnpoints
374   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
375       ++i) {
376     SpawnPoint* spawn = *i;
377     writer.start_list("spawn-points");
378     writer.write_string("name", spawn->name);
379     writer.write_float("x", spawn->pos.x);
380     writer.write_float("y", spawn->pos.y);
381     writer.end_list("spawn-points");
382   }
383
384   // write objects
385   for(GameObjects::iterator i = gameobjects.begin();
386       i != gameobjects.end(); ++i) {
387     Serializable* serializable = dynamic_cast<Serializable*> (*i);
388     if(serializable)
389       serializable->write(writer);
390   }
391 }
392
393 void
394 Sector::do_vertical_flip()
395 {
396   // remove or fix later
397 #if 0
398   for(GameObjects::iterator i = gameobjects_new.begin(); i != gameobjects_new.end(); ++i)
399     {
400     TileMap* tilemap = dynamic_cast<TileMap*> (*i);
401     if(tilemap)
402       {
403       tilemap->do_vertical_flip();
404       }
405
406     BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
407     if(badguy)
408       badguy->start_position.y = solids->get_height()*32 - badguy->start_position.y - 32;
409     Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
410     if(trampoline)
411       trampoline->base.y = solids->get_height()*32 - trampoline->base.y - 32;
412     FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (*i);
413     if(flying_platform)
414       flying_platform->base.y = solids->get_height()*32 - flying_platform->base.y - 32;
415     Door* door = dynamic_cast<Door*> (*i);
416     if(door)
417       door->set_area(door->get_area().x, solids->get_height()*32 - door->get_area().y - 32);
418     }
419
420   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
421       ++i) {
422     SpawnPoint* spawn = *i;
423     spawn->pos.y = solids->get_height()*32 - spawn->pos.y - 32;
424   }
425 #endif
426 }
427
428 void
429 Sector::add_object(GameObject* object)
430 {
431   // make sure the object isn't already in the list
432 #ifdef DEBUG
433   for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end();
434       ++i) {
435     if(*i == object) {
436       assert("object already added to sector" == 0);
437     }
438   }
439   for(GameObjects::iterator i = gameobjects_new.begin();
440       i != gameobjects_new.end(); ++i) {
441     if(*i == object) {
442       assert("object already added to sector" == 0);
443     }
444   }
445 #endif
446
447   gameobjects_new.push_back(object);
448 }
449
450 void
451 Sector::activate(const std::string& spawnpoint)
452 {
453   _current = this;
454
455   // Apply bonuses from former levels
456   switch (player_status.bonus)
457     {
458     case PlayerStatus::NO_BONUS:
459       break;
460                                                                                 
461     case PlayerStatus::FLOWER_BONUS:
462       player->got_power = Player::FIRE_POWER;  // FIXME: add ice power to here
463       // fall through
464                                                                                 
465     case PlayerStatus::GROWUP_BONUS:
466       player->grow(false);
467       break;
468     }
469
470   SpawnPoint* sp = 0;
471   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
472       ++i) {
473     if((*i)->name == spawnpoint) {
474       sp = *i;
475       break;
476     }
477   }
478   if(!sp) {
479     std::cerr << "Spawnpoint '" << spawnpoint << "' not found.\n";
480   } else {
481     player->move(sp->pos);
482   }
483
484   camera->reset(player->get_pos());
485 }
486
487 Vector
488 Sector::get_best_spawn_point(Vector pos)
489 {
490   Vector best_reset_point = Vector(-1,-1);
491
492   for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
493       ++i) {
494     if((*i)->name != "main")
495       continue;
496     if((*i)->pos.x > best_reset_point.x && (*i)->pos.x < pos.x)
497       best_reset_point = (*i)->pos;
498   }
499
500   return best_reset_point;
501 }
502
503 void
504 Sector::action(float elapsed_time)
505 {
506   player->check_bounds(camera);
507                                                                                 
508   /* update objects */
509   for(GameObjects::iterator i = gameobjects.begin();
510           i != gameobjects.end(); ++i) {
511     GameObject* object = *i;
512     if(!object->is_valid())
513       continue;
514     
515     object->action(elapsed_time);
516   }
517                                                                                 
518   /* Handle all possible collisions. */
519   collision_handler();
520                                                                                 
521   update_game_objects();
522 }
523
524 void
525 Sector::update_game_objects()
526 {
527   /** cleanup marked objects */
528   for(std::vector<GameObject*>::iterator i = gameobjects.begin();
529       i != gameobjects.end(); /* nothing */) {
530     if((*i)->is_valid() == false) {
531       Bullet* bullet = dynamic_cast<Bullet*> (*i);
532       if(bullet) {
533         bullets.erase(
534             std::remove(bullets.begin(), bullets.end(), bullet),
535             bullets.end());
536       }
537 #if 0
538       InteractiveObject* interactive_object =
539           dynamic_cast<InteractiveObject*> (*i);
540       if(interactive_object) {
541         interactive_objects.erase(
542             std::remove(interactive_objects.begin(), interactive_objects.end(),
543                 interactive_object), interactive_objects.end());
544       }
545 #endif
546       delete *i;
547       i = gameobjects.erase(i);
548     } else {
549       ++i;
550     }
551   }
552
553   /* add newly created objects */
554   for(std::vector<GameObject*>::iterator i = gameobjects_new.begin();
555       i != gameobjects_new.end(); ++i)
556   {
557           Bullet* bullet = dynamic_cast<Bullet*> (*i);
558           if(bullet)
559             bullets.push_back(bullet);
560 #if 0
561           InteractiveObject* interactive_object 
562               = dynamic_cast<InteractiveObject*> (*i);
563           if(interactive_object)
564             interactive_objects.push_back(interactive_object);
565 #endif
566
567           gameobjects.push_back(*i);
568   }
569   gameobjects_new.clear();
570 }
571
572 void
573 Sector::draw(DrawingContext& context)
574 {
575   context.push_transform();
576   context.set_translation(camera->get_translation());
577   
578   for(GameObjects::iterator i = gameobjects.begin();
579       i != gameobjects.end(); ++i) {
580     GameObject* object = *i; 
581     if(!object->is_valid())
582       continue;
583     
584     object->draw(context);
585   }
586
587   context.pop_transform();
588 }
589
590 void
591 Sector::collision_tilemap(MovingObject* object, int depth)
592 {
593   if(depth >= 4) {
594 #ifdef DEBUG
595     std::cout << "Max collision depth reached.\n";
596 #endif
597     object->movement = Vector(0, 0);
598     return;
599   }
600
601   // calculate rectangle where the object will move
602   float x1, x2;
603   if(object->get_movement().x >= 0) {
604     x1 = object->get_pos().x;
605     x2 = object->get_bbox().p2.x + object->get_movement().x;
606   } else {
607     x1 = object->get_pos().x + object->get_movement().x;
608     x2 = object->get_bbox().p2.x;
609   }
610   float y1, y2;
611   if(object->get_movement().y >= 0) {
612     y1 = object->get_pos().y;
613     y2 = object->get_bbox().p2.y + object->get_movement().y;
614   } else {
615     y1 = object->get_pos().y + object->get_movement().y;
616     y2 = object->get_bbox().p2.y;
617   }
618
619   // test with all tiles in this rectangle
620   int starttilex = int(x1-1) / 32;
621   int starttiley = int(y1-1) / 32;
622   int max_x = int(x2+1);
623   int max_y = int(y2+1);
624
625   CollisionHit temphit, hit;
626   Rectangle dest = object->get_bbox();
627   dest.move(object->movement);
628   hit.depth = -1;
629   for(int x = starttilex; x*32 < max_x; ++x) {
630     for(int y = starttiley; y*32 < max_y; ++y) {
631       const Tile* tile = solids->get_tile(x, y);
632       if(!tile)
633         continue;
634       if(!(tile->attributes & Tile::SOLID))
635         continue;
636       if((tile->attributes & Tile::UNISOLID) && object->movement.y < 0)
637         continue;
638
639       if(tile->attributes & Tile::SLOPE) { // slope tile
640         AATriangle triangle;
641         Vector p1(x*32, y*32);
642         Vector p2((x+1)*32, (y+1)*32);
643         switch(tile->data) {
644           case 0:
645             triangle = AATriangle(p1, p2, AATriangle::SOUTHWEST);
646             break;
647           case 1:
648             triangle = AATriangle(p1, p2, AATriangle::NORTHEAST);
649             break;
650           case 2:
651             triangle = AATriangle(p1, p2, AATriangle::SOUTHEAST);
652             break;
653           case 3:
654             triangle = AATriangle(p1, p2, AATriangle::NORTHWEST);
655             break;
656           default:
657             printf("Invalid slope angle in tile %d !\n", tile->id);
658             break;
659         }
660
661         if(Collision::rectangle_aatriangle(temphit, dest, triangle)) {
662           if(temphit.depth > hit.depth)
663             hit = temphit;
664         }
665       } else { // normal rectangular tile
666         Rectangle rect(x*32, y*32, (x+1)*32, (y+1)*32);
667         if(Collision::rectangle_rectangle(temphit, dest, rect)) {
668           if(temphit.depth > hit.depth)
669             hit = temphit;
670         }
671       }
672     }
673   }
674
675   // did we collide at all?
676   if(hit.depth == -1)
677     return;
678  
679   // call collision function
680   HitResponse response = object->collision(*solids, hit);
681   if(response == ABORT_MOVE) {
682     object->movement = Vector(0, 0);
683     return;
684   }
685   if(response == FORCE_MOVE) {
686       return;
687   }
688   // move out of collision and try again
689   object->movement += hit.normal * (hit.depth + .001);
690   collision_tilemap(object, depth+1);
691 }
692
693 void
694 Sector::collision_object(MovingObject* object1, MovingObject* object2)
695 {
696   CollisionHit hit;
697   Rectangle dest1 = object1->get_bbox();
698   dest1.move(object1->get_movement());
699   Rectangle dest2 = object2->get_bbox();
700   dest2.move(object2->get_movement());
701   if(Collision::rectangle_rectangle(hit, dest1, dest2)) {
702     HitResponse response = object1->collision(*object2, hit);
703     if(response == ABORT_MOVE) {
704       object1->movement = Vector(0, 0);
705     } else if(response == CONTINUE) {
706       object1->movement += hit.normal * (hit.depth/2 + .001);
707     }
708     hit.normal *= -1;
709     response = object2->collision(*object1, hit);
710     if(response == ABORT_MOVE) {
711       object2->movement = Vector(0, 0);
712     } else if(response == CONTINUE) {
713       object2->movement += hit.normal * (hit.depth/2 + .001);
714     }
715   }
716 }
717
718 void
719 Sector::collision_handler()
720 {
721   for(std::vector<GameObject*>::iterator i = gameobjects.begin();
722       i != gameobjects.end(); ++i) {
723     GameObject* gameobject = *i;
724     if(!gameobject->is_valid() 
725         || gameobject->get_flags() & GameObject::FLAG_NO_COLLDET)
726       continue;
727     MovingObject* movingobject = dynamic_cast<MovingObject*> (gameobject);
728     if(!movingobject)
729       continue;
730   
731     // collision with tilemap
732     if(! (movingobject->movement == Vector(0, 0)))
733       collision_tilemap(movingobject, 0);
734
735     // collision with other objects
736     for(std::vector<GameObject*>::iterator i2 = i+1;
737         i2 != gameobjects.end(); ++i2) {
738       GameObject* other_object = *i2;
739       if(!other_object->is_valid() 
740           || other_object->get_flags() & GameObject::FLAG_NO_COLLDET)
741         continue;
742       MovingObject* movingobject2 = dynamic_cast<MovingObject*> (other_object);
743       if(!movingobject2)
744         continue;
745
746       collision_object(movingobject, movingobject2);
747     }
748     
749     movingobject->bbox.move(movingobject->get_movement());
750     movingobject->movement = Vector(0, 0);
751   }
752 }
753
754 void
755 Sector::add_score(const Vector& pos, int s)
756 {
757   global_stats.add_points(SCORE_STAT, s);
758                                                                                 
759   add_object(new FloatingText(pos, s));
760 }
761                                                                                 
762 bool
763 Sector::add_bullet(const Vector& pos, float xm, Direction dir)
764 {
765   if(player->got_power == Player::FIRE_POWER) {
766     if(bullets.size() > MAX_FIRE_BULLETS-1)
767       return false;
768   } else if(player->got_power == Player::ICE_POWER) {
769     if(bullets.size() > MAX_ICE_BULLETS-1)
770       return false;
771   }
772                                                                                 
773   Bullet* new_bullet = 0;
774   if(player->got_power == Player::FIRE_POWER)
775     new_bullet = new Bullet(pos, xm, dir, FIRE_BULLET);
776   else if(player->got_power == Player::ICE_POWER)
777     new_bullet = new Bullet(pos, xm, dir, ICE_BULLET);
778   else
779     throw std::runtime_error("wrong bullet type.");
780   add_object(new_bullet);
781
782   SoundManager::get()->play_sound(IDToSound(SND_SHOOT));
783
784   return true;
785 }
786
787 bool
788 Sector::add_smoke_cloud(const Vector& pos)
789 {
790   add_object(new SmokeCloud(pos));
791   return true;
792 }
793
794 void
795 Sector::add_floating_text(const Vector& pos, const std::string& text)
796 {
797   add_object(new FloatingText(pos, text));
798 }
799
800 void
801 Sector::load_music()
802 {
803   char* song_path;
804   char* song_subtitle;
805                                                                                 
806   level_song = SoundManager::get()->load_music(datadir + "/music/" + song_title);
807                                                                                 
808   song_path = (char *) malloc(sizeof(char) * datadir.length() +
809                               strlen(song_title.c_str()) + 8 + 5);
810   song_subtitle = strdup(song_title.c_str());
811   strcpy(strstr(song_subtitle, "."), "\0");
812   sprintf(song_path, "%s/music/%s-fast%s", datadir.c_str(),
813           song_subtitle, strstr(song_title.c_str(), "."));
814   if(!SoundManager::get()->exists_music(song_path)) {
815     level_song_fast = level_song;
816   } else {
817     level_song_fast = SoundManager::get()->load_music(song_path);
818   }
819   free(song_subtitle);
820   free(song_path);
821 }
822
823 void
824 Sector::play_music(int type)
825 {
826   currentmusic = type;
827   switch(currentmusic) {
828     case HURRYUP_MUSIC:
829       SoundManager::get()->play_music(level_song_fast);
830       break;
831     case LEVEL_MUSIC:
832       SoundManager::get()->play_music(level_song);
833       break;
834     case HERRING_MUSIC:
835       SoundManager::get()->play_music(herring_song);
836       break;
837     default:
838       SoundManager::get()->halt_music();
839       break;
840   }
841 }
842
843 int
844 Sector::get_music_type()
845 {
846   return currentmusic;
847 }
848
849 int
850 Sector::get_total_badguys()
851 {
852   int total_badguys = 0;
853 #if 0
854   for(GameObjects::iterator i = gameobjects_new.begin(); i != gameobjects_new.end(); ++i)
855     {
856     BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
857     if(badguy)
858       total_badguys++;
859     }
860 #endif
861   return total_badguys;
862 }
863
864 bool
865 Sector::inside(const Rectangle& rect) const
866 {
867   if(rect.p1.x > solids->get_width() * 32 
868       || rect.p1.y > solids->get_height() * 32
869       || rect.p2.x < 0 || rect.p2.y < 0)
870     return false;
871
872   return true;
873 }