A reimplementation of Mario Kart Wii's physics engine in C++
Loading...
Searching...
No Matches
ObjectPress.cc
1#include "ObjectPress.hh"
2
3#include "game/field/CollisionDirector.hh"
4#include "game/field/ObjectDirector.hh"
5
6#include "game/kart/KartObject.hh"
7
8namespace Kinoko::Field {
9
11ObjectPress::ObjectPress(const System::MapdataGeoObj &params)
12 : ObjectCollidable(params), m_loweringVelocity(0.0f) {}
13
15ObjectPress::~ObjectPress() = default;
16
18void ObjectPress::init() {
19 constexpr f32 FLOOR_CHECK_SPEED = 100.0f;
20 constexpr f32 HEIGHT = 20.0f;
21 constexpr EGG::Vector3f HITBOX_OFFSET = EGG::Vector3f(0.0f, HEIGHT, 0.0f);
22
23 m_state = State::Raised;
24 m_startingRise = false;
25 m_anmDuration = 0;
26 ASSERT(m_mapObj);
27 m_raisedTimer = static_cast<u32>(m_mapObj->setting(1));
28 m_windUpTimer = 0;
29 m_raisedHeight = pos().y;
30
31 bool hasCol = false;
32 CollisionInfo info;
33 auto *colDir = CollisionDirector::Instance();
34
35 // The game performs floor collision checks here to make sure that if a stomper is positioned
36 // above a sloped floor, it does not partially clip through the floor when it stomps down.
37 // If there is no floor below the stomper, then we get stuck in an infinite loop here.
38 while (!hasCol) {
39 subPos(EGG::Vector3f(0.0f, FLOOR_CHECK_SPEED, 0.0f));
40
41 hasCol = colDir->checkSphereFull(HEIGHT, pos() + HITBOX_OFFSET, EGG::Vector3f::inf,
42 KCL_TYPE_FLOOR, &info, nullptr, 0);
43 }
44
45 EGG::Vector3f loweredPos = pos() + info.tangentOff;
46 setPos(EGG::Vector3f(loweredPos.x, m_raisedHeight, loweredPos.z));
47 m_loweredHeight = loweredPos.y;
48 m_startedLowered = false;
49}
50
52void ObjectPress::calc() {
53 m_startedLowered = false;
54
55 switch (m_state) {
56 case State::Raised:
57 calcRaised();
58 break;
59 case State::WindUp:
60 calcWindUp();
61 break;
62 case State::Lowering:
63 calcLowering();
64 break;
65 case State::Lowered:
66 calcLowered();
67 break;
68 case State::Raising:
69 calcRaising();
70 break;
71 }
72}
73
75void ObjectPress::loadAnims() {
76 std::array<const char *, 1> names = {{
77 "Press",
78 }};
79
80 std::array<Render::AnmType, 1> types = {{
81 Render::AnmType::Chr,
82 }};
83
84 linkAnims(names, types);
85}
86
88void ObjectPress::createCollision() {
89 constexpr f32 POINT_SCALE = 10.0f;
90 constexpr std::array<EGG::Vector3f, 16> POINTS = {{
91 EGG::Vector3f(37.5f, 0.0f, 43.5f) * POINT_SCALE,
92 EGG::Vector3f(43.0f, 0.0f, 38.0f) * POINT_SCALE,
93 EGG::Vector3f(37.5f, 0.0f, -43.5f) * POINT_SCALE,
94 EGG::Vector3f(43.0f, 0.0f, 38.0f) * POINT_SCALE,
95 EGG::Vector3f(-37.5f, 0.0f, -43.5f) * POINT_SCALE,
96 EGG::Vector3f(-43.0f, 0.0f, -38.0f) * POINT_SCALE,
97 EGG::Vector3f(-37.5f, 0.0f, 43.5f) * POINT_SCALE,
98 EGG::Vector3f(-43.0f, 0.0f, 38.0f) * POINT_SCALE,
99 EGG::Vector3f(40.0f, 143.8f, 46.0f) * POINT_SCALE,
100 EGG::Vector3f(45.5f, 143.8f, 40.5f) * POINT_SCALE,
101 EGG::Vector3f(40.0f, 143.8f, -46.0f) * POINT_SCALE,
102 EGG::Vector3f(45.5f, 143.8f, -40.5f) * POINT_SCALE,
103 EGG::Vector3f(-40.0f, 143.8f, -46.0f) * POINT_SCALE,
104 EGG::Vector3f(-45.5f, 143.8f, -40.5f) * POINT_SCALE,
105 EGG::Vector3f(-40.0f, 143.8f, 46.0f) * POINT_SCALE,
106 EGG::Vector3f(-45.5f, 143.8f, 40.5f) * POINT_SCALE,
107 }};
108
109 m_collision = EGG::egg_new<ObjectCollisionConvexHull>(POINTS);
110}
111
113Kart::Reaction ObjectPress::onCollision(Kart::KartObject *kartObj, Kart::Reaction reactionOnKart,
114 Kart::Reaction /*reactionOnObj*/, EGG::Vector3f & /*hitDepth*/) {
115 constexpr f32 CRUSH_THRESHOLD = 430.0f;
116
117 EGG::Vector3f diff = kartObj->pos() - pos();
118 bool close =
119 EGG::Mathf::abs(diff.x) < CRUSH_THRESHOLD && EGG::Mathf::abs(diff.z) < CRUSH_THRESHOLD;
120
121 // ObjectId::Press corresponds to a WallAllSpeed reaction, so when the stomper is coming down,
122 // the base game instead uses ObjectId::PressSoko to induce LongCrushLoseItem.
123 if (close && (m_state == State::Lowering || m_startedLowered)) {
124 const auto &hitTable = ObjectDirector::Instance()->hitTableKart();
125 return hitTable.reaction(hitTable.slot(ObjectId::PressSoko));
126 }
127
128 return reactionOnKart;
129}
130
132void ObjectPress::calcRaised() {
133 constexpr u32 WINDUP_FRAMES = 10;
134
135 if (--m_raisedTimer == 0) {
136 m_state = State::WindUp;
137 m_windUpTimer = WINDUP_FRAMES;
138 }
139}
140
141void ObjectPress::calcWindUp() {
142 constexpr f32 SPEED = 10.0f;
143
144 addPos(EGG::Vector3f(0.0f, SPEED, 0.0f));
145
146 if (--m_windUpTimer == 0) {
147 m_state = State::Lowering;
148 }
149}
150
152void ObjectPress::calcLowering() {
153 constexpr f32 ACCEL = 3.0f;
154
155 m_loweringVelocity -= ACCEL;
156 addPos(EGG::Vector3f(0.0f, m_loweringVelocity, 0.0f));
157 checkCollisionLowering();
158}
159
160void ObjectPress::calcLowered() {
161 if (--m_anmDuration > 0) {
162 return;
163 }
164
165 if (m_startingRise) {
166 m_state = State::Raising;
167 } else {
168 auto *anmMgr = m_drawMdl->anmMgr();
169 f32 frameCount = anmMgr->activeAnim(Render::AnmType::Chr)->frameCount();
170 anmMgr->playAnim(frameCount, -ANM_RATE, 0);
171
172 m_startingRise = true;
173 m_anmDuration = frameCount / ANM_RATE;
174 }
175}
176
177void ObjectPress::calcRaising() {
178 constexpr f32 SPEED = 10.0f;
179
180 f32 height = pos().y + SPEED;
181
182 if (height < m_raisedHeight) {
183 setPos(EGG::Vector3f(pos().x, height, pos().z));
184 } else {
185 setPos(EGG::Vector3f(pos().x, m_raisedHeight, pos().z));
186 m_state = State::Raised;
187 ASSERT(m_mapObj);
188 m_raisedTimer = static_cast<u32>(m_mapObj->setting(2));
189 }
190}
191
193void ObjectPress::checkCollisionLowering() {
194 constexpr f32 RADIUS = 10.0f;
195 constexpr EGG::Vector3f HITBOX_OFFSET = EGG::Vector3f(0.0f, RADIUS, 0.0f);
196
197 CollisionInfo info;
198
199 if (!CollisionDirector::Instance()->checkSphereFull(RADIUS, pos() + HITBOX_OFFSET,
200 EGG::Vector3f::inf, KCL_TYPE_FLOOR, &info, nullptr, 0)) {
201 return;
202 }
203
204 m_loweringVelocity = 0.0f;
205 addPos(info.tangentOff);
206
207 auto *anmMgr = m_drawMdl->anmMgr();
208 anmMgr->playAnim(0.0f, ANM_RATE, 0);
209
210 m_state = State::Lowered;
211 m_startingRise = false;
212 m_anmDuration = anmMgr->activeAnim(Render::AnmType::Chr)->frameCount() / ANM_RATE;
213 m_startedLowered = true;
214}
215
217ObjectPressSenko::ObjectPressSenko(const System::MapdataGeoObj &params)
218 : ObjectPress(params), m_startingWindup(false) {}
219
221ObjectPressSenko::~ObjectPressSenko() = default;
222
224void ObjectPressSenko::calcRaised() {
225 if (m_startingWindup) {
226 startWindup();
227 m_startingWindup = false;
228 }
229}
230
232void ObjectPressSenko::startWindup() {
233 constexpr u32 WINDUP_DURATION = 10;
234
235 m_state = State::WindUp;
236 m_windUpTimer = WINDUP_DURATION;
237}
238
239} // namespace Kinoko::Field
#define KCL_TYPE_FLOOR
0x20E80FFF - Any KCL that the player or items can drive/land on.
Pertains to collision.