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 if (m_nextStateId >= 0) {
133 m_currentStateId = m_nextStateId;
137 auto enterFunc = m_entries[m_entryIds[m_currentStateId]].onEnter;
138 (this->*enterFunc)();
143 auto calcFunc = m_entries[m_entryIds[m_currentStateId]].onCalc;
150 m_prevTangent = m_tangent;
151 m_tangent = Interpolate(m_interpRate, m_tangent, m_targetDir);
153 if (m_tangent.squaredLength() > std::numeric_limits<f32>::epsilon()) {
154 m_tangent.normalise2();
156 m_tangent = EGG::Vector3f::ez;
159 m_up = Interpolate(0.1f, m_up, m_floorNrm);
161 if (m_up.squaredLength() > std::numeric_limits<f32>::epsilon()) {
164 m_up = EGG::Vector3f::ey;
167 setMatrixTangentTo(m_up, m_tangent);
171void ObjectCowLeader::calcFloor() {
173 m_upForce = GRAVITY_FORCE;
174 m_floorNrm = m_railInterpolator->floorNrm(m_railInterpolator->nextPointIdx());
178void ObjectCowLeader::enterWait() {
179 setTarget(m_railInterpolator->curPos() + m_railInterpolator->curTangentDir() * 10.0f);
183void ObjectCowLeader::enterEat() {
184 m_state1AnmType = AnmType::EatST;
185 u32 rand = System::RaceManager::Instance()->random().getU32(120);
186 m_eatFrames = rand + 120;
190void ObjectCowLeader::enterRoam() {
191 m_endedRailSegment =
false;
195void ObjectCowLeader::calcWait() {
196 if (m_currentFrame > m_railInterpolator->curPoint().setting[0]) {
202void ObjectCowLeader::calcEat() {
203 constexpr u16 EAT_ST_FRAMES = 40;
204 constexpr u16 EAT_ED_FRAMES = 60;
206 switch (m_state1AnmType) {
207 case AnmType::EatST: {
208 if (m_currentFrame == EAT_ST_FRAMES) {
209 m_state1AnmType = AnmType::Eat;
213 if (m_currentFrame >
static_cast<u16>(m_eatFrames + EAT_ST_FRAMES)) {
214 m_state1AnmType = AnmType::EatED;
217 case AnmType::EatED: {
218 if (
static_cast<u16>(m_eatFrames + EAT_ST_FRAMES + EAT_ED_FRAMES) == m_currentFrame) {
228void ObjectCowLeader::calcRoam() {
229 if (m_endedRailSegment) {
232 if (m_railSpeed < 0.0f) {
235 if (m_railInterpolator->curPoint().setting[1] == 0) {
242 if (m_railSpeed < 4.0f) {
249 m_railInterpolator->setCurrVel(m_railSpeed);
251 auto status = m_railInterpolator->calc();
252 const auto &curPoint = m_railInterpolator->curPoint();
254 if (status == RailInterpolator::Status::SegmentEnd &&
255 (curPoint.setting[0] != 0 || curPoint.setting[1] != 0)) {
256 m_endedRailSegment =
true;
259 m_pos = m_railInterpolator->curPos() - EGG::Vector3f::ey * 10.0f;
260 m_flags.setBit(eFlags::Position);
262 setTarget(m_railInterpolator->curPos() + m_railInterpolator->curTangentDir() * 10.0f);
265const std::array<StateManagerEntry<ObjectCowLeader>, 3>
266 StateManager<ObjectCowLeader>::STATE_ENTRIES = {{
267 {0, &ObjectCowLeader::enterWait, &ObjectCowLeader::calcWait},
268 {1, &ObjectCowLeader::enterEat, &ObjectCowLeader::calcEat},
269 {2, &ObjectCowLeader::enterRoam, &ObjectCowLeader::calcRoam},
272StateManager<ObjectCowLeader>::StateManager(ObjectCowLeader *obj) {
273 constexpr size_t ENTRY_COUNT = 3;
276 m_entries = std::span{STATE_ENTRIES};
277 m_entryIds = std::span(
new u16[ENTRY_COUNT], ENTRY_COUNT);
280 for (
auto &
id : m_entryIds) {
284 for (
size_t i = 0; i < m_entryIds.size(); ++i) {
285 m_entryIds[STATE_ENTRIES[i].id] = i;
289StateManager<ObjectCowLeader>::~StateManager() {
290 delete[] m_entryIds.data();
296 : ObjectCow(params), StateManager(this), m_posOffset(pos), m_rail(nullptr) {
297 m_pos += m_posOffset;
298 m_flags.setBit(eFlags::Position, eFlags::Rotation);
303ObjectCowFollower::~ObjectCowFollower() =
default;
306void ObjectCowFollower::init() {
308 m_pos += m_posOffset;
309 m_flags.setBit(eFlags::Position);
311 local_1c.normalise2();
312 setMatrixTangentTo(EGG::Vector3f::ey, local_1c);
319 m_railSegThreshold = 0.0f;
323void ObjectCowFollower::calc() {
324 u32 t = System::RaceManager::Instance()->timer();
326 if (t < m_startFrame) {
327 if (m_currentFrame > m_waitFrames) {
331 if (m_rail->segmentT() > m_railSegThreshold) {
335 setTarget(m_pos + m_posOffset * 2.0f);
337 if (m_nextStateId >= 0) {
338 m_currentStateId = m_nextStateId;
342 auto enterFunc = m_entries[m_entryIds[m_currentStateId]].onEnter;
343 (this->*enterFunc)();
348 auto calcFunc = m_entries[m_entryIds[m_currentStateId]].onCalc;
355 m_prevTangent = m_tangent;
356 m_tangent = Interpolate(m_interpRate, m_tangent, m_targetDir);
358 if (m_tangent.squaredLength() > std::numeric_limits<f32>::epsilon()) {
359 m_tangent.normalise2();
361 m_tangent = EGG::Vector3f::ez;
364 m_up = Interpolate(0.1f, m_up, m_floorNrm);
366 if (m_up.squaredLength() > std::numeric_limits<f32>::epsilon()) {
369 m_up = EGG::Vector3f::ey;
372 setMatrixTangentTo(m_up, m_tangent);
376void ObjectCowFollower::enterWait() {
377 constexpr u32 BASE_WAIT_FRAMES = 100;
378 constexpr u32 WAIT_FRAMES_VARIANCE = 60;
379 constexpr f32 BASE_RAIL_THRESHOLD = 0.2f;
380 constexpr f32 RAIL_THRESHOLD_VARIANCE = 0.8f;
382 auto &rand = System::RaceManager::Instance()->random();
383 m_waitFrames = rand.getU32(WAIT_FRAMES_VARIANCE) + BASE_WAIT_FRAMES;
384 m_railSegThreshold = BASE_RAIL_THRESHOLD + rand.getF32(RAIL_THRESHOLD_VARIANCE);
388void ObjectCowFollower::enterFreeRoam() {
389 constexpr f32 BASE_WALK_DISTANCE = 400.0f;
390 constexpr f32 WALK_DISTANCE_VARIANCE = 300.0f;
391 constexpr f32 AVG_ANGLE = DEG2RAD360 * 10.0f;
392 STATIC_ASSERT(AVG_ANGLE == 0.34906584f);
395 auto &rand = System::RaceManager::Instance()->random();
397 m_topSpeed = BASE_TOP_SPEED + rand.getF32(TOP_SPEED_VARIANCE);
399 f32 dVar2 = AVG_ANGLE + rand.getF32(AVG_ANGLE);
403 f32 fVar3 = CheckPointAgainstLineSegment(m_pos, m_rail->curPoint().pos, m_rail->curPos());
404 f32 angle = fVar3 > 0.0f ? -dVar2 : dVar2;
410 f32 distance = BASE_WALK_DISTANCE + rand.getF32(WALK_DISTANCE_VARIANCE);
411 setTarget(m_pos + dir * distance);
415void ObjectCowFollower::enterFollowLeader() {
417 m_interpRate = 0.01f;
419 auto &rand = System::RaceManager::Instance()->random();
420 m_topSpeed = BASE_TOP_SPEED + rand.getF32(TOP_SPEED_VARIANCE);
424void ObjectCowFollower::calcWait() {
425 if (m_currentFrame > m_waitFrames) {
429 if (m_rail->segmentT() > m_railSegThreshold) {
435void ObjectCowFollower::calcFreeRoam() {
437 m_tangentFactor = -0.1f;
439 if (m_xzSpeed == 0.0f) {
443 if (m_xzSpeed < m_topSpeed) {
444 m_tangentFactor = 0.1f;
449 if (local_28.x * local_28.x + local_28.z * local_28.z < DIST_THRESHOLD * DIST_THRESHOLD) {
453 if (m_rail->segmentT() > m_railSegThreshold) {
459void ObjectCowFollower::calcFollowLeader() {
463 m_tangentFactor = -0.1f;
465 if (m_xzSpeed == 0.0f) {
466 m_interpRate = 0.05f;
470 dist = setTarget(m_rail->curPos() + m_posOffset);
472 if (m_xzSpeed < m_topSpeed) {
473 m_interpRate = 0.05f;
474 m_tangentFactor = 0.1f;
478 if (dist < DIST_THRESHOLD) {
483const std::array<StateManagerEntry<ObjectCowFollower>, 3>
484 StateManager<ObjectCowFollower>::STATE_ENTRIES = {{
485 {0, &ObjectCowFollower::enterWait, &ObjectCowFollower::calcWait},
486 {1, &ObjectCowFollower::enterFreeRoam, &ObjectCowFollower::calcFreeRoam},
487 {2, &ObjectCowFollower::enterFollowLeader, &ObjectCowFollower::calcFollowLeader},
490StateManager<ObjectCowFollower>::StateManager(ObjectCowFollower *obj) {
491 constexpr size_t ENTRY_COUNT = 3;
494 m_entries = std::span{STATE_ENTRIES};
495 m_entryIds = std::span(
new u16[ENTRY_COUNT], ENTRY_COUNT);
498 for (
auto &
id : m_entryIds) {
502 for (
size_t i = 0; i < m_entryIds.size(); ++i) {
503 m_entryIds[STATE_ENTRIES[i].id] = i;
507StateManager<ObjectCowFollower>::~StateManager() {
508 delete[] m_entryIds.data();
513 constexpr f32 FOLLOWER_SPACING = 600.0f;
515 u8 followerCount = params.setting(0);
517 m_leader =
new ObjectCowLeader(params);
521 std::span<ObjectCowFollower *>(
new ObjectCowFollower *[followerCount], followerCount);
523 for (
u32 i = 0; i < followerCount; ++i) {
524 auto *&child = m_followers[i];
525 f32 rot = F_TAU /
static_cast<f32
>(followerCount) *
static_cast<f32
>(i);
526 f32 z = EGG::Mathf::SinFIdx(RAD2FIDX * rot);
527 f32 x = EGG::Mathf::CosFIdx(RAD2FIDX * rot);
530 child =
new ObjectCowFollower(params, pos, rot);
534 auto *rail = RailManager::Instance()->rail(
static_cast<size_t>(params.pathId()));
535 rail->checkSphereFull();
539ObjectCowHerd::~ObjectCowHerd() {
540 delete[] m_followers.data();
545void ObjectCowHerd::init() {
546 for (
auto *&child : m_followers) {
547 child->m_rail = m_leader->m_railInterpolator;
552void ObjectCowHerd::calc() {
553 constexpr f32 MAX_DIST = 4000.0f;
557 for (
auto *&follower : m_followers) {
561 follower->m_nextStateId = 2;
568void ObjectCowHerd::checkCollision() {
569 constexpr f32 WIDTH = 400.0f;
571 for (
u32 i = 0; i < m_followers.size() - 1; ++i) {
572 auto *iFollower = m_followers[i];
574 for (
u32 j = i + 1; j < m_followers.size(); ++j) {
575 auto *jFollower = m_followers[j];
577 EGG::Vector3f posDelta = jFollower->pos() - iFollower->pos();
580 if (length < WIDTH) {
583 jFollower->m_pos += change;
584 jFollower->m_flags.setBit(eFlags::Position);
586 iFollower->m_pos -= change;
587 iFollower->m_flags.setBit(eFlags::Position);
592 for (
auto *&follower : m_followers) {
596 if (length < WIDTH) {
599 follower->m_pos -= change;
600 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.