1#include "ObjectFireSnake.hh"
3#include "game/field/CollisionDirector.hh"
4#include "game/field/ObjectDirector.hh"
6#include "game/system/RaceManager.hh"
11ObjectFireSnakeKid::ObjectFireSnakeKid(
const System::MapdataGeoObj ¶ms)
15ObjectFireSnakeKid::~ObjectFireSnakeKid() =
default;
18ObjectFireSnake::ObjectFireSnake(
const System::MapdataGeoObj ¶ms)
19 : StateManager(this, STATE_ENTRIES), ObjectProjectile(params), m_initialPos(params.pos()),
20 m_maxAge(static_cast<s16>(params.setting(1))) {
21 if (ObjectDirector::Instance()->managedObjects().size() > 0) {
22 registerManagedObject();
25 for (u32 i = 0; i < 2; ++i) {
26 auto *&kid = m_kids[i];
27 kid = EGG::egg_new<ObjectFireSnakeKid>(params);
29 f32 kidScale = (0.75f - 0.25f *
static_cast<f32
>(i)) * scale().y;
30 kid->setScale(kidScale);
33 m_prevTransforms.fill(EGG::Matrix34f::ident);
37ObjectFireSnake::~ObjectFireSnake() =
default;
40void ObjectFireSnake::init() {
47 m_initRot = transform().base(0);
48 m_bounceDir = transform().base(0);
52void ObjectFireSnake::calc() {
55 if (isCollisionEnabled()) {
56 if (++m_age >= m_maxAge && m_currentStateId == 3) {
68 m_xzSunDist = m_initialPos - m_sunPos;
70 m_xzSunDist.normalise2();
74 f32 dist = xzDist.
length();
77 m_xzSpeed = dist * EGG::Mathf::sqrt(GRAVITY * 0.5f / (m_sunPos.y - m_initialPos.y));
87void ObjectFireSnake::enterDespawned() {
89 unregisterCollision();
92 m_visualPos = m_sunPos;
97void ObjectFireSnake::enterFalling() {
104void ObjectFireSnake::enterHighBounce() {
105 m_visualPos = m_initialPos;
106 setPos(m_initialPos);
108 f32 rand = System::RaceManager::Instance()->random().getF32();
109 m_bounceDir = rand >= 0.5f ? m_initRot : -m_initRot;
115void ObjectFireSnake::enterRest() {
116 constexpr f32 XZ_RANGE = 1000.0f * 1000.0f;
119 if (m_visualPos.sqDistance(m_initialPos) >= XZ_RANGE) {
120 m_bounceDir = m_initialPos - m_visualPos;
121 m_bounceDir.normalise2();
124 auto &rand = System::RaceManager::Instance()->random();
125 f32 angle = rand.getF32(90.0f) - 45.0f;
127 if (rand.getF32() >= 0.5f) {
131 m_bounceDir = RotateXZByYaw(angle * DEG2RAD, m_initRot);
136void ObjectFireSnake::calcFalling() {
137 EGG::Vector2f fallVel;
138 fallVel.y = -GRAVITY *
static_cast<f32
>(m_currentFrame);
139 f32 fallVelY = fallVel.y;
140 f32 posY = m_visualPos.y + fallVel.y;
142 if (posY <= m_initialPos.y) {
144 setPos(m_visualPos * 0.5f + m_initialPos * 0.5f);
146 fallVel.x = m_xzSpeed;
147 m_visualPos.y = posY;
148 m_visualPos.x += m_xzSunDist.x * fallVel.x;
149 m_visualPos.z += m_xzSunDist.z * fallVel.x;
150 fallVel.normalise2();
152 f32 angle = fallVel.x;
153 EGG::Vector3f up = RotateAxisAngle(EGG::Mathf::acos(angle), m_fallAxis, EGG::Vector3f::ey);
154 auto axis = EGG::Vector3f(m_xzSunDist.x * m_xzSpeed, -fallVelY, m_xzSunDist.z * m_xzSpeed);
156 auto spiral = RotateAxisAngle(50.0f *
static_cast<f32
>(m_currentFrame) * DEG2RAD, axis, up);
158 f32 scaledDuration = 0.9f *
static_cast<f32
>(m_fallDuration);
159 f32 phase = m_currentFrame -
static_cast<u16
>(scaledDuration);
160 f32 spiralRadius = phase < 0 ? 200.0f : 200.0f - (200.0f / scaledDuration) * phase;
162 setPos(m_visualPos + spiral * spiralRadius);
167void ObjectFireSnake::calcHighBounce() {
168 constexpr f32 INITIAL_VELOCITY = 1.6f * 60.0f;
170 calcBounce(INITIAL_VELOCITY);
174void ObjectFireSnake::calcRest() {
175 if (m_currentFrame >= 30) {
181 EGG::Vector3f tangent = Interpolate(0.3f, transform().base(2), m_bounceDir);
182 tangent.normalise2();
184 setMatrixTangentTo(EGG::Vector3f::ey, tangent);
188void ObjectFireSnake::calcBounce() {
189 constexpr f32 INITIAL_VELOCITY = 60.0f;
191 calcBounce(INITIAL_VELOCITY);
195void ObjectFireSnake::calcChildren() {
197 for (
size_t i = m_prevTransforms.size() - 1; i > 0; --i) {
198 m_prevTransforms[i] = m_prevTransforms[i - 1];
202 m_prevTransforms[0] = transform();
203 m_prevTransforms[0].setBase(3, pos());
206 for (
auto *&kid : m_kids) {
207 if (m_currentStateId == 1 && idx == m_currentFrame) {
208 if (!kid->getUnit()) {
211 }
else if (m_currentStateId == 0 && m_currentFrame == 0) {
212 if (kid->getUnit()) {
213 kid->unregisterCollision();
217 if (m_currentStateId == 1 && m_currentFrame < idx) {
219 kid->setTransform(transform());
221 kid->setTransform(m_prevTransforms[idx]);
229void ObjectFireSnake::calcBounce(f32 initialVel) {
231 constexpr u32 BOUNCE_COL_CHECK_DELAY = 10;
232 constexpr f32 RADIUS = 130.0f;
233 constexpr f32 BOUNCE_SPEED = 20.0f;
235 m_visualPos.x += m_bounceDir.x * BOUNCE_SPEED;
236 m_visualPos.z += m_bounceDir.z * BOUNCE_SPEED;
237 m_visualPos.y += initialVel - GRAVITY *
static_cast<f32
>(m_currentFrame);
241 if (m_currentFrame > BOUNCE_COL_CHECK_DELAY) {
242 bool hasCol = CollisionDirector::Instance()->checkSphereFull(RADIUS, m_visualPos,
243 EGG::Vector3f::inf, KCL_TYPE_64EBDFFF, &info,
nullptr, 0);
246 m_visualPos += info.tangentOff;
static EGG::Vector3f RotateXZByYaw(f32 angle, const EGG::Vector3f &v)
Rotates a vector around the Y-axis and returns the XZ-plane portion of the vector.
u16 m_fallDuration
How long the firesnake falls from the sun.
void initProjectile(const EGG::Vector3f &pos) override
Callback function called by the managing ObjectSniper.
void onLaunch() override
Callback function use by the ObjectSniper that wants to throw this projectile.
constexpr f32 length() const
The square root of the vector's dot product.