3#include "game/field/CollisionDirector.hh"
4#include "game/field/RailInterpolator.hh"
5#include "game/field/RailManager.hh"
7#include "game/kart/KartCollide.hh"
8#include "game/kart/KartObject.hh"
10#include "game/system/RaceManager.hh"
16 m_startFrame = params.setting(2);
20ObjectCow::~ObjectCow() =
default;
23Kart::Reaction ObjectCow::onCollision(
Kart::KartObject *kartObj, Kart::Reaction reactionOnKart,
25 return kartObj->speedRatioCapped() < 0.5f ? Kart::Reaction::WallAllSpeed : reactionOnKart;
29void ObjectCow::setup() {
30 m_scale = m_mapObj->scale();
31 m_pos = m_mapObj->pos();
32 m_rot = m_mapObj->rot() * DEG2RAD;
33 m_flags.setBit(eFlags::Position, eFlags::Rotation, eFlags::Scale);
34 m_tangent = EGG::Vector3f::ez;
35 m_prevTangent = EGG::Vector3f::ez;
36 m_up = EGG::Vector3f::ey;
37 m_velocity = EGG::Vector3f::zero;
39 m_tangentFactor = 0.0f;
40 m_floorNrm = EGG::Vector3f::ey;
41 m_state1TargetPos = m_pos;
42 m_targetDir = EGG::Vector3f::ez;
43 m_upForce = EGG::Vector3f::zero;
48void ObjectCow::calcFloor() {
49 constexpr f32 RADIUS = 50.0f;
54 bool hasCol = CollisionDirector::Instance()->checkSphereFull(RADIUS, m_pos + POS_OFFSET,
55 EGG::Vector3f::inf, KCL_TYPE_64EBDFFF, &info,
nullptr, 0);
58 m_pos += info.tangentOff;
59 m_flags.setBit(eFlags::Position);
61 if (info.floorDist > -std::numeric_limits<f32>::min()) {
62 m_floorNrm = info.floorNrm;
66 m_upForce = GRAVITY_FORCE;
68 m_upForce = EGG::Vector3f::zero;
73void ObjectCow::calcPos() {
75 m_tangent * m_tangentFactor + (m_tangent - m_prevTangent) * m_xzSpeed + m_upForce;
76 m_velocity += accel - GRAVITY_FORCE;
77 m_xzSpeed = EGG::Mathf::sqrt(m_velocity.x * m_velocity.x + m_velocity.z * m_velocity.z);
79 if (m_tangent.z * m_velocity.z + m_tangent.x * m_velocity.x < 0.0f) {
86 m_flags.setBit(eFlags::Position);
87 m_tangentFactor = 0.0f;
92 m_state1TargetPos = v;
95 m_targetDir = m_state1TargetPos + posDiff * 1000.0f - m_pos;
96 m_targetDir.normalise2();
103 : ObjectCow(params), StateManager(this) {}
106ObjectCowLeader::~ObjectCowLeader() =
default;
109void ObjectCowLeader::init() {
111 m_railInterpolator->init(0.0f, 0);
112 m_pos = m_railInterpolator->curPos();
113 m_flags.setBit(eFlags::Position);
115 setTarget(m_pos + m_railInterpolator->curTangentDir() * 10.0f);
117 m_railInterpolator->setCurrVel(
static_cast<f32
>(m_mapObj->setting(1)));
120 m_endedRailSegment =
false;
121 m_state1AnmType = AnmType::EatST;
128void ObjectCowLeader::calc() {
129 u32 t = System::RaceManager::Instance()->timer();
131 if (t >= m_startFrame) {
132 StateManager::calc();
138 m_prevTangent = m_tangent;
139 m_tangent = Interpolate(m_interpRate, m_tangent, m_targetDir);
141 if (m_tangent.squaredLength() > std::numeric_limits<f32>::epsilon()) {
142 m_tangent.normalise2();
144 m_tangent = EGG::Vector3f::ez;
147 m_up = Interpolate(0.1f, m_up, m_floorNrm);
149 if (m_up.squaredLength() > std::numeric_limits<f32>::epsilon()) {
152 m_up = EGG::Vector3f::ey;
155 setMatrixTangentTo(m_up, m_tangent);
159void ObjectCowLeader::calcFloor() {
161 m_upForce = GRAVITY_FORCE;
162 m_floorNrm = m_railInterpolator->floorNrm(m_railInterpolator->nextPointIdx());
166void ObjectCowLeader::enterWait() {
167 setTarget(m_railInterpolator->curPos() + m_railInterpolator->curTangentDir() * 10.0f);
171void ObjectCowLeader::enterEat() {
172 m_state1AnmType = AnmType::EatST;
173 u32 rand = System::RaceManager::Instance()->random().getU32(120);
174 m_eatFrames = rand + 120;
178void ObjectCowLeader::enterRoam() {
179 m_endedRailSegment =
false;
183void ObjectCowLeader::calcWait() {
184 if (m_currentFrame > m_railInterpolator->curPoint().setting[0]) {
190void ObjectCowLeader::calcEat() {
191 constexpr u16 EAT_ST_FRAMES = 40;
192 constexpr u16 EAT_ED_FRAMES = 60;
194 switch (m_state1AnmType) {
195 case AnmType::EatST: {
196 if (m_currentFrame == EAT_ST_FRAMES) {
197 m_state1AnmType = AnmType::Eat;
201 if (m_currentFrame >
static_cast<u16>(m_eatFrames + EAT_ST_FRAMES)) {
202 m_state1AnmType = AnmType::EatED;
205 case AnmType::EatED: {
206 if (
static_cast<u16>(m_eatFrames + EAT_ST_FRAMES + EAT_ED_FRAMES) == m_currentFrame) {
216void ObjectCowLeader::calcRoam() {
217 if (m_endedRailSegment) {
220 if (m_railSpeed < 0.0f) {
223 if (m_railInterpolator->curPoint().setting[1] == 0) {
230 if (m_railSpeed < 4.0f) {
237 m_railInterpolator->setCurrVel(m_railSpeed);
239 auto status = m_railInterpolator->calc();
240 const auto &curPoint = m_railInterpolator->curPoint();
242 if (status == RailInterpolator::Status::SegmentEnd &&
243 (curPoint.setting[0] != 0 || curPoint.setting[1] != 0)) {
244 m_endedRailSegment =
true;
247 m_pos = m_railInterpolator->curPos() - EGG::Vector3f::ey * 10.0f;
248 m_flags.setBit(eFlags::Position);
250 setTarget(m_railInterpolator->curPos() + m_railInterpolator->curTangentDir() * 10.0f);
256 : ObjectCow(params), StateManager(this), m_posOffset(pos), m_rail(nullptr) {
257 m_pos += m_posOffset;
258 m_flags.setBit(eFlags::Position, eFlags::Rotation);
263ObjectCowFollower::~ObjectCowFollower() =
default;
266void ObjectCowFollower::init() {
268 m_pos += m_posOffset;
269 m_flags.setBit(eFlags::Position);
271 local_1c.normalise2();
272 setMatrixTangentTo(EGG::Vector3f::ey, local_1c);
279 m_railSegThreshold = 0.0f;
283void ObjectCowFollower::calc() {
284 u32 t = System::RaceManager::Instance()->timer();
286 if (t < m_startFrame) {
287 if (m_currentFrame > m_waitFrames) {
291 if (m_rail->segmentT() > m_railSegThreshold) {
295 setTarget(m_pos + m_posOffset * 2.0f);
297 StateManager::calc();
303 m_prevTangent = m_tangent;
304 m_tangent = Interpolate(m_interpRate, m_tangent, m_targetDir);
306 if (m_tangent.squaredLength() > std::numeric_limits<f32>::epsilon()) {
307 m_tangent.normalise2();
309 m_tangent = EGG::Vector3f::ez;
312 m_up = Interpolate(0.1f, m_up, m_floorNrm);
314 if (m_up.squaredLength() > std::numeric_limits<f32>::epsilon()) {
317 m_up = EGG::Vector3f::ey;
320 setMatrixTangentTo(m_up, m_tangent);
324void ObjectCowFollower::enterWait() {
325 constexpr u32 BASE_WAIT_FRAMES = 100;
326 constexpr u32 WAIT_FRAMES_VARIANCE = 60;
327 constexpr f32 BASE_RAIL_THRESHOLD = 0.2f;
328 constexpr f32 RAIL_THRESHOLD_VARIANCE = 0.8f;
330 auto &rand = System::RaceManager::Instance()->random();
331 m_waitFrames = rand.getU32(WAIT_FRAMES_VARIANCE) + BASE_WAIT_FRAMES;
332 m_railSegThreshold = BASE_RAIL_THRESHOLD + rand.getF32(RAIL_THRESHOLD_VARIANCE);
336void ObjectCowFollower::enterFreeRoam() {
337 constexpr f32 BASE_WALK_DISTANCE = 400.0f;
338 constexpr f32 WALK_DISTANCE_VARIANCE = 300.0f;
339 constexpr f32 AVG_ANGLE = DEG2RAD360 * 10.0f;
340 STATIC_ASSERT(AVG_ANGLE == 0.34906584f);
343 auto &rand = System::RaceManager::Instance()->random();
345 m_topSpeed = BASE_TOP_SPEED + rand.getF32(TOP_SPEED_VARIANCE);
347 f32 dVar2 = AVG_ANGLE + rand.getF32(AVG_ANGLE);
351 f32 fVar3 = CheckPointAgainstLineSegment(m_pos, m_rail->curPoint().pos, m_rail->curPos());
352 f32 angle = fVar3 > 0.0f ? -dVar2 : dVar2;
358 f32 distance = BASE_WALK_DISTANCE + rand.getF32(WALK_DISTANCE_VARIANCE);
359 setTarget(m_pos + dir * distance);
363void ObjectCowFollower::enterFollowLeader() {
365 m_interpRate = 0.01f;
367 auto &rand = System::RaceManager::Instance()->random();
368 m_topSpeed = BASE_TOP_SPEED + rand.getF32(TOP_SPEED_VARIANCE);
372void ObjectCowFollower::calcWait() {
373 if (m_currentFrame > m_waitFrames) {
377 if (m_rail->segmentT() > m_railSegThreshold) {
383void ObjectCowFollower::calcFreeRoam() {
385 m_tangentFactor = -0.1f;
387 if (m_xzSpeed == 0.0f) {
391 if (m_xzSpeed < m_topSpeed) {
392 m_tangentFactor = 0.1f;
397 if (local_28.x * local_28.x + local_28.z * local_28.z < DIST_THRESHOLD * DIST_THRESHOLD) {
401 if (m_rail->segmentT() > m_railSegThreshold) {
407void ObjectCowFollower::calcFollowLeader() {
411 m_tangentFactor = -0.1f;
413 if (m_xzSpeed == 0.0f) {
414 m_interpRate = 0.05f;
418 dist = setTarget(m_rail->curPos() + m_posOffset);
420 if (m_xzSpeed < m_topSpeed) {
421 m_interpRate = 0.05f;
422 m_tangentFactor = 0.1f;
426 if (dist < DIST_THRESHOLD) {
433 constexpr f32 FOLLOWER_SPACING = 600.0f;
435 u8 followerCount = params.setting(0);
437 m_leader =
new ObjectCowLeader(params);
441 std::span<ObjectCowFollower *>(
new ObjectCowFollower *[followerCount], followerCount);
443 for (
u32 i = 0; i < followerCount; ++i) {
444 auto *&child = m_followers[i];
445 f32 rot = F_TAU /
static_cast<f32
>(followerCount) *
static_cast<f32
>(i);
446 f32 z = EGG::Mathf::SinFIdx(RAD2FIDX * rot);
447 f32 x = EGG::Mathf::CosFIdx(RAD2FIDX * rot);
450 child =
new ObjectCowFollower(params, pos, rot);
454 auto *rail = RailManager::Instance()->rail(
static_cast<size_t>(params.pathId()));
455 rail->checkSphereFull();
459ObjectCowHerd::~ObjectCowHerd() {
460 delete[] m_followers.data();
465void ObjectCowHerd::init() {
466 for (
auto *&child : m_followers) {
467 child->m_rail = m_leader->m_railInterpolator;
472void ObjectCowHerd::calc() {
473 constexpr f32 MAX_DIST = 4000.0f;
477 for (
auto *&follower : m_followers) {
481 follower->m_nextStateId = 2;
488void ObjectCowHerd::checkCollision() {
489 constexpr f32 WIDTH = 400.0f;
491 for (
u32 i = 0; i < m_followers.size() - 1; ++i) {
492 auto *iFollower = m_followers[i];
494 for (
u32 j = i + 1; j < m_followers.size(); ++j) {
495 auto *jFollower = m_followers[j];
497 EGG::Vector3f posDelta = jFollower->pos() - iFollower->pos();
500 if (length < WIDTH) {
503 jFollower->m_pos += change;
504 jFollower->m_flags.setBit(eFlags::Position);
506 iFollower->m_pos -= change;
507 iFollower->m_flags.setBit(eFlags::Position);
512 for (
auto *&follower : m_followers) {
516 if (length < WIDTH) {
519 follower->m_pos -= change;
520 follower->m_flags.setBit(eFlags::Position);
The highest level abstraction for a kart.
f32 normalise()
Normalizes the vector and returns the original length.
f32 squaredLength() const
The dot product between the vector and itself.