(image "images/background/arctis.jpg")
(speed 0.500000)
)
- (path (name "path1") (circular #t) (speed 100) (x 0) (y 0) (x 0) (y -100) (x 100) (y -100) (x 100) (y 0))
- (path (name "path2") (circular #t) (speed 100) (x 0) (y 0) (x 0) (y 100) (x 100) (y 100) (x 100) (y 0))
+
+ (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))
+ )
+ )
+ (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))
+ )
+ )
+ (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))
+ )
+ )
+ (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))
+ )
+ )
+ (path
+ (name "path5")
+ (circular #f)
+ (nodes
+ (node)
+ (node (y -100))
+ (node (x 100))
+ (node (y 0))
+ (node (x 0))
+ )
+ )
+
(platform (use_path "path1") (x 200) (y 850) (type "block1"))
- (platform (use_path "path1") (x 232) (y 850) (type "block2"))
- (platform (use_path "path2") (x 264) (y 650) (type "block2"))
- (platform (use_path "path2") (x 296) (y 650) (type "block2"))
- (platform (use_path "path1") (x 328) (y 850) (type "block2"))
- (platform (use_path "path1") (x 360) (y 850) (type "block2"))
- (platform (use_path "path2") (x 392) (y 650) (type "block2"))
- (platform (use_path "path2") (x 424) (y 650) (type "block2"))
- (platform (use_path "path1") (x 456) (y 850) (type "block2"))
- (platform (use_path "path1") (x 488) (y 850) (type "block3"))
+ (platform (use_path "path1") (x 232) (y 850) (type "block3"))
+ (platform (use_path "path2") (x 264) (y 850) (type "block1"))
+ (platform (use_path "path2") (x 296) (y 850) (type "block3"))
+ (platform (use_path "path3") (x 328) (y 850) (type "block1"))
+ (platform (use_path "path3") (x 360) (y 850) (type "block3"))
+ (platform (use_path "path4") (x 392) (y 850) (type "block1"))
+ (platform (use_path "path4") (x 424) (y 850) (type "block3"))
+ (platform (use_path "path5") (x 456) (y 850) (type "block1"))
+ (platform (use_path "path5") (x 488) (y 850) (type "block3"))
(powerup (x 100) (y 700) (sprite "images/powerups/egg/egg.sprite"))
(jumpy (x 140) (y 750))
(spawnpoint (name "main") (x 340) (y 800))
// $Id$
//
-// SuperTux
+// SuperTux Path
// Copyright (C) 2005 Philipp <balinor@pnxs.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
#include "object_factory.hpp"
#include <assert.h>
+#include <iostream>
+#include <stdexcept>
-
-// some constants
-#define DEFAULT_PIXELS_PER_SECOND 50
-#define EPSILON 1.5
+// snap to destination if within EPSILON pixels
+#define EPSILON 1.5
Path::Path(const lisp::Lisp& reader)
{
+ circular = true;
forward = true;
- float x = 0, y = 0;
- lisp::ListIterator iter(&reader);
+ if (!reader.get("name", name)) throw std::runtime_error("Path without name");
+ reader.get("circular", circular);
+ reader.get("forward", forward);
- assert (iter.next());
- std::string token = iter.item();
- assert(token == "name");
- iter.value()->get(name);
+ const lisp::Lisp* nodes_lisp = reader.get_lisp("nodes");
+ if(!nodes_lisp) throw std::runtime_error("Path without nodes");
- circular = true;
- assert (iter.next());
- token = iter.item();
- if (token == "circular") {
- iter.value()->get(circular);
- iter.next();
- }
+ lisp::ListIterator iter(nodes_lisp);
- pixels_per_second = DEFAULT_PIXELS_PER_SECOND;
- assert (iter.next());
- token = iter.item();
- if (token == "speed") {
- iter.value()->get(pixels_per_second);
- iter.next();
- }
- do {
- token = iter.item();
- if(token == "x") {
- iter.value()->get(x);
- } else if(token == "y") {
- iter.value()->get(y);
- points.push_back(Vector(x,y));
+ PathNode node;
+ node.time = 1;
+
+ while(iter.next()) {
+ if(iter.item() != "node") {
+ std::cerr << "Warning: unknown token '" << iter.item() << "' in Path nodes list. Ignored." << std::endl;
+ continue;
}
- } while(iter.next());
+ 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_lisp->get("time", node.time);
+
+ if(node.time <= 0) throw std::runtime_error("Path node with non-positive time");
- next_target = points.begin();
- pos = *next_target;
+ pathNodes.push_back(node);
+ }
+
+ if (pathNodes.size() < 1) throw std::runtime_error("Path with zero nodes");
- calc_next_velocity();
+ // 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;
registry.erase(name);
}
-void
+ void
Path::update(float elapsed_time)
{
+
+ // 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;
- pos += last_movement;
- if ((pos - *next_target).norm() < EPSILON) {
- pos = *next_target;
- calc_next_velocity();
+ 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);
}
+
}
void
Path::draw(DrawingContext& )
{
- // TODO: Add a visible flag, draw the path if true
+ // TODO: Add a visible flag, draw the path if true
}
const Vector&
Path::GetPosition() {
- return pos;
-}
-
-const Vector&
-Path::GetStart() {
- return *(points.begin());
+ return position;
}
const Vector&
return last_movement;
}
-void
-Path::calc_next_velocity()
-{
- Vector distance;
-
- if (circular) {
- ++next_target;
- if (next_target == points.end()) {
- next_target = points.begin();
- }
- }
- else if (forward) {
- ++next_target;
- if (next_target == points.end()) {
- forward = false;
- }
- }
- else {
- //FIXME: Implement going backwards on the list
- // I have no f***ing idea how this is done in C++
- }
-
- distance = *next_target - pos;
- velocity = distance.unit() * pixels_per_second;
-}
//////////////////////////////////////////////////////////////////////////////
// static stuff
-PathRegistry Path::registry;
+std::map<std::string,Path*> Path::registry;
Path*
Path::GetByName(const std::string& name) {
// $Id$
//
-// SuperTux
+// SuperTux Path
// Copyright (C) 2005 Philipp <balinor@pnxs.de>
+// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
#include "lisp/lisp.hpp"
-class Path;
-typedef std::map<std::string,Path*> PathRegistry;
-
-
-typedef std::list<Vector> PathPoints;
-typedef std::list<Vector>::const_iterator PathPointIter;
+/**
+ * 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:
virtual void draw(DrawingContext& context);
const Vector& GetPosition();
- const Vector& GetStart();
const Vector& GetLastMovement();
// WARNING: returns NULL if not found !
static Path* GetByName(const std::string& name);
private:
- std::string name;
- float pixels_per_second;
- PathPoints points;
- PathPointIter next_target;
- Vector pos;
- Vector velocity;
- Vector last_movement;
+ 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 */
- bool circular;
- bool forward;
+ Vector position; /**< current position */
+ Vector velocity; /**< current velocity */
+ Vector last_movement; /**< amount of pixels we moved in the last call to update */
- void calc_next_velocity();
+ int destinationNode; /**< current destination Node */
+ float timeToGo; /**< seconds until we arrive at the destination */
- static PathRegistry registry;
+ static std::map<std::string,Path*> registry;
};
#endif