1// SuperTux - BicyclePlatform
2// Copyright (C) 2007 Christoph Sommer <christoph.sommer@2007.expires.deltadevelopment.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/bicycle_platform.hpp"
18
19#include <algorithm>
20#include <math.h>
21
22#include "math/util.hpp"
23#include "object/player.hpp"
24#include "object/portable.hpp"
25#include "supertux/debug.hpp"
26#include "supertux/sector.hpp"
27#include "util/log.hpp"
28#include "util/reader_mapping.hpp"
29
30BicyclePlatformChild::BicyclePlatformChild(const ReaderMapping& reader, float angle_offset, BicyclePlatform& parent) :
31 MovingSprite(reader, "images/objects/platforms/small.sprite", LAYER_OBJECTS, COLGROUP_STATIC),
32 m_parent(parent),
33 m_angle_offset(angle_offset),
34 m_momentum(),
35 m_contacts()
36{
37}
38
39void
40BicyclePlatformChild::update(float dt_sec)
41{
42 float angle = m_parent.m_angle + m_angle_offset;
43 angle = math::positive_fmodf(angle, math::TAU);
44
45 Vector dest = m_parent.m_center + Vector(cosf(angle), sinf(angle)) * m_parent.m_radius - (m_col.m_bbox.get_size().as_vector() * 0.5);
46 m_col.m_movement = dest - get_pos();
47}
48
49HitResponse
50BicyclePlatformChild::collision(GameObject& other, const CollisionHit& )
51{
52 const float gravity = Sector::get().get_gravity();
53
54 // somehow the hit parameter does not get filled in, so to determine (hit.top == true) we do this:
55 auto mo = dynamic_cast<MovingObject*>(&other);
56 if (!mo) return FORCE_MOVE;
57 if ((mo->get_bbox().get_bottom()) > (m_col.m_bbox.get_top() + 2)) return FORCE_MOVE;
58
59 auto pl = dynamic_cast<Player*>(mo);
60 if (pl) {
61 if (pl->is_big()) m_momentum += m_parent.m_momentum_change_rate * gravity;
62 auto po = pl->get_grabbed_object();
63 auto pomo = dynamic_cast<MovingObject*>(po);
64 if (m_contacts.insert(pomo).second) {
65 m_momentum += m_parent.m_momentum_change_rate * gravity;
66 }
67 }
68
69 if (m_contacts.insert(&other).second) {
70 m_momentum += m_parent.m_momentum_change_rate * Sector::get().get_gravity();
71 }
72
73 return FORCE_MOVE;
74}
75
76void BicyclePlatformChild::editor_delete()
77{
78 // removing a child removes the whole platform
79 m_parent.editor_delete();
80}
81
82BicyclePlatform::BicyclePlatform(const ReaderMapping& reader) :
83 GameObject(reader),
84 m_center(),
85 m_radius(128),
86 m_angle(0),
87 m_angular_speed(0.0f),
88 m_momentum_change_rate(0.1f),
89 m_children(),
90 m_walker(),
91 m_platforms(2)
92{
93 reader.get("x", m_center.x);
94 reader.get("y", m_center.y);
95 reader.get("radius", m_radius, 128.0f);
96 reader.get("momentum-change-rate", m_momentum_change_rate, 0.1f);
97
98 reader.get("platforms", m_platforms);
99 m_platforms = std::max(1, m_platforms);
100
101 for (int i = 0; i < m_platforms; ++i) {
102 const float offset = static_cast<float>(i) * (math::TAU / static_cast<float>(m_platforms));
103 m_children.push_back(&d_sector->add<BicyclePlatformChild>(reader, offset, *this));
104 }
105
106 std::string path_ref;
107 if (reader.get("path-ref", path_ref))
108 {
109 d_sector->request_name_resolve(path_ref, [this](UID uid){
110 if (!uid) {
111 log_fatal << "no path-ref entry for BicyclePlatform" << std::endl;
112 } else {
113 m_walker.reset(new PathWalker(uid, true));
114 }
115 });
116 }
117}
118
119BicyclePlatform::~BicyclePlatform()
120{
121}
122
123void
124BicyclePlatform::draw(DrawingContext& context)
125{
126 if (g_debug.show_collision_rects) {
127 context.color().draw_filled_rect(Rectf::from_center(m_center, Sizef(16, 16)), Color::MAGENTA, LAYER_OBJECTS);
128 }
129}
130
131void
132BicyclePlatform::update(float dt_sec)
133{
134 float total_angular_momentum = 0.0f;
135 for (auto& child : m_children)
136 {
137 const float child_angle = m_angle + child->m_angle_offset;
138 const float angular_momentum = cosf(child_angle) * child->m_momentum;
139 total_angular_momentum += angular_momentum;
140 child->m_momentum = 0.0f;
141 child->m_contacts.clear();
142 }
143
144 m_angular_speed += (total_angular_momentum * dt_sec) * math::PI;
145 m_angular_speed *= 1.0f - dt_sec * 0.2f;
146 m_angle += m_angular_speed * dt_sec;
147 m_angle = math::positive_fmodf(m_angle, math::TAU);
148
149 m_angular_speed = std::min(std::max(m_angular_speed, -128.0f * math::PI * dt_sec),
150 128.0f * math::PI * dt_sec);
151
152 if (m_walker)
153 {
154 m_walker->update(std::max(0.0f, dt_sec * m_angular_speed * 0.1f));
155 m_center = m_walker->get_pos();
156 }
157 else
158 {
159 m_center += Vector(m_angular_speed, 0) * dt_sec * 32;
160 }
161}
162
163void
164BicyclePlatform::editor_delete()
165{
166 // remove children
167 for (auto& child : m_children)
168 {
169 child->remove_me();
170 }
171
172 // remove self
173 remove_me();
174}
175
176void
177BicyclePlatform::after_editor_set()
178{
179 GameObject::after_editor_set();
180}
181
182ObjectSettings
183BicyclePlatform::get_settings()
184{
185 auto result = GameObject::get_settings();
186
187 result.add_float(_("X"), &m_center.x, "x", 0.0f, OPTION_HIDDEN);
188 result.add_float(_("Y"), &m_center.y, "y", 0.0f, OPTION_HIDDEN);
189
190 result.add_int(_("Platforms"), &m_platforms, "platforms", 2);
191 result.add_float(_("Radius"), &m_radius, "radius", 128);
192 result.add_float(_("Momentum change rate"), &m_momentum_change_rate, "momentum-change-rate", 0.1f);
193
194 result.reorder({"platforms", "x", "y"});
195
196 return result;
197}
198
199/* EOF */
200