Background can now render three images: Top, Center and Bottom
[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 #include "msg.hpp"
28
29 #include <assert.h>
30 #include <iostream>
31 #include <stdexcept>
32
33 // snap to destination if within EPSILON pixels
34 #define EPSILON 1.5
35
36 Path::Path(const lisp::Lisp& reader)
37 {
38   circular = true;
39   forward = true;
40
41   if (!reader.get("name", name)) throw std::runtime_error("Path without name");
42   reader.get("circular", circular);
43   reader.get("forward", forward);
44
45   const lisp::Lisp* nodes_lisp = reader.get_lisp("nodes");
46   if(!nodes_lisp) throw std::runtime_error("Path without nodes");
47
48   lisp::ListIterator iter(nodes_lisp);
49
50   PathNode node;
51   node.time = 1;
52
53   while(iter.next()) {
54     if(iter.item() != "node") {
55       msg_warning("unknown token '" << iter.item() << "' in Path nodes list. Ignored.");
56       continue;
57     }
58     const lisp::Lisp* node_lisp = iter.lisp();
59
60     // each new node will inherit all values from the last one
61     node_lisp->get("x", node.position.x);
62     node_lisp->get("y", node.position.y);
63     node_lisp->get("time", node.time);
64
65     if(node.time <= 0) throw std::runtime_error("Path node with non-positive time");
66
67     pathNodes.push_back(node);
68   }
69
70   if (pathNodes.size() < 1) throw std::runtime_error("Path with zero nodes");
71
72   // initial position and velocity will be set with the first update, as timeToGo is initialized to 0.
73   destinationNode = 0;
74
75   // register this path for lookup:
76   registry[name] = this;
77 }
78
79 Path::~Path()
80 {
81   registry.erase(name);
82 }
83
84         void
85 Path::update(float elapsed_time)
86 {
87
88   // TODO: carry excess time over to next node? This is how it was done in camera.cpp:
89   /*
90   if(auto_t - elapsed_time >= 0) {
91     translation += current_dir * elapsed_time;
92     auto_t -= elapsed_time;
93   } else {
94     // do the rest of the old movement
95     translation += current_dir * auto_t;
96     elapsed_time -= auto_t;
97     auto_t = 0;
98
99     // construct path for next point
100     if(auto_idx+1 >= scrollpoints.size()) {
101       keep_in_bounds(translation);
102       return;
103     }
104     Vector distance = scrollpoints[auto_idx+1].position 
105                       - scrollpoints[auto_idx].position;
106     current_dir = distance.unit() * scrollpoints[auto_idx].speed;
107     auto_t = distance.norm() / scrollpoints[auto_idx].speed;
108
109     // do movement for the remaining time
110     translation += current_dir * elapsed_time;
111     auto_t -= elapsed_time;
112     auto_idx++;
113   }
114   */
115
116   // advance to next node at scheduled time
117   if (timeToGo <= 0) {
118     position = pathNodes[destinationNode].position;
119
120     // set destinationNode to next node
121     if (forward) {
122       destinationNode++;
123       if (destinationNode >= (int)pathNodes.size()) {
124         if (circular) {
125           destinationNode = 0;
126         } else {
127           destinationNode = (int)pathNodes.size()-1;
128         }
129       }
130     } else {
131       destinationNode--;
132       if (destinationNode < 0) {
133         if (circular) {
134           destinationNode = (int)pathNodes.size()-1;
135         } else {
136           destinationNode = 0;
137         }
138       }
139     }
140
141     PathNode dn = pathNodes[destinationNode];
142     timeToGo = dn.time;
143     velocity = (dn.position - position) / timeToGo;
144   }
145
146   // move according to stored velocity
147   last_movement = velocity * elapsed_time;
148   position += last_movement;
149   timeToGo -= elapsed_time;
150
151   // stop when we arrive at our destination
152   PathNode dn = pathNodes[destinationNode];
153   if ((position - dn.position).norm() < EPSILON) {
154     velocity = Vector(0,0);
155   }
156
157 }
158
159 void
160 Path::draw(DrawingContext& )
161 {
162   // TODO: Add a visible flag, draw the path if true
163 }
164
165 void
166 Path::write(lisp::Writer& writer)
167 {
168   writer.start_list("path");
169
170   writer.write_string("name", name);
171   writer.write_bool("circular", circular);
172   writer.write_bool("forward", forward);
173
174   for (int i=0; i < (int)pathNodes.size(); i++) {
175     PathNode node = pathNodes[i];
176
177     writer.start_list("node");
178     writer.write_float("x", node.position.x);
179     writer.write_float("y", node.position.y);
180     writer.write_float("time", node.time);
181
182     writer.end_list("node");
183   }
184
185   writer.end_list("path");
186 }
187
188 const Vector&
189 Path::GetPosition() {
190   return position;
191 }
192
193 const Vector&
194 Path::GetLastMovement() {
195   return last_movement;
196 }
197
198 const std::string
199 Path::GetName() {
200   return name;
201 }
202
203 //////////////////////////////////////////////////////////////////////////////
204 // static stuff
205
206 std::map<std::string,Path*> Path::registry;
207
208 Path*
209 Path::GetByName(const std::string& name) {
210   return registry[name];
211 }
212
213 IMPLEMENT_FACTORY(Path, "path");