1// SuperTux
2// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
3//
4// This program is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// This program is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17#include "object/path_walker.hpp"
18
19#include <math.h>
20#include <assert.h>
21
22#include "editor/editor.hpp"
23#include "editor/object_option.hpp"
24#include "math/random.hpp"
25#include "object/path_gameobject.hpp"
26#include "supertux/d_scope.hpp"
27#include "supertux/sector.hpp"
28#include "util/gettext.hpp"
29
30PathWalker::PathWalker(UID path_uid, bool running_) :
31 m_path_uid(path_uid),
32 m_running(running_),
33 m_current_node_nr(0),
34 m_next_node_nr(),
35 m_stop_at_node_nr(m_running?-1:0),
36 m_node_time(0),
37 m_node_mult(),
38 m_walking_speed(1.0)
39{
40 Path* path = get_path();
41 if (!path) return;
42 if (!path->is_valid()) return;
43
44 m_next_node_nr = path->m_nodes.size() > 1 ? 1 : 0;
45 m_node_mult = 1 / path->m_nodes[0].time;
46}
47
48PathWalker::~PathWalker()
49{
50}
51
52Path*
53PathWalker::get_path() const
54{
55 if (!d_sector) return nullptr;
56
57 auto path_gameobject = d_sector->get_object_by_uid<PathGameObject>(m_path_uid);
58 if (!path_gameobject)
59 {
60 return nullptr;
61 }
62 else
63 {
64 return &path_gameobject->get_path();
65 }
66}
67
68void
69PathWalker::update(float dt_sec)
70{
71 Path* path = get_path();
72 if (!path) return;
73 if (!path->is_valid()) return;
74 if (Editor::is_active()) return;
75 if (!m_running) return;
76
77 float delta = fabsf(m_walking_speed) * dt_sec;
78
79 while (m_node_time + delta * m_node_mult >= 1) {
80 delta -= (1 - m_node_time) / m_node_mult;
81
82 if (m_walking_speed > 0) {
83 advance_node();
84 } else if (m_walking_speed < 0) {
85 goback_node();
86 }
87
88 auto current_node = & (path->m_nodes[m_current_node_nr]);
89 m_node_time = 0;
90 if (m_walking_speed > 0) {
91 m_node_mult = 1 / current_node->time;
92 } else {
93 m_node_mult = 1 / path->m_nodes[m_next_node_nr].time;
94 }
95 }
96
97 m_node_time += delta * m_node_mult;
98}
99
100Vector
101PathWalker::get_pos() const
102{
103 Path* path = get_path();
104 if (!path) return Vector(0, 0);
105 if (!path->is_valid()) return Vector(0, 0);
106 if (Editor::is_active()) return path->m_nodes.begin()->position;
107
108 const Path::Node* current_node = &(path->m_nodes[m_current_node_nr]);
109 const Path::Node* next_node = & (path->m_nodes[m_next_node_nr]);
110 Vector new_pos = current_node->position +
111 (next_node->position - current_node->position) * m_node_time;
112
113 return new_pos;
114}
115
116void
117PathWalker::goto_node(int node_no)
118{
119 Path* path = get_path();
120 if (!path) return;
121
122 if (node_no == m_stop_at_node_nr) return;
123 m_running = true;
124 m_stop_at_node_nr = node_no;
125}
126
127void
128PathWalker::start_moving()
129{
130 m_running = true;
131 m_stop_at_node_nr = -1;
132}
133
134void
135PathWalker::stop_moving()
136{
137 m_stop_at_node_nr = static_cast<int>(m_next_node_nr);
138}
139
140void
141PathWalker::advance_node()
142{
143 Path* path = get_path();
144 if (!path) return;
145 if (!path->is_valid()) return;
146
147 m_current_node_nr = m_next_node_nr;
148 if (static_cast<int>(m_current_node_nr) == m_stop_at_node_nr) m_running = false;
149
150 if (m_next_node_nr + 1 < path->m_nodes.size()) {
151 m_next_node_nr++;
152 return;
153 }
154
155 switch (path->m_mode) {
156 case WalkMode::ONE_SHOT:
157 m_next_node_nr = path->m_nodes.size() - 1;
158 m_walking_speed = 0;
159 return;
160
161 case WalkMode::PING_PONG:
162 m_walking_speed = -m_walking_speed;
163 m_next_node_nr = path->m_nodes.size() > 1 ? path->m_nodes.size() - 2 : 0;
164 return;
165
166 case WalkMode::CIRCULAR:
167 m_next_node_nr = 0;
168 return;
169 }
170
171 // we shouldn't get here
172 assert(false);
173 m_next_node_nr = path->m_nodes.size() - 1;
174 m_walking_speed = 0;
175}
176
177void
178PathWalker::goback_node()
179{
180 Path* path = get_path();
181 if (!path) return;
182 if (!path->is_valid()) return;
183
184 m_current_node_nr = m_next_node_nr;
185
186 if (m_next_node_nr > 0) {
187 m_next_node_nr--;
188 return;
189 }
190
191 switch (path->m_mode) {
192 case WalkMode::PING_PONG:
193 m_walking_speed = -m_walking_speed;
194 m_next_node_nr = path->m_nodes.size() > 1 ? 1 : 0;
195 return;
196 default:
197 break;
198 }
199
200 assert(false);
201 m_next_node_nr = 0;
202 m_walking_speed = 0;
203}
204
205/* EOF */
206