29ee6fcec65d849696cf4e772fb1f4d6a0e226f3
[supertux.git] / src / object / path_walker.cpp
1 //  $Id$
2 //
3 //  SuperTux
4 //  Copyright (C) 2006 Matthias Braun <matze@braunis.de>
5 //
6 //  This program is free software; you can redistribute it and/or
7 //  modify it under the terms of the GNU General Public License
8 //  as published by the Free Software Foundation; either version 2
9 //  of the License, or (at your option) any later version.
10 //
11 //  This program is distributed in the hope that it will be useful,
12 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //  GNU General Public License for more details.
15 //
16 //  You should have received a copy of the GNU General Public License
17 //  along with this program; if not, write to the Free Software
18 //  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 #include <config.h>
21
22 #include <math.h>
23 #include <assert.h>
24 #include "path_walker.hpp"
25
26 PathWalker::PathWalker(const Path* path, bool running)
27   : path(path), running(running), current_node_nr(0), next_node_nr(0), stop_at_node_nr(running?-1:0), node_time(0),
28     walking_speed(1.0)
29 {
30   node_mult = 1 / path->nodes[0].time;
31   next_node_nr = path->nodes.size() > 1 ? 1 : 0;
32 }
33
34 PathWalker::~PathWalker()
35 {
36 }
37
38 Vector
39 PathWalker::advance(float elapsed_time)
40 {
41   if (!running) return path->nodes[current_node_nr].position;
42
43   assert(elapsed_time >= 0);
44
45   elapsed_time *= fabsf(walking_speed);
46   
47   const Path::Node* current_node = & (path->nodes[current_node_nr]);
48   while(node_time + elapsed_time * node_mult >= 1) {
49     elapsed_time -= (1 - node_time) / node_mult;
50
51     if(walking_speed > 0) {
52       advance_node();
53     } else if(walking_speed < 0) {
54       goback_node();
55     }
56
57     current_node = & (path->nodes[current_node_nr]);
58     node_time = 0;
59     if(walking_speed > 0) {
60       node_mult = 1 / current_node->time;
61     } else {
62       node_mult = 1 / path->nodes[next_node_nr].time;
63     }
64   }
65   
66   const Path::Node* next_node = & (path->nodes[next_node_nr]);
67   node_time += elapsed_time * node_mult;
68  
69   Vector new_pos = current_node->position + 
70     (next_node->position - current_node->position) * node_time;
71     
72   return new_pos;
73 }
74
75 void 
76 PathWalker::goto_node(int node_no)
77 {
78   if (node_no == stop_at_node_nr) return;
79   running = true;
80   stop_at_node_nr = node_no;
81 }
82
83 void 
84 PathWalker::start_moving()
85 {
86   running = true;
87   stop_at_node_nr = -1;
88 }
89
90 void 
91 PathWalker::stop_moving()
92 {
93   stop_at_node_nr = next_node_nr;
94 }
95
96
97 void
98 PathWalker::advance_node()
99 {
100   current_node_nr = next_node_nr;
101   if (static_cast<int>(current_node_nr) == stop_at_node_nr) running = false;
102
103   if(next_node_nr + 1 < path->nodes.size()) {
104     next_node_nr++;
105     return;
106   }
107
108   switch(path->mode) {
109     case Path::ONE_SHOT:
110       next_node_nr = path->nodes.size() - 1;
111       walking_speed = 0;
112       return;
113
114     case Path::PING_PONG:
115       walking_speed = -walking_speed;
116       next_node_nr = path->nodes.size() > 1 ? path->nodes.size() - 2 : 0;
117       return;
118
119     case Path::CIRCULAR:
120       next_node_nr = 0;
121       return;
122   }
123
124   // we shouldn't get here
125   assert(false);
126   next_node_nr = path->nodes.size() - 1;
127   walking_speed = 0;
128 }
129
130 void
131 PathWalker::goback_node()
132 {
133   current_node_nr = next_node_nr;
134
135   if(next_node_nr > 0) {
136     next_node_nr--;
137     return;
138   }
139
140   switch(path->mode) {
141     case Path::PING_PONG:
142       walking_speed = -walking_speed;
143       next_node_nr = path->nodes.size() > 1 ? 1 : 0;
144       return;
145     default:
146       break;
147   }
148
149   assert(false);
150   next_node_nr = 0;
151   walking_speed = 0;
152 }