--- /dev/null
+(supertux-sprite
+ (action
+ (name "default")
+ (images "big.png")
+ )
+)
--- /dev/null
+(supertux-sprite
+ (action
+ (name "default")
+ (images "small.png")
+ )
+)
(supertux-sprite
(action
+ (name "normal")
(x-offset 0)
(y-offset 0)
(images "trampoline1-0.png"
(supertux-sprite
(action
+ (name "normal")
(images "iceshrub.png")
)
)
(mriceblock (x 1180) (y 15) (stay-on-platform #f))
(mriceblock (x 939) (y 22) (stay-on-platform #f))
(mriceblock (x 703) (y 25) (stay-on-platform #f))
- (stalactite (x 322) (y 287) (stay-on-platform #f))
+ (stalactite (x 322) (y 288) (stay-on-platform #f))
)
)
(speed 0.500000)
)
- (path
- (name "path1")
- (circular #t)
- (nodes
- (node (x 0) (y 0))
- (node (x 0) (y 0))
- (node (x 0) (y -100))
- (node (x -100)(y -100))
- )
+ (platform
+ (path
+ (mode "pingpong")
+ (node (x 200) (y 850))
+ (node (x 200) (y 750))
+ (node (x 100) (y 750))
)
- (path
- (name "path2")
- (circular #t)
- (nodes
- (node (x 0) (y 0) (time 1))
- (node (x 0) (y 0) (time 1))
- (node (x 0) (y -100) (time 1))
- (node (x 0) (y -100) (time 1))
- )
+ (sprite "images/objects/platforms/small.sprite")
+ )
+ (platform
+ (path
+ (mode "pingpong")
+ (node (x 264) (y 850) (time 1))
+ (node (x 264) (y 850) (time 1))
+ (node (x 264) (y 750) (time 1))
+ (node (x 264) (y 750) (time 1))
)
- (path
- (name "path3")
- (circular #t)
- (nodes
- (node (x 0) (y 0) (time 1))
- (node (x 0) (y 0) (time 1))
- (node (x 0) (y -100) (time 2))
- )
+ (sprite "images/objects/platforms/small.sprite")
+ )
+ (platform
+ (path
+ (mode "circular")
+ (node (x 328) (y 850) (time 1))
+ (node (x 328) (y 850) (time 1))
+ (node (x 328) (y 750) (time 2))
)
- (path
- (name "path4")
- (circular #t)
- (nodes
- (node (x 0) (y 0) (time 1))
- (node (x 0) (y -100) (time 1))
- (node (x 0) (y -200) (time 1))
- (node (x 0) (y -100) (time 1))
- )
+ (sprite "images/objects/platforms/small.sprite")
+ )
+ (platform
+ (path
+ (mode "circular")
+ (node (x 392) (y 850) (time 1))
+ (node (x 392) (y 750) (time 1))
+ (node (x 392) (y 650) (time 0.6))
+ (node (x 392) (y 750) (time 1))
)
- (path
- (name "path5")
- (circular #f)
- (nodes
- (node)
- (node (y -100))
- (node (x 100))
- (node (y 0))
- (node (x 0))
- )
+ (sprite "images/objects/platforms/small.sprite")
+ )
+ (platform
+ (path
+ (mode "circular")
+ (node (x 456) (y 750))
+ (node (x 100) (y 300))
+ (node (x 456) (y 300))
+ (node (x 456) (y 750))
)
-
- (platform (path "path1") (x 200) (y 850) (type "block1"))
- (platform (path "path1") (x 232) (y 850) (type "block3"))
- (platform (path "path2") (x 264) (y 850) (type "block1"))
- (platform (path "path2") (x 296) (y 850) (type "block3"))
- (platform (path "path3") (x 328) (y 850) (type "block1"))
- (platform (path "path3") (x 360) (y 850) (type "block3"))
- (platform (path "path4") (x 392) (y 850) (type "block1"))
- (platform (path "path4") (x 424) (y 850) (type "block3"))
- (platform (path "path5") (x 456) (y 850) (type "block1"))
- (platform (path "path5") (x 488) (y 850) (type "block3"))
+ (sprite "images/objects/platforms/small.sprite")
+ )
(powerup (x 100) (y 700) (sprite "images/powerups/egg/egg.sprite"))
(jumpy (x 140) (y 750))
(spawnpoint (name "main") (x 340) (y 800))
(speed 0.5))
(spawnpoint (name "main") (x 150) (y 100))
(rock (x 50) (y 50))
- ;(rock (x 50) (y 100))
+ (rock (x 50) (y 100))
;(rock (x 50) (y 150))
(tilemap
(layer "background")
)
(particles-snow
)
- (yeti_stalactite (x 97) (y 46))
- (yeti_stalactite (x 128) (y 47))
- (yeti_stalactite (x 161) (y 47))
- (yeti_stalactite (x 191) (y 47))
- (yeti_stalactite (x 223) (y 48))
- (yeti_stalactite (x 255) (y 49))
- (yeti_stalactite (x 288) (y 48))
- (yeti_stalactite (x 321) (y 49))
- (yeti_stalactite (x 352) (y 49))
- (yeti_stalactite (x 384) (y 49))
- (yeti_stalactite (x 417) (y 49))
- (yeti_stalactite (x 576) (y 49))
- (yeti_stalactite (x 545) (y 51))
- (yeti_stalactite (x 512) (y 50))
- (yeti_stalactite (x 479) (y 49))
- (yeti_stalactite (x 448) (y 49))
- (yeti_stalactite (x 607) (y 49))
- (yeti_stalactite (x 639) (y 49))
- (yeti_stalactite (x 671) (y 49))
+ (yeti_stalactite (x 97) (y 32))
+ (yeti_stalactite (x 128) (y 32))
+ (yeti_stalactite (x 161) (y 32))
+ (yeti_stalactite (x 191) (y 32))
+ (yeti_stalactite (x 223) (y 32))
+ (yeti_stalactite (x 255) (y 32))
+ (yeti_stalactite (x 288) (y 32))
+ (yeti_stalactite (x 321) (y 32))
+ (yeti_stalactite (x 352) (y 32))
+ (yeti_stalactite (x 384) (y 32))
+ (yeti_stalactite (x 417) (y 32))
+ (yeti_stalactite (x 576) (y 32))
+ (yeti_stalactite (x 545) (y 32))
+ (yeti_stalactite (x 512) (y 32))
+ (yeti_stalactite (x 479) (y 32))
+ (yeti_stalactite (x 448) (y 32))
+ (yeti_stalactite (x 607) (y 32))
+ (yeti_stalactite (x 639) (y 32))
+ (yeti_stalactite (x 671) (y 32))
)
)
(x 2674)
(y 1242)
)
- (platform (path "path1") (x 1984) (y 1568) (type "block1"))
- (platform (path "path1") (x 2016) (y 1568) (type "block2"))
- (platform (path "path1") (x 2048) (y 1568) (type "block2"))
- (platform (path "path1") (x 2080) (y 1568) (type "block3"))
- (platform (path "path2") (x 1856) (y 736) (type "block1"))
- (platform (path "path2") (x 1888) (y 736) (type "block2"))
- (platform (path "path2") (x 1920) (y 736) (type "block2"))
- (platform (path "path2") (x 1952) (y 736) (type "block3"))
- (platform (path "path3") (x 896) (y 736) (type "block1"))
- (platform (path "path3") (x 928) (y 736) (type "block2"))
- (platform (path "path3") (x 960) (y 736) (type "block2"))
- (platform (path "path3") (x 992) (y 736) (type "block3"))
+ (platform
+ (sprite "images/objects/platforms/big.sprite")
+ (path
+ (mode "circular")
+ (node (x 1984) (y 1568) (time 1))
+ (node (x 1984) (y 1568) (time 3))
+ (node (x 1984) (y 1152) (time 1))
+ (node (x 1984) (y 1152) (time 1))
+ )
+ )
+ (platform
+ (sprite "images/objects/platforms/big.sprite")
+ (path
+ (mode "circular")
+ (node (x 1856) (y 736) (time 1))
+ (node (x 1856) (y 736) (time 3))
+ (node (x 1856) (y 1152) (time 1))
+ (node (x 1856) (y 1152) (time 1))
+ )
+ )
+ (platform
+ (sprite "images/objects/platforms/big.sprite")
+ (path
+ (mode "circular")
+ (node (x 896) (y 736) (time 1))
+ (node (x 896) (y 736) (time 2))
+ (node (x 896) (y 288) (time 1))
+ (node (x 896) (y 288) (time 1))
+ )
+ )
)
)
(message (_ "Drink me")))
(powerup (x 3168) (y 416)
- (sprite "eat-me")
+ (sprite "images/objects/eat-me/eat-me.sprite")
(script "levelflip();")
)
(bonusblock (x 672) (y 1056)
(get_bbox().p1.y + get_bbox().p2.y) / 2) {
// if it's not possible to squish us, then this will hurt
if(collision_squished(player))
- return CONTINUE;
+ return ABORT_MOVE;
}
player.kill(Player::SHRINK);
if(state != STALACTITE_FALLING && state != STALACTITE_SQUISHED)
return FORCE_MOVE;
- if(fabsf(hit.normal.y) > .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);
* 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
/// if this happens to often then the move will just be aborted
CONTINUE,
/// do the move ignoring the collision
- FORCE_MOVE
+ FORCE_MOVE,
+ TEST
};
/**
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<GameObject*>::iterator i = currentsector->gameobjects.begin();
i != currentsector->gameobjects.end(); ++i)
{
GameObject* obj = *i;
-
+
LevelTime* lt = dynamic_cast<LevelTime*> (obj);
if(lt)
lt->stop();
using namespace std;
Level::Level()
- : name("noname"), author("Mr. X"), extro_music("leveldone.ogg"), extro_length(7.0)
+ : name("noname"), author("Mr. X")
{
}
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()));
public:
std::string name;
std::string author;
- std::string extro_music;
- float extro_length;
typedef std::vector<Sector*> Sectors;
Sectors sectors;
return *this;
}
+ const Vector& operator -=(const Vector& other)
+ {
+ x -= other.x;
+ y -= other.y;
+ return *this;
+ }
+
const Vector& operator *=(float val)
{
x *= val;
Vector movement;
/** The collision group */
CollisionGroup group;
+
+private:
+ // this is only here for internal collision detection use
+ Rect dest;
};
#endif
#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)
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 {
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");
}
update_scroll_normal(elapsed_time);
break;
case AUTOSCROLL:
- update_scroll_autoscroll();
+ update_scroll_autoscroll(elapsed_time);
break;
case SCROLLTO:
update_scroll_to(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();
#include <vector>
#include <cassert>
+#include <memory>
#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
{
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();
LeftRightScrollChange scrollchange;
// autoscroll mode
- Path* autoscrollPath;
+ std::auto_ptr<Path> autoscroll_path;
+ std::auto_ptr<PathWalker> autoscroll_walker;
// shaking
Timer shaketimer;
// SuperTux Path
// Copyright (C) 2005 Philipp <balinor@pnxs.de>
// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
+#include <config.h>
#include "path.hpp"
#include <assert.h>
#include <iostream>
#include <stdexcept>
+#include <sstream>
-// 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;
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
{
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);
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<std::string,Path*> 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");
// SuperTux Path
// Copyright (C) 2005 Philipp <balinor@pnxs.de>
// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
+// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
#ifndef __PATH_HPP__
#define __PATH_HPP__
-#include <string>
-#include <list>
-#include <map>
-
+#include <vector>
#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<PathNode> 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<std::string,Path*> 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<Node> nodes;
+
+ WalkMode mode;
};
#endif
+
#include "platform.hpp"
+#include <stdexcept>
#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
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
#ifndef __PLATFORM_H__
#define __PLATFORM_H__
+#include <memory>
#include "moving_object.hpp"
#include "sprite/sprite.hpp"
#include "object/path.hpp"
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> sprite;
+ std::auto_ptr<Path> path;
+ std::auto_ptr<PathWalker> walker;
+ Vector speed;
};
#endif
#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"
bbox.set_size(31.8, 63.8);
else
bbox.set_size(31.8, 31.8);
+ adjust_height = 0;
dir = RIGHT;
old_dir = dir;
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() +
bbox.get_height()*0.66666 - 32);
grabbed_object->grab(*this, pos, dir);
}
+
+ on_ground_flag = false;
}
bool
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);
}
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");
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
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<Platform*> (&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<Platform*> (&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<MovingObject*> (&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;
}
{
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;
}
}
void
+Player::add_velocity(const Vector& velocity)
+{
+ physic.set_velocity(physic.get_velocity() + velocity);
+}
+
+void
Player::bounce(BadGuy& )
{
if(controller->hold(Controller::JUMP))
bool dying;
bool backflipping;
int backflip_direction;
+
public:
-
Direction dir;
Direction old_dir;
bool jumping;
bool can_jump;
bool butt_jump;
-
+
Timer invincible_timer;
Timer skidding_timer;
Timer safe_timer;
// 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
bool visible;
+ float adjust_height;
+
Portable* grabbed_object;
Sprite* smalltux_gameover;
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());
}
sprite = sprite_manager->create("images/objects/rock/rock.sprite");
grabbed = false;
flags |= FLAG_SOLID | FLAG_PORTABLE;
+ set_group(COLGROUP_MOVING);
}
Rock::~Rock()
}
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;
}
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)
{
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
/// 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);
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
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);
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;
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);
// 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;
}
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;
}
} 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;
}
}
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) {
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;
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
|| !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;
}
}
}
|| !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);
}
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);
}
}
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);
/** 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)
}
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);
// 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);