1#include "ObjectPylon.hh"
3#include "game/field/CollisionDirector.hh"
4#include "game/field/ObjectDirector.hh"
6#include "game/kart/KartObject.hh"
7#include "game/kart/Reaction.hh"
9#include "game/system/RaceManager.hh"
14ObjectPylon::ObjectPylon(
const System::MapdataGeoObj ¶ms)
15 :
ObjectCollidable(params), m_initPos(pos()), m_initScale(scale()), m_initRot(rot()) {}
18ObjectPylon::~ObjectPylon() =
default;
21void ObjectPylon::init() {
22 constexpr f32 NEIGHBOR_SQUARE_RADIUS = 2400.0f;
24 m_state = State::Idle;
25 m_stateStartFrame = 0;
28 m_neighbors = {
nullptr};
29 subPos(EGG::Vector3f(0.0f, FALL_VEL, 0.0f));
33 EGG::Vector3f colPos = pos() + EGG::Vector3f::ey * RADIUS;
34 EGG::Vector3f prevPos = pos() + EGG::Vector3f::ey * (RADIUS + FALL_VEL);
36 bool hasCol = CollisionDirector::Instance()->checkSphereFull(RADIUS, colPos, prevPos,
37 KCL_TYPE_6CEBDFFF, &info, &mask, 0);
40 addPos(info.tangentOff);
43 setMatrixTangentTo(info.floorNrm, EGG::Vector3f::ez);
47 auto *objDir = ObjectDirector::Instance();
48 auto &managedObjs = objDir->managedObjects();
50 for (
auto *&obj : managedObjs) {
51 if ((obj->pos() - pos()).length() >= NEIGHBOR_SQUARE_RADIUS) {
56 ASSERT(obj->id() == ObjectId::Pylon);
57 auto *&pylon =
reinterpret_cast<ObjectPylon *&
>(obj);
58 for (
auto *&neighbor : m_neighbors) {
65 for (
auto *&neighbor : pylon->m_neighbors) {
73 objDir->addManagedObject(
this);
77void ObjectPylon::calc() {
92 if (System::RaceManager::Instance()->timer() - m_stateStartFrame > STATE_COOLDOWN_FRAMES) {
93 m_state = State::Idle;
109 constexpr f32 HIT_SPEED_RATIO_THRESHOLD = 0.7f;
110 constexpr f32 PI_OVER_SIX = 0.52359879f;
111 constexpr f32 FIVE_PI_OVER_SIX = 2.617994f;
112 constexpr f32 COME_BACK_HIT_FACTOR = 0.5f;
114 u32 t = System::RaceManager::Instance()->timer();
116 if (m_state == State::ComeBack) {
117 startHit(COME_BACK_HIT_FACTOR, hitDepth);
124 if (canChangeState && m_state == State::Moving) {
125 m_state = State::Idle;
129 if (canChangeState && m_state == State::Hit) {
133 if (m_state == State::Hit) {
137 f32 speedRatio = kartObj->speedRatioCapped();
138 if ((speedRatio >= HIT_SPEED_RATIO_THRESHOLD && m_state != State::Moving) ||
139 m_state == State::Hiding || m_state == State::Hide) {
140 if (m_state == State::Idle) {
141 startHit(speedRatio, hitDepth);
149 m_state = State::Moving;
152 f32 atan = EGG::Mathf::abs(EGG::Mathf::atan2(cross.
length(), hitDepth.
dot(zAxis)));
155 if (atan >= PI_OVER_SIX && atan < FIVE_PI_OVER_SIX) {
171 constexpr f32 TRAVEL_RADIUS = 1200.0f;
176 for (
auto *&neighbor : m_neighbors) {
177 if (neighbor && collision()->check(*neighbor->collision(), dist)) {
185 if (delta.
length() > TRAVEL_RADIUS) {
186 delta.x += hitDepth.z;
187 delta.z -= hitDepth.x;
190 setPos(m_initPos + delta * TRAVEL_RADIUS);
199 bool hasCol = CollisionDirector::Instance()->checkSphereFullPush(RADIUS, colPos, pos(),
200 KCL_TYPE_60E8DFFF, &info, &mask, 0);
202 addPos(info.tangentOff);
204 if (info.floorDist > -std::numeric_limits<f32>::min()) {
205 setMatrixTangentTo(info.floorNrm, EGG::Vector3f::ez);
211void ObjectPylon::startHit(f32 velFactor,
EGG::Vector3f &hitDepth) {
212 constexpr f32 ANG_VEL_SCALAR = 0.5f;
213 constexpr f32 VEL_SCALAR = 100.0f;
215 m_state = State::Hit;
218 m_angVel = m_vel * velFactor * ANG_VEL_SCALAR;
220 m_vel *= velFactor * VEL_SCALAR;
221 hitDepth.normalise2();
224void ObjectPylon::calcHit() {
225 constexpr f32 GRAVITY = 3.0f;
226 constexpr f32 SQ_VEL_MIN = 0.5f;
227 constexpr f32 HIT_DURATION = 300.0f;
228 constexpr f32 VEL_DAMPENER = 0.75f;
229 constexpr u32 MAX_BOUNCES = 4;
230 constexpr f32 SIDEWAYS_SCALAR = 0.6f;
231 constexpr f32 FORWARD_SCALAR = 0.4f;
232 constexpr f32 MIN_SPEED = 0.0f;
236 u32 t = System::RaceManager::Instance()->timer();
237 u32 stateFrames = t - m_stateStartFrame;
238 if (m_vel.squaredLength() < SQ_VEL_MIN || pos().y < 0.0f ||
239 static_cast<f32
>(stateFrames) > HIT_DURATION) {
240 m_stateStartFrame = t;
241 m_state = State::Hiding;
247 EGG::Vector3f colPos = pos() + m_vel;
250 if (stateFrames >= STATE_COOLDOWN_FRAMES) {
254 bool hasCol = CollisionDirector::Instance()->checkSphereFullPush(
255 (RADIUS + FALL_VEL) * scale().x, colPos, pos(), mask, &info, &maskOut, 0);
258 if ((maskOut &
KCL_TYPE_FLOOR) && stateFrames > STATE_COOLDOWN_FRAMES) {
259 m_vel = AdjustVecForward(SIDEWAYS_SCALAR, FORWARD_SCALAR, MIN_SPEED, m_vel,
261 m_vel.y = VEL_DAMPENER * -m_vel.y;
263 m_vel = AdjustVecForward(SIDEWAYS_SCALAR, FORWARD_SCALAR, MIN_SPEED, m_vel,
270 setPos(pos() + m_vel + info.tangentOff);
272 if (m_numBounces++ >= MAX_BOUNCES) {
273 m_state = State::Hiding;
274 m_stateStartFrame = t;
283void ObjectPylon::calcHiding() {
284 constexpr u32 HIDING_DURATION = 10;
286 u32 t = System::RaceManager::Instance()->timer();
287 u32 stateFrames = t - m_stateStartFrame;
288 if (stateFrames > HIDING_DURATION) {
289 m_state = State::Hide;
290 m_stateStartFrame = t;
295 f32 scale = 1.0f /
static_cast<f32
>(stateFrames);
302void ObjectPylon::calcHide() {
303 constexpr u32 HIDE_DURATION = 900;
305 u32 t = System::RaceManager::Instance()->timer();
306 if (t - m_stateStartFrame > HIDE_DURATION) {
307 m_state = State::ComeBack;
308 m_stateStartFrame = t;
313void ObjectPylon::calcComeBack() {
314 constexpr u32 COME_BACK_DURATION = 10;
315 constexpr f32 COME_BACK_VEL = 10.0f;
316 constexpr f32 INIT_DISPLACEMENT = COME_BACK_VEL *
static_cast<f32
>(COME_BACK_DURATION);
320 u32 t = System::RaceManager::Instance()->timer();
321 u32 stateFrames = t - m_stateStartFrame;
323 if (stateFrames > COME_BACK_DURATION) {
325 m_state = State::Idle;
326 m_stateStartFrame = t;
330 setPos(EGG::Vector3f(m_initPos.x, m_initPos.y - COME_BACK_VEL, m_initPos.z));
331 setScale(m_initScale);
336 EGG::Vector3f colPos = pos() + EGG::Vector3f::ey * RADIUS * scale().x;
337 EGG::Vector3f prevPos = pos() + EGG::Vector3f::ey * (RADIUS + COME_BACK_VEL) * scale().x;
339 bool hasCol = CollisionDirector::Instance()->checkSphereFullPush(RADIUS * scale().x, colPos,
340 prevPos, KCL_TYPE_60E8DFFF, &info, &mask, 0);
343 addPos(info.tangentOff);
346 setMatrixTangentTo(info.floorNrm, EGG::Vector3f::ez);
347 m_state = State::Idle;
355 EGG::Vector3f::ey * INIT_DISPLACEMENT *
356 static_cast<f32
>(COME_BACK_DURATION - stateFrames));
357 setScale(m_initScale);
361 EGG::Vector3f colPos = pos() + EGG::Vector3f::ey * RADIUS * scale().x;
362 EGG::Vector3f prevPos = pos() + EGG::Vector3f::ey * (RADIUS + COME_BACK_VEL) * scale().x;
364 bool hasCol = CollisionDirector::Instance()->checkSphereFull(RADIUS * scale().x, colPos,
365 prevPos, KCL_TYPE_60E8DFFF, &info,
nullptr, 0);
368 addPos(info.tangentOff);
#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
void checkIntraCollision(const EGG::Vector3f &hitDepth)
Kart::Reaction onCollision(Kart::KartObject *kartObj, Kart::Reaction reactionOnKart, Kart::Reaction reactionOnObj, EGG::Vector3f &hitDepth) override
u32 m_stateStartFrame
Frame when pylon entered the current m_state.
static constexpr u32 STATE_COOLDOWN_FRAMES
Minimum frames before a state change can occur.
The highest level abstraction for a kart.
Reaction
Represents the different reactions that can occur when a kart collides with an object.
@ None
No reaction occurs, the kart continues as normal.
@ WeakWall
The kart's speed drops by 82% due to a weak wall.
constexpr f32 length() const
The square root of the vector's dot product.
constexpr f32 dot(const Vector3f &rhs) const
The dot product between two vectors.