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 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 = m_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 m_flags.setBit(eFlags::Position);
40 m_pos.y -= FLOOR_CHECK_SPEED;
41
42 hasCol = colDir->checkSphereFull(HEIGHT, m_pos + HITBOX_OFFSET, EGG::Vector3f::inf,
43 KCL_TYPE_FLOOR, &info, nullptr, 0);
44 }
45
46 EGG::Vector3f loweredPos = m_pos + info.tangentOff;
47 m_pos.x = loweredPos.x;
48 m_pos.z = loweredPos.z;
49 m_loweredHeight = loweredPos.y;
50 m_flags.setBit(eFlags::Position);
51 m_pos.y = m_raisedHeight;
52 m_startedLowered = false;
53}
54
56void ObjectPress::calc() {
57 m_startedLowered = false;
58
59 switch (m_state) {
60 case State::Raised:
61 calcRaised();
62 break;
63 case State::WindUp:
64 calcWindUp();
65 break;
66 case State::Lowering:
67 calcLowering();
68 break;
69 case State::Lowered:
70 calcLowered();
71 break;
72 case State::Raising:
73 calcRaising();
74 break;
75 }
76}
77
79void ObjectPress::loadAnims() {
80 std::array<const char *, 1> names = {{
81 "Press",
82 }};
83
84 std::array<Render::AnmType, 1> types = {{
85 Render::AnmType::Chr,
86 }};
87
88 linkAnims(names, types);
89}
90
92void ObjectPress::createCollision() {
93 constexpr f32 POINT_SCALE = 10.0f;
94 constexpr std::array<EGG::Vector3f, 16> POINTS = {{
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(-37.5f, 0.0f, -43.5f) * POINT_SCALE,
100 EGG::Vector3f(-43.0f, 0.0f, -38.0f) * POINT_SCALE,
101 EGG::Vector3f(-37.5f, 0.0f, 43.5f) * POINT_SCALE,
102 EGG::Vector3f(-43.0f, 0.0f, 38.0f) * 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 EGG::Vector3f(-40.0f, 143.8f, -46.0f) * POINT_SCALE,
108 EGG::Vector3f(-45.5f, 143.8f, -40.5f) * POINT_SCALE,
109 EGG::Vector3f(-40.0f, 143.8f, 46.0f) * POINT_SCALE,
110 EGG::Vector3f(-45.5f, 143.8f, 40.5f) * POINT_SCALE,
111 }};
112
113 m_collision = new ObjectCollisionConvexHull(POINTS);
114}
115
117Kart::Reaction ObjectPress::onCollision(Kart::KartObject *kartObj, Kart::Reaction reactionOnKart,
118 Kart::Reaction /*reactionOnObj*/, EGG::Vector3f & /*hitDepth*/) {
119 constexpr f32 CRUSH_THRESHOLD = 430.0f;
120
121 EGG::Vector3f diff = kartObj->pos() - m_pos;
122 bool close =
123 EGG::Mathf::abs(diff.x) < CRUSH_THRESHOLD && EGG::Mathf::abs(diff.z) < CRUSH_THRESHOLD;
124
125 // ObjectId::Press corresponds to a WallAllSpeed reaction, so when the stomper is coming down,
126 // the base game instead uses ObjectId::PressSoko to induce LongCrushLoseItem.
127 if (close && (m_state == State::Lowering || m_startedLowered)) {
128 const auto &hitTable = ObjectDirector::Instance()->hitTableKart();
129 return hitTable.reaction(hitTable.slot(ObjectId::PressSoko));
130 }
131
132 return reactionOnKart;
133}
134
136void ObjectPress::calcRaised() {
137 constexpr u32 WINDUP_FRAMES = 10;
138
139 if (--m_raisedTimer == 0) {
140 m_state = State::WindUp;
141 m_windUpTimer = WINDUP_FRAMES;
142 }
143}
144
145void ObjectPress::calcWindUp() {
146 constexpr f32 SPEED = 10.0f;
147
148 m_flags.setBit(eFlags::Position);
149 m_pos.y += SPEED;
150
151 if (--m_windUpTimer == 0) {
152 m_state = State::Lowering;
153 }
154}
155
157void ObjectPress::calcLowering() {
158 constexpr f32 ACCEL = 3.0f;
159
160 m_loweringVelocity -= ACCEL;
161 m_flags.setBit(eFlags::Position);
162 m_pos.y += m_loweringVelocity;
163
164 checkCollisionLowering();
165}
166
167void ObjectPress::calcLowered() {
168 if (--m_anmDuration > 0) {
169 return;
170 }
171
172 if (m_startingRise) {
173 m_state = State::Raising;
174 } else {
175 auto *anmMgr = m_drawMdl->anmMgr();
176 f32 frameCount = anmMgr->activeAnim(Render::AnmType::Chr)->frameCount();
177 anmMgr->playAnim(frameCount, -ANM_RATE, 0);
178
179 m_startingRise = true;
180 m_anmDuration = frameCount / ANM_RATE;
181 }
182}
183
184void ObjectPress::calcRaising() {
185 constexpr f32 SPEED = 10.0f;
186
187 f32 height = m_pos.y + SPEED;
188
189 if (height < m_raisedHeight) {
190 m_pos.y = height;
191 } else {
192 m_pos.y = m_raisedHeight;
193 m_state = State::Raised;
194 ASSERT(m_mapObj);
195 m_raisedTimer = static_cast<u32>(m_mapObj->setting(2));
196 }
197
198 m_flags.setBit(eFlags::Position);
199}
200
202void ObjectPress::checkCollisionLowering() {
203 constexpr f32 RADIUS = 10.0f;
204 constexpr EGG::Vector3f HITBOX_OFFSET = EGG::Vector3f(0.0f, RADIUS, 0.0f);
205
206 CollisionInfo info;
207
208 if (!CollisionDirector::Instance()->checkSphereFull(RADIUS, m_pos + HITBOX_OFFSET,
209 EGG::Vector3f::inf, KCL_TYPE_FLOOR, &info, nullptr, 0)) {
210 return;
211 }
212
213 m_loweringVelocity = 0.0f;
214 m_pos = m_pos + info.tangentOff;
215 m_flags.setBit(eFlags::Position);
216
217 auto *anmMgr = m_drawMdl->anmMgr();
218 anmMgr->playAnim(0.0f, ANM_RATE, 0);
219
220 m_state = State::Lowered;
221 m_startingRise = false;
222 m_anmDuration = anmMgr->activeAnim(Render::AnmType::Chr)->frameCount() / ANM_RATE;
223 m_startedLowered = true;
224}
225
227ObjectPressSenko::ObjectPressSenko(const System::MapdataGeoObj &params)
228 : ObjectPress(params), m_startingWindup(false) {}
229
231ObjectPressSenko::~ObjectPressSenko() = default;
232
234void ObjectPressSenko::calcRaised() {
235 if (m_startingWindup) {
236 startWindup();
237 m_startingWindup = false;
238 }
239}
240
242void ObjectPressSenko::startWindup() {
243 constexpr u32 WINDUP_DURATION = 10;
244
245 m_state = State::WindUp;
246 m_windUpTimer = WINDUP_DURATION;
247}
248
249} // namespace Field
#define KCL_TYPE_FLOOR
0x20E80FFF - Any KCL that the player or items can drive/land on.
The highest level abstraction for a kart.
Definition KartObject.hh:11
Pertains to collision.
A 3D float vector.
Definition Vector.hh:88