From: Matthias Braun Date: Tue, 25 May 2004 00:28:24 +0000 (+0000) Subject: implemented autoscrolling and proper camera parsing in levelfiles X-Git-Url: https://git.verplant.org/?a=commitdiff_plain;h=a7e1aac92757aa9fa50863fb9abc683cc5388b1d;p=supertux.git implemented autoscrolling and proper camera parsing in levelfiles SVN-Revision: 1320 --- diff --git a/src/Makefile.am b/src/Makefile.am index 23427d01b..8c230e72b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -90,6 +90,8 @@ tilemap.h \ tilemap.cpp \ moving_object.h \ moving_object.cpp \ -serializable.h +serializable.h \ +vector.cpp \ +vector.h # EOF # diff --git a/src/camera.cpp b/src/camera.cpp index 6ef816884..c25cc2050 100644 --- a/src/camera.cpp +++ b/src/camera.cpp @@ -18,6 +18,8 @@ // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "camera.h" +#include +#include #include #include "lispwriter.h" #include "player.h" @@ -25,7 +27,8 @@ #include "globals.h" Camera::Camera(Player* newplayer, Level* newlevel) - : player(newplayer), level(newlevel), scrollchange(NONE) + : player(newplayer), level(newlevel), do_backscrolling(true), + scrollchange(NONE), auto_idx(0), auto_t(0) { if(!player || !level) mode = MANUAL; @@ -44,14 +47,74 @@ Camera::set_translation(const Vector& newtranslation) } void +Camera::read(LispReader& reader) +{ + std::string modename; + + reader.read_string("mode", &modename); + if(modename == "normal") { + mode = NORMAL; + + do_backscrolling = true; + reader.read_bool("backscrolling", &do_backscrolling); + } else if(modename == "autoscroll") { + mode = AUTOSCROLL; + + lisp_object_t* cur = 0; + reader.read_lisp("path", &cur); + if(cur == 0) { + throw std::runtime_error("No path specified in autoscroll camera."); + } + float speed = .5; + while(!lisp_nil_p(cur)) { + if(strcmp(lisp_symbol(lisp_car(lisp_car(cur))), "point") != 0) { + std::cerr << "Warning: unknown token in camera path.\n"; + continue; + } + + LispReader reader(lisp_cdr(lisp_car(cur))); + + ScrollPoint point; + if(!reader.read_float("x", &point.position.x) || + !reader.read_float("y", &point.position.y)) { + throw std::runtime_error("x and y missing in point of camerapath"); + } + reader.read_float("speed", &speed); + point.speed = speed; + scrollpoints.push_back(point); + + cur = lisp_cdr(cur); + } + } else if(modename == "manual") { + mode = MANUAL; + } else { + std::stringstream str; + str << "invalid camera mode '" << modename << "'found in worldfile."; + throw std::runtime_error(str.str()); + } +} + +void Camera::write(LispWriter& writer) { writer.start_list("camera"); if(mode == NORMAL) { writer.write_string("mode", "normal"); + writer.write_bool("backscrolling", do_backscrolling); } else if(mode == AUTOSCROLL) { writer.write_string("mode", "autoscroll"); + writer.start_list("path"); + for(std::vector::iterator i = scrollpoints.begin(); + i != scrollpoints.end(); ++i) { + writer.start_list("point"); + writer.write_float("x", i->position.x); + writer.write_float("y", i->position.y); + writer.write_float("speed", i->speed); + writer.end_list("point"); + } + + writer.end_list("path"); } else if(mode == MANUAL) { writer.write_string("mode", "manual"); } @@ -72,6 +135,20 @@ Camera::action(float elapsed_time) } void +Camera::keep_in_bounds() +{ + // don't scroll before the start or after the level's end + if(translation.y > level->height * 32 - screen->h) + translation.y = level->height * 32 - screen->h; + if(translation.y < 0) + translation.y = 0; + if(translation.x > level->width * 32 - screen->w) + translation.x = level->width * 32 - screen->w; + if(translation.x < 0) + translation.x = 0; +} + +void Camera::scroll_normal(float elapsed_time) { assert(level != 0 && player != 0); @@ -112,12 +189,6 @@ Camera::scroll_normal(float elapsed_time) // finally scroll with calculated speed translation.y -= speed_y * elapsed_time; - - // don't scroll before the start or after the level's end - if(translation.y > level->height * 32 - screen->h) - translation.y = level->height * 32 - screen->h; - if(translation.y < 0) - translation.y = 0; } /****** Horizontal scrolling part *******/ @@ -131,7 +202,7 @@ Camera::scroll_normal(float elapsed_time) || (player->dir == ::RIGHT && scrollchange == LEFT)) scrollchange = NONE; // when in left 1/3rd of screen scroll left - if(player->base.x < translation.x + screen->w/3 && level->back_scrolling) + if(player->base.x < translation.x + screen->w/3 && do_backscrolling) scrollchange = LEFT; // scroll right when in right 1/3rd of screen else if(player->base.x > translation.x + screen->w/3*2) @@ -161,15 +232,39 @@ Camera::scroll_normal(float elapsed_time) // apply scrolling translation.x -= speed_x * elapsed_time; - // don't scroll before the start or after the level's end - if(translation.x > level->width * 32 - screen->w) - translation.x = level->width * 32 - screen->w; - if(translation.x < 0) - translation.x = 0; + keep_in_bounds(); } void Camera::scroll_autoscroll(float elapsed_time) { - // TODO + if(player->dying) + return; + + if(auto_t - elapsed_time >= 0) { + translation += current_dir * elapsed_time; + auto_t -= elapsed_time; + } else { + // do the rest of the old movement + translation += current_dir * auto_t; + elapsed_time -= auto_t; + auto_t = 0; + + // construct path for next point + if(auto_idx+1 >= scrollpoints.size()) { + keep_in_bounds(); + return; + } + Vector distance = scrollpoints[auto_idx+1].position + - scrollpoints[auto_idx].position; + current_dir = distance.unit() * scrollpoints[auto_idx].speed; + auto_t = distance.norm() / scrollpoints[auto_idx].speed; + + // do movement for the remaining time + translation += current_dir * elapsed_time; + auto_t -= elapsed_time; + auto_idx++; + } + + keep_in_bounds(); } diff --git a/src/camera.h b/src/camera.h index fd37ed95a..453a06a8f 100644 --- a/src/camera.h +++ b/src/camera.h @@ -19,6 +19,7 @@ #ifndef __VIEWPORT_H__ #define __VIEWPORT_H__ +#include #include "vector.h" #include "game_object.h" #include "serializable.h" @@ -44,7 +45,7 @@ public: } /// parse camera mode from lisp file - void parse_camera(LispReader& reader); + void read(LispReader& reader); /// write camera mode to a lisp file virtual void write(LispWriter& writer); @@ -60,10 +61,12 @@ public: { NORMAL, AUTOSCROLL, MANUAL }; + CameraMode mode; private: void scroll_normal(float elapsed_time); void scroll_autoscroll(float elapsed_time); + void keep_in_bounds(); enum LeftRightScrollChange { @@ -74,12 +77,21 @@ private: Player* player; Level* level; - CameraMode mode; // normal mode + bool do_backscrolling; LeftRightScrollChange scrollchange; // autoscroll mode + class ScrollPoint { + public: + Vector position; + float speed; + }; + std::vector scrollpoints; + size_t auto_idx; + float auto_t; + Vector current_dir; }; #endif diff --git a/src/level.cpp b/src/level.cpp index 3a5539884..82c1d6216 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -223,8 +223,8 @@ Level::init_defaults() bkgd_image = "arctis.jpg"; width = 0; height = 0; - start_pos_x = 100; - start_pos_y = 170; + start_pos.x = 100; + start_pos.y = 170; time_left = 100; gravity = 10.; back_scrolling = false; @@ -277,8 +277,8 @@ Level::load(const std::string& filename, World* world) reader.read_int("version", &version); if(!reader.read_int("width", &width)) st_abort("No width specified for level.", ""); - if (!reader.read_int("start_pos_x", &start_pos_x)) start_pos_x = 100; - if (!reader.read_int("start_pos_y", &start_pos_y)) start_pos_y = 170; + if (!reader.read_float("start_pos_x", &start_pos.x)) start_pos.x = 100; + if (!reader.read_float("start_pos_y", &start_pos.y)) start_pos.y = 170; time_left = 500; if(!reader.read_int("time", &time_left)) { printf("Warning no time specified for level.\n"); @@ -355,7 +355,7 @@ Level::load(const std::string& filename, World* world) } } - { // Read BadGuys + { // Read Objects lisp_object_t* cur = 0; if (reader.read_lisp("objects", &cur)) { @@ -364,77 +364,15 @@ Level::load(const std::string& filename, World* world) } } -#if 0 // TODO fix this or remove it - // Convert old levels to the new tile numbers - if (version == 0) - { - std::map transtable; - transtable['.'] = 0; - transtable['x'] = 104; - transtable['X'] = 77; - transtable['y'] = 78; - transtable['Y'] = 105; - transtable['A'] = 83; - transtable['B'] = 102; - transtable['!'] = 103; - transtable['a'] = 84; - transtable['C'] = 85; - transtable['D'] = 86; - transtable['E'] = 87; - transtable['F'] = 88; - transtable['c'] = 89; - transtable['d'] = 90; - transtable['e'] = 91; - transtable['f'] = 92; - - transtable['G'] = 93; - transtable['H'] = 94; - transtable['I'] = 95; - transtable['J'] = 96; - - transtable['g'] = 97; - transtable['h'] = 98; - transtable['i'] = 99; - transtable['j'] = 100 - ; - transtable['#'] = 11; - transtable['['] = 13; - transtable['='] = 14; - transtable[']'] = 15; - transtable['$'] = 82; - transtable['^'] = 76; - transtable['*'] = 80; - transtable['|'] = 79; - transtable['\\'] = 81; - transtable['&'] = 75; - - int x = 0; - int y = 0; - for(std::vector::iterator i = ia_tm.begin(); i != ia_tm.end(); ++i) - { - if (*i == '0' || *i == '1' || *i == '2') - { - badguy_data.push_back(BadGuyData(static_cast(*i-'0'), - x*32, y*32, false)); - *i = 0; - } - else - { - std::map::iterator j = transtable.find(*i); - if (j != transtable.end()) - *i = j->second; - else - printf("Error: conversion will fail, unsupported char: '%c' (%d)\n", *i, *i); - } - ++x; - if (x >= width) - { - x = 0; - ++y; - } - } - } -#endif + { // Read Camera + lisp_object_t* cur = 0; + if (reader.read_lisp("camera", &cur)) + { + LispReader reader(cur); + if(world) + world->camera->read(reader); + } + } } lisp_free(root_obj); diff --git a/src/level.h b/src/level.h index 1d2b67829..70015fc59 100644 --- a/src/level.h +++ b/src/level.h @@ -92,8 +92,7 @@ class Level int width; int height; int bkgd_speed; - int start_pos_x; - int start_pos_y; + Vector start_pos; float gravity; bool back_scrolling; float hor_autoscroll_speed; diff --git a/src/player.cpp b/src/player.cpp index fde829293..18539a4c1 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -83,8 +83,6 @@ Player::~Player() void Player::init() { - Level* plevel = World::current()->get_level(); - holding_something = false; base.width = 32; @@ -93,12 +91,13 @@ Player::init() size = SMALL; got_power = NONE_POWER; - base.x = plevel->start_pos_x; - base.y = plevel->start_pos_y; + base.x = 0; + base.y = 0; previous_base = old_base = base; dir = RIGHT; old_dir = dir; duck = false; + dead = false; dying = DYING_NOT; last_ground_y = 0; @@ -181,6 +180,11 @@ Player::action(float elapsed_time) { bool jumped_in_solid = false; + if(dying && !dying_timer.check()) { + dead = true; + return; + } + if (input.fire == UP) holding_something = false; @@ -831,6 +835,9 @@ Player::collision(void* p_c_object, int c_object) void Player::kill(HurtMode mode) { + if(dying) + return; + play_sound(sounds[SND_HURT], SOUND_CENTER_SPEAKER); physic.set_velocity_x(0); @@ -854,32 +861,12 @@ Player::kill(HurtMode mode) physic.enable_gravity(true); physic.set_acceleration(0, 0); physic.set_velocity(0, 7); - if(dying != DYING_SQUISHED) --player_status.lives; dying = DYING_SQUISHED; + dying_timer.start(3000); } } -void -Player::is_dying() -{ - remove_powerups(); - dying = DYING_NOT; -} - -bool Player::is_dead() -{ - float scroll_x = - World::current()->camera->get_translation().x; - float scroll_y = - World::current()->camera->get_translation().y; - if(base.y > screen->h + scroll_y || base.y > World::current()->get_level()->height*32 || - base.x < scroll_x - AUTOSCROLL_DEAD_INTERVAL) // can happen in auto-scrolling - return true; - else - return false; -} - /* Remove Tux's power ups */ void Player::remove_powerups() @@ -890,6 +877,14 @@ Player::remove_powerups() } void +Player::move(const Vector& vector) +{ + base.x = vector.x; + base.y = vector.y; + old_base = previous_base = base; +} + +void Player::check_bounds(Camera& viewport, bool back_scrolling, bool hor_autoscroll) { @@ -904,10 +899,27 @@ Player::check_bounds(Camera& viewport, if (base.y > World::current()->get_level()->height * /*TILE_HEIGHT*/ 32) { kill(KILL); + return; } - if(base.x < viewport.get_translation().x && (!back_scrolling || hor_autoscroll)) // can happen if back scrolling is disabled + bool adjust = false; + // can happen if back scrolling is disabled + if(base.x < viewport.get_translation().x) { base.x = viewport.get_translation().x; + adjust = true; + } + if(base.x >= viewport.get_translation().x + screen->w - base.width) { + base.x = viewport.get_translation().x + screen->w - base.width; + adjust = true; + } + + if(adjust) { + // squished now? + if(collision_object_map(base)) { + kill(KILL); + return; + } + } if(hor_autoscroll) { diff --git a/src/player.h b/src/player.h index 04473f2a2..6852ebc14 100644 --- a/src/player.h +++ b/src/player.h @@ -119,6 +119,7 @@ public: int size; bool duck; bool holding_something; + bool dead; DyingType dying; Direction dir; @@ -140,6 +141,7 @@ public: Timer frame_timer; Timer kick_timer; Timer shooting_timer; // used to show the arm when Tux is shooting + Timer dying_timer; Physic physic; public: @@ -158,14 +160,15 @@ public: void collision(void* p_c_object, int c_object); void kill(HurtMode mode); - void is_dying(); - bool is_dead(); void player_remove_powerups(); void check_bounds(Camera& viewport, bool back_scrolling, bool hor_autoscroll); bool on_ground(); bool under_solid(); bool tiles_on_air(int tiles); void grow(); + void move(const Vector& vector); + bool is_dead() const + { return dead; } private: void init(); diff --git a/src/vector.cpp b/src/vector.cpp new file mode 100644 index 000000000..de3e53cd7 --- /dev/null +++ b/src/vector.cpp @@ -0,0 +1,12 @@ +#include "vector.h" +#include + +Vector Vector::unit() const +{ + return *this / norm(); +} + +float Vector::norm() const +{ + return sqrt(x*x + y*y); +} diff --git a/src/vector.h b/src/vector.h index 6436c3f5a..21f4a6ada 100644 --- a/src/vector.h +++ b/src/vector.h @@ -41,6 +41,11 @@ public: return Vector(x * s, y * s); } + Vector operator/(float s) const + { + return Vector(x / s, y / s); + } + const Vector& operator +=(const Vector& other) { x += other.x; @@ -48,6 +53,15 @@ public: return *this; } + // scalar product of 2 vectors + float operator*(const Vector& other) const + { + return x*other.x + y*other.y; + } + + float norm() const; + Vector unit() const; + // ... add the other operators as needed, I'm too lazy now ... float x, y; // leave this public, get/set methods just give me headaches diff --git a/src/world.cpp b/src/world.cpp index 9ba5d1867..0c28cb001 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -48,16 +48,20 @@ World::World(const std::string& filename, int level_nr) // world calls child functions current_ = this; + tux = new Player(displaymanager); + add_object(tux); + level = new Level(); + camera = new Camera(tux, level); + add_object(camera); + if(level_nr >= 0) { level->load(filename, level_nr, this); } else { level->load(filename, this); } - - tux = new Player(displaymanager); - add_object(tux); - + tux->move(level->start_pos); + set_defaults(); level->load_gfx(); @@ -75,9 +79,6 @@ World::World(const std::string& filename, int level_nr) add_object(new TileMap(displaymanager, level)); level->load_song(); - camera = new Camera(tux, level); - add_object(camera); - apply_bonuses(); }