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"
13ObjectPylon::ObjectPylon(
const System::MapdataGeoObj ¶ms)
14 :
ObjectCollidable(params), m_initPos(pos()), m_initScale(scale()), m_initRot(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 subPos(EGG::Vector3f(0.0f, FALL_VEL, 0.0f));
32 EGG::Vector3f colPos = pos() + EGG::Vector3f::ey * RADIUS;
33 EGG::Vector3f prevPos = pos() + EGG::Vector3f::ey * (RADIUS + FALL_VEL);
35 bool hasCol = CollisionDirector::Instance()->checkSphereFull(RADIUS, colPos, prevPos,
36 KCL_TYPE_6CEBDFFF, &info, &mask, 0);
39 addPos(info.tangentOff);
42 setMatrixTangentTo(info.floorNrm, EGG::Vector3f::ez);
46 auto *objDir = ObjectDirector::Instance();
47 auto &managedObjs = objDir->managedObjects();
49 for (
auto *&obj : managedObjs) {
50 if ((obj->pos() - pos()).length() >= NEIGHBOR_SQUARE_RADIUS) {
55 ASSERT(obj->id() == ObjectId::Pylon);
56 auto *&pylon =
reinterpret_cast<ObjectPylon *&
>(obj);
57 for (
auto *&neighbor : m_neighbors) {
64 for (
auto *&neighbor : pylon->m_neighbors) {
72 objDir->addManagedObject(
this);
76void ObjectPylon::calc() {
91 if (System::RaceManager::Instance()->timer() - m_stateStartFrame > STATE_COOLDOWN_FRAMES) {
92 m_state = State::Idle;
106 Kart::Reaction , Kart::Reaction ,
108 constexpr f32 HIT_SPEED_RATIO_THRESHOLD = 0.7f;
109 constexpr f32 PI_OVER_SIX = 0.52359879f;
110 constexpr f32 FIVE_PI_OVER_SIX = 2.617994f;
111 constexpr f32 COME_BACK_HIT_FACTOR = 0.5f;
113 u32 t = System::RaceManager::Instance()->timer();
115 if (m_state == State::ComeBack) {
116 startHit(COME_BACK_HIT_FACTOR, hitDepth);
118 return Kart::Reaction::WeakWall;
123 if (canChangeState && m_state == State::Moving) {
124 m_state = State::Idle;
125 return Kart::Reaction::WeakWall;
128 if (canChangeState && m_state == State::Hit) {
129 return Kart::Reaction::WeakWall;
132 if (m_state == State::Hit) {
133 return Kart::Reaction::None;
136 f32 speedRatio = kartObj->speedRatioCapped();
137 if ((speedRatio >= HIT_SPEED_RATIO_THRESHOLD && m_state != State::Moving) ||
138 m_state == State::Hiding || m_state == State::Hide) {
139 if (m_state == State::Idle) {
140 startHit(speedRatio, hitDepth);
144 return Kart::Reaction::WeakWall;
148 m_state = State::Moving;
151 f32 atan = EGG::Mathf::abs(EGG::Mathf::atan2(cross.
length(), hitDepth.
dot(zAxis)));
154 if (atan >= PI_OVER_SIX && atan < FIVE_PI_OVER_SIX) {
155 return Kart::Reaction::None;
157 return Kart::Reaction::WeakWall;
170 constexpr f32 TRAVEL_RADIUS = 1200.0f;
175 for (
auto *&neighbor : m_neighbors) {
176 if (neighbor && collision()->check(*neighbor->collision(), dist)) {
184 if (delta.
length() > TRAVEL_RADIUS) {
185 delta.x += hitDepth.z;
186 delta.z -= hitDepth.x;
189 setPos(m_initPos + delta * TRAVEL_RADIUS);
198 bool hasCol = CollisionDirector::Instance()->checkSphereFullPush(RADIUS, colPos, pos(),
199 KCL_TYPE_60E8DFFF, &info, &mask, 0);
201 addPos(info.tangentOff);
203 if (info.floorDist > -std::numeric_limits<f32>::min()) {
204 setMatrixTangentTo(info.floorNrm, EGG::Vector3f::ez);
210void ObjectPylon::startHit(f32 velFactor,
EGG::Vector3f &hitDepth) {
211 constexpr f32 ANG_VEL_SCALAR = 0.5f;
212 constexpr f32 VEL_SCALAR = 100.0f;
214 m_state = State::Hit;
217 m_angVel = m_vel * velFactor * ANG_VEL_SCALAR;
219 m_vel *= velFactor * VEL_SCALAR;
220 hitDepth.normalise2();
223void ObjectPylon::calcHit() {
224 constexpr f32 GRAVITY = 3.0f;
225 constexpr f32 SQ_VEL_MIN = 0.5f;
226 constexpr f32 HIT_DURATION = 300.0f;
227 constexpr f32 VEL_DAMPENER = 0.75f;
228 constexpr u32 MAX_BOUNCES = 4;
229 constexpr f32 SIDEWAYS_SCALAR = 0.6f;
230 constexpr f32 FORWARD_SCALAR = 0.4f;
231 constexpr f32 MIN_SPEED = 0.0f;
235 u32 t = System::RaceManager::Instance()->timer();
236 u32 stateFrames = t - m_stateStartFrame;
237 if (m_vel.squaredLength() < SQ_VEL_MIN || pos().y < 0.0f ||
238 static_cast<f32
>(stateFrames) > HIT_DURATION) {
239 m_stateStartFrame = t;
240 m_state = State::Hiding;
246 EGG::Vector3f colPos = pos() + m_vel;
249 if (stateFrames >= STATE_COOLDOWN_FRAMES) {
253 bool hasCol = CollisionDirector::Instance()->checkSphereFullPush(
254 (RADIUS + FALL_VEL) * scale().x, colPos, pos(), mask, &info, &maskOut, 0);
257 if ((maskOut &
KCL_TYPE_FLOOR) && stateFrames > STATE_COOLDOWN_FRAMES) {
258 m_vel = AdjustVecForward(SIDEWAYS_SCALAR, FORWARD_SCALAR, MIN_SPEED, m_vel,
260 m_vel.y = VEL_DAMPENER * -m_vel.y;
262 m_vel = AdjustVecForward(SIDEWAYS_SCALAR, FORWARD_SCALAR, MIN_SPEED, m_vel,
269 setPos(pos() + m_vel + info.tangentOff);
271 if (m_numBounces++ >= MAX_BOUNCES) {
272 m_state = State::Hiding;
273 m_stateStartFrame = t;
282void ObjectPylon::calcHiding() {
283 constexpr u32 HIDING_DURATION = 10;
285 u32 t = System::RaceManager::Instance()->timer();
286 u32 stateFrames = t - m_stateStartFrame;
287 if (stateFrames > HIDING_DURATION) {
288 m_state = State::Hide;
289 m_stateStartFrame = t;
294 f32 scale = 1.0f /
static_cast<f32
>(stateFrames);
301void ObjectPylon::calcHide() {
302 constexpr u32 HIDE_DURATION = 900;
304 u32 t = System::RaceManager::Instance()->timer();
305 if (t - m_stateStartFrame > HIDE_DURATION) {
306 m_state = State::ComeBack;
307 m_stateStartFrame = t;
312void ObjectPylon::calcComeBack() {
313 constexpr u32 COME_BACK_DURATION = 10;
314 constexpr f32 COME_BACK_VEL = 10.0f;
315 constexpr f32 INIT_DISPLACEMENT = COME_BACK_VEL *
static_cast<f32
>(COME_BACK_DURATION);
319 u32 t = System::RaceManager::Instance()->timer();
320 u32 stateFrames = t - m_stateStartFrame;
322 if (stateFrames > COME_BACK_DURATION) {
324 m_state = State::Idle;
325 m_stateStartFrame = t;
329 setPos(EGG::Vector3f(m_initPos.x, m_initPos.y - COME_BACK_VEL, m_initPos.z));
330 setScale(m_initScale);
335 EGG::Vector3f colPos = pos() + EGG::Vector3f::ey * RADIUS * scale().x;
336 EGG::Vector3f prevPos = pos() + EGG::Vector3f::ey * (RADIUS + COME_BACK_VEL) * scale().x;
338 bool hasCol = CollisionDirector::Instance()->checkSphereFullPush(RADIUS * scale().x, colPos,
339 prevPos, KCL_TYPE_60E8DFFF, &info, &mask, 0);
342 addPos(info.tangentOff);
345 setMatrixTangentTo(info.floorNrm, EGG::Vector3f::ez);
346 m_state = State::Idle;
354 EGG::Vector3f::ey * INIT_DISPLACEMENT *
355 static_cast<f32
>(COME_BACK_DURATION - stateFrames));
356 setScale(m_initScale);
360 EGG::Vector3f colPos = pos() + EGG::Vector3f::ey * RADIUS * scale().x;
361 EGG::Vector3f prevPos = pos() + EGG::Vector3f::ey * (RADIUS + COME_BACK_VEL) * scale().x;
363 bool hasCol = CollisionDirector::Instance()->checkSphereFull(RADIUS * scale().x, colPos,
364 prevPos, KCL_TYPE_60E8DFFF, &info,
nullptr, 0);
367 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.
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.