1// SuperTux Path
2// Copyright (C) 2005 Philipp <balinor@pnxs.de>
3// Copyright (C) 2006 Christoph Sommer <christoph.sommer@2006.expires.deltadevelopment.de>
4// Copyright (C) 2006 Matthias Braun <matze@braunis.de>
5//
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (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, see <http://www.gnu.org/licenses/>.
18
19#include "object/path.hpp"
20
21#include "editor/node_marker.hpp"
22#include "supertux/sector.hpp"
23#include "util/reader_mapping.hpp"
24#include "util/writer.hpp"
25#include "util/log.hpp"
26
27WalkMode
28string_to_walk_mode(const std::string& mode_string)
29{
30 if (mode_string == "oneshot")
31 return WalkMode::ONE_SHOT;
32 else if (mode_string == "pingpong")
33 return WalkMode::PING_PONG;
34 else if (mode_string == "circular")
35 return WalkMode::CIRCULAR;
36 else {
37 log_warning << "Unknown path mode '" << mode_string << "'found. Using oneshot instead.";
38 return WalkMode::ONE_SHOT;
39 }
40}
41
42std::string
43walk_mode_to_string(WalkMode walk_mode)
44{
45 if (walk_mode == WalkMode::ONE_SHOT)
46 return "oneshot";
47 else if (walk_mode == WalkMode::PING_PONG)
48 return "pingpong";
49 else if (walk_mode == WalkMode::CIRCULAR)
50 return "circular";
51 else {
52 log_warning << "Unknown path mode found. Using oneshot instead.";
53 return "oneshot";
54 }
55}
56
57Path::Path() :
58 m_nodes(),
59 m_mode(WalkMode::CIRCULAR)
60{
61}
62
63Path::Path(const Vector& pos) :
64 m_nodes(),
65 m_mode()
66{
67 Node first_node;
68 first_node.position = pos;
69 first_node.time = 1;
70 m_nodes.push_back(first_node);
71}
72
73void
74Path::read(const ReaderMapping& reader)
75{
76 auto iter = reader.get_iter();
77
78 m_mode = WalkMode::CIRCULAR;
79 while (iter.next()) {
80 if (iter.get_key() == "mode") {
81 std::string mode_string;
82 iter.get(mode_string);
83 m_mode = string_to_walk_mode(mode_string);
84 continue;
85 } else if (iter.get_key() == "node") {
86 ReaderMapping node_mapping = iter.as_mapping();
87
88 // each new node will inherit all values from the last one
89 Node node;
90 node.time = 1;
91 if ( (!node_mapping.get("x", node.position.x) ||
92 !node_mapping.get("y", node.position.y)))
93 throw std::runtime_error("Path node without x and y coordinate specified");
94 node_mapping.get("time", node.time);
95
96 if (node.time <= 0)
97 throw std::runtime_error("Path node with non-positive time");
98
99 m_nodes.push_back(node);
100 } else {
101 log_warning << "unknown token '" << iter.get_key() << "' in Path nodes list. Ignored." << std::endl;
102 }
103 }
104
105 if (m_nodes.empty())
106 throw std::runtime_error("Path with zero nodes");
107}
108
109void
110Path::save(Writer& writer)
111{
112 if (!is_valid()) return;
113
114 writer.start_list("path");
115 if (m_mode != WalkMode::CIRCULAR) {
116 writer.write("mode", walk_mode_to_string(m_mode), false);
117 }
118
119 for (auto& nod : m_nodes) {
120 writer.start_list("node");
121 writer.write("x", nod.position.x);
122 writer.write("y", nod.position.y);
123 if (nod.time != 1.0f) {
124 writer.write("time", nod.time);
125 }
126 writer.end_list("node");
127 }
128
129 writer.end_list("path");
130}
131
132Vector
133Path::get_base() const
134{
135 if (m_nodes.empty())
136 return Vector(0, 0);
137
138 return m_nodes[0].position;
139}
140
141int
142Path::get_nearest_node_no(const Vector& reference_point) const
143{
144 int nearest_node_id = -1;
145 float nearest_node_dist = 0;
146 int id = 0;
147 for (std::vector<Node>::const_iterator i = m_nodes.begin(); i != m_nodes.end(); ++i, ++id) {
148 float dist = (i->position - reference_point).norm();
149 if ((nearest_node_id == -1) || (dist < nearest_node_dist)) {
150 nearest_node_id = id;
151 nearest_node_dist = dist;
152 }
153 }
154 return nearest_node_id;
155}
156
157int
158Path::get_farthest_node_no(const Vector& reference_point) const
159{
160 int farthest_node_id = -1;
161 float farthest_node_dist = 0;
162 int id = 0;
163 for (std::vector<Node>::const_iterator i = m_nodes.begin(); i != m_nodes.end(); ++i, ++id)
164{
165 float dist = (i->position - reference_point).norm();
166 if ((farthest_node_id == -1) || (dist > farthest_node_dist)) {
167 farthest_node_id = id;
168 farthest_node_dist = dist;
169 }
170 }
171 return farthest_node_id;
172}
173
174void
175Path::move_by(const Vector& shift)
176{
177 for (auto& nod : m_nodes) {
178 nod.position += shift;
179 }
180}
181
182void
183Path::edit_path()
184{
185 int id = 0;
186 for (auto i = m_nodes.begin(); i != m_nodes.end(); ++i) {
187 Sector::get().add<NodeMarker>(this, i, id);
188 id++;
189 }
190}
191
192bool
193Path::is_valid() const
194{
195 return !m_nodes.empty();
196}
197
198/* EOF */
199