path_gameobject.cpp 5.5 KB

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