From 02288dc76442bd6616a200a2d22c0954d2fc8180 Mon Sep 17 00:00:00 2001 From: Matthias Braun Date: Fri, 31 Mar 2006 01:18:10 +0000 Subject: [PATCH] - Yet another try in the endless quest for perfect collision detection. This version seems to be working quiet good. Only bug I could find was when jumping on skull tiles that are jumping themselfes at the same moment... - Change paths to be no game objects anymore - slightly changed path syntax SVN-Revision: 3139 --- data/images/objects/platforms/big.png | Bin 0 -> 4525 bytes data/images/objects/platforms/big.sprite | 6 + data/images/objects/platforms/small.png | Bin 0 -> 3436 bytes data/images/objects/platforms/small.sprite | 6 + data/images/objects/trampoline/trampoline.sprite | 1 + data/images/tiles/doodads/iceshrub.sprite | 1 + data/levels/test/light.stl | 2 +- data/levels/test/platform.stl | 99 +++++----- data/levels/test/simple.stl | 2 +- data/levels/test/yeti.stl | 38 ++-- data/levels/world2/christoph2.stl | 42 +++-- data/levels/world2/level2.stl | 2 +- src/badguy/badguy.cpp | 2 +- src/badguy/stalactite.cpp | 2 +- src/collision.hpp | 2 +- src/collision_hit.hpp | 3 +- src/game_session.cpp | 10 +- src/level.cpp | 13 +- src/level.hpp | 2 - src/math/vector.hpp | 7 + src/moving_object.hpp | 4 + src/object/camera.cpp | 26 +-- src/object/camera.hpp | 9 +- src/object/path.cpp | 207 +++++++-------------- src/object/path.hpp | 78 ++++---- src/object/platform.cpp | 49 ++--- src/object/platform.hpp | 12 +- src/object/player.cpp | 63 +++++-- src/object/player.hpp | 11 +- src/object/powerup.cpp | 2 - src/object/rock.cpp | 8 +- src/physic.cpp | 39 ++-- src/physic.hpp | 11 +- src/sector.cpp | 220 ++++++++++++++--------- src/sector.hpp | 17 +- src/title.cpp | 10 -- 36 files changed, 522 insertions(+), 484 deletions(-) create mode 100644 data/images/objects/platforms/big.png create mode 100644 data/images/objects/platforms/big.sprite create mode 100644 data/images/objects/platforms/small.png create mode 100644 data/images/objects/platforms/small.sprite diff --git a/data/images/objects/platforms/big.png b/data/images/objects/platforms/big.png new file mode 100644 index 0000000000000000000000000000000000000000..c53d32bb29bce6e6f0675b7f3c97e4fa3ec1d9a3 GIT binary patch literal 4525 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 LNkvXXu0mjfNKTTU literal 0 HcmV?d00001 diff --git a/data/images/objects/platforms/big.sprite b/data/images/objects/platforms/big.sprite new file mode 100644 index 000000000..dda1e870c --- /dev/null +++ b/data/images/objects/platforms/big.sprite @@ -0,0 +1,6 @@ +(supertux-sprite + (action + (name "default") + (images "big.png") + ) +) diff --git a/data/images/objects/platforms/small.png b/data/images/objects/platforms/small.png new file mode 100644 index 0000000000000000000000000000000000000000..880915be9e3b283f6e789cadfda46097fab9f02a GIT binary patch literal 3436 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 .5) { // hit floor or roof? + if(hit.normal.y < .9) { // hit floor? state = STALACTITE_SQUISHED; set_group(COLGROUP_MOVING_ONLY_STATIC); physic.set_velocity_y(0); diff --git a/src/collision.hpp b/src/collision.hpp index d32de1ba3..360f94d0a 100644 --- a/src/collision.hpp +++ b/src/collision.hpp @@ -41,7 +41,7 @@ public: * Returns true in case of a collision and fills in the hit structure then. */ static bool rectangle_aatriangle(CollisionHit& hit, const Rect& rect, - const Vector& movement, const AATriangle& triangle); + const Vector& movement, const AATriangle& triangle); }; #endif diff --git a/src/collision_hit.hpp b/src/collision_hit.hpp index 204460348..81547d8c6 100644 --- a/src/collision_hit.hpp +++ b/src/collision_hit.hpp @@ -33,7 +33,8 @@ enum HitResponse /// if this happens to often then the move will just be aborted CONTINUE, /// do the move ignoring the collision - FORCE_MOVE + FORCE_MOVE, + TEST }; /** diff --git a/src/game_session.cpp b/src/game_session.cpp index f495becf0..e96f8215f 100644 --- a/src/game_session.cpp +++ b/src/game_session.cpp @@ -721,19 +721,19 @@ GameSession::start_sequence(const std::string& sequencename) if(sequencename == "endsequence" || sequencename == "fireworks") { if(end_sequence) return; - + end_sequence = ENDSEQUENCE_RUNNING; - endsequence_timer.start(level->extro_length); + endsequence_timer.start(7.3); last_x_pos = -1; - sound_manager->play_music("music/" + level->extro_music, false); - currentsector->player->invincible_timer.start(level->extro_length); + sound_manager->play_music("music/leveldone.ogg", false); + currentsector->player->invincible_timer.start(7.3); // Stop all clocks. for(std::vector::iterator i = currentsector->gameobjects.begin(); i != currentsector->gameobjects.end(); ++i) { GameObject* obj = *i; - + LevelTime* lt = dynamic_cast (obj); if(lt) lt->stop(); diff --git a/src/level.cpp b/src/level.cpp index f589380d4..57bc4a7f2 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -52,7 +52,7 @@ using namespace std; Level::Level() - : name("noname"), author("Mr. X"), extro_music("leveldone.ogg"), extro_length(7.0) + : name("noname"), author("Mr. X") { } @@ -86,17 +86,6 @@ Level::load(const std::string& filepath) iter.value()->get(name); } else if(token == "author") { iter.value()->get(author); - } else if(token == "extro") { - const lisp::Lisp* ext = iter.lisp(); - lisp::ListIterator ext_iter(ext); - while(ext_iter.next()) { - const std::string& ext_token = ext_iter.item(); - if(ext_token == "music") { - ext_iter.value()->get(extro_music); - } else if(ext_token == "length") { - ext_iter.value()->get(extro_length); - } - } } else if(token == "sector") { Sector* sector = new Sector; sector->parse(*(iter.lisp())); diff --git a/src/level.hpp b/src/level.hpp index 2175b3cd9..18d180312 100644 --- a/src/level.hpp +++ b/src/level.hpp @@ -35,8 +35,6 @@ class Level public: std::string name; std::string author; - std::string extro_music; - float extro_length; typedef std::vector Sectors; Sectors sectors; diff --git a/src/math/vector.hpp b/src/math/vector.hpp index 38c120426..6882cf160 100644 --- a/src/math/vector.hpp +++ b/src/math/vector.hpp @@ -82,6 +82,13 @@ public: return *this; } + const Vector& operator -=(const Vector& other) + { + x -= other.x; + y -= other.y; + return *this; + } + const Vector& operator *=(float val) { x *= val; diff --git a/src/moving_object.hpp b/src/moving_object.hpp index 79e415bb2..a58bc7527 100644 --- a/src/moving_object.hpp +++ b/src/moving_object.hpp @@ -110,6 +110,10 @@ protected: Vector movement; /** The collision group */ CollisionGroup group; + +private: + // this is only here for internal collision detection use + Rect dest; }; #endif diff --git a/src/object/camera.cpp b/src/object/camera.cpp index 8a4a9c01a..c1077cc43 100644 --- a/src/object/camera.cpp +++ b/src/object/camera.cpp @@ -33,6 +33,8 @@ #include "main.hpp" #include "object_factory.hpp" #include "msg.hpp" +#include "path.hpp" +#include "path_walker.hpp" Camera::Camera(Sector* newsector) : sector(newsector), do_backscrolling(true), scrollchange(NONE) @@ -63,15 +65,14 @@ Camera::parse(const lisp::Lisp& reader) reader.get("backscrolling", do_backscrolling); } else if(modename == "autoscroll") { mode = AUTOSCROLL; - std::string use_path; - - if (!reader.get("path", use_path)) throw std::runtime_error("No path specified in autoscroll camera."); - autoscrollPath = Path::GetByName(use_path); - if (autoscrollPath == NULL) { - msg_warning("Path for autoscroll camera not found! Make sure that the name is spelled correctly and that the path is initialized before the platform in the level file!"); - } + const lisp::Lisp* pathLisp = reader.get_lisp("path"); + if(pathLisp == NULL) + throw std::runtime_error("No path specified in autoscroll camera."); + autoscroll_path.reset(new Path()); + autoscroll_path->read(*pathLisp); + autoscroll_walker.reset(new PathWalker(autoscroll_path.get())); } else if(modename == "manual") { mode = MANUAL; } else { @@ -91,7 +92,7 @@ Camera::write(lisp::Writer& writer) writer.write_bool("backscrolling", do_backscrolling); } else if(mode == AUTOSCROLL) { writer.write_string("mode", "autoscroll"); - writer.write_string("path", autoscrollPath->GetName()); + autoscroll_path->write(writer); } else if(mode == MANUAL) { writer.write_string("mode", "manual"); } @@ -141,7 +142,7 @@ Camera::update(float elapsed_time) update_scroll_normal(elapsed_time); break; case AUTOSCROLL: - update_scroll_autoscroll(); + update_scroll_autoscroll(elapsed_time); break; case SCROLLTO: update_scroll_to(elapsed_time); @@ -269,14 +270,13 @@ Camera::update_scroll_normal(float elapsed_time) } void -Camera::update_scroll_autoscroll() +Camera::update_scroll_autoscroll(float elapsed_time) { - Player* player = sector->player; - + Player* player = sector->player; if(player->is_dying()) return; - translation = autoscrollPath->GetPosition(); + translation += autoscroll_walker->advance(elapsed_time); keep_in_bounds(translation); shake(); diff --git a/src/object/camera.hpp b/src/object/camera.hpp index b310822a7..0be750e92 100644 --- a/src/object/camera.hpp +++ b/src/object/camera.hpp @@ -21,19 +21,21 @@ #include #include +#include #include "math/vector.hpp" #include "game_object.hpp" #include "video/drawing_context.hpp" #include "serializable.hpp" #include "timer.hpp" -#include "object/path.hpp" namespace lisp { class Lisp; } class Sector; +class Path; +class PathWalker; class Camera : public GameObject, public Serializable { @@ -81,7 +83,7 @@ public: private: void update_scroll_normal(float elapsed_time); - void update_scroll_autoscroll(); + void update_scroll_autoscroll(float elapsed_time); void update_scroll_to(float elapsed_time); void keep_in_bounds(Vector& vector); void shake(); @@ -100,7 +102,8 @@ private: LeftRightScrollChange scrollchange; // autoscroll mode - Path* autoscrollPath; + std::auto_ptr autoscroll_path; + std::auto_ptr autoscroll_walker; // shaking Timer shaketimer; diff --git a/src/object/path.cpp b/src/object/path.cpp index c16f1deea..e4c415ec4 100644 --- a/src/object/path.cpp +++ b/src/object/path.cpp @@ -3,6 +3,7 @@ // SuperTux Path // Copyright (C) 2005 Philipp // Copyright (C) 2006 Christoph Sommer +// Copyright (C) 2006 Matthias Braun // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -18,6 +19,7 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA // 02111-1307, USA. +#include #include "path.hpp" @@ -29,28 +31,42 @@ #include #include #include +#include -// snap to destination if within EPSILON pixels -#define EPSILON 1.5 - -Path::Path(const lisp::Lisp& reader) +Path::Path() { - circular = true; - forward = true; - - if (!reader.get("name", name)) throw std::runtime_error("Path without name"); - reader.get("circular", circular); - reader.get("forward", forward); - - const lisp::Lisp* nodes_lisp = reader.get_lisp("nodes"); - if(!nodes_lisp) throw std::runtime_error("Path without nodes"); +} - lisp::ListIterator iter(nodes_lisp); +Path::~Path() +{ +} - PathNode node; - node.time = 1; +void +Path::read(const lisp::Lisp& reader) +{ + lisp::ListIterator iter(&reader); + mode = CIRCULAR; while(iter.next()) { + if(iter.item() == "mode") { + std::string mode_string; + if(!iter.value()->get(mode_string)) + throw new std::runtime_error("Pathmode not a string"); + + if(mode_string == "oneshot") + mode = ONE_SHOT; + else if(mode_string == "pingpong") + mode = PING_PONG; + else if(mode_string == "circular") + mode = CIRCULAR; + else { + std::ostringstream msg; + msg << "Unknown pathmode '" << mode_string << "' found"; + throw new std::runtime_error(msg.str()); + } + continue; + } + if(iter.item() != "node") { msg_warning("unknown token '" << iter.item() << "' in Path nodes list. Ignored."); continue; @@ -58,108 +74,21 @@ Path::Path(const lisp::Lisp& reader) const lisp::Lisp* node_lisp = iter.lisp(); // each new node will inherit all values from the last one - node_lisp->get("x", node.position.x); - node_lisp->get("y", node.position.y); + Node node; + node.time = 1; + if( (!node_lisp->get("x", node.position.x) || + !node_lisp->get("y", node.position.y))) + throw new std::runtime_error("Path node without x and y coordinate specified"); node_lisp->get("time", node.time); - if(node.time <= 0) throw std::runtime_error("Path node with non-positive time"); + if(node.time <= 0) + throw std::runtime_error("Path node with non-positive time"); - pathNodes.push_back(node); - } - - if (pathNodes.size() < 1) throw std::runtime_error("Path with zero nodes"); - - // initial position and velocity will be set with the first update, as timeToGo is initialized to 0. - destinationNode = 0; - - // register this path for lookup: - registry[name] = this; -} - -Path::~Path() -{ - registry.erase(name); -} - - void -Path::update(float elapsed_time) -{ - - // TODO: carry excess time over to next node? This is how it was done in camera.cpp: - /* - 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(translation); - 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++; - } - */ - - // advance to next node at scheduled time - if (timeToGo <= 0) { - position = pathNodes[destinationNode].position; - - // set destinationNode to next node - if (forward) { - destinationNode++; - if (destinationNode >= (int)pathNodes.size()) { - if (circular) { - destinationNode = 0; - } else { - destinationNode = (int)pathNodes.size()-1; - } - } - } else { - destinationNode--; - if (destinationNode < 0) { - if (circular) { - destinationNode = (int)pathNodes.size()-1; - } else { - destinationNode = 0; - } - } - } - - PathNode dn = pathNodes[destinationNode]; - timeToGo = dn.time; - velocity = (dn.position - position) / timeToGo; - } - - // move according to stored velocity - last_movement = velocity * elapsed_time; - position += last_movement; - timeToGo -= elapsed_time; - - // stop when we arrive at our destination - PathNode dn = pathNodes[destinationNode]; - if ((position - dn.position).norm() < EPSILON) { - velocity = Vector(0,0); + nodes.push_back(node); } -} - -void -Path::draw(DrawingContext& ) -{ - // TODO: Add a visible flag, draw the path if true + if (nodes.empty()) + throw std::runtime_error("Path with zero nodes"); } void @@ -167,12 +96,23 @@ Path::write(lisp::Writer& writer) { writer.start_list("path"); - writer.write_string("name", name); - writer.write_bool("circular", circular); - writer.write_bool("forward", forward); + switch(mode) { + case ONE_SHOT: + writer.write_string("mode", "oneshot"); + break; + case PING_PONG: + writer.write_string("mode", "pingpong"); + break; + case CIRCULAR: + writer.write_string("mode", "circular"); + break; + default: + msg_warning("Don't know how to write mode " << (int) mode << " ?!?"); + break; + } - for (int i=0; i < (int)pathNodes.size(); i++) { - PathNode node = pathNodes[i]; + for (size_t i=0; i < nodes.size(); i++) { + const Node& node = nodes[i]; writer.start_list("node"); writer.write_float("x", node.position.x); @@ -185,29 +125,12 @@ Path::write(lisp::Writer& writer) writer.end_list("path"); } -const Vector& -Path::GetPosition() { - return position; -} - -const Vector& -Path::GetLastMovement() { - return last_movement; -} - -const std::string -Path::GetName() { - return name; -} - -////////////////////////////////////////////////////////////////////////////// -// static stuff - -std::map Path::registry; - -Path* -Path::GetByName(const std::string& name) { - return registry[name]; +Vector +Path::get_base() const +{ + if(nodes.empty()) + return Vector(0, 0); + + return nodes[0].position; } -IMPLEMENT_FACTORY(Path, "path"); diff --git a/src/object/path.hpp b/src/object/path.hpp index f04623bc0..60812ecfb 100644 --- a/src/object/path.hpp +++ b/src/object/path.hpp @@ -3,6 +3,7 @@ // SuperTux Path // Copyright (C) 2005 Philipp // Copyright (C) 2006 Christoph Sommer +// Copyright (C) 2006 Matthias Braun // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -21,63 +22,48 @@ #ifndef __PATH_HPP__ #define __PATH_HPP__ -#include -#include -#include - +#include #include "math/vector.hpp" -#include "game_object.hpp" #include "lisp/lisp.hpp" #include "serializable.hpp" - -/** - * Helper class that stores an individual node of a Path - */ -class PathNode -{ -public: - Vector position; /**< position (in pixels) of this node */ - float time; /**< time (in seconds) to get to this node */ -}; - - -/** - * Path an object can travel along. Made up of multiple nodes of type PathNode. - */ -class Path : public GameObject, public Serializable +class Path : public Serializable { public: - Path(const lisp::Lisp& reader); + Path(); ~Path(); - virtual void update(float elapsed_time); - virtual void draw(DrawingContext& context); + void read(const lisp::Lisp& reader); + void write(lisp::Writer& writer); - virtual void write(lisp::Writer& writer); - - const Vector& GetPosition(); - const Vector& GetLastMovement(); - - const std::string GetName(); - - // WARNING: returns NULL if not found ! - static Path* GetByName(const std::string& name); + Vector get_base() const; private: - std::string name; /**< name this path can be referenced with, stored in PathRegistry */ - bool circular; /**< true: start with the first node once the last one has been reached. false: path will stop at last node */ - bool forward; /**< true: travel to nodes in the order they were defined. false: inverse order */ - std::vector pathNodes; /**< list of nodes that make up this path */ - - Vector position; /**< current position */ - Vector velocity; /**< current velocity */ - Vector last_movement; /**< amount of pixels we moved in the last call to update */ - - int destinationNode; /**< current destination Node */ - float timeToGo; /**< seconds until we arrive at the destination */ - - static std::map registry; + friend class PathWalker; + + enum WalkMode { + // moves from first to last path node and stops + ONE_SHOT, + // moves from first to last node then in reverse order back to first + PING_PONG, + // moves from last node back to the first node + CIRCULAR + }; + + /** + * Helper class that stores an individual node of a Path + */ + class Node + { + public: + Vector position; /**< the position of this node */ + float time; /**< time (in seconds) to get from this node to next node */ + }; + + std::vector nodes; + + WalkMode mode; }; #endif + diff --git a/src/object/platform.cpp b/src/object/platform.cpp index a28ec87a5..cbe01888a 100644 --- a/src/object/platform.cpp +++ b/src/object/platform.cpp @@ -21,41 +21,41 @@ #include "platform.hpp" +#include #include "msg.hpp" #include "video/drawing_context.hpp" #include "resources.hpp" #include "player.hpp" +#include "path.hpp" +#include "path_walker.hpp" #include "sprite/sprite_manager.hpp" #include "lisp/lisp.hpp" #include "object_factory.hpp" Platform::Platform(const lisp::Lisp& reader) { - std::string use_path; - std::string type; + std::string sprite_name; + reader.get("sprite", sprite_name); + if(sprite_name == "") + throw new std::runtime_error("No sprite specified in platform object"); + sprite.reset(sprite_manager->create(sprite_name)); - reader.get("x", bbox.p1.x); - reader.get("y", bbox.p1.y); - reader.get("type", type); - reader.get("path", use_path); - sprite = sprite_manager->create("images/objects/flying_platform/platform.sprite"); - sprite->set_action(type); - bbox.set_size(sprite->get_width(), sprite->get_height()); + const lisp::Lisp* pathLisp = reader.get_lisp("path"); + if(pathLisp == NULL) + throw new std::runtime_error("No path specified for platform"); + path.reset(new Path()); + path->read(*pathLisp); + walker.reset(new PathWalker(path.get())); + bbox.p1 = path->get_base(); + bbox.set_size(sprite->get_width(), sprite->get_height()); + + set_group(COLGROUP_STATIC); flags |= FLAG_SOLID; - - path = Path::GetByName(use_path); - - if (path == NULL) { - msg_warning("Path \"" << use_path << "\" for moving platform not found! Make sure that the name is spelled correctly and that the path is initialized before the platform in the level file!"); - } - - path_offset = bbox.p1; } Platform::~Platform() { - delete sprite; } //TODO: Squish Tux when standing between platform and solid tile/object @@ -65,24 +65,25 @@ HitResponse Platform::collision(GameObject& other, const CollisionHit& hit) { if (typeid(other) == typeid(Player)) { - Player* player = (Player*) &other; - if ((hit.normal.x == 0) && (hit.normal.y == 1)) { + if (hit.normal.y >= 0.9) { //Tux is standing on the platform - player->movement += path->GetLastMovement(); + //Player* player = (Player*) &other; + //player->add_velocity(speed * 1.5); + return TEST; } } if(other.get_flags() & FLAG_SOLID) { //Collision with a solid tile - //does nothing, because the movement vector isn't used at the moment return ABORT_MOVE; } return FORCE_MOVE; } void -Platform::update(float ) +Platform::update(float elapsed_time) { - set_pos(path->GetPosition() + path_offset); + movement = walker->advance(elapsed_time); + speed = movement / elapsed_time; } void diff --git a/src/object/platform.hpp b/src/object/platform.hpp index c6286c1c0..82eff0bda 100644 --- a/src/object/platform.hpp +++ b/src/object/platform.hpp @@ -20,6 +20,7 @@ #ifndef __PLATFORM_H__ #define __PLATFORM_H__ +#include #include "moving_object.hpp" #include "sprite/sprite.hpp" #include "object/path.hpp" @@ -36,11 +37,16 @@ public: virtual HitResponse collision(GameObject& other, const CollisionHit& hit); virtual void update(float elapsed_time); virtual void draw(DrawingContext& context); + const Vector& get_speed() const + { + return speed; + } private: - Sprite* sprite; - Path* path; - Vector path_offset; + std::auto_ptr sprite; + std::auto_ptr path; + std::auto_ptr walker; + Vector speed; }; #endif diff --git a/src/object/player.cpp b/src/object/player.cpp index 628752e35..a27973992 100644 --- a/src/object/player.cpp +++ b/src/object/player.cpp @@ -42,6 +42,7 @@ #include "trigger/trigger_base.hpp" #include "control/joystickkeyboardcontroller.hpp" #include "main.hpp" +#include "platform.hpp" #include "badguy/badguy.hpp" #include "player_status.hpp" #include "msg.hpp" @@ -122,6 +123,7 @@ Player::init() bbox.set_size(31.8, 63.8); else bbox.set_size(31.8, 31.8); + adjust_height = 0; dir = RIGHT; old_dir = dir; @@ -156,21 +158,17 @@ Player::set_controller(Controller* controller) void Player::update(float elapsed_time) { - // do we need to enable gravity again? - if(on_ground_flag) { - Rect lower = bbox; - lower.move(Vector(0, 4.0)); - if(Sector::current()->is_free_space(lower)) { - physic.enable_gravity(true); - on_ground_flag = false; - } - } - if(dying && dying_timer.check()) { dead = true; return; } + if(adjust_height != 0) { + bbox.move(Vector(0, bbox.get_height() - adjust_height)); + bbox.set_height(adjust_height); + adjust_height = 0; + } + if(!controller->hold(Controller::ACTION) && grabbed_object) { // move the grabbed object a bit away from tux Vector pos = get_pos() + @@ -210,6 +208,8 @@ Player::update(float elapsed_time) bbox.get_height()*0.66666 - 32); grabbed_object->grab(*this, pos, dir); } + + on_ground_flag = false; } bool @@ -525,8 +525,7 @@ Player::set_bonus(BonusType type, bool animate) return; if(player_status->bonus == NO_BONUS) { - bbox.set_height(63.8); - bbox.move(Vector(0, -32)); + adjust_height = 63.8; if(animate) growing_timer.start(GROWING_TIME); } @@ -600,7 +599,7 @@ Player::draw(DrawingContext& context) else // dir == RIGHT tux_body->set_action("buttjump-right"); } - else if (physic.get_velocity_y() != 0) + else if (physic.get_velocity_y() != 0 && !on_ground()) { if(dir == LEFT) tux_body->set_action("jump-left"); @@ -731,6 +730,7 @@ Player::collision(GameObject& other, const CollisionHit& hit) if(hit.normal.y < 0) { // landed on floor? if(physic.get_velocity_y() < 0) physic.set_velocity_y(0); + on_ground_flag = true; // remember normal of this tile @@ -744,16 +744,41 @@ Player::collision(GameObject& other, const CollisionHit& hit) floor_normal.y = (floor_normal.y * 0.9) + (hit.normal.y * 0.1); } - // disable gravity - physic.enable_gravity(false); + // hack platforms so that we stand normally on them when going down... + Platform* platform = dynamic_cast (&other); + if(platform != NULL) { + if(platform->get_speed().y > 0) + physic.set_velocity_y(-platform->get_speed().y); + //physic.set_velocity_x(platform->get_speed().x); + } } else if(hit.normal.y > 0) { // bumped against the roof physic.set_velocity_y(.1); + + // hack platform so that we are not glued to it from below + Platform* platform = dynamic_cast (&other); + if(platform != NULL) { + physic.set_velocity_y(-platform->get_speed().y); + } } if(fabsf(hit.normal.x) > .9) { // hit on the side? physic.set_velocity_x(0); } + MovingObject* omov = dynamic_cast (&other); + if(omov != NULL) { + Vector mov = movement - omov->get_movement(); + /* + printf("W %p - HITN: %3.1f %3.1f D:%3.1f TM: %3.1f %3.1f TD: %3.1f %3.1f PM: %3.2f %3.1f\n", + omov, + hit.normal.x, hit.normal.y, + hit.depth, + movement.x, movement.y, + dest.p1.x, dest.p1.y, + omov->get_movement().x, omov->get_movement().y); + */ + } + return CONTINUE; } @@ -817,7 +842,7 @@ Player::kill(HurtMode mode) { growing_timer.start(GROWING_TIME); safe_timer.start(TUX_SAFE_TIME + GROWING_TIME); - bbox.set_height(31.8); + adjust_height = 31.8; duck = false; player_status->bonus = NO_BONUS; } @@ -898,6 +923,12 @@ Player::check_bounds(Camera* camera) } void +Player::add_velocity(const Vector& velocity) +{ + physic.set_velocity(physic.get_velocity() + velocity); +} + +void Player::bounce(BadGuy& ) { if(controller->hold(Controller::JUMP)) diff --git a/src/object/player.hpp b/src/object/player.hpp index 880337275..add9a09d0 100644 --- a/src/object/player.hpp +++ b/src/object/player.hpp @@ -92,8 +92,8 @@ private: bool dying; bool backflipping; int backflip_direction; + public: - Direction dir; Direction old_dir; @@ -104,7 +104,7 @@ public: bool jumping; bool can_jump; bool butt_jump; - + Timer invincible_timer; Timer skidding_timer; Timer safe_timer; @@ -148,6 +148,11 @@ public: // set kick animation void kick(); + /** + * Adds velocity to the player (be carefull when using this) + */ + void add_velocity(const Vector& velocity); + void bounce(BadGuy& badguy); bool is_dead() const @@ -173,6 +178,8 @@ private: bool visible; + float adjust_height; + Portable* grabbed_object; Sprite* smalltux_gameover; diff --git a/src/object/powerup.cpp b/src/object/powerup.cpp index 3fce45b49..5f28bd7ea 100644 --- a/src/object/powerup.cpp +++ b/src/object/powerup.cpp @@ -57,8 +57,6 @@ PowerUp::collision(GameObject& other, const CollisionHit& hit) if(fabsf(hit.normal.y) > .5) { // roof or ground physic.set_velocity_y(0); } else { // bumped left or right - msg_debug("Normal: " << hit.normal.x << "," << hit.normal.y); - msg_debug("LRbounce, new speed: " << physic.get_velocity_x()); physic.set_velocity_x(-physic.get_velocity_x()); } diff --git a/src/object/rock.cpp b/src/object/rock.cpp index 5b022b5aa..2b17713af 100644 --- a/src/object/rock.cpp +++ b/src/object/rock.cpp @@ -35,6 +35,7 @@ Rock::Rock(const lisp::Lisp& reader) sprite = sprite_manager->create("images/objects/rock/rock.sprite"); grabbed = false; flags |= FLAG_SOLID | FLAG_PORTABLE; + set_group(COLGROUP_MOVING); } Rock::~Rock() @@ -73,16 +74,21 @@ Rock::update(float elapsed_time) } grabbed = false; + printf("%p - V %3.1f %3.1f - P %3.1f %3.1f\n", this, + physic.get_velocity().x, physic.get_velocity().y, + get_pos().x, get_pos().y); } HitResponse -Rock::collision(GameObject& object, const CollisionHit& ) +Rock::collision(GameObject& object, const CollisionHit& hit) { if(grabbed) { return FORCE_MOVE; } if(object.get_flags() & FLAG_SOLID) { + printf("%p vs %p - %3.1f %3.1f D %3.1f\n", this, &object, + hit.normal.x, hit.normal.y, hit.depth); physic.set_velocity(0, 0); return CONTINUE; } diff --git a/src/physic.cpp b/src/physic.cpp index e9fe94d97..329418a6e 100644 --- a/src/physic.cpp +++ b/src/physic.cpp @@ -56,28 +56,41 @@ Physic::set_velocity(float nvx, float nvy) vy = -nvy; } +void +Physic::set_velocity(const Vector& vector) +{ + vx = vector.x; + vy = vector.y; +} + void Physic::inverse_velocity_x() { -vx = -vx; + vx = -vx; } void Physic::inverse_velocity_y() { -vy = -vy; + vy = -vy; } float -Physic::get_velocity_x() +Physic::get_velocity_x() const { return vx; } float -Physic::get_velocity_y() +Physic::get_velocity_y() const { return -vy; } +Vector +Physic::get_velocity() const +{ + return Vector(vx, -vy); +} + void Physic::set_acceleration_x(float nax) { @@ -93,20 +106,26 @@ Physic::set_acceleration_y(float nay) void Physic::set_acceleration(float nax, float nay) { - ax = nax; - ay = -nay; + ax = nax; + ay = -nay; } float -Physic::get_acceleration_x() +Physic::get_acceleration_x() const { - return ax; + return ax; } float -Physic::get_acceleration_y() +Physic::get_acceleration_y() const +{ + return -ay; +} + +Vector +Physic::get_acceleration() const { - return -ay; + return Vector(ax, -ay); } void diff --git a/src/physic.hpp b/src/physic.hpp index 828b1e105..da44f9b7f 100644 --- a/src/physic.hpp +++ b/src/physic.hpp @@ -37,6 +37,7 @@ public: /// Sets velocity to a fixed value. void set_velocity(float vx, float vy); + void set_velocity(const Vector& vector); void set_velocity_x(float vx); void set_velocity_y(float vy); @@ -45,8 +46,9 @@ public: void inverse_velocity_x(); void inverse_velocity_y(); - float get_velocity_x(); - float get_velocity_y(); + Vector get_velocity() const; + float get_velocity_x() const; + float get_velocity_y() const; /// Set acceleration. /** Sets acceleration applied to the object. (Note that gravity is @@ -57,8 +59,9 @@ public: void set_acceleration_x(float ax); void set_acceleration_y(float ay); - float get_acceleration_x(); - float get_acceleration_y(); + Vector get_acceleration() const; + float get_acceleration_x() const; + float get_acceleration_y() const; /// Enables or disables handling of gravity. void enable_gravity(bool gravity_enabled); diff --git a/src/sector.cpp b/src/sector.cpp index 7ea513a1c..071e059c2 100644 --- a/src/sector.cpp +++ b/src/sector.cpp @@ -575,25 +575,14 @@ Sector::draw(DrawingContext& context) static const float DELTA = .001; void -Sector::collision_tilemap(MovingObject* object, CollisionHit& hit) const +Sector::collision_tilemap(const Rect& dest, const Vector& movement, + CollisionHit& hit) const { // calculate rectangle where the object will move - float x1, x2; - if(object->get_movement().x >= 0) { - x1 = object->get_bbox().p1.x; - x2 = object->get_bbox().p2.x + object->get_movement().x; - } else { - x1 = object->get_bbox().p1.x + object->get_movement().x; - x2 = object->get_bbox().p2.x; - } - float y1, y2; - if(object->get_movement().y >= 0) { - y1 = object->get_bbox().p1.y; - y2 = object->get_bbox().p2.y + object->get_movement().y; - } else { - y1 = object->get_bbox().p1.y + object->get_movement().y; - y2 = object->get_bbox().p2.y; - } + float x1 = dest.get_left(); + float x2 = dest.get_right(); + float y1 = dest.get_top(); + float y2 = dest.get_bottom(); // test with all tiles in this rectangle int starttilex = int(x1) / 32; @@ -603,8 +592,6 @@ Sector::collision_tilemap(MovingObject* object, CollisionHit& hit) const int max_y = int(y2+1); CollisionHit temphit; - Rect dest = object->get_bbox(); - dest.move(object->movement); for(int x = starttilex; x*32 < max_x; ++x) { for(int y = starttiley; y*32 < max_y; ++y) { const Tile* tile = solids->get_tile(x, y); @@ -616,7 +603,7 @@ Sector::collision_tilemap(MovingObject* object, CollisionHit& hit) const // only handle unisolid when the player is falling down and when he was // above the tile before if(tile->getAttributes() & Tile::UNISOLID) { - if(object->movement.y < 0 || object->get_bbox().p2.y > y*32) + if(movement.y < 0 || dest.get_top() - movement.y > y*32) continue; } @@ -626,7 +613,7 @@ Sector::collision_tilemap(MovingObject* object, CollisionHit& hit) const Vector p2((x+1)*32, (y+1)*32); triangle = AATriangle(p1, p2, tile->getData()); - if(Collision::rectangle_aatriangle(temphit, dest, object->movement, + if(Collision::rectangle_aatriangle(temphit, dest, movement, triangle)) { if(temphit.time > hit.time && (tile->getAttributes() & Tile::SOLID)) { hit = temphit; @@ -634,8 +621,7 @@ Sector::collision_tilemap(MovingObject* object, CollisionHit& hit) const } } else { // normal rectangular tile Rect rect(x*32, y*32, (x+1)*32, (y+1)*32); - if(Collision::rectangle_rectangle(temphit, dest, - object->movement, rect)) { + if(Collision::rectangle_rectangle(temphit, dest, movement, rect)) { if(temphit.time > hit.time && (tile->getAttributes() & Tile::SOLID)) { hit = temphit; } @@ -646,14 +632,15 @@ Sector::collision_tilemap(MovingObject* object, CollisionHit& hit) const } uint32_t -Sector::collision_tile_attributes(MovingObject* object) const +Sector::collision_tile_attributes(const Rect& dest) const { /** XXX This function doesn't work correctly as it will check all tiles * in the bounding box of the object movement, this might include tiles * that have actually never been touched by the object * (though this only occures for very fast objects...) */ - + +#if 0 // calculate rectangle where the object will move float x1, x2; if(object->get_movement().x >= 0) { @@ -671,6 +658,11 @@ Sector::collision_tile_attributes(MovingObject* object) const y1 = object->get_bbox().p1.y + object->get_movement().y; y2 = object->get_bbox().p2.y; } +#endif + float x1 = dest.p1.x; + float y1 = dest.p1.y; + float x2 = dest.p2.x; + float y2 = dest.p2.y; // test with all tiles in this rectangle int starttilex = int(x1-1) / 32; @@ -695,37 +687,109 @@ void Sector::collision_object(MovingObject* object1, MovingObject* object2) const { CollisionHit hit; - Rect dest1 = object1->get_bbox(); - dest1.move(object1->get_movement()); - Rect dest2 = object2->get_bbox(); - dest2.move(object2->get_movement()); Vector movement = object1->get_movement() - object2->get_movement(); - if(Collision::rectangle_rectangle(hit, dest1, movement, dest2)) { + if(Collision::rectangle_rectangle(hit, object1->dest, movement, object2->dest)) { HitResponse response1 = object1->collision(*object2, hit); hit.normal *= -1; HitResponse response2 = object2->collision(*object1, hit); if(response1 != CONTINUE) { if(response1 == ABORT_MOVE) - object1->movement = Vector(0, 0); + object1->dest = object1->get_bbox(); if(response2 == CONTINUE) - object2->movement += hit.normal * (hit.depth + DELTA); + object2->dest.move(hit.normal * (hit.depth + DELTA)); } else if(response2 != CONTINUE) { if(response2 == ABORT_MOVE) - object2->movement = Vector(0, 0); + object2->dest = object2->get_bbox(); if(response1 == CONTINUE) - object1->movement += -hit.normal * (hit.depth + DELTA); + object1->dest.move(-hit.normal * (hit.depth + DELTA)); } else { - object1->movement += -hit.normal * (hit.depth/2 + DELTA); - object2->movement += hit.normal * (hit.depth/2 + DELTA); + object1->dest.move(-hit.normal * (hit.depth/2 + DELTA)); + object2->dest.move(hit.normal * (hit.depth/2 + DELTA)); + } + } +} + +bool +Sector::collision_static(MovingObject* object, const Vector& movement) +{ + GameObject* collided_with = solids; + CollisionHit hit; + hit.time = -1; + + collision_tilemap(object->dest, movement, hit); + + // collision with other (static) objects + CollisionHit temphit; + for(MovingObjects::iterator i2 = moving_objects.begin(); + i2 != moving_objects.end(); ++i2) { + MovingObject* moving_object_2 = *i2; + if(moving_object_2->get_group() != COLGROUP_STATIC + || !moving_object_2->is_valid()) + continue; + + Rect dest2 = moving_object_2->get_bbox(); + // We're using the old position of the object here, + // this might seem a bit wrong but improves some situations + // like stacked boxes and badguys alot + // + dest2.move(moving_object_2->get_movement()); + Vector rel_movement + = movement - moving_object_2->get_movement(); + //Vector movement = + + if(Collision::rectangle_rectangle(temphit, object->dest, rel_movement, dest2) + && temphit.time > hit.time) { + hit = temphit; + collided_with = moving_object_2; + } + } + + if(hit.time < 0) + return true; + + HitResponse response = object->collision(*collided_with, hit); + hit.normal *= -1; + if(collided_with != solids) { + MovingObject* moving_object = (MovingObject*) collided_with; + HitResponse other_response = moving_object->collision(*object, hit); + if(other_response == ABORT_MOVE) { + moving_object->dest = moving_object->get_bbox(); + } else if(other_response == FORCE_MOVE) { + // the static object "wins" move tux out of the collision + object->dest.move(-hit.normal * (hit.depth + DELTA)); + return false; + } else if(other_response == TEST) { + object->dest.move(moving_object->get_movement()); + //object->movement += moving_object->get_movement(); } } + + if(response == CONTINUE) { + object->dest.move(-hit.normal * (hit.depth + DELTA)); + return false; + } else if(response == ABORT_MOVE) { + object->dest = object->get_bbox(); + return true; + } + + // force move + return false; } void Sector::handle_collisions() { + // calculate destination positions of the objects + for(MovingObjects::iterator i = moving_objects.begin(); + i != moving_objects.end(); ++i) { + MovingObject* moving_object = *i; + + moving_object->dest = moving_object->get_bbox(); + moving_object->dest.move(moving_object->get_movement()); + } + // part1: COLGROUP_MOVING vs COLGROUP_STATIC and tilemap // we do this up to 4 times and have to sort all results for the smallest // one before we can continue here @@ -737,59 +801,39 @@ Sector::handle_collisions() || !moving_object->is_valid()) continue; - // up to 4 tries - for(int t = 0; t < 4; ++t) { - CollisionHit hit; - hit.time = -1; - MovingObject* collided_with = NULL; - - // collision with tilemap - collision_tilemap(moving_object, hit); - - // collision with other objects - Rect dest1 = moving_object->get_bbox(); - dest1.move(moving_object->get_movement()); - CollisionHit temphit; - - for(MovingObjects::iterator i2 = moving_objects.begin(); - i2 != moving_objects.end(); ++i2) { - MovingObject* moving_object_2 = *i2; - if(moving_object_2->get_group() != COLGROUP_STATIC - || !moving_object_2->is_valid()) - continue; - - Rect dest2 = moving_object_2->get_bbox(); - dest2.move(moving_object_2->get_movement()); - Vector movement - = moving_object->get_movement() - moving_object_2->get_movement(); - if(Collision::rectangle_rectangle(temphit, dest1, movement, dest2) - && temphit.time > hit.time) { - hit = temphit; - collided_with = moving_object_2; - } - } + Vector movement = moving_object->get_movement(); - if(hit.time < 0) - break; + // test if x or y movement is dominant + if(fabsf(moving_object->get_movement().x) > fabsf(moving_object->get_movement().y)) { - // call collision callbacks - HitResponse response; - if(collided_with != 0) { - response = moving_object->collision(*collided_with, hit); - hit.normal *= -1; - collided_with->collision(*moving_object, hit); - } else { - response = moving_object->collision(*solids, hit); - hit.normal *= -1; + // test in x direction first, then y direction + moving_object->dest.move(Vector(0, -movement.y)); + for(int i = 0; i < 2; ++i) { + bool res = collision_static(moving_object, /*Vector(movement.x, 0)*/ movement); + if(res) + break; + } + moving_object->dest.move(Vector(0, movement.y)); + for(int i = 0; i < 2; ++i) { + bool res = collision_static(moving_object, /*Vector(0, movement.y)*/ movement); + if(res) + break; + } + + } else { + + // test in y direction first, then x direction + moving_object->dest.move(Vector(-movement.x, 0)); + for(int i = 0; i < 2; ++i) { + bool res = collision_static(moving_object, movement/*Vector(0, movement.y)*/); + if(res) + break; } - - if(response == CONTINUE) { - moving_object->movement += -hit.normal * (hit.depth + DELTA); - } else if(response == ABORT_MOVE) { - moving_object->movement = Vector(0, 0); - break; - } else { // force move - break; + moving_object->dest.move(Vector(movement.x, 0)); + for(int i = 0; i < 2; ++i) { + bool res = collision_static(moving_object, movement /*Vector(movement.x, 0)*/); + if(res) + break; } } } @@ -803,7 +847,7 @@ Sector::handle_collisions() || !moving_object->is_valid()) continue; - uint32_t tile_attributes = collision_tile_attributes(moving_object); + uint32_t tile_attributes = collision_tile_attributes(moving_object->dest); if(tile_attributes > Tile::FIRST_INTERESTING_FLAG) { moving_object->collision_tile(tile_attributes); } @@ -853,7 +897,7 @@ Sector::handle_collisions() i != moving_objects.end(); ++i) { MovingObject* moving_object = *i; - moving_object->bbox.move(moving_object->get_movement()); + moving_object->bbox = moving_object->dest; moving_object->movement = Vector(0, 0); } } diff --git a/src/sector.hpp b/src/sector.hpp index 05d5af47a..5bf497a05 100644 --- a/src/sector.hpp +++ b/src/sector.hpp @@ -87,11 +87,6 @@ public: void play_music(MusicType musictype); MusicType get_music_type(); - /** Checks for all possible collisions. And calls the - collision_handlers, which the collision_objects provide for this - case (or not). */ - void handle_collisions(); - bool add_bullet(const Vector& pos, float xm, Direction dir); bool add_smoke_cloud(const Vector& pos); void add_floating_text(const Vector& pos, const std::string& text); @@ -103,8 +98,7 @@ public: /** Get total number of badguys */ int get_total_badguys(); - void collision_tilemap(MovingObject* object, CollisionHit& hit) const; - uint32_t collision_tile_attributes(MovingObject* object) const; + void collision_tilemap(const Rect& dest, const Vector& movement, CollisionHit& hit) const; /** Checks if at the specified rectangle are gameobjects with STATIC flag set * (or solid tiles from the tilemap) @@ -119,6 +113,15 @@ public: } private: + uint32_t collision_tile_attributes(const Rect& dest) const; + + bool collision_static(MovingObject* object, const Vector& movement); + + /** Checks for all possible collisions. And calls the + collision_handlers, which the collision_objects provide for this + case (or not). */ + void handle_collisions(); + void collision_object(MovingObject* object1, MovingObject* object2) const; GameObject* parse_object(const std::string& name, const lisp::Lisp& lisp); diff --git a/src/title.cpp b/src/title.cpp index b9577e291..f4d6ea084 100644 --- a/src/title.cpp +++ b/src/title.cpp @@ -381,16 +381,6 @@ void title() // Contrib Menu generate_contrib_menu(); break; -#if 0 - case MNID_LEVELEDITOR: { - LevelEdtiro* leveleditor = new LevelEditor(); - leveleditor->run(); - delete leveleditor; - Menu::set_current(main_menu); - resume_demo(); - break; - } -#endif case MNID_CREDITS: sound_manager->stop_music(); fadeout(500); -- 2.11.0