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"
15ObjectCow::ObjectCow(
const System::MapdataGeoObj ¶ms) :
ObjectCollidable(params) {
16 m_startFrame = params.setting(2);
20ObjectCow::~ObjectCow() =
default;
23Kart::Reaction ObjectCow::onCollision(Kart::KartObject *kartObj, Kart::Reaction reactionOnKart,
24 Kart::Reaction , EGG::Vector3f & ) {
25 return kartObj->speedRatioCapped() < 0.5f ? Kart::Reaction::WallAllSpeed : reactionOnKart;
29void ObjectCow::setup() {
31 setScale(m_mapObj->scale());
32 setPos(m_mapObj->pos());
33 setRot(m_mapObj->rot() * DEG2RAD);
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 = pos();
42 m_targetDir = EGG::Vector3f::ez;
43 m_upForce = EGG::Vector3f::zero;
48void ObjectCow::calcFloor() {
49 constexpr f32 RADIUS = 50.0f;
50 constexpr EGG::Vector3f POS_OFFSET = EGG::Vector3f(0.0f, RADIUS, 0.0f);
54 bool hasCol = CollisionDirector::Instance()->checkSphereFull(RADIUS, pos() + POS_OFFSET,
55 EGG::Vector3f::inf, KCL_TYPE_64EBDFFF, &info,
nullptr, 0);
58 addPos(info.tangentOff);
60 if (info.floorDist > -std::numeric_limits<f32>::min()) {
61 m_floorNrm = info.floorNrm;
65 m_upForce = GRAVITY_FORCE;
67 m_upForce = EGG::Vector3f::zero;
72void ObjectCow::calcPos() {
74 m_tangent * m_tangentFactor + (m_tangent - m_prevTangent) * m_xzSpeed + m_upForce;
75 m_velocity += accel - GRAVITY_FORCE;
76 m_xzSpeed = EGG::Mathf::sqrt(m_velocity.x * m_velocity.x + m_velocity.z * m_velocity.z);
78 if (m_tangent.z * m_velocity.z + m_tangent.x * m_velocity.x < 0.0f) {
85 m_tangentFactor = 0.0f;
89f32 ObjectCow::setTarget(
const EGG::Vector3f &v) {
90 m_state1TargetPos = v;
91 EGG::Vector3f posDiff = m_state1TargetPos - pos();
92 f32 dist = posDiff.normalise();
93 m_targetDir = m_state1TargetPos + posDiff * 1000.0f - pos();
94 m_targetDir.normalise2();
100ObjectCowLeader::ObjectCowLeader(
const System::MapdataGeoObj ¶ms)
101 : ObjectCow(params), StateManager(this, STATE_ENTRIES) {}
104ObjectCowLeader::~ObjectCowLeader() =
default;
107void ObjectCowLeader::init() {
109 m_railInterpolator->init(0.0f, 0);
110 setPos(m_railInterpolator->curPos());
112 setTarget(pos() + m_railInterpolator->curTangentDir() * 10.0f);
114 m_railInterpolator->setCurrVel(
static_cast<f32
>(m_mapObj->setting(1)));
117 m_endedRailSegment =
false;
118 m_state1AnmType = AnmType::EatST;
125void ObjectCowLeader::calc() {
126 u32 t = System::RaceManager::Instance()->timer();
128 if (t >= m_startFrame) {
129 StateManager::calc();
135 m_prevTangent = m_tangent;
136 m_tangent = Interpolate(m_interpRate, m_tangent, m_targetDir);
138 if (m_tangent.squaredLength() > std::numeric_limits<f32>::epsilon()) {
139 m_tangent.normalise2();
141 m_tangent = EGG::Vector3f::ez;
144 m_up = Interpolate(0.1f, m_up, m_floorNrm);
146 if (m_up.squaredLength() > std::numeric_limits<f32>::epsilon()) {
149 m_up = EGG::Vector3f::ey;
152 setMatrixTangentTo(m_up, m_tangent);
156void ObjectCowLeader::calcFloor() {
158 m_upForce = GRAVITY_FORCE;
159 m_floorNrm = m_railInterpolator->floorNrm(m_railInterpolator->nextPointIdx());
163void ObjectCowLeader::enterWait() {
164 setTarget(m_railInterpolator->curPos() + m_railInterpolator->curTangentDir() * 10.0f);
168void ObjectCowLeader::enterEat() {
169 m_state1AnmType = AnmType::EatST;
170 u32 rand = System::RaceManager::Instance()->random().getU32(120);
171 m_eatFrames = rand + 120;
175void ObjectCowLeader::enterRoam() {
176 m_endedRailSegment =
false;
180void ObjectCowLeader::calcWait() {
181 if (m_currentFrame > m_railInterpolator->curPoint().setting[0]) {
187void ObjectCowLeader::calcEat() {
188 constexpr u16 EAT_ST_FRAMES = 40;
189 constexpr u16 EAT_ED_FRAMES = 60;
191 switch (m_state1AnmType) {
192 case AnmType::EatST: {
193 if (m_currentFrame == EAT_ST_FRAMES) {
194 m_state1AnmType = AnmType::Eat;
198 if (m_currentFrame >
static_cast<u16
>(m_eatFrames + EAT_ST_FRAMES)) {
199 m_state1AnmType = AnmType::EatED;
202 case AnmType::EatED: {
203 if (
static_cast<u16
>(m_eatFrames + EAT_ST_FRAMES + EAT_ED_FRAMES) == m_currentFrame) {
213void ObjectCowLeader::calcRoam() {
214 if (m_endedRailSegment) {
217 if (m_railSpeed < 0.0f) {
220 if (m_railInterpolator->curPoint().setting[1] == 0) {
227 if (m_railSpeed < 4.0f) {
234 m_railInterpolator->setCurrVel(m_railSpeed);
236 auto status = m_railInterpolator->calc();
237 const auto &curPoint = m_railInterpolator->curPoint();
239 if (status == RailInterpolator::Status::SegmentEnd &&
240 (curPoint.setting[0] != 0 || curPoint.setting[1] != 0)) {
241 m_endedRailSegment =
true;
244 setPos(m_railInterpolator->curPos() - EGG::Vector3f::ey * 10.0f);
245 setTarget(m_railInterpolator->curPos() + m_railInterpolator->curTangentDir() * 10.0f);
249ObjectCowFollower::ObjectCowFollower(
const System::MapdataGeoObj ¶ms,
const EGG::Vector3f &pos,
251 : ObjectCow(params), StateManager(this, STATE_ENTRIES), m_posOffset(pos), m_rail(nullptr) {
253 setRot(EGG::Vector3f(rot().x, initRot, rot().z));
257ObjectCowFollower::~ObjectCowFollower() =
default;
260void ObjectCowFollower::init() {
263 EGG::Vector3f local_1c = m_posOffset;
264 local_1c.normalise2();
265 setMatrixTangentTo(EGG::Vector3f::ey, local_1c);
272 m_railSegThreshold = 0.0f;
276void ObjectCowFollower::calc() {
277 u32 t = System::RaceManager::Instance()->timer();
279 if (t < m_startFrame) {
280 if (m_currentFrame > m_waitFrames) {
284 if (m_rail->segmentT() > m_railSegThreshold) {
288 setTarget(pos() + m_posOffset * 2.0f);
290 StateManager::calc();
296 m_prevTangent = m_tangent;
297 m_tangent = Interpolate(m_interpRate, m_tangent, m_targetDir);
299 if (m_tangent.squaredLength() > std::numeric_limits<f32>::epsilon()) {
300 m_tangent.normalise2();
302 m_tangent = EGG::Vector3f::ez;
305 m_up = Interpolate(0.1f, m_up, m_floorNrm);
307 if (m_up.squaredLength() > std::numeric_limits<f32>::epsilon()) {
310 m_up = EGG::Vector3f::ey;
313 setMatrixTangentTo(m_up, m_tangent);
317void ObjectCowFollower::enterWait() {
318 constexpr u32 BASE_WAIT_FRAMES = 100;
319 constexpr u32 WAIT_FRAMES_VARIANCE = 60;
320 constexpr f32 BASE_RAIL_THRESHOLD = 0.2f;
321 constexpr f32 RAIL_THRESHOLD_VARIANCE = 0.8f;
323 auto &rand = System::RaceManager::Instance()->random();
324 m_waitFrames = rand.getU32(WAIT_FRAMES_VARIANCE) + BASE_WAIT_FRAMES;
325 m_railSegThreshold = BASE_RAIL_THRESHOLD + rand.getF32(RAIL_THRESHOLD_VARIANCE);
329void ObjectCowFollower::enterFreeRoam() {
330 constexpr f32 BASE_WALK_DISTANCE = 400.0f;
331 constexpr f32 WALK_DISTANCE_VARIANCE = 300.0f;
332 constexpr f32 AVG_ANGLE = DEG2RAD360 * 10.0f;
333 STATIC_ASSERT(AVG_ANGLE == 0.34906584f);
336 auto &rand = System::RaceManager::Instance()->random();
338 m_topSpeed = BASE_TOP_SPEED + rand.getF32(TOP_SPEED_VARIANCE);
340 f32 dVar2 = AVG_ANGLE + rand.getF32(AVG_ANGLE);
344 f32 fVar3 = CheckPointAgainstLineSegment(pos(), m_rail->curPoint().pos, m_rail->curPos());
345 f32 angle = fVar3 > 0.0f ? -dVar2 : dVar2;
347 EGG::Vector3f dir = RotateXZByYaw(angle, m_tangent);
351 f32 distance = BASE_WALK_DISTANCE + rand.getF32(WALK_DISTANCE_VARIANCE);
352 setTarget(pos() + dir * distance);
356void ObjectCowFollower::enterFollowLeader() {
358 m_interpRate = 0.01f;
360 auto &rand = System::RaceManager::Instance()->random();
361 m_topSpeed = BASE_TOP_SPEED + rand.getF32(TOP_SPEED_VARIANCE);
365void ObjectCowFollower::calcWait() {
366 if (m_currentFrame > m_waitFrames) {
370 if (m_rail->segmentT() > m_railSegThreshold) {
376void ObjectCowFollower::calcFreeRoam() {
378 m_tangentFactor = -0.1f;
380 if (m_xzSpeed == 0.0f) {
384 if (m_xzSpeed < m_topSpeed) {
385 m_tangentFactor = 0.1f;
389 EGG::Vector3f local_28 = m_state1TargetPos - pos();
390 if (local_28.x * local_28.x + local_28.z * local_28.z < DIST_THRESHOLD * DIST_THRESHOLD) {
394 if (m_rail->segmentT() > m_railSegThreshold) {
400void ObjectCowFollower::calcFollowLeader() {
404 m_tangentFactor = -0.1f;
406 if (m_xzSpeed == 0.0f) {
407 m_interpRate = 0.05f;
411 dist = setTarget(m_rail->curPos() + m_posOffset);
413 if (m_xzSpeed < m_topSpeed) {
414 m_interpRate = 0.05f;
415 m_tangentFactor = 0.1f;
419 if (dist < DIST_THRESHOLD) {
425ObjectCowHerd::ObjectCowHerd(
const System::MapdataGeoObj ¶ms) : ObjectCollidable(params) {
426 constexpr f32 FOLLOWER_SPACING = 600.0f;
428 u8 followerCount = params.setting(0);
430 m_leader = EGG::egg_new<ObjectCowLeader>(params);
433 m_followers = owning_span<ObjectCowFollower *>(followerCount);
435 for (u32 i = 0; i < followerCount; ++i) {
436 auto *&child = m_followers[i];
437 f32 rot = F_TAU /
static_cast<f32
>(followerCount) *
static_cast<f32
>(i);
438 f32 z = EGG::Mathf::SinFIdx(RAD2FIDX * rot);
439 f32 x = EGG::Mathf::CosFIdx(RAD2FIDX * rot);
440 EGG::Vector3f pos = EGG::Vector3f(x, 0.0f, z) * FOLLOWER_SPACING;
442 child = EGG::egg_new<ObjectCowFollower>(params, pos, rot);
446 auto *rail = RailManager::Instance()->rail(
static_cast<size_t>(params.pathId()));
447 rail->checkSphereFull();
451ObjectCowHerd::~ObjectCowHerd() =
default;
456 for (
auto *&child : m_followers) {
457 child->m_rail = m_leader->m_railInterpolator;
463 constexpr f32 MAX_DIST = 4000.0f;
467 for (
auto *&follower : m_followers) {
471 follower->m_nextStateId = 2;
479 constexpr f32 WIDTH = 400.0f;
481 for (u32 i = 0; i < m_followers.size() - 1; ++i) {
482 auto *iFollower = m_followers[i];
484 for (u32 j = i + 1; j < m_followers.size(); ++j) {
485 auto *jFollower = m_followers[j];
487 EGG::Vector3f posDelta = jFollower->pos() - iFollower->pos();
490 if (length < WIDTH) {
492 jFollower->addPos(change);
493 iFollower->subPos(change);
498 for (
auto *&follower : m_followers) {
502 if (length < WIDTH) {
505 follower->subPos(change);
void checkIntraCollision()
Prevents cows from walking into each other.
void init() override
Assigns the herd's rail to each child.
constexpr f32 normalise()
Normalizes the vector and returns the original length.
constexpr f32 squaredLength() const
The dot product between the vector and itself.