A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
KartJump.cc
1#include "KartJump.hh"
2
3#include "game/kart/KartCollide.hh"
4#include "game/kart/KartMove.hh"
5#include "game/kart/KartParam.hh"
6#include "game/kart/KartPhysics.hh"
7#include "game/kart/KartState.hh"
8
9#include "game/system/KPadController.hh"
10
11#include <egg/math/Math.hh>
12
13namespace Kinoko::Kart {
14
16KartJump::KartJump(KartMove *move) : m_move(move) {
17 m_cooldown = 0;
18
19 // The base game doesn't initialize these explicitly, since EGG::Heaps are memset to 0.
20 m_type = TrickType::StuntTrickBasic;
21 m_nextAllowTimer = 0;
22}
23
25KartJump::~KartJump() = default;
26
28void KartJump::calcRot() {
29 m_angleDelta *= m_angleDeltaFactor;
30 m_angleDelta = std::max(m_angleDelta, m_properties.angleDeltaMin);
31 m_angleDeltaFactor -= m_angleDeltaFactorDec;
32 m_angleDeltaFactor = std::max(m_angleDeltaFactor, m_properties.angleDeltaFactorMin);
33 m_angle += m_angleDelta;
34 m_angle = std::min(m_angle, m_finalAngle);
35
36 switch (m_type) {
37 case TrickType::KartFlipTrickZ:
38 m_rot.setRPY(0.0f, 0.0f, -(m_angle * DEG2RAD) * m_rotSign);
39 break;
40 case TrickType::FlipTrickYLeft:
41 case TrickType::FlipTrickYRight:
42 m_rot.setRPY(0.0f, m_angle * DEG2RAD * m_rotSign, 0.0f);
43 break;
44 default:
45 break;
46 }
47
48 physics()->composeStuntRot(m_rot);
49}
50
52void KartJump::setupProperties() {
53 static constexpr std::array<TrickProperties, 3> TRICK_PROPERTIES = {{
54 {11.0f, 1.5f, 0.9f, 0.0018f},
55 {14.0f, 1.5f, 0.9f, 0.0006f},
56 {7.5f, 2.5f, 0.93f, 0.05f},
57 }};
58
59 static constexpr std::array<f32, 3> FINAL_ANGLES = {{
60 360.0f,
61 720.0f,
62 180.0f,
63 }};
64
65 if (m_variant == SurfaceVariant::SingleFlipTrick) {
66 m_properties = TRICK_PROPERTIES[0];
67 m_finalAngle = FINAL_ANGLES[0];
68 } else if (m_variant == SurfaceVariant::StuntTrick) {
69 m_properties = TRICK_PROPERTIES[1];
70 m_finalAngle = FINAL_ANGLES[1];
71 } else if (m_type == TrickType::BikeSideStuntTrick) {
72 m_properties = TRICK_PROPERTIES[2];
73 m_finalAngle = FINAL_ANGLES[2];
74 }
75
76 m_angleDelta = m_properties.initialAngleDiff;
77 m_angleDeltaFactorDec = m_properties.angleDiffMulDec;
78 m_angle = 0.0f;
79 m_angleDeltaFactor = 1.0f;
80 m_rot = EGG::Quatf::ident;
81}
82
84void KartJump::reset() {
85 m_cooldown = 0;
86}
87
89void KartJump::tryStart(const EGG::Vector3f &left) {
90 auto &status = KartObjectProxy::status();
91
92 if (status.offBit(eStatus::TrickStart)) {
93 return;
94 }
95
96 if (m_move->speedRatioCapped() > 0.5f) {
97 s32 boostRampType = state()->boostRampType();
98
99 if (boostRampType == 0) {
100 m_variant = SurfaceVariant::StuntTrick;
101 } else if (boostRampType == 1) {
102 m_variant = SurfaceVariant::SingleFlipTrick;
103 } else {
104 m_variant = SurfaceVariant::DoubleFlipTrick;
105 }
106
107 start(left);
108 }
109
110 status.resetBit(eStatus::TrickStart);
111}
112
114void KartJump::calc() {
115 m_cooldown = std::max(0, m_cooldown - 1);
116
117 if (status().onBit(eStatus::TrickRot)) {
118 calcRot();
119 }
120
121 calcInput();
122}
123
124bool KartJump::someFlagCheck() {
125 return status().onBit(eStatus::InAction, eStatus::TrickStart, eStatus::InATrick,
126 eStatus::OverZipper);
127}
128
130void KartJump::calcInput() {
131 constexpr s16 TRICK_ALLOW_TIMER = 14;
132
133 System::Trick trick = inputs()->currentState().trick;
134
135 if (!someFlagCheck() && trick != System::Trick::None) {
136 m_nextTrick = trick;
137 m_nextAllowTimer = TRICK_ALLOW_TIMER;
138 }
139
140 auto &status = KartObjectProxy::status();
141
142 u32 airtime = state()->airtime();
143 if (airtime == 0 || m_nextAllowTimer < 1 || airtime > 10 ||
144 (status.offBit(eStatus::Trickable) && state()->boostRampType() < 0) ||
145 someFlagCheck()) {
146 m_nextAllowTimer = std::max(0, m_nextAllowTimer - 1);
147 } else {
148 if (airtime > 2) {
149 status.setBit(eStatus::TrickStart);
150 }
151 if (status.onBit(eStatus::RampBoost)) {
152 m_boostRampEnabled = true;
153 }
154 }
155 if (status.onBit(eStatus::TouchingGround) &&
156 collide()->surfaceFlags().offBit(KartCollide::eSurfaceFlags::BoostRamp)) {
157 m_boostRampEnabled = false;
158 }
159}
160
162void KartJump::end() {
163 auto &status = KartObjectProxy::status();
164
165 if (status.onBit(eStatus::TrickRot)) {
166 physics()->composeDecayingStuntRot(m_rot);
167 }
168
169 status.resetBit(eStatus::InATrick, eStatus::TrickRot);
170 m_boostRampEnabled = false;
171}
172
174void KartJump::setAngle(const EGG::Vector3f &left) {
175 static constexpr std::array<std::array<AngleProperties, 3>, 3> ANGLE_PROPERTIES = {{
176 {{
177 {40.0f, 15.0f},
178 {45.0f, 20.0f},
179 {45.0f, 20.0f},
180 }},
181 {{
182 {36.0f, 13.0f},
183 {42.0f, 18.0f},
184 {42.0f, 18.0f},
185 }},
186 {{
187 {32.0f, 11.0f},
188 {39.0f, 16.0f},
189 {39.0f, 16.0f},
190 }},
191 }};
192
193 f32 vel1YDot = m_move->vel1Dir().dot(EGG::Vector3f::ey);
194 EGG::Vector3f vel1YCross = m_move->vel1Dir().cross(EGG::Vector3f::ey);
195 f32 vel1YCrossMag = vel1YCross.length();
196 f32 pitch = EGG::Mathf::abs(EGG::Mathf::atan2(vel1YCrossMag, vel1YDot));
197 f32 angle = 90.0f - (pitch * RAD2DEG);
198 u32 weightClass = static_cast<u32>(param()->stats().weightClass);
199 f32 targetAngle = ANGLE_PROPERTIES[weightClass][static_cast<u32>(m_variant)].targetAngle;
200
201 if (status().onBit(eStatus::JumpPad) || angle > targetAngle) {
202 return;
203 }
204
205 f32 rotAngle = ANGLE_PROPERTIES[weightClass][static_cast<u32>(m_variant)].rotAngle;
206
207 if (angle + rotAngle > targetAngle) {
208 rotAngle = targetAngle - angle;
209 }
210
211 EGG::Matrix34f nextDir;
212 nextDir.setAxisRotation(-rotAngle * DEG2RAD, left);
213 m_move->setDir(nextDir.ps_multVector(m_move->dir()));
214 m_move->setVel1Dir(m_move->dir());
215}
216
218void KartJump::start(const EGG::Vector3f &left) {
219 init();
220 setAngle(left);
221 status().setBit(eStatus::InATrick);
222 m_cooldown = 5;
223}
224
226void KartJump::init() {
227 if (m_variant == SurfaceVariant::DoubleFlipTrick) {
228 m_type = TrickType::StuntTrickBasic;
229 return;
230 }
231
232 if (m_nextTrick < System::Trick::Left) {
233 m_type = TrickType::KartFlipTrickZ;
234 m_rotSign = (m_nextTrick == System::Trick::Up) ? -1.0f : 1.0f;
235 } else {
236 m_type = static_cast<TrickType>(m_nextTrick);
237 m_rotSign = (m_type == TrickType::FlipTrickYRight) ? -1.0f : 1.0f;
238 }
239
240 setupProperties();
241 status().setBit(eStatus::TrickRot);
242}
243
244KartJumpBike::KartJumpBike(KartMove *move) : KartJump(move) {}
245
247KartJumpBike::~KartJumpBike() = default;
248
252 constexpr f32 PI_OVER_9 = 0.34906584f;
254 constexpr f32 PI_OVER_3 = 1.0471976f;
255
256 m_angleDelta *= m_angleDeltaFactor;
257 m_angleDelta = std::max(m_angleDelta, m_properties.angleDeltaMin);
258 m_angleDeltaFactor -= m_angleDeltaFactorDec;
259 m_angleDeltaFactor = std::max(m_angleDeltaFactor, m_properties.angleDeltaFactorMin);
260 m_angle += m_angleDelta;
261 m_angle = std::min(m_angle, m_finalAngle);
262
263 switch (m_type) {
266 m_rot.setRPY(-(m_angle * DEG2RAD) * m_rotSign, 0.0f, 0.0f);
267 break;
270 m_rot.setRPY(0.0f, m_angle * DEG2RAD * m_rotSign, 0.0f);
271 break;
273 f32 sin = EGG::Mathf::SinFIdx(m_angle * DEG2FIDX);
274 m_rot.setRPY(sin * -PI_OVER_9, (sin * m_rotSign) * -PI_OVER_3,
275 (sin * m_rotSign) * PI_OVER_9);
276 } break;
277 default:
278 break;
279 }
280
281 physics()->composeStuntRot(m_rot);
282}
283
285void KartJumpBike::start(const EGG::Vector3f &left) {
286 KartJump::start(left);
287
288 KartMoveBike *moveBike = static_cast<KartMoveBike *>(m_move);
289 moveBike->cancelWheelie();
290}
291
293void KartJumpBike::init() {
294 constexpr f32 DOUBLE_FLIP_TRICK_FINAL_ANGLE = 180.0f;
295
296 if (m_variant == SurfaceVariant::DoubleFlipTrick) {
297 if (m_nextTrick < System::Trick::Left) {
298 return;
299 }
300
301 m_type = TrickType::BikeSideStuntTrick;
302 m_rotSign = (m_nextTrick == System::Trick::Right) ? -1.0f : 1.0f;
303 setupProperties();
304 m_finalAngle = DOUBLE_FLIP_TRICK_FINAL_ANGLE;
305 } else {
306 m_type = static_cast<TrickType>(m_nextTrick);
307 m_rotSign =
308 (m_type == TrickType::FlipTrickYRight || m_type == TrickType::BikeFlipTrickTail) ?
309 -1.0f :
310 1.0f;
311 setupProperties();
312 }
313
314 status().setBit(eStatus::TrickRot);
315}
316
317} // namespace Kinoko::Kart
void calcRot() override
Definition KartJump.cc:250
Responsible for reacting to player inputs and moving the bike.
Definition KartMove.hh:481
virtual void cancelWheelie()
Clears the wheelie bit flag and resets the rotation decrement.
Definition KartMove.cc:2502
Responsible for reacting to player inputs and moving the kart.
Definition KartMove.hh:19
Pertains to kart-related functionality.
@ FlipTrickYLeft
A left trick with kart/bike.
Definition KartJump.hh:22
@ BikeSideStuntTrick
A side StuntTrick with a bike.
Definition KartJump.hh:25
@ BikeFlipTrickTail
A down trick with bike.
Definition KartJump.hh:21
@ BikeFlipTrickNose
An up trick with bike.
Definition KartJump.hh:20
@ FlipTrickYRight
A right trick with kart/bike.
Definition KartJump.hh:23
A 3D float vector.
Definition Vector.hh:107