mirror of
https://github.com/encounter/Petari.git
synced 2026-03-30 11:34:15 -07:00
1099 lines
27 KiB
C++
1099 lines
27 KiB
C++
#include "Game/Enemy/Poihana.hpp"
|
|
#include "Game/Util.hpp"
|
|
#include "JSystem/JMath/JMath.hpp"
|
|
|
|
namespace {
|
|
const Vec sNormalBinderPos = { 0.0f, 130.0f, 120.0f };
|
|
const Vec sTrampleBinderPos = { 0.0f, 150.0f, 0.0f };
|
|
};
|
|
|
|
Poihana::Poihana(const char *pName) : LiveActor(pName) {
|
|
mAnimScaleCtrl = nullptr;
|
|
mBindStarPointer = nullptr;
|
|
mCamInfo = nullptr;
|
|
mBindedActor = nullptr;
|
|
mLaunchIntensity = 0.0f;
|
|
mRespawnPos.setInline(0.0f);
|
|
_AC = 0.0f;
|
|
_B0 = 0.0f;
|
|
mHomePos.setInline(0.0f);
|
|
_C0 = 1.0f;
|
|
mBoundTimer = -1;
|
|
mBehavior = POIHANA_BEHAVIOR_NORMAL;
|
|
mCanDrown = false;
|
|
mWaterColumn = nullptr;
|
|
mIsActive = false;
|
|
_E5 = 0;
|
|
}
|
|
|
|
/*void Poihana::init(const JMapInfoIter &rIter) {
|
|
MR::initDefaultPos(this, rIter);
|
|
MR::initActorCamera(this, rIter, &mCamInfo);
|
|
initModelManagerWithAnm("Poihana", nullptr, false);
|
|
|
|
MR::connectToSceneEnemy(this);
|
|
|
|
// Initialize sensors
|
|
initHitSensor(2);
|
|
MR::addHitSensorPriorBinder(this, "binder", 8, 125.0f, sNormalBinderPos);
|
|
MR::addHitSensorAtJoint(this, "body", "Body", 30, 8, 70.0f, TVec3f(0.0f, 0.0f, 0.0f));
|
|
|
|
// Initialize binder
|
|
bool useSmallBinder = false;
|
|
MR::getJMapInfoArg7NoInit(rIter, &useSmallBinder);
|
|
|
|
if (useSmallBinder) {
|
|
initBinder(100.0f, 100.0f, 0);
|
|
}
|
|
else {
|
|
initBinder(150.0f, 150.0f, 0);
|
|
}
|
|
|
|
mIsActive = true;
|
|
MR::setGroupClipping(this, rIter, 0x20);
|
|
initEffectKeeper(1, nullptr, false);
|
|
initSound(4, false);
|
|
|
|
// Initialize 2P behavior
|
|
MR::initStarPointerTarget(this, 150.0f, TVec3f(0.0f, 100.0f, 0.0f));
|
|
mAnimScaleCtrl = new AnimScaleController(nullptr);
|
|
mBindStarPointer = new WalkerStateBindStarPointer(this, mAnimScaleCtrl);
|
|
|
|
MR::initShadowVolumeSphere(this, 80.0f);
|
|
MR::addToAttributeGroupSearchTurtle(this);
|
|
|
|
mRespawnPos.set(mPosition);
|
|
mHomePos.set(mRespawnPos);
|
|
|
|
// Calculate launch intensity, thanks to Shibbo for helping me on that one
|
|
s32 launchIntensity = 1000;
|
|
MR::getJMapInfoArg0NoInit(rIter, &launchIntensity);
|
|
float launchIntensityF = 2.0f * launchIntensity;
|
|
|
|
if (launchIntensityF > 0) {
|
|
f32 factor;
|
|
f32 reciprocal = __frsqrte(launchIntensityF);
|
|
factor = reciprocal * launchIntensityF;
|
|
launchIntensityF = -(factor * reciprocal - 3.0f) * factor * 0.5f;
|
|
}
|
|
|
|
mLaunchIntensity = -launchIntensityF;
|
|
|
|
// Setup color
|
|
//s32 color = 0;
|
|
//MR::getJMapInfoArg2NoInit(rIter, &color);
|
|
//MR::startBtpAndSetFrameAndStop(this, "ColorChange", color);
|
|
|
|
// Setup behaviors
|
|
//MR::getJMapInfoArg1NoInit(rIter, &mActiveRange);
|
|
MR::getJMapInfoArg3NoInit(rIter, &mBehavior);
|
|
MR::getJMapInfoArg4NoInit(rIter, &mCanDrown);
|
|
|
|
if (mCanDrown) {
|
|
mWaterColumn = MR::createModelObjMapObj("エフェクト水柱", "WaterColumn", (MtxPtr)getBaseMtx());
|
|
mWaterColumn->mScale.setAll(2.0f);
|
|
}
|
|
|
|
MR::tryCreateMirrorActor(this, nullptr);
|
|
//MR::useStageSwitchAwake(this, rIter);
|
|
|
|
if (mBehavior == POIHANA_BEHAVIOR_SLEEP) {
|
|
initNerve(&NrvPoihana::PoihanaNrvSleep::sInstance);
|
|
}
|
|
else {
|
|
initNerve(&NrvPoihana::PoihanaNrvWait::sInstance);
|
|
}
|
|
|
|
makeActorAppeared();
|
|
}*/
|
|
|
|
void Poihana::initAfterPlacement() {
|
|
TVec3f gravityNegated;
|
|
JGeometry::negateInternal((f32 *)&mGravity, (f32 *)&gravityNegated);
|
|
TPos3f baseMtx;
|
|
MR::makeMtxUpNoSupportPos(&baseMtx, gravityNegated, mPosition);
|
|
MR::setBaseTRMtx(this, baseMtx);
|
|
MR::calcFrontVec(&mFrontVec, this);
|
|
MR::trySetMoveLimitCollision(this);
|
|
}
|
|
|
|
void Poihana::control() {
|
|
if (!isNerve(&NrvPoihana::PoihanaNrvHide::sInstance)) {
|
|
mAnimScaleCtrl->updateNerve();
|
|
calcBound();
|
|
controlVelocity();
|
|
calcMyGravity();
|
|
|
|
if (!tryDrown() && !tryDPDSwoon() && tryHipDropShock()) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void Poihana::calcAndSetBaseMtx() {
|
|
TPos3f baseMtx;
|
|
MR::calcMtxFromGravityAndZAxis(&baseMtx, this, mGravity, mFrontVec);
|
|
|
|
if (isNerveTypeWalkOrWait()) {
|
|
MR::blendMtx((MtxPtr)getBaseMtx(), (MtxPtr)&baseMtx, 0.3f, (MtxPtr)&baseMtx);
|
|
}
|
|
|
|
MR::setBaseTRMtx(this, baseMtx);
|
|
|
|
TVec3f newScale;
|
|
newScale.multPS(mScale, mAnimScaleCtrl->_C);
|
|
MR::setBaseScale(this, newScale);
|
|
}
|
|
|
|
// This inline function might be used elsewhere too? It seems unusual for it to be used once
|
|
inline void calcRepelVector(const TVec3f &agent, const TVec3f &object, TVec3f& dst) {
|
|
JMAVECScaleAdd(agent.toCVec(), object.toCVec(), dst.toVec(), -agent.dot(object));
|
|
}
|
|
|
|
void Poihana::attackSensor(HitSensor *pSender, HitSensor *pReceiver) {
|
|
bool ret = false;
|
|
|
|
if (MR::isSensorPlayer(pReceiver) || MR::isSensorEnemy(pReceiver) ||
|
|
MR::isSensorMapObj(pReceiver)) {
|
|
ret = true;
|
|
}
|
|
|
|
if (!ret) {
|
|
return;
|
|
}
|
|
|
|
if (MR::isSensorEnemy(pSender)) {
|
|
if (MR::isSensorPlayer(pReceiver)) {
|
|
contactMario(pSender, pReceiver);
|
|
}
|
|
|
|
if (MR::sendMsgPush(pReceiver, pSender)) {
|
|
if (MR::isSensorPlayer(pReceiver)) {
|
|
ret = false;
|
|
if (isNerve(&NrvPoihana::PoihanaNrvShock::sInstance) ||
|
|
isNerve(&NrvPoihana::PoihanaNrvSwoonLand::sInstance) ||
|
|
isNerve(&NrvPoihana::PoihanaNrvSwoon::sInstance) ||
|
|
isNerve(&NrvPoihana::PoihanaNrvRecover::sInstance)) {
|
|
ret = true;
|
|
}
|
|
|
|
if (ret) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
TVec3f pushVelocity(mPosition - pReceiver->mActor->mPosition);
|
|
MR::normalizeOrZero(&pushVelocity);
|
|
|
|
if (mVelocity.dot(pushVelocity) < 0.0f) {
|
|
const TVec3f& velocity = mVelocity;
|
|
calcRepelVector(pushVelocity, velocity, mVelocity);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool Poihana::receiveMsgPush(HitSensor *pSender, HitSensor *pReceiver) {
|
|
if (MR::isSensorEnemy(pSender) || MR::isSensorMapObj(pSender)) {
|
|
TVec3f pushOffset(mPosition - pSender->mActor->mPosition);
|
|
MR::normalizeOrZero(&pushOffset);
|
|
JMAVECScaleAdd(pushOffset.toCVec(), mVelocity.toCVec(), mVelocity.toVec(), 1.5f);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Poihana::receiveMsgPlayerAttack(u32 msg, HitSensor *pSender, HitSensor *pReceiver) {
|
|
if (MR::isMsgStarPieceAttack(msg)) {
|
|
return true;
|
|
}
|
|
|
|
if (MR::isMsgPlayerTrample(msg) || MR::isMsgPlayerHipDrop(msg)) {
|
|
bool flag = false;
|
|
|
|
if (isNerve(&NrvPoihana::PoihanaNrvDrown::sInstance) ||
|
|
isNerve(&NrvPoihana::PoihanaNrvHide::sInstance) ||
|
|
isNerve(&NrvPoihana::PoihanaNrvAppear::sInstance) ||
|
|
isNerve(&NrvPoihana::PoihanaNrvShock::sInstance)) {
|
|
flag = true;
|
|
}
|
|
|
|
if (flag) {
|
|
goto here;
|
|
}
|
|
|
|
flag = false;
|
|
if (isNerve(&NrvPoihana::PoihanaNrvSleepStart::sInstance) ||
|
|
isNerve(&NrvPoihana::PoihanaNrvSleep::sInstance) ||
|
|
isNerve(&NrvPoihana::PoihanaNrvGetUp::sInstance)) {
|
|
flag = true;
|
|
}
|
|
|
|
if (flag) {
|
|
here:
|
|
startBound();
|
|
MR::startSound(this, "SE_EV_POIHANA_TRAMPLE", -1, -1);
|
|
|
|
if (MR::isMsgPlayerHipDrop(msg)) {
|
|
MR::sendMsgAwayJump(pSender, pReceiver);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
MR::setSensorOffset(this, "binder", sTrampleBinderPos);
|
|
MR::setSensorRadius(this, "binder", 125.0f);
|
|
MR::validateHitSensor(this, "binder");
|
|
}
|
|
|
|
if (MR::isMsgPlayerHitAll(msg) && tryShock()) {
|
|
MR::stopSceneForDefaultHit(3);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Poihana::receiveMsgEnemyAttack(u32 msg, HitSensor *pSender, HitSensor *pReceiver) {
|
|
if (MR::isMsgExplosionAttack(msg) && tryShock()) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/*bool Poihana::receiveOtherMsg(u32 msg, HitSensor *pSender, HitSensor *pReceiver) {
|
|
if (MR::isMsgAutoRushBegin(msg) && MR::isSensorPlayer(pSender)) {
|
|
if (isNerve(&NrvPoihana::PoihanaNrvShootUpCharge::sInstance)) {
|
|
return false;
|
|
}
|
|
else if (MR::isOnGroundPlayer()) {
|
|
if (isNerve(&NrvPoihana::PoihanaNrvShootUp::sInstance)) {
|
|
if (getNerveStep() < 30) {
|
|
if (!tryToStartBind(pSender)) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else if (isNerve(&NrvPoihana::PoihanaNrvShootUpCharge::sInstance) ||
|
|
!MR::isNear(pSender, pReceiver, 100.0f)) {
|
|
return false;
|
|
}
|
|
else {
|
|
setNerve(&NrvPoihana::PoihanaNrvShootUpCharge::sInstance);
|
|
}
|
|
}
|
|
else if (tryToStartBind(pSender)) {
|
|
setNerve(&NrvPoihana::PoihanaNrvShootUp::sInstance);
|
|
return true;
|
|
}
|
|
}
|
|
else if (MR::isMsgUpdateBaseMtx(msg) && mBindedActor) {
|
|
updateBindActorMtx();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}*/
|
|
|
|
void Poihana::exeNonActive() {
|
|
if (MR::isFirstStep(this)) {
|
|
mVelocity.zero();
|
|
MR::offBind(this);
|
|
MR::offCalcShadow(this, nullptr);
|
|
MR::offCalcAnim(this);
|
|
|
|
mIsActive = false;
|
|
|
|
MR::invalidateHitSensors(this);
|
|
}
|
|
|
|
if (MR::isNearPlayerAnyTime(this, 3500.0f)) {
|
|
if (mBehavior == POIHANA_BEHAVIOR_SLEEP) {
|
|
setNerve(&NrvPoihana::PoihanaNrvSleep::sInstance);
|
|
}
|
|
else {
|
|
setNerve(&NrvPoihana::PoihanaNrvWait::sInstance);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Poihana::endNonActive() {
|
|
MR::onBind(this);
|
|
MR::onCalcShadow(this, nullptr);
|
|
MR::onCalcAnim(this);
|
|
|
|
mIsActive = true;
|
|
|
|
MR::setSensorOffset(this, "binder", sNormalBinderPos);
|
|
MR::setSensorRadius(this, "binder", 125.0f);
|
|
MR::validateHitSensor(this, "binder");
|
|
MR::validateHitSensors(this);
|
|
}
|
|
|
|
void Poihana::exeWait() {
|
|
if (MR::isFirstStep(this)) {
|
|
MR::startBckNoInterpole(this, "Wait");
|
|
MR::setSensorOffset(this, "binder", sNormalBinderPos);
|
|
MR::setSensorRadius(this, "binder", 125.0f);
|
|
MR::validateHitSensor(this, "binder");
|
|
}
|
|
|
|
if (MR::isNearPlayer(this, 800.0f)) {
|
|
setNerve(&NrvPoihana::PoihanaNrvSearch::sInstance);
|
|
}
|
|
else if (MR::isGreaterStep(this, 180)) {
|
|
setNerve(&NrvPoihana::PoihanaNrvWalkAround::sInstance);
|
|
}
|
|
else {
|
|
tryNonActive();
|
|
}
|
|
}
|
|
|
|
void Poihana::exeWalkAround() {
|
|
if (MR::isFirstStep(this)) {
|
|
if (!MR::isBckPlaying(this, "Walk")) {
|
|
MR::startBck(this, "Walk", nullptr);
|
|
}
|
|
|
|
mRandDir = MR::getRandom((s32)-2, (s32)2);
|
|
}
|
|
|
|
MR::rotateVecDegree(&mFrontVec, mGravity, mRandDir);
|
|
JMAVECScaleAdd(mFrontVec.toCVec(), mVelocity.toCVec(), mVelocity.toVec(), 0.5f);
|
|
|
|
if (isNeedForBackHome()) {
|
|
setNerve(&NrvPoihana::PoihanaNrvGoBack::sInstance);
|
|
}
|
|
|
|
else if (MR::isNearPlayer(this, 800.0f)) {
|
|
setNerve(&NrvPoihana::PoihanaNrvSearch::sInstance);
|
|
}
|
|
|
|
else if (MR::isGreaterStep(this, 120)) {
|
|
if (mBehavior == POIHANA_BEHAVIOR_SLEEP) {
|
|
setNerve(&NrvPoihana::PoihanaNrvSleepStart::sInstance);
|
|
}
|
|
else {
|
|
setNerve(&NrvPoihana::PoihanaNrvWait::sInstance);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Poihana::exeSleepStart() {
|
|
if (MR::isFirstStep(this)) {
|
|
MR::startBck(this, "SleepStart", nullptr);
|
|
MR::startSound(this, "SE_EV_POIHANA_SLEEP_START", -1, -1);
|
|
MR::invalidateHitSensor(this, "binder");
|
|
}
|
|
|
|
if (MR::isBckStopped(this)) {
|
|
setNerve(&NrvPoihana::PoihanaNrvSleep::sInstance);
|
|
}
|
|
}
|
|
|
|
void Poihana::exeSleep() {
|
|
if (MR::isFirstStep(this)) {
|
|
MR::startBck(this, "Sleep", nullptr);
|
|
MR::invalidateHitSensor(this, "binder");
|
|
}
|
|
|
|
if (isNeedForGetUp()) {
|
|
setNerve(&NrvPoihana::PoihanaNrvGetUp::sInstance);
|
|
}
|
|
else {
|
|
tryNonActive();
|
|
}
|
|
}
|
|
|
|
void Poihana::exeGetUp() {
|
|
if (MR::isFirstStep(this)) {
|
|
MR::startBck(this, "GetUp", nullptr);
|
|
MR::startSound(this, "SE_EV_POIHANA_WAKEUP", -1, -1);
|
|
MR::startSound(this, "SE_EM_POIHANA_WAKEUP", -1, -1);
|
|
}
|
|
|
|
if (MR::isBckStopped(this)) {
|
|
MR::setSensorOffset(this, "binder", sNormalBinderPos);
|
|
MR::setSensorRadius(this, "binder", 125.0f);
|
|
MR::validateHitSensor(this, "binder");
|
|
setNerve(&NrvPoihana::PoihanaNrvWait::sInstance);
|
|
}
|
|
}
|
|
|
|
void Poihana::exeSearch() {
|
|
if (MR::isFirstStep(this)) {
|
|
MR::startBck(this, "Search", nullptr);
|
|
MR::startSound(this, "SE_EV_POIHANA_FIND", -1, -1);
|
|
}
|
|
|
|
MR::turnDirectionToTargetUseGroundNormalDegree(this, &mFrontVec, *MR::getPlayerPos(), 4.0f);
|
|
|
|
if (MR::isBckStopped(this)) {
|
|
setNerve(&NrvPoihana::PoihanaNrvChasePlayer::sInstance);
|
|
}
|
|
}
|
|
|
|
void Poihana::exeChasePlayer() {
|
|
if (MR::isFirstStep(this)) {
|
|
MR::startBck(this, "Run", nullptr);
|
|
MR::setSensorOffset(this, "binder", sNormalBinderPos);
|
|
MR::setSensorRadius(this, "binder", 125.0f);
|
|
MR::validateHitSensor(this, "binder");
|
|
}
|
|
|
|
MR::turnDirectionToTargetUseGroundNormalDegree(this, &mFrontVec, *MR::getPlayerPos(), 4.0f);
|
|
JMAVECScaleAdd(mFrontVec.toCVec(), mVelocity.toCVec(), mVelocity.toVec(), 0.5f);
|
|
|
|
if (isNeedForBackHome()) {
|
|
setNerve(&NrvPoihana::PoihanaNrvGoBack::sInstance);
|
|
}
|
|
}
|
|
|
|
void Poihana::exeShootUpCharge() {
|
|
if (MR::isFirstStep(this)) {
|
|
MR::startBck(this, "ThrowStart", nullptr);
|
|
}
|
|
|
|
TVec3f& gravity = mGravity;
|
|
f32 dot = gravity.dot(mVelocity);
|
|
mVelocity.scale(dot, gravity);
|
|
|
|
if (MR::isBckStopped(this)) {
|
|
setNerve(&NrvPoihana::PoihanaNrvShootUp::sInstance);
|
|
}
|
|
}
|
|
|
|
/*void Poihana::exeShootUp() {
|
|
if (MR::isFirstStep(this)) {
|
|
MR::startBck(this, "Throw", nullptr);
|
|
MR::startSound(this, "SE_EV_POIHANA_SHOOT_UP", -1, -1);
|
|
MR::startActorCameraNoTarget(this, mCamInfo, -1);
|
|
}
|
|
|
|
f32 dot = mGravity.dot(mVelocity);
|
|
mVelocity.scale(dot, mGravity);
|
|
|
|
if (MR::isStep(this, 2)) {
|
|
endBind();
|
|
MR::invalidateHitSensor(this, "binder");
|
|
}
|
|
|
|
if (MR::isStep(this, 30)) {
|
|
MR::setSensorOffset(this, "binder", sNormalBinderPos);
|
|
MR::setSensorRadius(this, "binder", 125.0f);
|
|
MR::validateHitSensor(this, "binder");
|
|
}
|
|
|
|
if (MR::isBckStopped(this)) {
|
|
if (mBehavior == POIHANA_BEHAVIOR_NEW_HOME) {
|
|
mHomePos.set(mPosition);
|
|
setNerve(&NrvPoihana::PoihanaNrvWalkAround::sInstance);
|
|
}
|
|
else {
|
|
setNerve(&NrvPoihana::PoihanaNrvGoBack::sInstance);
|
|
}
|
|
}
|
|
}*/
|
|
|
|
void Poihana::endShootUp() {
|
|
endBind();
|
|
|
|
MR::setSensorOffset(this, "binder", sNormalBinderPos);
|
|
MR::setSensorRadius(this, "binder", 125.0f);
|
|
MR::validateHitSensor(this, "binder");
|
|
|
|
MR::endActorCamera(this, mCamInfo, true, -1);
|
|
}
|
|
|
|
void Poihana::exeGoBack() {
|
|
if (MR::isFirstStep(this) && !MR::isBckPlaying(this, "Walk")) {
|
|
MR::startBck(this, "Walk", nullptr);
|
|
}
|
|
|
|
MR::turnDirectionToTargetUseGroundNormalDegree(this, &mFrontVec, mHomePos, 2.0f);
|
|
JMAVECScaleAdd(mFrontVec.toCVec(), mVelocity.toCVec(), mVelocity.toVec(), 0.5f);
|
|
|
|
if (MR::isNearPlayer(this, 800.0f) && MR::isGreaterStep(this, 120)) {
|
|
setNerve(&NrvPoihana::PoihanaNrvSearch::sInstance);
|
|
}
|
|
else if (MR::isNear(this, mHomePos, 100.0f)) {
|
|
setNerve(&NrvPoihana::PoihanaNrvWait::sInstance);
|
|
}
|
|
}
|
|
|
|
void Poihana::exeShock() {
|
|
if (MR::isFirstStep(this)) {
|
|
MR::startBck(this, "PunchDamage", nullptr);
|
|
MR::startBlowHitSound(this);
|
|
MR::invalidateHitSensor(this, "binder");
|
|
}
|
|
|
|
if (MR::isOnGround(this) && MR::isGreaterStep(this, 12)) {
|
|
setNerve(&NrvPoihana::PoihanaNrvSwoon::sInstance);
|
|
}
|
|
}
|
|
|
|
void Poihana::exeSwoonLand() {
|
|
if (MR::isFirstStep(this)) {
|
|
MR::startBckNoInterpole(this, "SwoonLand");
|
|
MR::startSound(this, "SE_EV_POIHANA_SWOON", -1, -1);
|
|
}
|
|
|
|
if (MR::isBckStopped(this)) {
|
|
setNerve(&NrvPoihana::PoihanaNrvSwoon::sInstance);
|
|
}
|
|
}
|
|
|
|
void Poihana::exeSwoon() {
|
|
if (MR::isFirstStep(this)) {
|
|
MR::startBck(this, "Swoon", nullptr);
|
|
}
|
|
|
|
MR::startLevelSound(this, "SE_EM_LV_SWOON_S", -1, -1, -1);
|
|
|
|
if (MR::isStep(this, 110)) {
|
|
setNerve(&NrvPoihana::PoihanaNrvRecover::sInstance);
|
|
}
|
|
}
|
|
|
|
void Poihana::exeRecover() {
|
|
if (MR::isFirstStep(this)) {
|
|
MR::startBck(this, "Recover", nullptr);
|
|
MR::startSound(this, "SE_EM_POIHANA_RECOVER", -1, -1);
|
|
MR::startSound(this, "SE_EV_POIHANA_RECOVER", -1, -1);
|
|
mScale.setInline(1.0f);
|
|
}
|
|
|
|
if (MR::isBckStopped(this)) {
|
|
MR::setSensorOffset(this, "binder", sNormalBinderPos);
|
|
MR::setSensorRadius(this, "binder", 125.0f);
|
|
MR::validateHitSensor(this, "binder");
|
|
setNerve(&NrvPoihana::PoihanaNrvWait::sInstance);
|
|
}
|
|
}
|
|
|
|
// Needs to be reviewed
|
|
void Poihana::exeShake() {
|
|
f32 _f31 = 0.2f - 0.01f * getNerveStep();
|
|
f32 scale = MR::sinDegree(getNerveStep() * 0.01745329251f) * _f31 + 36.0f;
|
|
mScale.setInline(scale);
|
|
|
|
if (MR::isStep(this, 20)) {
|
|
mScale.setInline(1.0f);
|
|
setNerve(&NrvPoihana::PoihanaNrvSearch::sInstance);
|
|
}
|
|
}
|
|
|
|
void Poihana::exeDrown() {
|
|
if (MR::isFirstStep(this)) {
|
|
MR::startBck(this, "Drown", nullptr);
|
|
MR::invalidateClipping(this);
|
|
MR::invalidateHitSensors(this);
|
|
MR::startSound(this, "SE_EM_FALL_INTO_WATER_S", -1, -1);
|
|
|
|
mWaterColumn->appear();
|
|
MR::tryStartAllAnim(mWaterColumn, "Splash");
|
|
}
|
|
|
|
if (MR::isBckStopped(this)) {
|
|
setNerve(&NrvPoihana::PoihanaNrvHide::sInstance);
|
|
}
|
|
}
|
|
|
|
void Poihana::exeHide() {
|
|
if (MR::isFirstStep(this)) {
|
|
MR::startSound(this, "SE_EM_EXPLODE_S_WATER", -1, -1);
|
|
MR::emitEffect(this, "DeathWater");
|
|
MR::hideModel(this);
|
|
}
|
|
|
|
if (MR::isStep(this, 150)) {
|
|
setNerve(&NrvPoihana::PoihanaNrvAppear::sInstance);
|
|
}
|
|
}
|
|
|
|
void Poihana::exeAppear() {
|
|
if (MR::isFirstStep(this)) {
|
|
MR::resetPosition(this, mRespawnPos);
|
|
MR::emitEffect(this, "Appear");
|
|
}
|
|
|
|
if (MR::isLessStep(this, 60)) {
|
|
MR::startLevelSound(this, "SE_EM_LV_POIHANA_REVIVE_EFFECT", -1, -1, -1);
|
|
}
|
|
|
|
if (MR::isStep(this, 60)) {
|
|
MR::showModel(this);
|
|
MR::startBck(this, "Appear", nullptr);
|
|
MR::startSound(this, "SE_EM_POIHANA_REVIVE_APPEAR", -1, -1);
|
|
}
|
|
else if (MR::isBckStopped(this) && MR::isGreaterStep(this, 60)) {
|
|
MR::validateClipping(this);
|
|
MR::validateHitSensors(this);
|
|
MR::setSensorOffset(this, "binder", sNormalBinderPos);
|
|
MR::setSensorRadius(this, "binder", 125.0f);
|
|
MR::validateHitSensor(this, "binder");
|
|
setNerve(&NrvPoihana::PoihanaNrvWait::sInstance);
|
|
}
|
|
}
|
|
|
|
void Poihana::exeDPDSwoon() {
|
|
if (MR::isFirstStep(this)) {
|
|
MR::invalidateHitSensor(this, "Binder");
|
|
}
|
|
|
|
MR::updateActorStateAndNextNerve(this, (ActorStateBaseInterface*)mBindStarPointer,
|
|
&NrvPoihana::PoihanaNrvWait::sInstance);
|
|
}
|
|
|
|
void Poihana::endDPDSwoon() {
|
|
mBindStarPointer->kill();
|
|
|
|
MR::setSensorOffset(this, "binder", sNormalBinderPos);
|
|
MR::setSensorRadius(this, "binder", 125.0f);
|
|
MR::validateHitSensor(this, "binder");
|
|
}
|
|
|
|
bool Poihana::tryToStartBind(HitSensor* pSender) {
|
|
if (mBindedActor != nullptr) {
|
|
return false;
|
|
}
|
|
|
|
LiveActor *bindedActor = pSender->mActor;
|
|
if (MR::isInWater(bindedActor, TVec3f(0.0f, 0.0f, 0.0f))) {
|
|
return false;
|
|
}
|
|
|
|
MR::tryRumblePadMiddle(this, 0);
|
|
mBindedActor = pSender->mActor;
|
|
MR::startBckPlayer("Rise", (const char *)nullptr);
|
|
MR::invalidateClipping(this);
|
|
return true;
|
|
}
|
|
|
|
void Poihana::updateBindActorMtx() {
|
|
TPos3f binderMtx;
|
|
MR::makeMtxTR(binderMtx.toMtxPtr(), mBindedActor);
|
|
MR::setBaseTRMtx(mBindedActor, binderMtx);
|
|
}
|
|
|
|
void Poihana::endBind() {
|
|
MR::validateClipping(this);
|
|
|
|
if (MR::isPlayerInRush()) {
|
|
TVec3f jumpVec;
|
|
jumpVec.scale(-mLaunchIntensity, mGravity);
|
|
MR::endBindAndPlayerJump(this, jumpVec, 0);
|
|
MR::startSound(this, "SE_PM_HELI_JUMP", -1, -1);
|
|
}
|
|
|
|
mBindedActor = nullptr;
|
|
}
|
|
|
|
void Poihana::startBound() {
|
|
mBoundTimer = 0;
|
|
mScale.setInline(1.0f);
|
|
}
|
|
|
|
/*
|
|
* This function calculates Poihana's scale for 40 frames after being trampled. This is
|
|
* used to simulate the "vibrating" visual effect. This is not 1:1 the same as in SMG1,
|
|
* but it looks VERY similar and appears to be even more efficient compared to SMG1's
|
|
* unusually complicated calculations.
|
|
*/
|
|
/*void Poihana::calcBound() {
|
|
if (mBoundTimer != -1) {
|
|
f32 scale = 1.0f;
|
|
|
|
if (mBoundTimer < 40) {
|
|
scale = 0.05f * sin(0.393f * mBoundTimer) + 1.0f;
|
|
mBoundTimer++;
|
|
}
|
|
else {
|
|
mBoundTimer = -1;
|
|
}
|
|
|
|
mScale.setAll(scale);
|
|
}
|
|
}*/
|
|
|
|
void Poihana::contactMario(HitSensor *pSender, HitSensor *pReceiver) {
|
|
bool isShooting;
|
|
|
|
if (!isNerveTypeWalkOrWait()) {
|
|
isShooting = false;
|
|
if (isNerve(&NrvPoihana::PoihanaNrvShootUpCharge::sInstance) ||
|
|
isNerve(&NrvPoihana::PoihanaNrvShootUp::sInstance)) {
|
|
isShooting = true;
|
|
}
|
|
} else {
|
|
goto doFlip;
|
|
}
|
|
|
|
if (isShooting) {
|
|
doFlip:
|
|
if (!isBackAttack(pReceiver)) {
|
|
return;
|
|
}
|
|
|
|
if (MR::sendMsgEnemyAttackFlipWeak(pReceiver, pSender)) {
|
|
setNerve(&NrvPoihana::PoihanaNrvShake::sInstance);
|
|
}
|
|
} else if (isNerve(&NrvPoihana::PoihanaNrvSleep::sInstance)) {
|
|
setNerve(&NrvPoihana::PoihanaNrvGetUp::sInstance);
|
|
}
|
|
}
|
|
|
|
// Needs review
|
|
/*void Poihana::controlVelocity() {
|
|
if (!mIsActive) {
|
|
return;
|
|
}
|
|
|
|
// Calculate front vector
|
|
TVec3f gravity;
|
|
if (MR::isBindedGround(this)) {
|
|
gravity.set(-*MR::getGroundNormal(this));
|
|
}
|
|
else {
|
|
gravity.set(mGravity);
|
|
}
|
|
|
|
TVec3f frontVec(mFrontVec);
|
|
MR::turnVecToPlane(&mFrontVec, frontVec, gravity);
|
|
|
|
// Calculate velocity
|
|
if (MR::isBindedGround(this)) {
|
|
f32 dot = mFrontVec.dot(mVelocity) * -1.0f;
|
|
|
|
TVec3f addVel;
|
|
JMAVECScaleAdd(mFrontVec.toCVec(), mVelocity.toCVec(), addVel.toVec(), dot);
|
|
addVel.scale(0.8f);
|
|
|
|
mVelocity.scale(mFrontVec.dot(mVelocity), mFrontVec);
|
|
mVelocity.add(addVel);
|
|
|
|
if (mVelocity.dot(gravity) > 0.0f) {
|
|
dot = gravity.dot(mVelocity) * -1.0f;
|
|
JMAVECScaleAdd(gravity.toCVec(), mVelocity.toCVec(), mVelocity.toVec(), dot);
|
|
}
|
|
|
|
mVelocity.scale(0.95f);
|
|
}
|
|
|
|
JMAVECScaleAdd(gravity.toCVec(), mVelocity.toCVec(), mVelocity.toVec(), 2.0f);
|
|
|
|
if (!isNerve(&NrvPoihana::PoihanaNrvShock::sInstance)) {
|
|
f32 magVel = isNerve(&NrvPoihana::PoihanaNrvChasePlayer::sInstance) ? 10.0f : 5.0f;
|
|
|
|
if (PSVECMag((Vec *)&mVelocity) > magVel) {
|
|
f32 squared = mVelocity.squared();
|
|
|
|
if (squared > 0.0000038146973f) {
|
|
mVelocity.scale(JGeometry::TUtil<f32>::inv_sqrt(squared));
|
|
}
|
|
}
|
|
|
|
if (MR::isNearZero(mVelocity, 0.001f)) {
|
|
mVelocity.zero();
|
|
}
|
|
}
|
|
}*/
|
|
|
|
void Poihana::calcMyGravity() {
|
|
if (!mIsActive) {
|
|
return;
|
|
}
|
|
|
|
TVec3f upVec, gravityPos;
|
|
MR::calcUpVec(&upVec, this);
|
|
JMAVECScaleAdd(upVec.toCVec(), mPosition.toCVec(), gravityPos.toVec(), 20.0f);
|
|
MR::calcGravity(this, gravityPos);
|
|
}
|
|
|
|
bool Poihana::tryNonActive() {
|
|
if (!MR::isStageStateScenarioOpeningCamera()) {
|
|
bool notNear = !MR::isNearPlayerAnyTime(this, 3500.0f);
|
|
if (notNear && MR::isBindedGround(this)) {
|
|
setNerve(&NrvPoihana::PoihanaNrvNonActive::sInstance);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Poihana::tryDrown() {
|
|
if (!mCanDrown) {
|
|
return false;
|
|
}
|
|
|
|
if (isNerve(&NrvPoihana::PoihanaNrvAppear::sInstance)) {
|
|
return false;
|
|
}
|
|
|
|
if (isNerve(&NrvPoihana::PoihanaNrvDrown::sInstance)) {
|
|
return false;
|
|
}
|
|
|
|
if (!MR::isInWater(this, TVec3f(0.0f, 0.0f, 0.0f))) {
|
|
return false;
|
|
}
|
|
|
|
setNerve(&NrvPoihana::PoihanaNrvDrown::sInstance);
|
|
return true;
|
|
}
|
|
|
|
bool Poihana::tryDPDSwoon() {
|
|
if (!isNerveTypeWalkOrWait()) {
|
|
return false;
|
|
}
|
|
|
|
if (!mBindStarPointer->tryStartPointBind()) {
|
|
return false;
|
|
}
|
|
|
|
setNerve(&NrvPoihana::PoihanaNrvDPDSwoon::sInstance);
|
|
return true;
|
|
}
|
|
|
|
bool Poihana::tryShock() {
|
|
bool ret = false;
|
|
|
|
if (isNerve(&NrvPoihana::PoihanaNrvDrown::sInstance) ||
|
|
isNerve(&NrvPoihana::PoihanaNrvHide::sInstance) ||
|
|
isNerve(&NrvPoihana::PoihanaNrvAppear::sInstance)) {
|
|
ret = true;
|
|
}
|
|
|
|
if (ret) {
|
|
return false;
|
|
}
|
|
|
|
if (isNerve(&NrvPoihana::PoihanaNrvShock::sInstance)) {
|
|
return false;
|
|
}
|
|
|
|
if (isNerve(&NrvPoihana::PoihanaNrvSwoonLand::sInstance)) {
|
|
return false;
|
|
}
|
|
|
|
mVelocity.scale(-30.0f, mGravity);
|
|
setNerve(&NrvPoihana::PoihanaNrvShock::sInstance);
|
|
return true;
|
|
}
|
|
|
|
bool Poihana::tryHipDropShock() {
|
|
bool isNotNear = !MR::isNearPlayerAnyTime(this, 500.0f);
|
|
|
|
if (isNotNear) {
|
|
return false;
|
|
}
|
|
|
|
if (MR::isPlayerHipDropLand()) {
|
|
return tryShock();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Poihana::isNerveTypeWalkOrWait() const {
|
|
return (isNerve(&NrvPoihana::PoihanaNrvWait::sInstance) ||
|
|
isNerve(&NrvPoihana::PoihanaNrvSearch::sInstance) ||
|
|
isNerve(&NrvPoihana::PoihanaNrvWalkAround::sInstance) ||
|
|
isNerve(&NrvPoihana::PoihanaNrvChasePlayer::sInstance) ||
|
|
isNerve(&NrvPoihana::PoihanaNrvGoBack::sInstance));
|
|
}
|
|
|
|
bool Poihana::isNeedForBackHome() const {
|
|
if (isNerve(&NrvPoihana::PoihanaNrvWalkAround::sInstance)) {
|
|
return !MR::isNear(this, mHomePos, 350.0f);
|
|
}
|
|
|
|
if (!isNerve(&NrvPoihana::PoihanaNrvChasePlayer::sInstance)) {
|
|
bool ret;
|
|
if (mBehavior == POIHANA_BEHAVIOR_NEW_HOME) {
|
|
ret = !MR::isNearPlayer(this, 1100.0f);
|
|
} else {
|
|
ret = false;
|
|
if (!MR::isNear(this, mHomePos, 2000.0f) || !MR::isNearPlayer(this, 1110.0f)) {
|
|
ret = true;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Poihana::isNeedForGetUp() const {
|
|
bool ret = false;
|
|
|
|
if (MR::isNearPlayer(this, 500.0f)) {
|
|
bool flag = true;
|
|
|
|
f32 mag = PSVECMag((Vec *)MR::getPlayerVelocity());
|
|
|
|
if (!(mag >= 10.0f) && !MR::isPlayerSwingAction()) {
|
|
flag = false;
|
|
}
|
|
|
|
if (flag) {
|
|
ret = true;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
bool Poihana::isBackAttack(HitSensor *pMySensor) const {
|
|
TVec3f frontVec;
|
|
MR::calcFrontVec(&frontVec, this);
|
|
JGeometry::negateInternal((f32 *)&frontVec, (f32 *)&frontVec);
|
|
|
|
TVec3f sensorRelative(pMySensor->mPosition - mPosition);
|
|
return sensorRelative.dot(frontVec) > 0.0f;
|
|
}
|
|
|
|
namespace NrvPoihana {
|
|
INIT_NERVE(PoihanaNrvNonActive);
|
|
INIT_NERVE(PoihanaNrvWait);
|
|
INIT_NERVE(PoihanaNrvWalkAround);
|
|
INIT_NERVE(PoihanaNrvSleepStart);
|
|
INIT_NERVE(PoihanaNrvSleep);
|
|
INIT_NERVE(PoihanaNrvGetUp);
|
|
INIT_NERVE(PoihanaNrvSearch);
|
|
INIT_NERVE(PoihanaNrvChasePlayer);
|
|
INIT_NERVE(PoihanaNrvShootUpCharge);
|
|
INIT_NERVE(PoihanaNrvShootUp);
|
|
INIT_NERVE(PoihanaNrvGoBack);
|
|
INIT_NERVE(PoihanaNrvShock);
|
|
INIT_NERVE(PoihanaNrvSwoon);
|
|
INIT_NERVE(PoihanaNrvSwoonLand);
|
|
INIT_NERVE(PoihanaNrvRecover);
|
|
INIT_NERVE(PoihanaNrvShake);
|
|
INIT_NERVE(PoihanaNrvDrown);
|
|
INIT_NERVE(PoihanaNrvHide);
|
|
INIT_NERVE(PoihanaNrvAppear);
|
|
INIT_NERVE(PoihanaNrvDPDSwoon);
|
|
|
|
void PoihanaNrvNonActive::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeNonActive();
|
|
}
|
|
|
|
void PoihanaNrvNonActive::executeOnEnd(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->endNonActive();
|
|
}
|
|
|
|
void PoihanaNrvWait::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeWait();
|
|
}
|
|
|
|
void PoihanaNrvWalkAround::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeWalkAround();
|
|
}
|
|
|
|
void PoihanaNrvSleepStart::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeSleepStart();
|
|
}
|
|
|
|
void PoihanaNrvSleep::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeSleep();
|
|
}
|
|
|
|
void PoihanaNrvGetUp::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeGetUp();
|
|
}
|
|
|
|
void PoihanaNrvSearch::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeSearch();
|
|
}
|
|
|
|
void PoihanaNrvChasePlayer::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeChasePlayer();
|
|
}
|
|
|
|
void PoihanaNrvShootUpCharge::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeShootUpCharge();
|
|
}
|
|
|
|
void PoihanaNrvShootUp::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeShootUp();
|
|
}
|
|
|
|
void PoihanaNrvShootUp::executeOnEnd(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->endShootUp();
|
|
}
|
|
|
|
void PoihanaNrvGoBack::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeGoBack();
|
|
}
|
|
|
|
void PoihanaNrvShock::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeShock();
|
|
}
|
|
|
|
void PoihanaNrvSwoon::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeSwoon();
|
|
}
|
|
|
|
void PoihanaNrvSwoonLand::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeSwoonLand();
|
|
}
|
|
|
|
void PoihanaNrvRecover::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeRecover();
|
|
}
|
|
|
|
void PoihanaNrvShake::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeShake();
|
|
}
|
|
|
|
void PoihanaNrvDrown::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeDrown();
|
|
}
|
|
|
|
void PoihanaNrvHide::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeHide();
|
|
}
|
|
|
|
void PoihanaNrvAppear::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeAppear();
|
|
}
|
|
|
|
void PoihanaNrvDPDSwoon::execute(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->exeDPDSwoon();
|
|
}
|
|
|
|
void PoihanaNrvDPDSwoon::executeOnEnd(Spine *pSpine) const {
|
|
Poihana *pActor = (Poihana*)pSpine->mExecutor;
|
|
pActor->endDPDSwoon();
|
|
}
|
|
}
|