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