8 #include "lispreader.h"
14 #include "background.h"
15 #include "particlesystem.h"
18 #include "music_manager.h"
20 #include "resources.h"
22 Sector* Sector::_current = 0;
25 : gravity(10), player(0), solids(0), background(0), camera(0),
26 currentmusic(LEVEL_MUSIC)
28 song_title = "Mortimers_chipdisko.mod";
29 player = new Player();
35 for(GameObjects::iterator i = gameobjects.begin(); i != gameobjects.end();
39 for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
48 Sector::parse(LispReader& lispreader)
52 for(lisp_object_t* cur = lispreader.get_lisp(); !lisp_nil_p(cur);
53 cur = lisp_cdr(cur)) {
54 std::string token = lisp_symbol(lisp_car(lisp_car(cur)));
55 lisp_object_t* data = lisp_car(lisp_cdr(lisp_car(cur)));
56 LispReader reader(lisp_cdr(lisp_car(cur)));
59 name = lisp_string(data);
60 } else if(token == "gravity") {
61 gravity = lisp_integer(data);
62 } else if(token == "music") {
63 song_title = lisp_string(data);
65 } else if(token == "camera") {
67 std::cerr << "Warning: More than 1 camera defined in sector.\n";
70 camera = new Camera(this);
73 } else if(token == "background") {
74 background = new Background(reader);
75 add_object(background);
76 } else if(token == "playerspawn") {
77 SpawnPoint* sp = new SpawnPoint;
78 reader.read_string("name", sp->name);
79 reader.read_float("x", sp->pos.x);
80 reader.read_float("y", sp->pos.y);
81 } else if(token == "tilemap") {
82 TileMap* tilemap = new TileMap(reader);
85 if(tilemap->is_solid()) {
87 std::cerr << "Warning multiple solid tilemaps in sector.\n";
92 } else if(badguykind_from_string(token) != BAD_INVALID) {
93 add_object(new BadGuy(badguykind_from_string(token), reader));
94 } else if(token == "trampoline") {
95 add_object(new Trampoline(reader));
96 } else if(token == "flying-platform") {
97 add_object(new FlyingPlatform(reader));
98 } else if(token == "particles-snow") {
99 SnowParticleSystem* partsys = new SnowParticleSystem();
100 partsys->parse(reader);
102 } else if(token == "particles-clouds") {
103 CloudParticleSystem* partsys = new CloudParticleSystem();
104 partsys->parse(reader);
110 std::cerr << "sector does not contain a camera.\n";
111 camera = new Camera(this);
114 throw std::runtime_error("sector does not contain a solid tile layer.");
118 Sector::parse_old_format(LispReader& reader)
123 reader.read_float("gravity", gravity);
125 std::string backgroundimage;
126 reader.read_string("background", backgroundimage);
128 reader.read_float("bkgd_speed", bgspeed);
130 Color bkgd_top, bkgd_bottom;
131 int r = 0, g = 0, b = 128;
132 reader.read_int("bkgd_red_top", r);
133 reader.read_int("bkgd_green_top", g);
134 reader.read_int("bkgd_blue_top", b);
139 reader.read_int("bkgd_red_bottom", r);
140 reader.read_int("bkgd_green_bottom", g);
141 reader.read_int("bkgd_blue_bottom", b);
143 bkgd_bottom.green = g;
144 bkgd_bottom.blue = b;
146 if(backgroundimage != "") {
147 background = new Background;
148 background->set_image(backgroundimage, bgspeed);
149 add_object(background);
151 background = new Background;
152 background->set_gradient(bkgd_top, bkgd_bottom);
153 add_object(background);
156 std::string particlesystem;
157 reader.read_string("particle_system", particlesystem);
158 if(particlesystem == "clouds")
159 add_object(new CloudParticleSystem());
160 else if(particlesystem == "snow")
161 add_object(new SnowParticleSystem());
163 Vector startpos(100, 170);
164 reader.read_float("start_pos_x", startpos.x);
165 reader.read_float("start_pos_y", startpos.y);
167 SpawnPoint* spawn = new SpawnPoint;
168 spawn->pos = startpos;
169 spawn->name = "main";
170 spawnpoints.push_back(spawn);
172 song_title = "Mortimers_chipdisko.mod";
173 reader.read_string("music", song_title);
176 int width, height = 15;
177 reader.read_int("width", width);
178 reader.read_int("height", height);
180 std::vector<unsigned int> tiles;
181 if(reader.read_int_vector("interactive-tm", tiles)
182 || reader.read_int_vector("tilemap", tiles)) {
183 TileMap* tilemap = new TileMap();
184 tilemap->set(width, height, tiles, LAYER_TILES, true);
189 if(reader.read_int_vector("background-tm", tiles)) {
190 TileMap* tilemap = new TileMap();
191 tilemap->set(width, height, tiles, LAYER_BACKGROUNDTILES, false);
195 if(reader.read_int_vector("foreground-tm", tiles)) {
196 TileMap* tilemap = new TileMap();
197 tilemap->set(width, height, tiles, LAYER_FOREGROUNDTILES, false);
201 // TODO read resetpoints
205 lisp_object_t* cur = 0;
206 if(reader.read_lisp("objects", cur)) {
207 while(!lisp_nil_p(cur)) {
208 lisp_object_t* data = lisp_car(cur);
209 std::string object_type = lisp_symbol(lisp_car(data));
211 LispReader reader(lisp_cdr(data));
213 if(object_type == "trampoline") {
214 add_object(new Trampoline(reader));
216 else if(object_type == "flying-platform") {
217 add_object(new FlyingPlatform(reader));
220 BadGuyKind kind = badguykind_from_string(object_type);
221 add_object(new BadGuy(kind, reader));
230 camera = new Camera(this);
235 Sector::write(LispWriter& writer)
237 writer.write_string("name", name);
238 writer.write_float("gravity", gravity);
240 for(GameObjects::iterator i = gameobjects.begin();
241 i != gameobjects.end(); ++i) {
242 Serializable* serializable = dynamic_cast<Serializable*> (*i);
244 serializable->write(writer);
249 Sector::add_object(GameObject* object)
251 // XXX a bit hackish, at least try to keep the number of these things down...
252 BadGuy* badguy = dynamic_cast<BadGuy*> (object);
254 badguys.push_back(badguy);
255 Bullet* bullet = dynamic_cast<Bullet*> (object);
257 bullets.push_back(bullet);
258 Upgrade* upgrade = dynamic_cast<Upgrade*> (object);
260 upgrades.push_back(upgrade);
261 Trampoline* trampoline = dynamic_cast<Trampoline*> (object);
263 trampolines.push_back(trampoline);
264 FlyingPlatform* flying_platform = dynamic_cast<FlyingPlatform*> (object);
266 flying_platforms.push_back(flying_platform);
267 Background* background = dynamic_cast<Background*> (object);
269 this->background = background;
271 gameobjects.push_back(object);
275 Sector::activate(const std::string& spawnpoint)
279 // Apply bonuses from former levels
280 switch (player_status.bonus)
282 case PlayerStatus::NO_BONUS:
285 case PlayerStatus::FLOWER_BONUS:
286 player->got_power = Player::FIRE_POWER; // FIXME: add ice power to here
289 case PlayerStatus::GROWUP_BONUS:
295 for(SpawnPoints::iterator i = spawnpoints.begin(); i != spawnpoints.end();
297 if((*i)->name == spawnpoint) {
303 std::cerr << "Spawnpoint '" << spawnpoint << "' not found.\n";
305 player->move(sp->pos);
308 camera->reset(Vector(player->base.x, player->base.y));
312 Sector::action(float elapsed_time)
314 player->check_bounds(camera);
316 /* update objects (don't use iterators here, because the list might change
317 * during the iteration)
319 for(size_t i = 0; i < gameobjects.size(); ++i)
320 if(gameobjects[i]->is_valid())
321 gameobjects[i]->action(elapsed_time);
323 /* Handle all possible collisions. */
326 /** cleanup marked objects */
327 for(std::vector<GameObject*>::iterator i = gameobjects.begin();
328 i != gameobjects.end(); /* nothing */) {
329 if((*i)->is_valid() == false) {
330 BadGuy* badguy = dynamic_cast<BadGuy*> (*i);
332 badguys.erase(std::remove(badguys.begin(), badguys.end(), badguy),
335 Bullet* bullet = dynamic_cast<Bullet*> (*i);
338 std::remove(bullets.begin(), bullets.end(), bullet),
341 Upgrade* upgrade = dynamic_cast<Upgrade*> (*i);
344 std::remove(upgrades.begin(), upgrades.end(), upgrade),
347 Trampoline* trampoline = dynamic_cast<Trampoline*> (*i);
350 std::remove(trampolines.begin(), trampolines.end(), trampoline),
353 FlyingPlatform* flying_platform= dynamic_cast<FlyingPlatform*> (*i);
354 if(flying_platform) {
355 flying_platforms.erase(
356 std::remove(flying_platforms.begin(), flying_platforms.end(), flying_platform),
357 flying_platforms.end());
361 i = gameobjects.erase(i);
369 Sector::draw(DrawingContext& context)
371 context.push_transform();
372 context.set_translation(camera->get_translation());
374 for(GameObjects::iterator i = gameobjects.begin();
375 i != gameobjects.end(); ++i) {
376 if( (*i)->is_valid() )
380 context.pop_transform();
384 Sector::collision_handler()
386 // CO_BULLET & CO_BADGUY check
387 for(unsigned int i = 0; i < bullets.size(); ++i)
389 for (BadGuys::iterator j = badguys.begin(); j != badguys.end(); ++j)
391 if((*j)->dying != DYING_NOT)
394 if(rectcollision(bullets[i]->base, (*j)->base))
396 // We have detected a collision and now call the
397 // collision functions of the collided objects.
398 (*j)->collision(bullets[i], CO_BULLET, COLLISION_NORMAL);
399 bullets[i]->collision(CO_BADGUY);
400 break; // bullet is invalid now, so break
405 /* CO_BADGUY & CO_BADGUY check */
406 for (BadGuys::iterator i = badguys.begin(); i != badguys.end(); ++i)
408 if((*i)->dying != DYING_NOT)
411 BadGuys::iterator j = i;
413 for (; j != badguys.end(); ++j)
415 if(j == i || (*j)->dying != DYING_NOT)
418 if(rectcollision((*i)->base, (*j)->base))
420 // We have detected a collision and now call the
421 // collision functions of the collided objects.
422 (*j)->collision(*i, CO_BADGUY);
423 (*i)->collision(*j, CO_BADGUY);
427 if(player->dying != DYING_NOT) return;
429 // CO_BADGUY & CO_PLAYER check
430 for (BadGuys::iterator i = badguys.begin(); i != badguys.end(); ++i)
432 if((*i)->dying != DYING_NOT)
435 if(rectcollision_offset((*i)->base, player->base, 0, 0))
437 // We have detected a collision and now call the collision
438 // functions of the collided objects.
439 if (player->previous_base.y < player->base.y &&
440 player->previous_base.y + player->previous_base.height
441 < (*i)->base.y + (*i)->base.height/2
442 && !player->invincible_timer.started())
444 (*i)->collision(player, CO_PLAYER, COLLISION_SQUISH);
448 player->collision(*i, CO_BADGUY);
449 (*i)->collision(player, CO_PLAYER, COLLISION_NORMAL);
454 // CO_UPGRADE & CO_PLAYER check
455 for(unsigned int i = 0; i < upgrades.size(); ++i)
457 if(rectcollision(upgrades[i]->base, player->base))
459 // We have detected a collision and now call the collision
460 // functions of the collided objects.
461 upgrades[i]->collision(player, CO_PLAYER, COLLISION_NORMAL);
465 // CO_TRAMPOLINE & (CO_PLAYER or CO_BADGUY)
466 for (Trampolines::iterator i = trampolines.begin(); i != trampolines.end(); ++i)
468 if (rectcollision((*i)->base, player->base))
470 if (player->previous_base.y < player->base.y &&
471 player->previous_base.y + player->previous_base.height
472 < (*i)->base.y + (*i)->base.height/2)
474 (*i)->collision(player, CO_PLAYER, COLLISION_SQUISH);
476 else if (player->previous_base.y <= player->base.y)
478 player->collision(*i, CO_TRAMPOLINE);
479 (*i)->collision(player, CO_PLAYER, COLLISION_NORMAL);
484 // CO_FLYING_PLATFORM & (CO_PLAYER or CO_BADGUY)
485 for (FlyingPlatforms::iterator i = flying_platforms.begin(); i != flying_platforms.end(); ++i)
487 if (rectcollision((*i)->base, player->base))
489 if (player->previous_base.y < player->base.y &&
490 player->previous_base.y + player->previous_base.height
491 < (*i)->base.y + (*i)->base.height/2)
493 (*i)->collision(player, CO_PLAYER, COLLISION_SQUISH);
494 player->collision(*i, CO_FLYING_PLATFORM);
496 /* else if (player->previous_base.y <= player->base.y)
504 Sector::add_score(const Vector& pos, int s)
506 player_status.score += s;
508 add_object(new FloatingScore(pos, s));
512 Sector::add_bouncy_distro(const Vector& pos)
514 add_object(new BouncyDistro(pos));
518 Sector::add_broken_brick(const Vector& pos, Tile* tile)
520 add_broken_brick_piece(pos, Vector(-1, -4), tile);
521 add_broken_brick_piece(pos + Vector(0, 16), Vector(-1.5, -3), tile);
523 add_broken_brick_piece(pos + Vector(16, 0), Vector(1, -4), tile);
524 add_broken_brick_piece(pos + Vector(16, 16), Vector(1.5, -3), tile);
528 Sector::add_broken_brick_piece(const Vector& pos, const Vector& movement,
531 add_object(new BrokenBrick(tile, pos, movement));
535 Sector::add_bouncy_brick(const Vector& pos)
537 add_object(new BouncyBrick(pos));
541 Sector::add_bad_guy(float x, float y, BadGuyKind kind)
543 BadGuy* badguy = new BadGuy(kind, x, y);
549 Sector::add_upgrade(const Vector& pos, Direction dir, UpgradeKind kind)
551 add_object(new Upgrade(pos, dir, kind));
555 Sector::add_bullet(const Vector& pos, float xm, Direction dir)
557 if(player->got_power == Player::FIRE_POWER)
559 if(bullets.size() > MAX_FIRE_BULLETS-1)
562 else if(player->got_power == Player::ICE_POWER)
564 if(bullets.size() > MAX_ICE_BULLETS-1)
568 Bullet* new_bullet = 0;
569 if(player->got_power == Player::FIRE_POWER)
570 new_bullet = new Bullet(pos, xm, dir, FIRE_BULLET);
571 else if(player->got_power == Player::ICE_POWER)
572 new_bullet = new Bullet(pos, xm, dir, ICE_BULLET);
574 throw std::runtime_error("wrong bullet type.");
575 add_object(new_bullet);
577 play_sound(sounds[SND_SHOOT], SOUND_CENTER_SPEAKER);
584 Sector::trybreakbrick(const Vector& pos, bool small)
586 Tile* tile = solids->get_tile_at(pos);
587 if (tile->attributes & Tile::BRICK)
591 /* Get a distro from it: */
593 Vector(((int)(pos.x + 1) / 32) * 32, (int)(pos.y / 32) * 32));
595 // TODO: don't handle this in a global way but per-tile...
596 if (!counting_distros)
598 counting_distros = true;
606 if (distro_counter <= 0)
608 counting_distros = false;
609 solids->change_at(pos, tile->next_tile);
612 play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
613 player_status.score = player_status.score + SCORE_DISTRO;
614 player_status.distros++;
620 solids->change_at(pos, tile->next_tile);
622 /* Replace it with broken bits: */
623 add_broken_brick(Vector(
624 ((int)(pos.x + 1) / 32) * 32,
625 (int)(pos.y / 32) * 32), tile);
627 /* Get some score: */
628 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
629 player_status.score = player_status.score + SCORE_BRICK;
640 Sector::tryemptybox(const Vector& pos, Direction col_side)
642 Tile* tile = solids->get_tile_at(pos);
643 if (!(tile->attributes & Tile::FULLBOX))
646 // according to the collision side, set the upgrade direction
652 int posx = ((int)(pos.x+1) / 32) * 32;
653 int posy = (int)(pos.y/32) * 32 - 32;
656 case 1: // Box with a distro!
657 add_bouncy_distro(Vector(posx, posy));
658 play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
659 player_status.score = player_status.score + SCORE_DISTRO;
660 player_status.distros++;
663 case 2: // Add a fire flower upgrade!
664 if (player->size == SMALL) /* Tux is small, add mints! */
665 add_upgrade(Vector(posx, posy), col_side, UPGRADE_GROWUP);
666 else /* Tux is big, add a fireflower: */
667 add_upgrade(Vector(posx, posy), col_side, UPGRADE_FIREFLOWER);
668 play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER);
671 case 5: // Add an ice flower upgrade!
672 if (player->size == SMALL) /* Tux is small, add mints! */
673 add_upgrade(Vector(posx, posy), col_side, UPGRADE_GROWUP);
674 else /* Tux is big, add an iceflower: */
675 add_upgrade(Vector(posx, posy), col_side, UPGRADE_ICEFLOWER);
676 play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER);
679 case 3: // Add a golden herring
680 add_upgrade(Vector(posx, posy), col_side, UPGRADE_HERRING);
683 case 4: // Add a 1up extra
684 add_upgrade(Vector(posx, posy), col_side, UPGRADE_1UP);
691 solids->change_at(pos, tile->next_tile);
694 /* Try to grab a distro: */
696 Sector::trygrabdistro(const Vector& pos, int bounciness)
698 Tile* tile = solids->get_tile_at(pos);
699 if (!(tile->attributes & Tile::COIN))
702 solids->change_at(pos, tile->next_tile);
703 play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
705 if (bounciness == BOUNCE)
707 add_bouncy_distro(Vector(((int)(pos.x + 1) / 32) * 32,
708 (int)(pos.y / 32) * 32));
711 player_status.score = player_status.score + SCORE_DISTRO;
712 player_status.distros++;
715 /* Try to bump a bad guy from below: */
717 Sector::trybumpbadguy(const Vector& pos)
720 for (BadGuys::iterator i = badguys.begin(); i != badguys.end(); ++i)
722 if ((*i)->base.x >= pos.x - 32 && (*i)->base.x <= pos.x + 32 &&
723 (*i)->base.y >= pos.y - 16 && (*i)->base.y <= pos.y + 16)
725 (*i)->collision(player, CO_PLAYER, COLLISION_BUMP);
730 for (unsigned int i = 0; i < upgrades.size(); i++)
732 if (upgrades[i]->base.height == 32 &&
733 upgrades[i]->base.x >= pos.x - 32 && upgrades[i]->base.x <= pos.x + 32 &&
734 upgrades[i]->base.y >= pos.y - 16 && upgrades[i]->base.y <= pos.y + 16)
736 upgrades[i]->collision(player, CO_PLAYER, COLLISION_BUMP);
747 level_song = music_manager->load_music(datadir + "/music/" + song_title);
749 song_path = (char *) malloc(sizeof(char) * datadir.length() +
750 strlen(song_title.c_str()) + 8 + 5);
751 song_subtitle = strdup(song_title.c_str());
752 strcpy(strstr(song_subtitle, "."), "\0");
753 sprintf(song_path, "%s/music/%s-fast%s", datadir.c_str(),
754 song_subtitle, strstr(song_title.c_str(), "."));
755 if(!music_manager->exists_music(song_path)) {
756 level_song_fast = level_song;
758 level_song_fast = music_manager->load_music(song_path);
765 Sector::play_music(int type)
768 switch(currentmusic) {
770 music_manager->play_music(level_song_fast);
773 music_manager->play_music(level_song);
776 music_manager->play_music(herring_song);
779 music_manager->halt_music();
785 Sector::get_music_type()