1#include "ObjectPylon.hh"
3#include "game/field/CollisionDirector.hh"
4#include "game/field/ObjectDirector.hh"
6#include "game/kart/KartObject.hh"
8#include "game/system/RaceManager.hh"
14 : ObjectCollidable(params), m_initPos(m_pos), m_initScale(m_scale), m_initRot(m_rot) {}
17ObjectPylon::~ObjectPylon() =
default;
20void ObjectPylon::init() {
21 constexpr f32 NEIGHBOR_SQUARE_RADIUS = 2400.0f;
23 m_state = State::Idle;
24 m_stateStartFrame = 0;
27 m_neighbors = {
nullptr};
28 m_flags.setBit(eFlags::Position);
34 EGG::Vector3f prevPos = m_pos + EGG::Vector3f::ey * (RADIUS + FALL_VEL);
36 bool hasCol = CollisionDirector::Instance()->checkSphereFull(RADIUS, pos, prevPos,
37 KCL_TYPE_6CEBDFFF, &info, &mask, 0);
40 m_pos += info.tangentOff;
41 m_flags.setBit(eFlags::Position);
44 setMatrixTangentTo(info.floorNrm, EGG::Vector3f::ez);
48 auto *objDir = ObjectDirector::Instance();
49 auto &managedObjs = objDir->managedObjects();
51 for (
auto *&obj : managedObjs) {
52 if ((obj->pos() - m_pos).length() >= NEIGHBOR_SQUARE_RADIUS) {
57 ASSERT(obj->id() == ObjectId::Pylon);
58 auto *&pylon =
reinterpret_cast<ObjectPylon *&
>(obj);
59 for (
auto *&neighbor : m_neighbors) {
66 for (
auto *&neighbor : pylon->m_neighbors) {
74 objDir->addManagedObject(
this);
78void ObjectPylon::calc() {
93 if (System::RaceManager::Instance()->timer() - m_stateStartFrame > STATE_COOLDOWN_FRAMES) {
94 m_state = State::Idle;
108 Kart::Reaction , Kart::Reaction ,
110 constexpr f32 HIT_SPEED_RATIO_THRESHOLD = 0.7f;
111 constexpr f32 PI_OVER_SIX = 0.52359879f;
112 constexpr f32 FIVE_PI_OVER_SIX = 2.617994f;
113 constexpr f32 COME_BACK_HIT_FACTOR = 0.5f;
115 u32 t = System::RaceManager::Instance()->timer();
117 if (m_state == State::ComeBack) {
118 startHit(COME_BACK_HIT_FACTOR, hitDepth);
119 m_stateStartFrame = t;
120 return Kart::Reaction::WeakWall;
123 u32 stateFrame = t - m_stateStartFrame;
124 bool canChangeState = stateFrame > STATE_COOLDOWN_FRAMES;
125 if (canChangeState && m_state == State::Moving) {
126 m_state = State::Idle;
127 return Kart::Reaction::WeakWall;
130 if (canChangeState && m_state == State::Hit) {
131 return Kart::Reaction::WeakWall;
134 if (m_state == State::Hit) {
135 return Kart::Reaction::None;
138 f32 speedRatio = kartObj->speedRatioCapped();
139 if ((speedRatio >= HIT_SPEED_RATIO_THRESHOLD && m_state != State::Moving) ||
140 m_state == State::Hiding || m_state == State::Hide) {
141 if (m_state == State::Idle) {
142 startHit(speedRatio, hitDepth);
143 m_stateStartFrame = t;
146 return Kart::Reaction::WeakWall;
149 checkCollision(hitDepth);
150 m_state = State::Moving;
153 f32 atan = EGG::Mathf::abs(EGG::Mathf::atan2(cross.
length(), hitDepth.
dot(zAxis)));
156 if (atan >= PI_OVER_SIX && atan < FIVE_PI_OVER_SIX) {
157 return Kart::Reaction::None;
159 return Kart::Reaction::WeakWall;
172 constexpr f32 TRAVEL_RADIUS = 1200.0f;
175 m_flags.setBit(eFlags::Position);
178 for (
auto *&neighbor : m_neighbors) {
179 if (neighbor && collision()->check(*neighbor->collision(), dist)) {
181 m_flags.setBit(eFlags::Position);
188 if (delta.
length() > TRAVEL_RADIUS) {
189 delta.x += hitDepth.z;
190 delta.z -= hitDepth.x;
193 m_pos = m_initPos + delta * TRAVEL_RADIUS;
194 m_flags.setBit(eFlags::Position);
198 m_flags.setBit(eFlags::Position);
204 bool hasCol = CollisionDirector::Instance()->checkSphereFullPush(RADIUS, pos, m_pos,
205 KCL_TYPE_60E8DFFF, &info, &mask, 0);
207 m_pos += info.tangentOff;
208 m_flags.setBit(eFlags::Position);
210 if (info.floorDist > -std::numeric_limits<f32>::min()) {
211 setMatrixTangentTo(info.floorNrm, EGG::Vector3f::ez);
217void ObjectPylon::startHit(f32 velFactor,
EGG::Vector3f &hitDepth) {
218 constexpr f32 ANG_VEL_SCALAR = 0.5f;
219 constexpr f32 VEL_SCALAR = 100.0f;
221 m_state = State::Hit;
224 m_angVel = m_vel * velFactor * ANG_VEL_SCALAR;
226 m_vel *= velFactor * VEL_SCALAR;
227 hitDepth.normalise2();
230void ObjectPylon::calcHit() {
231 constexpr f32 GRAVITY = 3.0f;
232 constexpr f32 SQ_VEL_MIN = 0.5f;
233 constexpr f32 HIT_DURATION = 300.0f;
234 constexpr f32 VEL_DAMPENER = 0.75f;
235 constexpr u32 MAX_BOUNCES = 4;
236 constexpr f32 SIDEWAYS_SCALAR = 0.6f;
237 constexpr f32 FORWARD_SCALAR = 0.4f;
238 constexpr f32 MIN_SPEED = 0.0f;
242 u32 t = System::RaceManager::Instance()->timer();
243 u32 stateFrames = t - m_stateStartFrame;
244 if (m_vel.squaredLength() < SQ_VEL_MIN || m_pos.y < 0.0f ||
245 static_cast<f32
>(stateFrames) > HIT_DURATION) {
246 m_stateStartFrame = t;
247 m_state = State::Hiding;
256 if (stateFrames >= STATE_COOLDOWN_FRAMES) {
260 bool hasCol = CollisionDirector::Instance()->checkSphereFullPush(
261 (RADIUS + FALL_VEL) * m_scale.x, pos, m_pos, mask, &info, &maskOut, 0);
264 if ((maskOut &
KCL_TYPE_FLOOR) && stateFrames > STATE_COOLDOWN_FRAMES) {
265 m_vel = AdjustVecForward(SIDEWAYS_SCALAR, FORWARD_SCALAR, MIN_SPEED, m_vel,
267 m_vel.y = VEL_DAMPENER * -m_vel.y;
269 m_vel = AdjustVecForward(SIDEWAYS_SCALAR, FORWARD_SCALAR, MIN_SPEED, m_vel,
276 m_flags.setBit(eFlags::Rotation);
278 m_pos = m_pos + m_vel + info.tangentOff;
279 m_flags.setBit(eFlags::Position);
281 if (m_numBounces++ >= MAX_BOUNCES) {
282 m_state = State::Hiding;
283 m_stateStartFrame = t;
290 m_flags.setBit(eFlags::Rotation, eFlags::Position);
294void ObjectPylon::calcHiding() {
295 constexpr u32 HIDING_DURATION = 10;
297 u32 t = System::RaceManager::Instance()->timer();
298 u32 stateFrames = t - m_stateStartFrame;
299 if (stateFrames > HIDING_DURATION) {
300 m_state = State::Hide;
301 m_stateStartFrame = t;
304 m_flags.setBit(eFlags::Position);
309 m_scale.set(1.0f /
static_cast<f32
>(stateFrames));
310 m_flags.setBit(eFlags::Rotation, eFlags::Scale);
316void ObjectPylon::calcHide() {
317 constexpr u32 HIDE_DURATION = 900;
319 u32 t = System::RaceManager::Instance()->timer();
320 if (t - m_stateStartFrame > HIDE_DURATION) {
321 m_state = State::ComeBack;
322 m_stateStartFrame = t;
325 m_flags.setBit(eFlags::Rotation);
329void ObjectPylon::calcComeBack() {
330 constexpr u32 COME_BACK_DURATION = 10;
331 constexpr f32 COME_BACK_VEL = 10.0f;
332 constexpr f32 INIT_DISPLACEMENT = COME_BACK_VEL *
static_cast<f32
>(COME_BACK_DURATION);
336 u32 t = System::RaceManager::Instance()->timer();
337 u32 stateFrames = t - m_stateStartFrame;
339 if (stateFrames > COME_BACK_DURATION) {
341 m_state = State::Idle;
342 m_stateStartFrame = t;
347 m_scale = m_initScale;
349 m_pos.y -= COME_BACK_VEL;
350 m_flags.setBit(eFlags::Position, eFlags::Rotation, eFlags::Scale);
355 EGG::Vector3f pos = m_pos + EGG::Vector3f::ey * RADIUS * m_scale.x;
356 EGG::Vector3f prevPos = m_pos + EGG::Vector3f::ey * (RADIUS + COME_BACK_VEL) * m_scale.x;
358 bool hasCol = CollisionDirector::Instance()->checkSphereFullPush(RADIUS * m_scale.x, pos,
359 prevPos, KCL_TYPE_60E8DFFF, &info, &mask, 0);
362 m_pos += info.tangentOff;
363 m_flags.setBit(eFlags::Position);
366 setMatrixTangentTo(info.floorNrm, EGG::Vector3f::ez);
367 m_state = State::Idle;
375 EGG::Vector3f::ey * INIT_DISPLACEMENT *
376 static_cast<f32
>(COME_BACK_DURATION - stateFrames);
377 m_scale = m_initScale;
379 m_flags.setBit(eFlags::Position, eFlags::Scale, eFlags::Rotation);
383 EGG::Vector3f pos = m_pos + EGG::Vector3f::ey * RADIUS * m_scale.x;
384 EGG::Vector3f prevPos = m_pos + EGG::Vector3f::ey * (RADIUS + COME_BACK_VEL) * m_scale.x;
386 bool hasCol = CollisionDirector::Instance()->checkSphereFull(RADIUS * m_scale.x, pos,
387 prevPos, KCL_TYPE_60E8DFFF, &info,
nullptr, 0);
390 m_pos += info.tangentOff;
391 m_flags.setBit(eFlags::Position);
#define KCL_TYPE_OBJECT_WALL
0x4000D000
#define KCL_TYPE_FLOOR
0x20E80FFF - Any KCL that the player or items can drive/land on.
#define KCL_TYPE_WALL
0xD010F000
The highest level abstraction for a kart.
f32 dot(const Vector3f &rhs) const
The dot product between two vectors.
f32 length() const
The square root of the vector's dot product.