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/platform.hpp"
18
19#include "editor/editor.hpp"
20#include "object/player.hpp"
21#include "supertux/sector.hpp"
22#include "util/reader_mapping.hpp"
23#include "util/writer.hpp"
24
25Platform::Platform(const ReaderMapping& reader) :
26 Platform(reader, "images/objects/flying_platform/flying_platform.sprite")
27{
28}
29
30Platform::Platform(const ReaderMapping& reader, const std::string& default_sprite) :
31 MovingSprite(reader, default_sprite, LAYER_OBJECTS, COLGROUP_STATIC),
32 ExposedObject<Platform, scripting::Platform>(this),
33 PathObject(),
34 m_speed(Vector(0,0)),
35 m_automatic(false),
36 m_player_contact(false),
37 m_last_player_contact(false)
38{
39 bool running = true;
40 reader.get("running", running);
41 if ((get_name().empty()) && (!running)) {
42 m_automatic = true;
43 }
44
45 init_path(reader, true);
46}
47
48void
49Platform::finish_construction()
50{
51 if (!get_path())
52 {
53 // If no path is given, make a one-node dummy path
54 init_path_pos(m_col.m_bbox.p1(), false);
55 }
56
57 m_col.m_bbox.set_pos(get_path()->get_base());
58}
59
60ObjectSettings
61Platform::get_settings()
62{
63 ObjectSettings result = MovingSprite::get_settings();
64
65 result.add_path_ref(_("Path"), get_path_ref(), "path-ref");
66 result.add_walk_mode(_("Path Mode"), &get_path()->m_mode, {}, {});
67 result.add_bool(_("Running"), &get_walker()->m_running, "running", true, 0);
68
69 result.reorder({"running", "name", "path-ref", "sprite", "x", "y"});
70
71 return result;
72}
73
74HitResponse
75Platform::collision(GameObject& other, const CollisionHit& )
76{
77 if (dynamic_cast<Player*>(&other)) {
78 m_player_contact = true;
79 }
80 return FORCE_MOVE;
81}
82
83void
84Platform::update(float dt_sec)
85{
86 if (!get_path()) return;
87 if (!get_path()->is_valid()) return;
88
89 // check if Platform should automatically pick a destination
90 if (m_automatic)
91 {
92 if (!m_player_contact && !get_walker()->is_running()) {
93 // Player doesn't touch platform and Platform is not moving
94
95 // Travel to node nearest to nearest player
96 if (auto* player = Sector::get().get_nearest_player(m_col.m_bbox)) {
97 int nearest_node_id = get_path()->get_nearest_node_no(player->get_bbox().p2());
98 if (nearest_node_id != -1) {
99 goto_node(nearest_node_id);
100 }
101 }
102 }
103
104 if (m_player_contact && !m_last_player_contact && !get_walker()->is_running()) {
105 // Player touched platform, didn't touch last frame and Platform is not moving
106
107 // Travel to node farthest from current position
108 int farthest_node_id = get_path()->get_farthest_node_no(get_pos());
109 if (farthest_node_id != -1) {
110 goto_node(farthest_node_id);
111 }
112 }
113
114 // Clear player_contact flag set by collision() method
115 m_last_player_contact = m_player_contact;
116 m_player_contact = false;
117 }
118
119 get_walker()->update(dt_sec);
120 Vector new_pos = get_walker()->get_pos();
121 m_col.m_movement = new_pos - get_pos();
122 m_speed = m_col.m_movement / dt_sec;
123}
124
125void
126Platform::editor_update()
127{
128 if (!get_path()) return;
129 if (!get_path()->is_valid()) return;
130
131 set_pos(get_walker()->get_pos());
132}
133
134void
135Platform::goto_node(int node_no)
136{
137 get_walker()->goto_node(node_no);
138}
139
140void
141Platform::start_moving()
142{
143 get_walker()->start_moving();
144}
145
146void
147Platform::stop_moving()
148{
149 get_walker()->stop_moving();
150}
151
152void
153Platform::move_to(const Vector& pos)
154{
155 Vector shift = pos - m_col.m_bbox.p1();
156 if (get_path()) {
157 get_path()->move_by(shift);
158 }
159 set_pos(pos);
160}
161
162/* EOF */
163