mirror of
https://github.com/encounter/Petari.git
synced 2026-03-30 11:34:15 -07:00
967 lines
24 KiB
C++
967 lines
24 KiB
C++
#include "Game/MapObj/CocoNut.hpp"
|
|
#include "Game/LiveActor/Spine.hpp"
|
|
#include "Game/NameObj/NameObjArchiveListCollector.hpp"
|
|
#include "Game/Util.hpp"
|
|
#include "JSystem/JMath.hpp"
|
|
|
|
inline void negateInternalInline(const TVec3f &src, TVec3f *dst) {
|
|
JGeometry::negateInternal((f32 *)&src, (f32 *)dst);
|
|
}
|
|
|
|
CocoNut::CocoNut(const char *pName) : LiveActor(pName),
|
|
_8C(0.0f),
|
|
_90(0.0f),
|
|
_94(0.0f, 1.0f),
|
|
_D0(55.0f),
|
|
_D4(false),
|
|
_138(0),
|
|
_13C(0),
|
|
mSpawnPosition(gZeroVec),
|
|
_14C(false),
|
|
_150(gZeroVec),
|
|
mSphericalShadow(false),
|
|
mRespawnWhenOutOfView(false),
|
|
_15E(false),
|
|
mContinueRolling(false)
|
|
{
|
|
_A0.identity();
|
|
_D8.identity();
|
|
_108.identity();
|
|
}
|
|
|
|
CocoNut::~CocoNut() {
|
|
}
|
|
|
|
void CocoNut::init(const JMapInfoIter &rIter) {
|
|
initMapToolInfo(rIter);
|
|
initModel();
|
|
MR::connectToSceneNoSilhouettedMapObjStrongLight(this);
|
|
MR::initLightCtrl(this);
|
|
initSensor();
|
|
initBinder(_D0, 0.0f, 0);
|
|
MR::onCalcGravity(this);
|
|
initEffect();
|
|
initSound(6, 0);
|
|
|
|
if (!mSphericalShadow) {
|
|
MR::initShadowVolumeCylinder(this, _D0);
|
|
}
|
|
else {
|
|
MR::initShadowVolumeSphere(this, _D0);
|
|
}
|
|
|
|
MR::setShadowDropLength(this, nullptr, 1500.0f);
|
|
|
|
s32 stack_8;
|
|
if (MR::getJMapInfoClippingGroupID(rIter, &stack_8)) {
|
|
MR::setGroupClipping(this, rIter, 32);
|
|
_15E = true;
|
|
}
|
|
else {
|
|
MR::setClippingFar200m(this);
|
|
}
|
|
|
|
MR::tryRegisterDemoCast(this, rIter);
|
|
|
|
if (!mSphericalShadow) {
|
|
initNerve(&NrvCocoNut::CocoNutNrvWaitOnBind::sInstance);
|
|
}
|
|
else {
|
|
initNerve(&NrvCocoNut::CocoNutNrvWait::sInstance);
|
|
}
|
|
|
|
makeActorAppeared();
|
|
}
|
|
|
|
void CocoNut::initAfterPlacement() {
|
|
TPos3f stack_50;
|
|
|
|
TVec3f gravity(mGravity);
|
|
MR::makeMtxTR(&stack_50.mMtx[0], this);
|
|
|
|
_94.set(stack_50.mMtx[0][2], stack_50.mMtx[1][2], stack_50.mMtx[2][2]);
|
|
|
|
if (MR::isSameDirection(_94, gravity, 0.01f)) {
|
|
TVec3f gravityNegated;
|
|
negateInternalInline(gravity, &gravityNegated);
|
|
|
|
TPos3f stack_20;
|
|
MR::makeMtxUpNoSupport(&stack_20, gravityNegated);
|
|
_94.set(stack_20.mMtx[0][2], stack_20.mMtx[1][2], stack_20.mMtx[2][2]);
|
|
}
|
|
}
|
|
|
|
void CocoNut::startClipped() {
|
|
if (isNerve(&NrvCocoNut::CocoNutNrvReplaceReady::sInstance)) {
|
|
if (!MR::isDemoActive()) {
|
|
_90 = 0.0f;
|
|
_8C = 0.0f;
|
|
_150.zero();
|
|
mVelocity.zero();
|
|
MR::onBind(this);
|
|
MR::onCalcGravity(this);
|
|
MR::showModel(this);
|
|
MR::validateHitSensors(this);
|
|
if (!mSphericalShadow) {
|
|
_D4 = false;
|
|
setNerve(&NrvCocoNut::CocoNutNrvWait::sInstance);
|
|
}
|
|
else {
|
|
setNerve(&NrvCocoNut::CocoNutNrvWaitOnBind::sInstance);
|
|
}
|
|
}
|
|
}
|
|
else if (mRespawnWhenOutOfView) {
|
|
statusToHide();
|
|
mPosition.set(mSpawnPosition);
|
|
setNerve(&NrvCocoNut::CocoNutNrvReplaceReady::sInstance);
|
|
}
|
|
LiveActor::startClipped();
|
|
}
|
|
|
|
void CocoNut::hit(const TVec3f &a1, f32 a2) {
|
|
setFrontVec(a1);
|
|
|
|
f32 var_f0;
|
|
if (a2 < 1.5f) {
|
|
var_f0 = 1.5f;
|
|
}
|
|
else if (a2 > 35.0f) {
|
|
var_f0 = 35.0f;
|
|
}
|
|
else {
|
|
var_f0 = a2;
|
|
}
|
|
|
|
_8C = var_f0;
|
|
if (!isNerve(&NrvCocoNut::CocoNutNrvMove::sInstance)) {
|
|
setNerve(&NrvCocoNut::CocoNutNrvMove::sInstance);
|
|
}
|
|
}
|
|
|
|
bool CocoNut::isPossibleToHit(const TVec3f &a1, const TVec3f &a2, const TVec3f &a3) const {
|
|
TVec3f stack_2C;
|
|
TVec3f stack_20;
|
|
TVec3f stack_14;
|
|
TVec3f stack_8;
|
|
|
|
stack_2C.sub(a2, a1);
|
|
if (MR::normalizeOrZero(&stack_2C)) {
|
|
return false;
|
|
}
|
|
if (MR::normalizeOrZero(a3, &stack_14)) {
|
|
return false;
|
|
}
|
|
if (isNerve(&NrvCocoNut::CocoNutNrvMove::sInstance)) {
|
|
if (MR::normalizeOrZero(mVelocity, &stack_8)) {
|
|
return false;
|
|
}
|
|
stack_20.sub(stack_14, stack_8);
|
|
if (MR::normalizeOrZero(&stack_20)) {
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
stack_20.set(stack_14);
|
|
}
|
|
return stack_2C.dot(stack_20) < 0.0f;
|
|
}
|
|
|
|
f32 CocoNut::calcMoveSpeed() const {
|
|
return !isNerve(&NrvCocoNut::CocoNutNrvMove::sInstance) ? 0.0f : MR::max(_8C, PSVECMag(_150.toCVec()));
|
|
}
|
|
|
|
void CocoNut::initSensor() {
|
|
initHitSensor(2);
|
|
MR::addHitSensor(this, "body", 0x17, 0x10, 65.0f * mScale.x, TVec3f(0.0f, 0.0f, 0.0f));
|
|
MR::addHitSensor(this, "eye", 0x7f, 0x10, 1.1f * mScale.x, TVec3f(0.0f, 0.0f, 0.0f));
|
|
}
|
|
|
|
void CocoNut::initModel() {
|
|
initModelManagerWithAnm(getModelName(), nullptr, false);
|
|
}
|
|
|
|
void CocoNut::initEffect() {
|
|
initEffectKeeper(0, "CocoNut", false);
|
|
MR::setEffectHostMtx(this, "RollingSmoke", _D8.toMtxPtr());
|
|
MR::setEffectHostMtx(this, "RollingSmokeAttrWater", _D8.toMtxPtr());
|
|
MR::setEffectHostMtx(this, "RollingSmokeAttrSand", _D8.toMtxPtr());
|
|
MR::setEffectHostMtx(this, "Land", _D8.toMtxPtr());
|
|
MR::setEffectHostMtx(this, "LandAttrWater", _D8.toMtxPtr());
|
|
MR::setEffectHostMtx(this, "WaterColumn", _D8.toMtxPtr());
|
|
MR::setEffectHostMtx(this, getBreakEffectName(), _D8.toMtxPtr());
|
|
MR::setEffectHostMtx(this, "SpinHitMark", _108.toMtxPtr());
|
|
}
|
|
|
|
#ifdef NON_MATCHING
|
|
// hell function
|
|
void CocoNut::updateRotate(float a1) {
|
|
TMtx34f stack_38;
|
|
TVec3f stack_2C;
|
|
TVec3f stack_20;
|
|
TVec3f stack_14;
|
|
TVec3f stack_8;
|
|
|
|
negateInternalInline(mGravity, &stack_20);
|
|
if (!MR::normalizeOrZero(mVelocity, &stack_2C) && !MR::isSameDirection(stack_2C, stack_20, 0.01f)) {
|
|
|
|
PSVECCrossProduct(stack_2C.toCVec(), stack_20.toCVec(), stack_14.toVec());
|
|
|
|
f32 temp1 = PSVECMag(mVelocity.toCVec()) * -180.0f;
|
|
f32 temp2 = a1 * temp1;
|
|
f32 f = PI_180 * (temp2 / (PI * getSize()));
|
|
|
|
stack_38.mMtx[0][3] = 0.0f;
|
|
stack_38.mMtx[1][3] = 0.0f;
|
|
stack_38.mMtx[2][3] = 0.0f;
|
|
|
|
stack_8.set(stack_14);
|
|
PSVECMag(stack_8.toCVec());
|
|
PSVECNormalize(stack_8.toCVec(), stack_8.toVec());
|
|
|
|
f32 fsin = sin(f);
|
|
f32 fcos = cos(f);
|
|
f32 rx = stack_8.x;
|
|
f32 ry = stack_8.y;
|
|
f32 rz = stack_8.z;
|
|
f32 fcos1 = 1.0f - fcos;
|
|
|
|
stack_38.mMtx[0][0] = (rx * rx * fcos1) + fcos;
|
|
stack_38.mMtx[0][1] = fcos1 * rx * ry - (fsin * rz);
|
|
stack_38.mMtx[0][2] = fcos1 * rx * rz + (fsin * ry);
|
|
stack_38.mMtx[1][0] = fcos1 * rx * ry + (fsin * rz);
|
|
stack_38.mMtx[1][1] = (ry * ry * fcos1) + fcos;
|
|
stack_38.mMtx[1][2] = fcos1 * ry * rz - (fsin * rx);
|
|
stack_38.mMtx[2][0] = fcos1 * rx * rz - (fsin * ry);
|
|
stack_38.mMtx[2][1] = fcos1 * ry * rz + (fsin * rx);
|
|
stack_38.mMtx[2][2] = (rz * rz * fcos1) + fcos;
|
|
|
|
_A0.concat(stack_38, _A0);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void CocoNut::updateGravity() {
|
|
TVec3f stack_8;
|
|
|
|
f32 f31 = _13C ? 0.4f : 1.0f;
|
|
stack_8.set(mGravity);
|
|
f32 f0 = _90 + f31;
|
|
|
|
f32 f2 = f0 >= 25.0f ? 25.0f : f0;
|
|
_90 = f2;
|
|
stack_8.scale((f32)(f64)f2); // real
|
|
mVelocity.add(stack_8);
|
|
}
|
|
|
|
#ifdef NON_MATCHING
|
|
// issues around MR::deleteEffect and PSVECNormalize calls
|
|
void CocoNut::processMove() {
|
|
TVec3f stack_2C;
|
|
TVec3f stack_20;
|
|
TVec3f stack_14;
|
|
TVec3f stack_8;
|
|
|
|
if (isOnGround()) {
|
|
f32 temp_f31 = calcMoveSpeed();
|
|
bool temp_r31 = MR::isBindedGroundWater(this);
|
|
TVec3f *temp_r3_1 = MR::getGroundNormal(this);
|
|
if (2.5f < _90) {
|
|
if (mGravity.dot(*temp_r3_1) < 0.0f) {
|
|
_90 *= 0.5f;
|
|
s32 var_r5 = 100.0f * (temp_f31 / 35.0f);
|
|
if (var_r5 > 100) {
|
|
var_r5 = 100;
|
|
}
|
|
if (var_r5 > 0) {
|
|
if (temp_r31) {
|
|
MR::startSound(this, "SE_OJ_COCONUT_BOUND_WATER", var_r5, -1);
|
|
}
|
|
else {
|
|
MR::startSound(this, "SE_OJ_COCONUT_BOUND", var_r5, -1);
|
|
}
|
|
}
|
|
}
|
|
if (_138 >= 10 && 3.0f < _90) {
|
|
MR::emitEffect(this, "Land");
|
|
}
|
|
}
|
|
else {
|
|
_90 = 0.0f;
|
|
}
|
|
MR::emitEffect(this, "RollingSmoke");
|
|
s32 var_r5_2 = 100.0f * (temp_f31 / 35.0f);
|
|
if (var_r5_2 > 100) {
|
|
var_r5_2 = 100;
|
|
}
|
|
if (var_r5_2 >= 10) {
|
|
if (temp_r31) {
|
|
MR::startLevelSound(this, "SE_OJ_LV_COCONUT_ROLL_WATER", var_r5_2, -1, -1);
|
|
}
|
|
else {
|
|
MR::startLevelSound(this, "SE_OJ_LV_COCONUT_ROLL", var_r5_2, -1, -1);
|
|
}
|
|
}
|
|
_8C *= 0.925f;
|
|
_14C = MR::calcVelocityAreaMoveOnGround(&stack_2C, this);
|
|
if (_14C) {
|
|
_150.set(stack_2C);
|
|
_150.scaleInline(0.75f);
|
|
}
|
|
_138 = 0;
|
|
_13C = false;
|
|
updateRotate(1.0f);
|
|
}
|
|
else {
|
|
// volatile?
|
|
if (_138 < 10) {
|
|
_138++;
|
|
}
|
|
else {
|
|
MR::deleteEffect(this, "RollingSmoke");
|
|
}
|
|
updateRotate(0.75f);
|
|
}
|
|
|
|
if (getWallNormal(&stack_20) && _94.dot(stack_20) < 0.0f) {
|
|
stack_14.set(_94);
|
|
PSVECMag(stack_14.toCVec());
|
|
PSVECNormalize(stack_14.toCVec(), stack_14.toVec());
|
|
|
|
stack_8.set(stack_20);
|
|
PSVECMag(stack_8.toCVec());
|
|
PSVECNormalize(stack_8.toCVec(), stack_8.toVec());
|
|
|
|
f32 ok2 = -2.0f * stack_14.dot(stack_8);
|
|
JMAVECScaleAdd(stack_8.toCVec(), _94.toCVec(), _94.toVec(), ok2);
|
|
|
|
PSVECMag(_94.toCVec());
|
|
PSVECNormalize(_94.toCVec(), _94.toVec());
|
|
_94.normalize(_94);
|
|
|
|
_8C *= 0.8f;
|
|
|
|
MR::startSound(this, "SE_OJ_COCONUT_BOUND", 100.0f * (calcMoveSpeed() / 35.0f), -1);
|
|
}
|
|
setFrontVec(_94);
|
|
mVelocity.set(_94);
|
|
mVelocity.scale(_8C);
|
|
|
|
bool ok = _14C && _138 < 10;
|
|
if (!ok) {
|
|
_150.scaleInline(0.925f);
|
|
}
|
|
mVelocity.add(_150);
|
|
updateGravity();
|
|
}
|
|
#endif
|
|
|
|
void CocoNut::setFrontVec(const TVec3f &a1) {
|
|
TVec3f stack_14;
|
|
TVec3f stack_8(mGravity);
|
|
if (!MR::normalizeOrZero(a1, &stack_14)) {
|
|
if (MR::isSameDirection(a1, stack_8, 0.01f)) {
|
|
_94.set(stack_14);
|
|
}
|
|
else {
|
|
MR::vecKillElement(stack_14, stack_8, &_94);
|
|
MR::normalize(&_94);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CocoNut::tryHit(HitSensor *pOtherSensor, HitSensor *pMySensor) {
|
|
CocoNut *nut = reinterpret_cast<CocoNut *>(pMySensor->mActor);
|
|
if (!isNerve(&NrvCocoNut::CocoNutNrvMove::sInstance)) {
|
|
return false;
|
|
}
|
|
f32 moveSpeed = nut->calcMoveSpeed();
|
|
if (calcMoveSpeed() < moveSpeed) {
|
|
return false;
|
|
}
|
|
TVec3f *otherSensorPos = &pOtherSensor->mPosition;
|
|
TVec3f *mySensorPos = &pMySensor->mPosition;
|
|
if (!nut->isPossibleToHit(*mySensorPos, *otherSensorPos, mVelocity)) {
|
|
return false;
|
|
}
|
|
|
|
TVec3f stack_1C;
|
|
TVec3f stack_10;
|
|
f32 stack_C;
|
|
f32 stack_8;
|
|
calcHitSpeedAndFrontVec(&stack_C, &stack_8, &stack_1C, &stack_10, *otherSensorPos, *mySensorPos);
|
|
hit(stack_1C, stack_C);
|
|
nut->hit(stack_10, stack_8);
|
|
return true;
|
|
}
|
|
|
|
bool CocoNut::tryPushedFromActor(HitSensor *pOtherSensor, HitSensor *pMySensor) {
|
|
TVec3f stack_34;
|
|
TVec3f stack_28;
|
|
TVec3f stack_1C;
|
|
TVec3f stack_10;
|
|
f32 stack_C;
|
|
f32 stack_8;
|
|
|
|
TVec3f *otherSensorPos = &pOtherSensor->mPosition;
|
|
TVec3f *mySensorPos = &pMySensor->mPosition;
|
|
if (_13C) {
|
|
return false;
|
|
}
|
|
if (isNerve(&NrvCocoNut::CocoNutNrvMove::sInstance)) {
|
|
stack_34.sub(*otherSensorPos, *mySensorPos);
|
|
MR::normalize(&stack_34);
|
|
if (0.0f < stack_34.dot(_94)) {
|
|
return false;
|
|
}
|
|
calcHitSpeedAndFrontVec(&stack_C, &stack_8, &stack_28, &stack_1C, *otherSensorPos, *mySensorPos);
|
|
hit(stack_28, stack_C);
|
|
}
|
|
else {
|
|
f32 mySensorRadius = pMySensor->mRadius;
|
|
f32 otherSensorRadius = pOtherSensor->mRadius;
|
|
if (((otherSensorRadius + mySensorRadius) - PSVECDistance(otherSensorPos->toCVec(), mySensorPos->toVec())) < 0.0f) {
|
|
return false;
|
|
}
|
|
stack_10.sub(*otherSensorPos, *mySensorPos);
|
|
if (MR::normalizeOrZero(&stack_10)) {
|
|
return false;
|
|
}
|
|
hit(stack_10, 1.5f);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void CocoNut::reviseFrontVec() {
|
|
HitSensor *sensor;
|
|
|
|
HitSensor *eye = getSensor("eye");
|
|
LiveActor *found_actor = nullptr;
|
|
|
|
for (int i = 0; i < eye->mSensorCount; i++) {
|
|
sensor = eye->mSensors[i];
|
|
if ((sensor->isType(0x26) || sensor->isType(0x56)) && !MR::isDead(sensor->mActor)) {
|
|
found_actor = sensor->mActor;
|
|
break;
|
|
}
|
|
}
|
|
if (found_actor == nullptr) {
|
|
return;
|
|
}
|
|
|
|
TVec3f stack_28(mGravity);
|
|
TVec3f stack_20;
|
|
TVec3f stack_14;
|
|
TVec3f stack_8;
|
|
|
|
stack_14.sub(found_actor->mPosition, this->mPosition);
|
|
if (!MR::isSameDirection(stack_14, stack_20, 0.01f)) {
|
|
MR::vecKillElement(stack_14, stack_20, &stack_8);
|
|
MR::normalize(&stack_8);
|
|
f32 temp_f31 = stack_8.dot(_94);
|
|
if (MR::cosDegree(15.0f) < temp_f31) {
|
|
JMAVECLerp(_94.toCVec(), stack_8.toCVec(), _94.toVec(), 0.8f);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CocoNut::statusToWait() {
|
|
mVelocity.zero();
|
|
_90 = 0.0f;
|
|
_8C = 0.0f;
|
|
_150.zero();
|
|
MR::validateClipping(this);
|
|
if (!mSphericalShadow && !MR::isBindedGroundIce(this)) {
|
|
_D4 = true;
|
|
MR::offBind(this);
|
|
MR::offCalcGravity(this);
|
|
if (!isNerve(&NrvCocoNut::CocoNutNrvWait::sInstance)) {
|
|
setNerve(&NrvCocoNut::CocoNutNrvWait::sInstance);
|
|
}
|
|
}
|
|
else {
|
|
setNerve(&NrvCocoNut::CocoNutNrvWaitOnBind::sInstance);
|
|
}
|
|
}
|
|
|
|
void CocoNut::tryMoveEnd() {
|
|
if (isOnGround()) {
|
|
bool var_r3 = _14C && _138 < 10;
|
|
if (!var_r3 && calcMoveSpeed() < 1.5f && (!mContinueRolling || !isContactWithOtherCocoNut())) {
|
|
statusToWait();
|
|
return;
|
|
}
|
|
}
|
|
if (sendMsgToBindedSensor()) {
|
|
setNerve(&NrvCocoNut::CocoNutNrvBreak::sInstance);
|
|
return;
|
|
}
|
|
if (!tryDisappear()) {
|
|
_8C = MR::max(_8C, 1.5f);
|
|
}
|
|
}
|
|
|
|
bool CocoNut::tryDisappear() {
|
|
TVec3f stack_14;
|
|
stack_14.scale(-100.0f, mGravity);
|
|
if (MR::isInWater(this, stack_14)) {
|
|
setNerve(&NrvCocoNut::CocoNutNrvInWater::sInstance);
|
|
return true;
|
|
}
|
|
if (MR::isInDeath(this, TVec3f(0.0f, 0.0f, 0.0f))) {
|
|
setNerve(&NrvCocoNut::CocoNutNrvBreak::sInstance);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CocoNut::isValidPushedFromPlayer(const HitSensor *arg0, const HitSensor *arg1) const {
|
|
if (_90 < 0.0f) {
|
|
return false;
|
|
}
|
|
if (isNerve(&NrvCocoNut::CocoNutNrvMove::sInstance) && MR::isLessStep(this, 15)) {
|
|
return false;
|
|
}
|
|
TVec3f *playerVelocity = MR::getPlayerVelocity();
|
|
f32 nutVelocitySquared = mVelocity.squared();
|
|
if (playerVelocity->squared() < nutVelocitySquared) {
|
|
return false;
|
|
}
|
|
TVec3f stack_14;
|
|
stack_14.sub(arg0->mPosition, arg1->mPosition);
|
|
if (MR::normalizeOrZero(&stack_14)) {
|
|
return false;
|
|
}
|
|
TVec3f stack_8;
|
|
if (MR::normalizeOrZero(*playerVelocity, &stack_8)) {
|
|
return false;
|
|
}
|
|
if (stack_14.dot(stack_8) < MR::cosDegree(45.0f)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#ifdef NON_MATCHING
|
|
// the frsqrte is most likely an inlined function. possibly JGeometry::TUtil<f32>::sqrt
|
|
void CocoNut::calcHitSpeedAndFrontVec(f32 *arg0, f32 *arg1, TVec3f *arg2, TVec3f *arg3, const TVec3f &arg4, const TVec3f &arg5) const {
|
|
TVec3f stack_14;
|
|
TVec3f stack_8;
|
|
|
|
arg3->sub(arg5, arg4);
|
|
MR::normalize(arg3);
|
|
stack_14.set(mGravity);
|
|
PSVECCrossProduct(arg3->toCVec(), stack_14.toCVec(), arg2->toVec());
|
|
MR::normalize(arg2);
|
|
if (MR::normalizeOrZero(mVelocity, &stack_8)) {
|
|
stack_8.set(_94);
|
|
}
|
|
f32 var_f30 = stack_8.dot(*arg2);
|
|
if (var_f30 < 0.0f) {
|
|
negateInternalInline(stack_14, &stack_14);
|
|
PSVECCrossProduct(arg3->toCVec(), stack_14.toCVec(), arg2->toVec());
|
|
MR::normalize(arg2);
|
|
var_f30 = stack_8.dot(*arg2);
|
|
}
|
|
|
|
f32 temp_f2 = 1.0f - (var_f30 * var_f30);
|
|
|
|
f32 var_f31;
|
|
if (temp_f2 > 0.0f) {
|
|
f32 temp_f31 = __frsqrte(temp_f2);
|
|
f32 temp_f3 = temp_f31 * temp_f2;
|
|
var_f31 = -((temp_f3 * temp_f31) - 3.0f) * temp_f3 * 0.5f;
|
|
} else {
|
|
var_f31 = temp_f2;
|
|
}
|
|
|
|
f32 temp_f1_2 = calcMoveSpeed();
|
|
*arg1 = temp_f1_2 * var_f31;
|
|
*arg0 = temp_f1_2 * var_f30;
|
|
}
|
|
#endif
|
|
|
|
bool CocoNut::isOnGround() const {
|
|
if (0.0f < _90 && MR::isOnGround(this)) {
|
|
TVec3f *groundNormal = MR::getGroundNormal(this);
|
|
if (groundNormal->dot(mGravity) < MR::cosDegree(120.0f)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CocoNut::getWallNormal(TVec3f *arg0) const {
|
|
if (MR::isBindedWall(this)) {
|
|
arg0->set(*MR::getWallNormal(this));
|
|
return true;
|
|
}
|
|
if (0.0f < _90 && (MR::isOnGround(this)) && !isOnGround()) {
|
|
arg0->set(*MR::getGroundNormal(this));
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CocoNut::sendMsgToBindedSensor() {
|
|
if (MR::isBindedGround(this)) {
|
|
return sendMsgEnemyAttackToBindedSensor(MR::getGroundSensor(this));
|
|
}
|
|
if (MR::isBindedWall(this)) {
|
|
return sendMsgEnemyAttackToBindedSensor(MR::getWallSensor(this));
|
|
}
|
|
if (MR::isBindedRoof(this)) {
|
|
return sendMsgEnemyAttackToBindedSensor(MR::getRoofSensor(this));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
bool CocoNut::sendMsgEnemyAttackToBindedSensor(HitSensor *arg0) {
|
|
if (_13C) {
|
|
return MR::sendMsgEnemyAttack(arg0, getSensor("body"));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool CocoNut::isValidReceiveMsg(const HitSensor *a1) const {
|
|
return (
|
|
isNerve(&NrvCocoNut::CocoNutNrvWait::sInstance) ||
|
|
isNerve(&NrvCocoNut::CocoNutNrvWaitOnBind::sInstance) ||
|
|
isNerve(&NrvCocoNut::CocoNutNrvMove::sInstance)
|
|
) && a1 == getSensor("body");
|
|
}
|
|
|
|
const char *CocoNut::getModelName() {
|
|
bool watermelonMode = MR::isStarPieceCounterStop();
|
|
return watermelonMode ? "Watermelon" : "CocoNut";
|
|
}
|
|
|
|
const char *CocoNut::getBreakEffectName() {
|
|
bool watermelonMode = MR::isStarPieceCounterStop();
|
|
return watermelonMode ? "BreakWatermelon" : "CocoNutBreak";
|
|
}
|
|
|
|
void CocoNut::makeArchiveList(NameObjArchiveListCollector *pArchiveList, const JMapInfoIter &rIter) {
|
|
pArchiveList->addArchive(getModelName());
|
|
}
|
|
|
|
void CocoNut::calcAndSetBaseMtx() {
|
|
_A0.mMtx[0][3] = this->mPosition.x;
|
|
_A0.mMtx[1][3] = this->mPosition.y;
|
|
_A0.mMtx[2][3] = this->mPosition.z;
|
|
|
|
MR::setBaseTRMtx(this, _A0);
|
|
|
|
if (isNerve(&NrvCocoNut::CocoNutNrvMove::sInstance) && MR::isOnGround(this)) {
|
|
TVec3f *groundNormal = MR::getGroundNormal(this);
|
|
f32 temp_f31 = _D0;
|
|
|
|
TVec3f stack_8(*groundNormal);
|
|
stack_8.scale(temp_f31);
|
|
|
|
TVec3f stack_14;
|
|
stack_14.sub(mPosition, stack_8);
|
|
|
|
if (MR::isSameDirection(*groundNormal, _94, 0.01f)) {
|
|
MR::makeMtxUpNoSupportPos(&_D8, *groundNormal, stack_14);
|
|
}
|
|
else {
|
|
MR::makeMtxUpFrontPos(&_D8, *groundNormal, _94, stack_14);
|
|
}
|
|
|
|
f32 temp_f9 = mScale.x;
|
|
_D8.mMtx[0][0] *= temp_f9;
|
|
_D8.mMtx[0][1] *= temp_f9;
|
|
_D8.mMtx[0][2] *= temp_f9;
|
|
_D8.mMtx[1][0] *= temp_f9;
|
|
_D8.mMtx[1][1] *= temp_f9;
|
|
_D8.mMtx[1][2] *= temp_f9;
|
|
_D8.mMtx[2][0] *= temp_f9;
|
|
_D8.mMtx[2][1] *= temp_f9;
|
|
_D8.mMtx[2][2] *= temp_f9;
|
|
}
|
|
}
|
|
|
|
void CocoNut::attackSensor(HitSensor *arg0, HitSensor *arg1) {
|
|
if (isValidReceiveMsg(arg0)) {
|
|
if (MR::isSensorPlayer(arg1) && !isValidPushedFromPlayer(arg0, arg1)) {
|
|
if (!MR::isPlayerHipDropFalling()) {
|
|
MR::sendMsgPush(arg1, arg0);
|
|
}
|
|
}
|
|
else if (arg1->isType(0x17)) {
|
|
if (MR::sendMsgPush(arg1, arg0)) {
|
|
MR::startSound(this, "SE_OJ_COCONUT_HIT", -1, -1);
|
|
}
|
|
}
|
|
else {
|
|
if (_13C && isNerve(&NrvCocoNut::CocoNutNrvMove::sInstance) && MR::sendMsgToEnemyAttackBlow(arg1, arg0)) {
|
|
MR::startSound(this, "SE_OJ_COCONUT_HIT", -1, -1);
|
|
setNerve(&NrvCocoNut::CocoNutNrvBreak::sInstance);
|
|
}
|
|
else {
|
|
MR::sendMsgPush(arg1, arg0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CocoNut::receiveMsgPush(HitSensor *pMySensor, HitSensor *pOtherSensor) {
|
|
if (!isValidReceiveMsg(pOtherSensor)) {
|
|
return false;
|
|
}
|
|
if (pMySensor->isType(0x17)) {
|
|
return tryHit(pOtherSensor, pMySensor);
|
|
}
|
|
return tryPushedFromActor(pOtherSensor, pMySensor);
|
|
}
|
|
|
|
bool CocoNut::receiveMsgPlayerAttack(u32 a1, HitSensor *pOtherSensor, HitSensor *pMySensor) {
|
|
if (!isValidReceiveMsg(pMySensor)) {
|
|
return false;
|
|
}
|
|
if (!MR::isMsgPlayerHitAll(a1) && !MR::isMsgPlayerTrample(a1) && !MR::isMsgPlayerHipDrop(a1) && !MR::isMsgStarPieceReflect(a1)) {
|
|
return false;
|
|
}
|
|
f32 var_f31;
|
|
f32 var_f30;
|
|
if (MR::isMsgPlayerSpinAttack(a1)) {
|
|
var_f31 = 35.0f;
|
|
var_f30 = -20.0f;
|
|
MR::startSound(this, "SE_PM_SPIN_HIT", -1, -1);
|
|
MR::startSound(this, "SE_OJ_COCONUT_LAUNCH", -1, -1);
|
|
emitEffectSpinHit(pOtherSensor, pMySensor);
|
|
_13C = true;
|
|
}
|
|
else if (MR::isMsgPlayerHipDrop(a1) || MR::isMsgInvincibleAttack(a1)) {
|
|
var_f31 = 25.0f;
|
|
var_f30 = -15.0f;
|
|
MR::startSound(this, "SE_OJ_COCONUT_FLIP_M", -1, -1);
|
|
if (MR::isMsgPlayerHipDrop(a1)) {
|
|
MR::sendMsgAwayJump(pOtherSensor, pMySensor);
|
|
}
|
|
}
|
|
else {
|
|
var_f31 = 10.0f;
|
|
var_f30 = -5.0f;
|
|
MR::startSound(this, "SE_OJ_COCONUT_FLIP_S", -1, -1);
|
|
}
|
|
_8C = var_f31;
|
|
_90 = var_f30;
|
|
TVec3f stack_2C;
|
|
TVec3f stack_20;
|
|
TVec3f stack_14;
|
|
stack_2C.sub(pMySensor->mPosition, pOtherSensor->mPosition);
|
|
if (MR::isMsgStarPieceReflect(a1) && !MR::normalizeOrZero(stack_2C, &stack_20) && !MR::normalizeOrZero(pOtherSensor->mActor->mVelocity, &stack_14)) {
|
|
if (stack_20.dot(stack_14) < MR::cosDegree(60.0f)) {
|
|
TVec3f stack_8(stack_14);
|
|
stack_8.scale(65.0f);
|
|
stack_2C.add(stack_8);
|
|
}
|
|
}
|
|
setFrontVec(stack_2C);
|
|
if (MR::isMsgPlayerSpinAttack(a1)) {
|
|
reviseFrontVec();
|
|
}
|
|
setNerve(&NrvCocoNut::CocoNutNrvMove::sInstance);
|
|
return true;
|
|
}
|
|
|
|
bool CocoNut::receiveOtherMsg(u32 arg0, HitSensor *pOtherSensor, HitSensor *pMySensor) {
|
|
f32 temp_f0;
|
|
f32 temp_f1;
|
|
f32 var_f2;
|
|
|
|
if (!isValidReceiveMsg(pMySensor)) {
|
|
return false;
|
|
}
|
|
if (MR::isMsgPlayerKick(arg0)) {
|
|
if (isValidPushedFromPlayer(pMySensor, pOtherSensor)) {
|
|
TVec3f *playerVelocity = MR::getPlayerVelocity();
|
|
setFrontVec(*playerVelocity);
|
|
temp_f1 = _94.dot(*playerVelocity);
|
|
var_f2 = 18.0f;
|
|
temp_f0 = 1.1f * temp_f1;
|
|
|
|
_8C = MR::max(var_f2, temp_f0);
|
|
_90 = -(0.7f * temp_f1);
|
|
MR::startSound(this, "SE_OJ_COCONUT_FLIP_S", -1, -1);
|
|
setNerve(&NrvCocoNut::CocoNutNrvMove::sInstance);
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
if (MR::isMsgHitmarkEmit(arg0)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CocoNut::initMapToolInfo(const JMapInfoIter &rIter) {
|
|
MR::initDefaultPos(this, rIter);
|
|
MR::getJMapInfoArg0NoInit(rIter, &mSphericalShadow);
|
|
MR::getJMapInfoArg1NoInit(rIter, &mRespawnWhenOutOfView);
|
|
MR::getJMapInfoArg2NoInit(rIter, &mContinueRolling);
|
|
_D0 = 55.0f * mScale.x;
|
|
mSpawnPosition.set(mPosition);
|
|
}
|
|
|
|
void CocoNut::statusToHide() {
|
|
mVelocity.zero();
|
|
MR::offBind(this);
|
|
MR::offCalcGravity(this);
|
|
MR::hideModel(this);
|
|
MR::invalidateHitSensors(this);
|
|
MR::clearHitSensors(this);
|
|
MR::deleteEffectAll(this);
|
|
}
|
|
|
|
void CocoNut::emitEffectSpinHit(const HitSensor *pOtherSensor, const HitSensor *pMySensor) {
|
|
TVec3f point; // point 70% of the way between pOtherSensor and pMySensor
|
|
JMAVECLerp(pOtherSensor->mPosition.toCVec(), pMySensor->mPosition.toCVec(), point.toVec(), 0.7f);
|
|
_108.mMtx[0][0] = 1.0f;
|
|
_108.mMtx[1][0] = 0.0f;
|
|
_108.mMtx[2][0] = 0.0f;
|
|
_108.mMtx[0][1] = 0.0f;
|
|
_108.mMtx[1][1] = 1.0f;
|
|
_108.mMtx[2][1] = 0.0f;
|
|
_108.mMtx[0][2] = 0.0f;
|
|
_108.mMtx[1][3] = 0.0f;
|
|
_108.mMtx[2][3] = 1.0f;
|
|
_108.mMtx[0][3] = point.x;
|
|
_108.mMtx[1][3] = point.y;
|
|
_108.mMtx[2][3] = point.z;
|
|
MR::emitEffect(this, "SpinHitMark");
|
|
}
|
|
|
|
bool CocoNut::isContactWithOtherCocoNut() const {
|
|
HitSensor *body = getSensor("body");
|
|
for (int i = 0; i < body->mSensorCount; i++) {
|
|
HitSensor *sensor = body->mSensors[i];
|
|
if (body->mSensors[i]->isType(0x17)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CocoNut::exeWait() {
|
|
if (MR::isFirstStep(this)) {
|
|
MR::deleteEffect(this, "RollingSmoke");
|
|
}
|
|
if (!_D4) {
|
|
if (tryDisappear()) {
|
|
return;
|
|
}
|
|
updateGravity();
|
|
}
|
|
if (!_D4 && MR::isOnGround(this)) {
|
|
statusToWait();
|
|
}
|
|
}
|
|
|
|
void CocoNut::exeWaitOnBind() {
|
|
if (MR::isFirstStep(this)) {
|
|
MR::deleteEffect(this, "RollingSmoke");
|
|
}
|
|
if (!tryDisappear()) {
|
|
updateGravity();
|
|
if (MR::isOnGround(this)) {
|
|
statusToWait();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CocoNut::exeMove() {
|
|
if (MR::isFirstStep(this)) {
|
|
_138 = 0;
|
|
_14C = false;
|
|
_150.zero();
|
|
MR::onBind(this);
|
|
MR::onCalcGravity(this);
|
|
MR::calcGravity(this);
|
|
if (!_15E) {
|
|
MR::invalidateClipping(this);
|
|
}
|
|
}
|
|
if (_13C && MR::isStep(this, 1)) {
|
|
MR::stopScene(4);
|
|
}
|
|
processMove();
|
|
tryMoveEnd();
|
|
}
|
|
|
|
void CocoNut::exeInWater() {
|
|
if (MR::isFirstStep(this)) {
|
|
mVelocity.zero();
|
|
MR::offBind(this);
|
|
MR::offCalcGravity(this);
|
|
MR::hideModel(this);
|
|
MR::invalidateHitSensors(this);
|
|
MR::clearHitSensors(this);
|
|
MR::deleteEffectAll(this);
|
|
TVec3f gravityNegated;
|
|
negateInternalInline(mGravity, &gravityNegated);
|
|
MR::makeMtxUpNoSupportPos(&_D8, gravityNegated, mPosition);
|
|
MR::emitEffect(this, "WaterColumn");
|
|
MR::startSound(this, "SE_OJ_FALL_IN_WATER_M", -1, -1);
|
|
MR::releaseSoundHandle(this, "SE_OJ_FALL_IN_WATER_M");
|
|
}
|
|
if (!MR::isEffectValid(this, "WaterColumn")) {
|
|
mPosition.set(mSpawnPosition);
|
|
setNerve(&NrvCocoNut::CocoNutNrvReplaceReady::sInstance);
|
|
}
|
|
}
|
|
|
|
void CocoNut::exeBreak() {
|
|
if (MR::isFirstStep(this)) {
|
|
statusToHide();
|
|
TVec3f gravityNegated;
|
|
negateInternalInline(mGravity, &gravityNegated);
|
|
MR::makeMtxUpNoSupportPos(&_D8, gravityNegated, mPosition);
|
|
MR::emitEffect(this, getBreakEffectName());
|
|
}
|
|
if (!MR::isEffectValid(this, getBreakEffectName())) {
|
|
mPosition.set(mSpawnPosition);
|
|
setNerve(&NrvCocoNut::CocoNutNrvReplaceReady::sInstance);
|
|
}
|
|
}
|
|
|
|
namespace NrvCocoNut {
|
|
INIT_NERVE(CocoNutNrvWait)
|
|
INIT_NERVE(CocoNutNrvWaitOnBind)
|
|
INIT_NERVE(CocoNutNrvMove)
|
|
INIT_NERVE(CocoNutNrvInWater)
|
|
INIT_NERVE(CocoNutNrvBreak)
|
|
INIT_NERVE(CocoNutNrvReplaceReady)
|
|
|
|
void CocoNutNrvReplaceReady::execute(Spine *pSpine) const {
|
|
CocoNut *nut = reinterpret_cast<CocoNut*>(pSpine->mExecutor);
|
|
if (MR::isFirstStep(nut)) {
|
|
MR::validateClipping(nut);
|
|
}
|
|
}
|
|
void CocoNutNrvBreak::execute(Spine *pSpine) const {
|
|
CocoNut *nut = reinterpret_cast<CocoNut*>(pSpine->mExecutor);
|
|
nut->exeBreak();
|
|
}
|
|
void CocoNutNrvInWater::execute(Spine *pSpine) const {
|
|
CocoNut *nut = reinterpret_cast<CocoNut*>(pSpine->mExecutor);
|
|
nut->exeInWater();
|
|
}
|
|
void CocoNutNrvMove::execute(Spine *pSpine) const {
|
|
CocoNut *nut = reinterpret_cast<CocoNut*>(pSpine->mExecutor);
|
|
nut->exeMove();
|
|
}
|
|
void CocoNutNrvWaitOnBind::execute(Spine *pSpine) const {
|
|
CocoNut *nut = reinterpret_cast<CocoNut*>(pSpine->mExecutor);
|
|
nut->exeWaitOnBind();
|
|
}
|
|
void CocoNutNrvWait::execute(Spine *pSpine) const {
|
|
CocoNut *nut = reinterpret_cast<CocoNut*>(pSpine->mExecutor);
|
|
nut->exeWait();
|
|
}
|
|
}
|