3 // SuperTux - A Jump'n Run
4 // Copyright (C) 2004 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.
26 #include "lispreader.h"
32 #include "background.h"
33 #include "particlesystem.h"
36 #include "sound_manager.h"
38 #include "resources.h"
39 #include "interactive_object.h"
42 Sector* Sector::_current = 0;
45 : gravity(10), player(0), solids(0), background(0), camera(0),
46 currentmusic(LEVEL_MUSIC)
48 song_title = "Mortimers_chipdisko.mod";
49 player = new Player();
55 for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end();
59 for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
68 Sector::parse(LispReader& lispreader)
72 for(lisp_object_t* cur = lispreader.get_lisp(); !lisp_nil_p(cur);
73 cur = lisp_cdr(cur)) {
74 std::string token = lisp_symbol(lisp_car(lisp_car(cur)));
75 lisp_object_t* data = lisp_car(lisp_cdr(lisp_car(cur)));
76 LispReader reader(lisp_cdr(lisp_car(cur)));
79 name = lisp_string(data);
80 } else if(token == "gravity") {
81 gravity = lisp_integer(data);
82 } else if(token == "music") {
83 song_title = lisp_string(data);
85 } else if(token == "camera") {
87 std::cerr << "Warning: More than 1 camera defined in sector.\n";
90 camera = new Camera(this);
93 } else if(token == "background") {
94 background = new Background(reader);
95 add_object(background);
96 } else if(token == "playerspawn") {
97 SpawnPoint* sp = new SpawnPoint;
98 reader.read_string("name", sp->name);
99 reader.read_float("x", sp->pos.x);
100 reader.read_float("y", sp->pos.y);
101 spawnpoints.push_back(sp);
102 } else if(token == "tilemap") {
103 TileMap* tilemap = new TileMap(reader);
106 if(tilemap->is_solid()) {
108 std::cerr << "Warning multiple solid tilemaps in sector.\n";
113 } else if(badguykind_from_string(token) != BAD_INVALID) {
114 add_object(new BadGuy(badguykind_from_string(token), reader));
115 } else if(token == "trampoline") {
116 add_object(new Trampoline(reader));
117 } else if(token == "flying-platform") {
118 add_object(new FlyingPlatform(reader));
119 } else if(token == "particles-snow") {
120 SnowParticleSystem* partsys = new SnowParticleSystem();
121 partsys->parse(reader);
123 } else if(token == "particles-clouds") {
124 CloudParticleSystem* partsys = new CloudParticleSystem();
125 partsys->parse(reader);
127 } else if(token == "door") {
128 add_object(new Door(reader));
130 std::cerr << "Unknown object type '" << token << "'.\n";
135 std::cerr << "sector '" << name << "' does not contain a camera.\n";
136 camera = new Camera(this);
140 throw std::runtime_error("sector does not contain a solid tile layer.");
144 Sector::parse_old_format(LispReader& reader)
149 reader.read_float("gravity", gravity);
151 std::string backgroundimage;
152 reader.read_string("background", backgroundimage);
154 reader.read_float("bkgd_speed", bgspeed);
156 Color bkgd_top, bkgd_bottom;
157 int r = 0, g = 0, b = 128;
158 reader.read_int("bkgd_red_top", r);
159 reader.read_int("bkgd_green_top", g);
160 reader.read_int("bkgd_blue_top", b);
165 reader.read_int("bkgd_red_bottom", r);
166 reader.read_int("bkgd_green_bottom", g);
167 reader.read_int("bkgd_blue_bottom", b);
169 bkgd_bottom.green = g;
170 bkgd_bottom.blue = b;
172 if(backgroundimage != "") {
173 background = new Background;
174 background->set_image(backgroundimage, bgspeed);
175 add_object(background);
177 background = new Background;
178 background->set_gradient(bkgd_top, bkgd_bottom);
179 add_object(background);
182 std::string particlesystem;
183 reader.read_string("particle_system", particlesystem);
184 if(particlesystem == "clouds")
185 add_object(new CloudParticleSystem());
186 else if(particlesystem == "snow")
187 add_object(new SnowParticleSystem());
189 Vector startpos(100, 170);
190 reader.read_float("start_pos_x", startpos.x);
191 reader.read_float("start_pos_y", startpos.y);
193 SpawnPoint* spawn = new SpawnPoint;
194 spawn->pos = startpos;
195 spawn->name = "main";
196 spawnpoints.push_back(spawn);
198 song_title = "Mortimers_chipdisko.mod";
199 reader.read_string("music", song_title);
202 int width, height = 15;
203 reader.read_int("width", width);
204 reader.read_int("height", height);
206 std::vector<unsigned int> tiles;
207 if(reader.read_int_vector("interactive-tm", tiles)
208 || reader.read_int_vector("tilemap", tiles)) {
209 TileMap* tilemap = new TileMap();
210 tilemap->set(width, height, tiles, LAYER_TILES, true);
215 if(reader.read_int_vector("background-tm", tiles)) {
216 TileMap* tilemap = new TileMap();
217 tilemap->set(width, height, tiles, LAYER_BACKGROUNDTILES, false);
221 if(reader.read_int_vector("foreground-tm", tiles)) {
222 TileMap* tilemap = new TileMap();
223 tilemap->set(width, height, tiles, LAYER_FOREGROUNDTILES, false);
227 // TODO read resetpoints
231 lisp_object_t* cur = 0;
232 if(reader.read_lisp("objects", cur)) {
233 while(!lisp_nil_p(cur)) {
234 lisp_object_t* data = lisp_car(cur);
235 std::string object_type = lisp_symbol(lisp_car(data));
237 LispReader reader(lisp_cdr(data));
239 if(object_type == "trampoline") {
240 add_object(new Trampoline(reader));
242 else if(object_type == "flying-platform") {
243 add_object(new FlyingPlatform(reader));
246 BadGuyKind kind = badguykind_from_string(object_type);
247 add_object(new BadGuy(kind, reader));
256 camera = new Camera(this);
261 Sector::write(LispWriter& writer)
263 writer.write_string("name", name);
264 writer.write_float("gravity", gravity);
266 for(GameObjects::iterator i = gameobjects.begin();
267 i != gameobjects.end(); ++i) {
268 Serializable* serializable = dynamic_cast<Serializable*> (*i);
270 serializable->write(writer);
275 Sector::add_object(GameObject* object)
277 gameobjects_new.push_back(object);
279 BadGuy* badguy = dynamic_cast<BadGuy*> (object);
281 badguys.push_back(badguy);
282 Bullet* bullet = dynamic_cast<Bullet*> (object);
284 bullets.push_back(bullet);
285 Upgrade* upgrade = dynamic_cast<Upgrade*> (object);
287 upgrades.push_back(upgrade);
288 Trampoline* trampoline = dynamic_cast<Trampoline*> (object);
290 trampolines.push_back(trampoline);
291 FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (object);
293 flying_platforms.push_back(flying_platform);
294 InteractiveObject* interactive_object
295 = dynamic_cast<InteractiveObject*> (object);
296 if(interactive_object)
297 interactive_objects.push_back(interactive_object);
299 gameobjects.push_back(object);
305 Sector::activate(const std::string& spawnpoint)
309 // Apply bonuses from former levels
310 switch (player_status.bonus)
312 case PlayerStatus::NO_BONUS:
315 case PlayerStatus::FLOWER_BONUS:
316 player->got_power = Player::FIRE_POWER; // FIXME: add ice power to here
319 case PlayerStatus::GROWUP_BONUS:
325 for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
327 if((*i)->name == spawnpoint) {
333 std::cerr << "Spawnpoint '" << spawnpoint << "' not found.\n";
335 player->move(sp->pos);
338 camera->reset(Vector(player->base.x, player->base.y));
342 Sector::action(float elapsed_time)
344 player->check_bounds(camera);
346 /* update objects (don't use iterators here, because the list might change
347 * during the iteration)
349 for(size_t i = 0; i < gameobjects.size(); ++i)
350 if(gameobjects[i]->is_valid())
351 gameobjects[i]->action(elapsed_time);
353 /* Handle all possible collisions. */
356 /** cleanup marked objects */
357 for(std::vector<GameObject*>::iterator i = gameobjects.begin();
358 i != gameobjects.end(); /* nothing */) {
359 if((*i)->is_valid() == false) {
360 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
362 badguys.erase(std::remove(badguys.begin(), badguys.end(), badguy),
365 Bullet* bullet = dynamic_cast<Bullet*> (*i);
368 std::remove(bullets.begin(), bullets.end(), bullet),
371 InteractiveObject* interactive_object =
372 dynamic_cast<InteractiveObject*> (*i);
373 if(interactive_object) {
374 interactive_objects.erase(
375 std::remove(interactive_objects.begin(), interactive_objects.end(),
376 interactive_object), interactive_objects.end());
378 Upgrade* upgrade = dynamic_cast<Upgrade*> (*i);
381 std::remove(upgrades.begin(), upgrades.end(), upgrade),
384 Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
387 std::remove(trampolines.begin(), trampolines.end(), trampoline),
390 FlyingPlatform* flying_platform= dynamic_cast<FlyingPlatform*> (*i);
391 if(flying_platform) {
392 flying_platforms.erase(
393 std::remove(flying_platforms.begin(), flying_platforms.end(), flying_platform),
394 flying_platforms.end());
398 i = gameobjects.erase(i);
404 /* add newly created objects */
405 for(std::vector<GameObject*>::iterator i = gameobjects_new.begin();
406 i != gameobjects_new.end(); ++i)
408 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
410 badguys.push_back(badguy);
411 Bullet* bullet = dynamic_cast<Bullet*> (*i);
413 bullets.push_back(bullet);
414 Upgrade* upgrade = dynamic_cast<Upgrade*> (*i);
416 upgrades.push_back(upgrade);
417 Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
419 trampolines.push_back(trampoline);
420 FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (*i);
422 flying_platforms.push_back(flying_platform);
423 InteractiveObject* interactive_object
424 = dynamic_cast<InteractiveObject*> (*i);
425 if(interactive_object)
426 interactive_objects.push_back(interactive_object);
428 gameobjects.push_back(*i);
430 gameobjects_new.clear();
435 Sector::draw(DrawingContext& context)
437 context.push_transform();
438 context.set_translation(camera->get_translation());
440 for(GameObjects::iterator i = gameobjects.begin();
441 i != gameobjects.end(); ++i) {
442 if( (*i)->is_valid() )
446 context.pop_transform();
450 Sector::collision_handler()
452 // CO_BULLET & CO_BADGUY check
453 for(unsigned int i = 0; i < bullets.size(); ++i)
455 for (BadGuys::iterator j = badguys.begin(); j != badguys.end(); ++j)
457 if((*j)->dying != DYING_NOT)
460 if(rectcollision(bullets[i]->base, (*j)->base))
462 // We have detected a collision and now call the
463 // collision functions of the collided objects.
464 (*j)->collision(bullets[i], CO_BULLET, COLLISION_NORMAL);
465 bullets[i]->collision(CO_BADGUY);
466 break; // bullet is invalid now, so break
471 /* CO_BADGUY & CO_BADGUY check */
472 for (BadGuys::iterator i = badguys.begin(); i != badguys.end(); ++i)
474 if((*i)->dying != DYING_NOT)
477 BadGuys::iterator j = i;
479 for (; j != badguys.end(); ++j)
481 if(j == i || (*j)->dying != DYING_NOT)
484 if(rectcollision((*i)->base, (*j)->base))
486 // We have detected a collision and now call the
487 // collision functions of the collided objects.
488 (*j)->collision(*i, CO_BADGUY);
489 (*i)->collision(*j, CO_BADGUY);
493 if(player->dying != DYING_NOT) return;
495 // CO_BADGUY & CO_PLAYER check
496 for (BadGuys::iterator i = badguys.begin(); i != badguys.end(); ++i)
498 if((*i)->dying != DYING_NOT)
501 if(rectcollision_offset((*i)->base, player->base, 0, 0))
503 // We have detected a collision and now call the collision
504 // functions of the collided objects.
505 if (player->previous_base.y < player->base.y &&
506 player->previous_base.y + player->previous_base.height
507 < (*i)->base.y + (*i)->base.height/2
508 && !player->invincible_timer.started())
510 (*i)->collision(player, CO_PLAYER, COLLISION_SQUISH);
514 player->collision(*i, CO_BADGUY);
515 (*i)->collision(player, CO_PLAYER, COLLISION_NORMAL);
520 // CO_UPGRADE & CO_PLAYER check
521 for(unsigned int i = 0; i < upgrades.size(); ++i)
523 if(rectcollision(upgrades[i]->base, player->base))
525 // We have detected a collision and now call the collision
526 // functions of the collided objects.
527 upgrades[i]->collision(player, CO_PLAYER, COLLISION_NORMAL);
531 // CO_TRAMPOLINE & (CO_PLAYER or CO_BADGUY)
532 for (Trampolines::iterator i = trampolines.begin(); i != trampolines.end(); ++i)
534 if (rectcollision((*i)->base, player->base))
536 if (player->previous_base.y < player->base.y &&
537 player->previous_base.y + player->previous_base.height
538 < (*i)->base.y + (*i)->base.height/2)
540 (*i)->collision(player, CO_PLAYER, COLLISION_SQUISH);
542 else if (player->previous_base.y <= player->base.y)
544 player->collision(*i, CO_TRAMPOLINE);
545 (*i)->collision(player, CO_PLAYER, COLLISION_NORMAL);
550 // CO_FLYING_PLATFORM & (CO_PLAYER or CO_BADGUY)
551 for (FlyingPlatforms::iterator i = flying_platforms.begin(); i != flying_platforms.end(); ++i)
553 if (rectcollision((*i)->base, player->base))
555 if (player->previous_base.y < player->base.y &&
556 player->previous_base.y + player->previous_base.height
557 < (*i)->base.y + (*i)->base.height/2)
559 (*i)->collision(player, CO_PLAYER, COLLISION_SQUISH);
560 player->collision(*i, CO_FLYING_PLATFORM);
562 /* else if (player->previous_base.y <= player->base.y)
570 Sector::add_score(const Vector& pos, int s)
572 player_status.score += s;
574 add_object(new FloatingScore(pos, s));
578 Sector::add_bouncy_distro(const Vector& pos)
580 add_object(new BouncyDistro(pos));
584 Sector::add_broken_brick(const Vector& pos, Tile* tile)
586 add_broken_brick_piece(pos, Vector(-1, -4), tile);
587 add_broken_brick_piece(pos + Vector(0, 16), Vector(-1.5, -3), tile);
589 add_broken_brick_piece(pos + Vector(16, 0), Vector(1, -4), tile);
590 add_broken_brick_piece(pos + Vector(16, 16), Vector(1.5, -3), tile);
594 Sector::add_broken_brick_piece(const Vector& pos, const Vector& movement,
597 add_object(new BrokenBrick(tile, pos, movement));
601 Sector::add_bouncy_brick(const Vector& pos)
603 add_object(new BouncyBrick(pos));
607 Sector::add_bad_guy(float x, float y, BadGuyKind kind)
609 BadGuy* badguy = new BadGuy(kind, x, y);
615 Sector::add_upgrade(const Vector& pos, Direction dir, UpgradeKind kind)
617 add_object(new Upgrade(pos, dir, kind));
621 Sector::add_bullet(const Vector& pos, float xm, Direction dir)
623 if(player->got_power == Player::FIRE_POWER)
625 if(bullets.size() > MAX_FIRE_BULLETS-1)
628 else if(player->got_power == Player::ICE_POWER)
630 if(bullets.size() > MAX_ICE_BULLETS-1)
634 Bullet* new_bullet = 0;
635 if(player->got_power == Player::FIRE_POWER)
636 new_bullet = new Bullet(pos, xm, dir, FIRE_BULLET);
637 else if(player->got_power == Player::ICE_POWER)
638 new_bullet = new Bullet(pos, xm, dir, ICE_BULLET);
640 throw std::runtime_error("wrong bullet type.");
641 add_object(new_bullet);
643 sound_manager->play_sound(sounds[SND_SHOOT]);
650 Sector::trybreakbrick(const Vector& pos, bool small)
652 Tile* tile = solids->get_tile_at(pos);
656 sprintf(errmsg, "Invalid tile at %i,%i", (int)((pos.x+1)/32*32), (int)((pos.y+1)/32*32));
657 throw SuperTuxException(errmsg, __FILE__, __LINE__);
660 if (tile->attributes & Tile::BRICK)
664 /* Get a distro from it: */
666 Vector(((int)(pos.x + 1) / 32) * 32, (int)(pos.y / 32) * 32));
668 // TODO: don't handle this in a global way but per-tile...
669 if (!counting_distros)
671 counting_distros = true;
679 if (distro_counter <= 0)
681 counting_distros = false;
682 solids->change_at(pos, tile->next_tile);
685 sound_manager->play_sound(sounds[SND_DISTRO]);
686 player_status.score = player_status.score + SCORE_DISTRO;
687 player_status.distros++;
693 solids->change_at(pos, tile->next_tile);
695 /* Replace it with broken bits: */
696 add_broken_brick(Vector(
697 ((int)(pos.x + 1) / 32) * 32,
698 (int)(pos.y / 32) * 32), tile);
700 /* Get some score: */
701 sound_manager->play_sound(sounds[SND_BRICK]);
702 player_status.score = player_status.score + SCORE_BRICK;
713 Sector::tryemptybox(const Vector& pos, Direction col_side)
715 Tile* tile = solids->get_tile_at(pos);
719 sprintf(errmsg, "Invalid tile at %i,%i", (int)((pos.x+1)/32*32), (int)((pos.y+1)/32*32));
720 throw SuperTuxException(errmsg, __FILE__, __LINE__);
724 if (!(tile->attributes & Tile::FULLBOX))
727 // according to the collision side, set the upgrade direction
733 int posx = ((int)(pos.x+1) / 32) * 32;
734 int posy = (int)(pos.y/32) * 32 - 32;
737 case 1: // Box with a distro!
738 add_bouncy_distro(Vector(posx, posy));
739 sound_manager->play_sound(sounds[SND_DISTRO]);
740 player_status.score = player_status.score + SCORE_DISTRO;
741 player_status.distros++;
744 case 2: // Add a fire flower upgrade!
745 if (player->size == SMALL) /* Tux is small, add mints! */
746 add_upgrade(Vector(posx, posy), col_side, UPGRADE_GROWUP);
747 else /* Tux is big, add a fireflower: */
748 add_upgrade(Vector(posx, posy), col_side, UPGRADE_FIREFLOWER);
749 sound_manager->play_sound(sounds[SND_UPGRADE]);
752 case 5: // Add an ice flower upgrade!
753 if (player->size == SMALL) /* Tux is small, add mints! */
754 add_upgrade(Vector(posx, posy), col_side, UPGRADE_GROWUP);
755 else /* Tux is big, add an iceflower: */
756 add_upgrade(Vector(posx, posy), col_side, UPGRADE_ICEFLOWER);
757 sound_manager->play_sound(sounds[SND_UPGRADE]);
760 case 3: // Add a golden herring
761 add_upgrade(Vector(posx, posy), col_side, UPGRADE_HERRING);
764 case 4: // Add a 1up extra
765 add_upgrade(Vector(posx, posy), col_side, UPGRADE_1UP);
772 solids->change_at(pos, tile->next_tile);
775 /* Try to grab a distro: */
777 Sector::trygrabdistro(const Vector& pos, int bounciness)
779 Tile* tile = solids->get_tile_at(pos);
783 sprintf(errmsg, "Invalid tile at %i,%i", (int)((pos.x+1)/32*32), (int)((pos.y+1)/32*32));
784 throw SuperTuxException(errmsg, __FILE__, __LINE__); */
786 //Bad tiles (i.e. tiles that are not defined in supertux.stgt but appear in the map) are changed to ID 0 (blank tile)
787 std::cout << "Warning: Undefined tile at " <<(int)pos.x/32 << "/" << (int)pos.y/32 << " (ID: " << (int)solids->get_tile_id_at(pos) << ")" << std::endl;
788 solids->change_at(pos,0);
789 tile = solids->get_tile_at(pos);
793 if (!(tile->attributes & Tile::COIN))
796 solids->change_at(pos, tile->next_tile);
797 sound_manager->play_sound(sounds[SND_DISTRO]);
799 if (bounciness == BOUNCE)
801 add_bouncy_distro(Vector(((int)(pos.x + 1) / 32) * 32,
802 (int)(pos.y / 32) * 32));
805 player_status.score = player_status.score + SCORE_DISTRO;
806 player_status.distros++;
810 /* Try to bump a bad guy from below: */
812 Sector::trybumpbadguy(const Vector& pos)
815 for (BadGuys::iterator i = badguys.begin(); i != badguys.end(); ++i)
817 if ((*i)->base.x >= pos.x - 32 && (*i)->base.x <= pos.x + 32 &&
818 (*i)->base.y >= pos.y - 16 && (*i)->base.y <= pos.y + 16)
820 (*i)->collision(player, CO_PLAYER, COLLISION_BUMP);
825 for (unsigned int i = 0; i < upgrades.size(); i++)
827 if (upgrades[i]->base.height == 32 &&
828 upgrades[i]->base.x >= pos.x - 32 && upgrades[i]->base.x <= pos.x + 32 &&
829 upgrades[i]->base.y >= pos.y - 16 && upgrades[i]->base.y <= pos.y + 16)
831 upgrades[i]->collision(player, CO_PLAYER, COLLISION_BUMP);
842 level_song = sound_manager->load_music(datadir + "/music/" + song_title);
844 song_path = (char *) malloc(sizeof(char) * datadir.length() +
845 strlen(song_title.c_str()) + 8 + 5);
846 song_subtitle = strdup(song_title.c_str());
847 strcpy(strstr(song_subtitle, "."), "\0");
848 sprintf(song_path, "%s/music/%s-fast%s", datadir.c_str(),
849 song_subtitle, strstr(song_title.c_str(), "."));
850 if(!sound_manager->exists_music(song_path)) {
851 level_song_fast = level_song;
853 level_song_fast = sound_manager->load_music(song_path);
860 Sector::play_music(int type)
863 switch(currentmusic) {
865 sound_manager->play_music(level_song_fast);
868 sound_manager->play_music(level_song);
871 sound_manager->play_music(herring_song);
874 sound_manager->halt_music();
880 Sector::get_music_type()