1// SuperTux
2// Copyright (C) 2018 Ingo Ruhnke <grumbel@gmail.com>
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_gameobject.hpp"
18
19#include <boost/optional.hpp>
20
21#include "object/path.hpp"
22#include "sprite/sprite.hpp"
23#include "sprite/sprite_manager.hpp"
24#include "supertux/debug.hpp"
25#include "util/log.hpp"
26#include "util/reader_mapping.hpp"
27#include "util/unique_name.hpp"
28#include "video/color.hpp"
29#include "video/drawing_context.hpp"
30
31namespace {
32
33PathStyle PathStyle_from_string(const std::string& text)
34{
35 if (text == "none") {
36 return PathStyle::NONE;
37 } else if (text == "solid") {
38 return PathStyle::SOLID;
39 } else {
40 log_warning << "unknown PathStyle: " << text << std::endl;
41 return PathStyle::NONE;
42 }
43}
44
45} // namespace
46
47PathGameObject::PathGameObject() :
48 m_path(new Path),
49 m_style(PathStyle::NONE),
50 m_edge_sprite(),
51 m_node_sprite()
52{
53 m_name = make_unique_name("path", this);
54}
55
56PathGameObject::PathGameObject(const Vector& pos) :
57 m_path(new Path(pos)),
58 m_style(PathStyle::NONE),
59 m_edge_sprite(),
60 m_node_sprite()
61{
62 m_name = make_unique_name("path", this);
63}
64
65PathGameObject::PathGameObject(const ReaderMapping& mapping, bool backward_compatibility_hack) :
66 GameObject(mapping),
67 m_path(new Path),
68 m_style(PathStyle::NONE),
69 m_edge_sprite(),
70 m_node_sprite()
71{
72 if (backward_compatibility_hack)
73 {
74 m_path->read(mapping);
75 }
76 else
77 {
78 boost::optional<ReaderMapping> path_mapping;
79 if (mapping.get("path", path_mapping))
80 {
81 m_path->read(*path_mapping);
82 }
83 }
84
85 mapping.get_custom("style", m_style, PathStyle_from_string);
86
87 if (m_style == PathStyle::SOLID)
88 {
89 m_edge_sprite = SpriteManager::current()->create("images/objects/path/edge.sprite");
90 m_node_sprite = SpriteManager::current()->create("images/objects/path/node.sprite");
91 }
92
93 if (m_name.empty()) {
94 set_name(make_unique_name("path", this));
95 }
96}
97
98PathGameObject::~PathGameObject()
99{
100}
101
102void
103PathGameObject::update(float dt_sec)
104{
105 // nothing to do
106}
107
108void
109PathGameObject::draw(DrawingContext& context)
110{
111 if (m_style == PathStyle::SOLID)
112 {
113 boost::optional<Vector> previous_node;
114 for (const auto& node : m_path->get_nodes())
115 {
116 if (previous_node)
117 {
118 const Vector p1 = *previous_node;
119 const Vector p2 = node.position;
120 const Vector diff = (p2 - p1);
121 const float length = diff.norm();
122 const Vector unit = diff.unit();
123 float dot_distance = 16.0f;
124
125 // Recalculate the dot distance to evenly spread across the
126 // whole edge
127 dot_distance = length / floorf(length / dot_distance);
128
129 for (float i = dot_distance; i < length; i += dot_distance) // NOLINT
130 {
131 Vector dot_pos = p1 + unit * i;
132 m_edge_sprite->draw(context.color(), Vector(dot_pos), LAYER_OBJECTS - 1);
133 }
134 }
135
136 m_node_sprite->draw(context.color(), node.position, LAYER_OBJECTS - 1);
137
138 previous_node = node.position;
139 }
140 }
141
142 if (g_debug.show_collision_rects)
143 {
144 const Color node_color = Color::BLUE;
145 const Color edge_color = Color::MAGENTA;
146
147 boost::optional<Vector> previous_node;
148 for (const auto& node : m_path->get_nodes())
149 {
150 if (previous_node)
151 {
152 context.color().draw_line(*previous_node, node.position, edge_color, LAYER_OBJECTS - 2);
153 }
154
155 context.color().draw_filled_rect(Rectf::from_center(node.position, Sizef(16.0f, 16.0f)),
156 node_color, LAYER_OBJECTS - 1);
157
158 previous_node = node.position;
159 }
160 }
161}
162
163ObjectSettings
164PathGameObject::get_settings()
165{
166 ObjectSettings result = GameObject::get_settings();
167
168 result.add_path(_("Path"), m_path.get(), "path");
169
170 return result;
171}
172
173void
174PathGameObject::editor_select()
175{
176 log_fatal << "PathGameObject::selected" << std::endl;
177}
178
179void
180PathGameObject::editor_deselect()
181{
182 log_fatal << "PathGameObject::deselected" << std::endl;
183}
184
185/* EOF */
186