4 // Copyright (C) 2000 Bill Kendrick <bill@newbreedsoftware.com>
5 // Copyright (C) 2004 Tobias Glaesser <tobi.web@gmx.de>
6 // Copyright (C) 2004 Ingo Ruhnke <grumbel@gmx.de>
8 // This program is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU General Public License
10 // as published by the Free Software Foundation; either version 2
11 // of the License, or (at your option) any later version.
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 // GNU General Public License for more details.
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the Free Software
20 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
34 #include "resources.h"
37 #include "display_manager.h"
38 #include "background.h"
41 Surface* img_distro[4];
43 World* World::current_ = 0;
45 World::World(const std::string& filename)
47 // FIXME: Move this to action and draw and everywhere else where the
48 // world calls child functions
51 level = new Level(filename);
56 get_level()->load_gfx();
59 activate_particle_systems();
60 Background* bg = new Background(displaymanager);
62 bg->set_image(level->img_bkgd, level->bkgd_speed);
64 bg->set_gradient(level->bkgd_top, level->bkgd_bottom);
66 gameobjects.push_back(bg);
69 gameobjects.push_back(new TileMap(displaymanager, get_level()));
71 get_level()->load_song();
75 scrolling_timer.init(true);
78 World::World(const std::string& subset, int level_nr)
80 // FIXME: Move this to action and draw and everywhere else where the
81 // world calls child functions
84 level = new Level(subset, level_nr);
89 get_level()->load_gfx();
92 activate_particle_systems();
93 Background* bg = new Background(displaymanager);
95 bg->set_image(level->img_bkgd, level->bkgd_speed);
97 bg->set_gradient(level->bkgd_top, level->bkgd_bottom);
99 gameobjects.push_back(bg);
101 gameobjects.push_back(new TileMap(displaymanager, get_level()));
102 get_level()->load_song();
106 scrolling_timer.init(true);
110 World::apply_bonuses()
112 // Apply bonuses from former levels
113 switch (player_status.bonus)
115 case PlayerStatus::NO_BONUS:
118 case PlayerStatus::FLOWER_BONUS:
119 tux.got_power = tux.FIRE_POWER; // FIXME: add ice power to here
122 case PlayerStatus::GROWUP_BONUS:
123 // FIXME: Move this to Player class
125 tux.base.height = 64;
133 for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
136 for (Trampolines::iterator i = trampolines.begin(); i != trampolines.end(); ++i)
139 for (std::vector<_GameObject*>::iterator i = gameobjects.begin();
140 i != gameobjects.end(); ++i) {
141 Drawable* drawable = dynamic_cast<Drawable*> (*i);
143 displaymanager.remove_drawable(drawable);
151 World::set_defaults()
156 player_status.score_multiplier = 1;
158 counting_distros = false;
161 /* set current song/music */
162 currentmusic = LEVEL_MUSIC;
166 World::activate_bad_guys()
168 for (std::vector<BadGuyData>::iterator i = level->badguy_data.begin();
169 i != level->badguy_data.end();
172 add_bad_guy(i->x, i->y, i->kind, i->stay_on_platform);
177 World::activate_objects()
179 for (std::vector< ObjectData<TrampolineData> >::iterator i = level->trampoline_data.begin();
180 i != level->trampoline_data.end();
183 add_object<Trampoline, ObjectData<TrampolineData> >(*i);
188 World::activate_particle_systems()
190 if (level->particle_system == "clouds")
192 gameobjects.push_back(new CloudParticleSystem(displaymanager));
194 else if (level->particle_system == "snow")
196 gameobjects.push_back(new SnowParticleSystem(displaymanager));
198 else if (level->particle_system != "")
200 st_abort("unknown particle system specified in level", "");
208 displaymanager.get_viewport().set_translation(Vector(scroll_x, scroll_y));
209 displaymanager.draw();
211 for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
214 for (Trampolines::iterator i = trampolines.begin(); i != trampolines.end(); ++i)
219 for (unsigned int i = 0; i < bullets.size(); ++i)
222 for (unsigned int i = 0; i < upgrades.size(); ++i)
227 World::action(double frame_ratio)
229 tux.action(frame_ratio);
230 tux.check_bounds(level->back_scrolling, (bool)level->hor_autoscroll_speed);
231 scrolling(frame_ratio);
233 for (unsigned int i = 0; i < bullets.size(); ++i)
234 bullets[i].action(frame_ratio);
236 for (unsigned int i = 0; i < upgrades.size(); i++)
237 upgrades[i].action(frame_ratio);
239 for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
240 (*i)->action(frame_ratio);
242 for (Trampolines::iterator i = trampolines.begin(); i != trampolines.end(); ++i)
243 (*i)->action(frame_ratio);
246 for(std::vector<_GameObject*>::iterator i = gameobjects.begin();
247 i != gameobjects.end(); ++i)
248 (*i)->action(frame_ratio);
250 /* Handle all possible collisions. */
253 // Cleanup marked badguys
254 for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end();
255 /* ++i handled at end of the loop */) {
256 if ((*i)->is_removable()) {
258 i = bad_guys.erase(i);
264 for(std::vector<_GameObject*>::iterator i = gameobjects.begin();
265 i != gameobjects.end(); /* nothing */) {
266 if((*i)->is_valid() == false) {
267 Drawable* drawable = dynamic_cast<Drawable*> (*i);
269 displaymanager.remove_drawable(drawable);
272 i = gameobjects.erase(i);
279 /* the space that it takes for the screen to start scrolling, regarding */
280 /* screen bounds (in pixels) */
281 // should be higher than screen->w/2 (400)
282 #define X_SPACE (500-16)
283 // should be less than screen->h/2 (300)
286 // the time it takes to move the camera (in ms)
287 #define CHANGE_DIR_SCROLL_SPEED 2000
289 /* This functions takes cares of the scrolling */
290 void World::scrolling(double frame_ratio)
292 /* Y-axis scrolling */
294 float tux_pos_y = tux.base.y + (tux.base.height/2);
296 if(level->height > VISIBLE_TILES_Y-1 && !tux.dying)
298 if (scroll_y < tux_pos_y - (screen->h - Y_SPACE))
299 scroll_y = tux_pos_y - (screen->h - Y_SPACE);
300 else if (scroll_y > tux_pos_y - Y_SPACE)
301 scroll_y = tux_pos_y - Y_SPACE;
304 // this code prevent the screen to scroll before the start or after the level's end
305 if(scroll_y > level->height * 32 - screen->h)
306 scroll_y = level->height * 32 - screen->h;
310 /* X-axis scrolling */
313 if(level->hor_autoscroll_speed)
315 scroll_x += level->hor_autoscroll_speed * frame_ratio;
320 /* Horizontal backscrolling */
321 float tux_pos_x = tux.base.x + (tux.base.width/2);
323 if(tux.old_dir != tux.dir && level->back_scrolling)
324 scrolling_timer.start(CHANGE_DIR_SCROLL_SPEED);
328 if (tux.physic.get_velocity_x() > 0)
330 else if (tux.physic.get_velocity_x() < 0)
334 if (tux.dir == RIGHT)
340 if(scrolling_timer.check())
342 float final_scroll_x;
346 final_scroll_x = tux_pos_x - (screen->w - X_SPACE);
348 final_scroll_x = tux_pos_x - X_SPACE;
350 if((tux.physic.get_velocity_x() > 0 && tux.dir == RIGHT) || (tux.physic.get_velocity_x() < 0 && tux.dir == LEFT))
361 float number = 2.5/(frame_ratio * CHANGE_DIR_SCROLL_SPEED/1000)*exp((CHANGE_DIR_SCROLL_SPEED-scrolling_timer.get_left())/1400.);
362 if(left) number *= -1.;
365 + constant1 * tux.physic.get_velocity_x() * frame_ratio
366 + constant2 * tux.physic.get_acceleration_x() * frame_ratio * frame_ratio;
368 if ((right && final_scroll_x - scroll_x < 0) || (left && final_scroll_x - scroll_x > 0))
369 scroll_x = final_scroll_x;
374 if (right && scroll_x < tux_pos_x - (screen->w - X_SPACE))
375 scroll_x = tux_pos_x - (screen->w - X_SPACE);
376 else if (left && scroll_x > tux_pos_x - X_SPACE && level->back_scrolling)
377 scroll_x = tux_pos_x - X_SPACE;
380 // this code prevent the screen to scroll before the start or after the level's end
381 if(scroll_x > level->width * 32 - screen->w)
382 scroll_x = level->width * 32 - screen->w;
388 World::collision_handler()
390 // CO_BULLET & CO_BADGUY check
391 for(unsigned int i = 0; i < bullets.size(); ++i)
393 for (BadGuys::iterator j = bad_guys.begin(); j != bad_guys.end(); ++j)
395 if((*j)->dying != DYING_NOT)
398 if(rectcollision(bullets[i].base, (*j)->base))
400 // We have detected a collision and now call the
401 // collision functions of the collided objects.
402 // collide with bad_guy first, since bullet_collision will
404 (*j)->collision(&bullets[i], CO_BULLET);
405 bullets[i].collision(CO_BADGUY);
406 break; // bullet is invalid now, so break
411 /* CO_BADGUY & CO_BADGUY check */
412 for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
414 if((*i)->dying != DYING_NOT)
417 BadGuys::iterator j = i;
419 for (; j != bad_guys.end(); ++j)
421 if(j == i || (*j)->dying != DYING_NOT)
424 if(rectcollision((*i)->base, (*j)->base))
426 // We have detected a collision and now call the
427 // collision functions of the collided objects.
428 (*j)->collision(*i, CO_BADGUY);
429 (*i)->collision(*j, CO_BADGUY);
434 if(tux.dying != DYING_NOT) return;
436 // CO_BADGUY & CO_PLAYER check
437 for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
439 if((*i)->dying != DYING_NOT)
442 if(rectcollision_offset((*i)->base, tux.base, 0, 0))
444 // We have detected a collision and now call the collision
445 // functions of the collided objects.
446 if (tux.previous_base.y < tux.base.y &&
447 tux.previous_base.y + tux.previous_base.height
448 < (*i)->base.y + (*i)->base.height/2
449 && !tux.invincible_timer.started())
451 (*i)->collision(&tux, CO_PLAYER, COLLISION_SQUISH);
455 tux.collision(*i, CO_BADGUY);
456 (*i)->collision(&tux, CO_PLAYER, COLLISION_NORMAL);
461 // CO_UPGRADE & CO_PLAYER check
462 for(unsigned int i = 0; i < upgrades.size(); ++i)
464 if(rectcollision(upgrades[i].base, tux.base))
466 // We have detected a collision and now call the collision
467 // functions of the collided objects.
468 upgrades[i].collision(&tux, CO_PLAYER, COLLISION_NORMAL);
472 // CO_TRAMPOLINE & (CO_PLAYER or CO_BADGUY)
473 for (Trampolines::iterator i = trampolines.begin(); i != trampolines.end(); ++i)
475 if (rectcollision((*i)->base, tux.base))
477 if (tux.previous_base.y < tux.base.y &&
478 tux.previous_base.y + tux.previous_base.height
479 < (*i)->base.y + (*i)->base.height/2)
481 (*i)->collision(&tux, CO_PLAYER, COLLISION_SQUISH);
483 else if (tux.previous_base.y <= tux.base.y)
485 tux.collision(*i, CO_TRAMPOLINE);
486 (*i)->collision(&tux, CO_PLAYER, COLLISION_NORMAL);
493 World::add_score(const Vector& pos, int s)
495 player_status.score += s;
497 gameobjects.push_back(new FloatingScore(displaymanager, pos, s));
501 World::add_bouncy_distro(const Vector& pos)
503 gameobjects.push_back(new BouncyDistro(displaymanager, pos));
507 World::add_broken_brick(const Vector& pos, Tile* tile)
509 add_broken_brick_piece(pos, Vector(-1, -4), tile);
510 add_broken_brick_piece(pos + Vector(0, 16), Vector(-1.5, -3), tile);
512 add_broken_brick_piece(pos + Vector(16, 0), Vector(1, -4), tile);
513 add_broken_brick_piece(pos + Vector(16, 16), Vector(1.5, -3), tile);
517 World::add_broken_brick_piece(const Vector& pos, const Vector& movement,
520 gameobjects.push_back(new BrokenBrick(displaymanager, tile, pos, movement));
524 World::add_bouncy_brick(const Vector& pos)
526 gameobjects.push_back(new BouncyBrick(displaymanager, pos));
530 World::add_bad_guy(float x, float y, BadGuyKind kind, bool stay_on_platform)
532 BadGuy* badguy = new BadGuy(x,y,kind, stay_on_platform);
533 bad_guys.push_back(badguy);
537 template<class T, class U>
539 World::add_object(U data)
541 T* tobject = new T(data);
543 if (data.type == OBJ_TRAMPOLINE)
544 trampolines.push_back(tobject);
550 World::add_upgrade(float x, float y, Direction dir, UpgradeKind kind)
553 new_upgrade.init(x,y,dir,kind);
554 upgrades.push_back(new_upgrade);
558 World::add_bullet(float x, float y, float xm, Direction dir)
560 if(tux.got_power == tux.FIRE_POWER)
562 if(bullets.size() > MAX_FIRE_BULLETS-1)
565 else if(tux.got_power == tux.ICE_POWER)
567 if(bullets.size() > MAX_ICE_BULLETS-1)
572 if(tux.got_power == tux.FIRE_POWER)
573 new_bullet.init(x,y,xm,dir, FIRE_BULLET);
574 else if(tux.got_power == tux.ICE_POWER)
575 new_bullet.init(x,y,xm,dir, ICE_BULLET);
576 bullets.push_back(new_bullet);
578 play_sound(sounds[SND_SHOOT], SOUND_CENTER_SPEAKER);
582 World::play_music(int musictype)
584 currentmusic = musictype;
585 switch(currentmusic) {
587 music_manager->play_music(get_level()->get_level_music_fast());
590 music_manager->play_music(get_level()->get_level_music());
593 music_manager->play_music(herring_song);
596 music_manager->halt_music();
602 World::get_music_type()
609 World::trybreakbrick(float x, float y, bool small)
611 Level* plevel = get_level();
613 Tile* tile = gettile(x, y);
618 /* Get a distro from it: */
620 Vector(((int)(x + 1) / 32) * 32, (int)(y / 32) * 32));
622 // TODO: don't handle this in a global way but per-tile...
623 if (!counting_distros)
625 counting_distros = true;
633 if (distro_counter <= 0)
635 counting_distros = false;
636 plevel->change(x, y, TM_IA, tile->next_tile);
639 play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
640 player_status.score = player_status.score + SCORE_DISTRO;
641 player_status.distros++;
647 plevel->change(x, y, TM_IA, tile->next_tile);
649 /* Replace it with broken bits: */
650 add_broken_brick(Vector(
651 ((int)(x + 1) / 32) * 32,
652 (int)(y / 32) * 32), tile);
654 /* Get some score: */
655 play_sound(sounds[SND_BRICK], SOUND_CENTER_SPEAKER);
656 player_status.score = player_status.score + SCORE_BRICK;
667 World::tryemptybox(float x, float y, Direction col_side)
669 Tile* tile = gettile(x,y);
673 // according to the collision side, set the upgrade direction
679 int posx = ((int)(x+1) / 32) * 32;
680 int posy = (int)(y/32) * 32 - 32;
683 case 1: // Box with a distro!
684 add_bouncy_distro(Vector(posx, posy));
685 play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
686 player_status.score = player_status.score + SCORE_DISTRO;
687 player_status.distros++;
690 case 2: // Add a fire flower upgrade!
691 if (tux.size == SMALL) /* Tux is small, add mints! */
692 add_upgrade(posx, posy, col_side, UPGRADE_GROWUP);
693 else /* Tux is big, add a fireflower: */
694 add_upgrade(posx, posy, col_side, UPGRADE_FIREFLOWER);
695 play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER);
698 case 5: // Add an ice flower upgrade!
699 if (tux.size == SMALL) /* Tux is small, add mints! */
700 add_upgrade(posx, posy, col_side, UPGRADE_GROWUP);
701 else /* Tux is big, add an iceflower: */
702 add_upgrade(posx, posy, col_side, UPGRADE_ICEFLOWER);
703 play_sound(sounds[SND_UPGRADE], SOUND_CENTER_SPEAKER);
706 case 3: // Add a golden herring
707 add_upgrade(posx, posy, col_side, UPGRADE_HERRING);
710 case 4: // Add a 1up extra
711 add_upgrade(posx, posy, col_side, UPGRADE_1UP);
718 level->change(x, y, TM_IA, tile->next_tile);
721 /* Try to grab a distro: */
723 World::trygrabdistro(float x, float y, int bounciness)
725 Tile* tile = gettile(x, y);
726 if (tile && tile->distro)
728 level->change(x, y, TM_IA, tile->next_tile);
729 play_sound(sounds[SND_DISTRO], SOUND_CENTER_SPEAKER);
731 if (bounciness == BOUNCE)
733 add_bouncy_distro(Vector(((int)(x + 1) / 32) * 32,
734 (int)(y / 32) * 32));
737 player_status.score = player_status.score + SCORE_DISTRO;
738 player_status.distros++;
742 /* Try to bump a bad guy from below: */
744 World::trybumpbadguy(float x, float y)
747 for (BadGuys::iterator i = bad_guys.begin(); i != bad_guys.end(); ++i)
749 if ((*i)->base.x >= x - 32 && (*i)->base.x <= x + 32 &&
750 (*i)->base.y >= y - 16 && (*i)->base.y <= y + 16)
752 (*i)->collision(&tux, CO_PLAYER, COLLISION_BUMP);
757 for (unsigned int i = 0; i < upgrades.size(); i++)
759 if (upgrades[i].base.height == 32 &&
760 upgrades[i].base.x >= x - 32 && upgrades[i].base.x <= x + 32 &&
761 upgrades[i].base.y >= y - 16 && upgrades[i].base.y <= y + 16)
763 upgrades[i].collision(&tux, CO_PLAYER, COLLISION_BUMP);