New Path based on time intervals; see levels/test/platform.stl
[supertux.git] / src / object / path.cpp
1 //  $Id$
2 // 
3 //  SuperTux Path
4 //  Copyright (C) 2005 Philipp <balinor@pnxs.de>
5 //  Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
6 //
7 //  This program is free software; you can redistribute it and/or
8 //  modify it under the terms of the GNU General Public License
9 //  as published by the Free Software Foundation; either version 2
10 //  of the License, or (at your option) any later version.
11 //
12 //  This program is distributed in the hope that it will be useful,
13 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
14 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 //  GNU General Public License for more details.
16 // 
17 //  You should have received a copy of the GNU General Public License
18 //  along with this program; if not, write to the Free Software
19 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
20 //  02111-1307, USA.
21
22 #include "path.hpp"
23
24 #include "lisp/lisp.hpp"
25 #include "lisp/list_iterator.hpp"
26 #include "object_factory.hpp"
27
28 #include <assert.h>
29 #include <iostream>
30 #include <stdexcept>
31
32 // snap to destination if within EPSILON pixels
33 #define EPSILON 1.5
34
35 Path::Path(const lisp::Lisp& reader)
36 {
37   circular = true;
38   forward = true;
39
40   if (!reader.get("name", name)) throw std::runtime_error("Path without name");
41   reader.get("circular", circular);
42   reader.get("forward", forward);
43
44   const lisp::Lisp* nodes_lisp = reader.get_lisp("nodes");
45   if(!nodes_lisp) throw std::runtime_error("Path without nodes");
46
47   lisp::ListIterator iter(nodes_lisp);
48
49   PathNode node;
50   node.time = 1;
51
52   while(iter.next()) {
53     if(iter.item() != "node") {
54       std::cerr << "Warning: unknown token '" << iter.item() << "' in Path nodes list. Ignored." << std::endl;
55       continue;
56     }
57     const lisp::Lisp* node_lisp = iter.lisp();
58
59     // each new node will inherit all values from the last one
60     node_lisp->get("x", node.position.x);
61     node_lisp->get("y", node.position.y);
62     node_lisp->get("time", node.time);
63
64     if(node.time <= 0) throw std::runtime_error("Path node with non-positive time");
65
66     pathNodes.push_back(node);
67   }
68
69   if (pathNodes.size() < 1) throw std::runtime_error("Path with zero nodes");
70
71   // initial position and velocity will be set with the first update, as timeToGo is initialized to 0.
72   destinationNode = 0;
73
74   // register this path for lookup:
75   registry[name] = this;
76 }
77
78 Path::~Path()
79 {
80   registry.erase(name);
81 }
82
83         void
84 Path::update(float elapsed_time)
85 {
86
87   // advance to next node at scheduled time
88   if (timeToGo <= 0) {
89     position = pathNodes[destinationNode].position;
90
91     // set destinationNode to next node
92     if (forward) {
93       destinationNode++;
94       if (destinationNode >= (int)pathNodes.size()) {
95         if (circular) {
96           destinationNode = 0;
97         } else {
98           destinationNode = (int)pathNodes.size()-1;
99         }
100       }
101     } else {
102       destinationNode--;
103       if (destinationNode < 0) {
104         if (circular) {
105           destinationNode = (int)pathNodes.size()-1;
106         } else {
107           destinationNode = 0;
108         }
109       }
110     }
111
112     PathNode dn = pathNodes[destinationNode];
113     timeToGo = dn.time;
114     velocity = (dn.position - position) / timeToGo;
115   }
116
117   // move according to stored velocity
118   last_movement = velocity * elapsed_time;
119   position += last_movement;
120   timeToGo -= elapsed_time;
121
122   // stop when we arrive at our destination
123   PathNode dn = pathNodes[destinationNode];
124   if ((position - dn.position).norm() < EPSILON) {
125     velocity = Vector(0,0);
126   }
127
128 }
129
130 void
131 Path::draw(DrawingContext& )
132 {
133   // TODO: Add a visible flag, draw the path if true
134 }
135
136 const Vector&
137 Path::GetPosition() {
138   return position;
139 }
140
141 const Vector&
142 Path::GetLastMovement() {
143   return last_movement;
144 }
145
146
147 //////////////////////////////////////////////////////////////////////////////
148 // static stuff
149
150 std::map<std::string,Path*> Path::registry;
151
152 Path*
153 Path::GetByName(const std::string& name) {
154   return registry[name];
155 }
156
157 IMPLEMENT_FACTORY(Path, "path");