diff --git a/.gitignore b/.gitignore index 9bc17b380..460106770 100644 --- a/.gitignore +++ b/.gitignore @@ -37,3 +37,6 @@ compile_commands.json /*.txt *.exe tools/mwcc_compiler + +# Apple +.DS_Store diff --git a/include/Enemy/BossWanwan.hpp b/include/Enemy/BossWanwan.hpp index 7bd1f1d4b..8a2e3a47f 100644 --- a/include/Enemy/BossWanwan.hpp +++ b/include/Enemy/BossWanwan.hpp @@ -2,8 +2,250 @@ #define ENEMY_BOSS_WANWAN_HPP #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -class TLiveActor; +class TBossWanwan; +class TBWParams; +class TBWLeash; +class TBWLeashNode; +class TRope; +class TBossWanwanMtxCalc; + +class TBWHit : public THitActor { +public: + TBWHit(TBossWanwan* owner, int jointIndex, + const char* name = "ボスワンワンヒット"); + virtual void perform(u32 cue, JDrama::TGraphics* graphics); + virtual BOOL receiveMessage(THitActor* sender, u32 message); + +private: + /* 0x68 */ TBossWanwan* mOwner; + /* 0x6C */ s32 mJointIndex; +}; + +class TBWPicket : public TTakeActor { +public: + TBWPicket(TBossWanwan* owner, const char* name = "ボスワンワン杭"); + virtual void perform(u32 cue, JDrama::TGraphics* graphics); + virtual BOOL receiveMessage(THitActor* sender, u32 message); + virtual MtxPtr getTakingMtx(); + virtual BOOL moveRequest(const JGeometry::TVec3&); + +private: + /* 0x70 */ TBossWanwan* mOwner; + /* 0x74 */ TMtx34f unk74; + /* 0xA8 */ MActor* mActor; +}; + +class TBWBinder : public TBinder { +public: + TBWBinder(); + virtual ~TBWBinder() { }; + virtual void bind(TLiveActor*); +}; + +class TBWLeash : public JDrama::TViewObj { +public: + TBWLeash(TBossWanwan* owner, int nodeCount, + const char* name = "ボスワンワンリーシュ"); + virtual ~TBWLeash() { }; + void perform(u32 cue, JDrama::TGraphics* graphics); + TBWLeashNode* getNode(s32 idx) const { return mNodes[idx]; } // fabricated + + // UNUSED + void pullTail(const JGeometry::TVec3& pos); + void invalidateAllCollision(); + + /* 0x10 */ TBossWanwan* mOwner; + /* 0x14 */ TRope* mRope; + /* 0x18 */ TBWLeashNode** mNodes; +}; + +class TBWLeashNode : public THitActor { +public: + TBWLeashNode(TBWLeash*, int, const char*); + + virtual void perform(u32 cue, JDrama::TGraphics* graphics); + void calcMatrix(); + void calcTemperature(); + + TBWLeash* mLeash; // +0x68 + MActor* mMActor; // +0x6C + f32 mTemperature; // +0x70 + int mIndex; // +0x74 +}; + +class TBWParams : public TSpineEnemyParams { +public: + /* * TSpineEnemyParams ends at 0xA8. * + * Every TParamRT occupies 0x14 bytes: + * * +0x00 : TParam / base data + * * +0x04 : ... + * * +0x0C : ... + * * +0x10 : actual parameter value + * * * Therefore: * parameter start = X * parameter value = X + 0x10 */ + TBWParams(const char*); + + /* 0x0A8 */ TParamRT mSLMarchSpeed; + /* 0x0BC */ TParamRT mSLTurnSpeed; + /* 0x0D0 */ TParamRT mSLLeashNodeLen; + /* 0x0E4 */ TParamRT mSLPicketHeight; + /* 0x0F8 */ TParamRT mSLPicketRadius; + /* 0x10C */ TParamRT mSLChainHitHeight; + /* 0x120 */ TParamRT mSLChainHitRadius; + /* 0x134 */ TParamRT mSLChainGroundRadius; + /* 0x148 */ TParamRT mSLPullLimit; + /* 0x15C */ TParamRT mSLAttackSpeed; + /* 0x170 */ TParamRT mSLStunTimer; + /* 0x184 */ TParamRT mSLSearchLength; + /* 0x198 */ TParamRT mSLSearchAngle; + /* 0x1AC */ TParamRT mSLBWHitPointMax; + /* 0x1C0 */ TParamRT mSLHeadGap; + /* 0x1D4 */ TParamRT mSLShakeLengthMax; + /* 0x1E8 */ TParamRT mSLShakeLengthMaxHP0; +}; + +class TBossWanwanMtxCalc : public M3UMtxCalcSIAnmBlendQuat { +public: + TBossWanwanMtxCalc(TBossWanwan*); + virtual ~TBossWanwanMtxCalc() { }; + void calc(u16); + + void joinAnm(int i); + +public: + /* 0x64 */ TBossWanwan* mOwner; +}; + +// TBossWanwan size: 0x1b8 +class TBossWanwan : public TSpineEnemy { +public: + TBossWanwan(const char* name = "ボスワンワン"); + + virtual void kill() { }; + virtual void init(TLiveManager*); + void shakeCamera(int shakeType); + virtual BOOL receiveMessage(THitActor* sender, u32 message); + virtual void calcRootMatrix(); + void slideToCurPathNode(float, float); + virtual void control(); + void emitEffects(); + virtual void perform(u32 cue, JDrama::TGraphics* graphics); + + f32 getUnk168() const { return unk168; } + + // fabricated + TBWParams* getSaveParam() const { return (TBWParams*)getSaveParam(); } + + // fabricated + f32 getHeadHeight() const { return mHeadHeight * mBodyScale; } + + // UNUSED + BOOL isHeadPulled(); + bool isMarioInSight(); + bool isTailBurning(); + bool isBurning(); + void startGoldBrk(); + void releasePicket(); + void takeBath(); + void rollNextGraphNode(); + void reverseNextGraphNode(); + void showMessage(u32 msg); + void changeBck(int); + + // fabricated + const TNerveBase* getLatestNerve() + { + return mSpine->getLatestNerve(); + } + + // fabricated + void setBckFromIndex(int idx) + { + mMtxCalc->joinAnm(idx); + getMActor()->getAnmBck()->setFrameCtrl(idx); + } + + // fabricated + static inline void setRotDirY(JGeometry::TVec3& dir, f32 rot, + f32 scale) + { + u16 angle = (int)(rot * 182.04445f); + dir.set(scale * JMASSin(angle), 0.0f, scale * JMASCos(angle)); + } + + // fabricated + JGeometry::TVec3 getRotDirY(f32 rot) const + { + JGeometry::TVec3 dir; + setRotDirY(dir, rot, 1.0f); + return dir; + } + + enum { + BOSS_WANWAN_FLAGS_UNK1 = 0xE001A, + BOSS_WANWAN_FLAGS_UNK2 = 0xE001B, + BOSS_WANWAN_FLAGS_UNK3 = 0xE001C, + BOSS_WANWAN_FLAGS_UNK4 = 0xE001D, + }; + + /* 0x150 */ TBossWanwanMtxCalc* mMtxCalc; + /* 0x154 */ TBWLeash* mLeash; + /* 0x158 */ TBWPicket* mPicket; + /* 0x15C */ JGeometry::TVec3 mPicketPullDelta; + /* 0x168 */ f32 unk168; + /* 0x16C */ s32 unk16C; + /* 0x170 */ TBWHit* mHits[2]; + /* 0x178 */ f32 unk178; + /* 0x17C */ BOOL unk17C; + /* 0x180 */ u32 unk180; + /* 0x184 */ u32 unk184; + /* 0x188 */ BOOL unk188; + /* 0x18C */ s8 msInvincible; + /* 0x18D */ u8 unk18D; + /* 0x190 */ s32 unk190; + /* 0x194 */ s8 unk194; + /* 0x195 */ u8 mHasSpawnedWater; + /* 0x198 */ u32 unk198; + /* 0x19C */ u32 unk19C; + /* 0x1A0 */ u8 unk1A0; + /* 0x1A4 */ JGeometry::TVec3 unk1A4; + /* 0x1A8 */ s8 mWaterHitCount; + /* 0x1AC */ f32 unk1AC; + /* 0x1B0 */ TBWParams* mParams; + /* 0x1B4 */ u16 unk1B4; +}; + +class TBossWanwanManager : public TEnemyManager { +public: + TBossWanwanManager(const char* name = "ボスワンワンマネージャ"); + + virtual void load(JSUMemoryInputStream&); + virtual TSpineEnemy* createEnemyInstance(); + virtual void createModelData(); + void initJParticle(); +}; DECLARE_NERVE(TNerveBWGraphWander, TLiveActor); DECLARE_NERVE(TNerveBWRoll, TLiveActor); diff --git a/include/M3DUtil/MActorAnm.hpp b/include/M3DUtil/MActorAnm.hpp index e1ea3cdc4..5b0c906e0 100644 --- a/include/M3DUtil/MActorAnm.hpp +++ b/include/M3DUtil/MActorAnm.hpp @@ -318,6 +318,9 @@ public: mMtxCalcType = MActorMtxCalcType_User; } + // fabricated for bosswanwan.cpp + void setCalcType(u8 type) { mMtxCalcType = type; } + void setJointIndex(u16 value) { mJointIndex = value; } private: diff --git a/include/MarioUtil/MtxUtil.hpp b/include/MarioUtil/MtxUtil.hpp index 9438d1395..7cb88e359 100644 --- a/include/MarioUtil/MtxUtil.hpp +++ b/include/MarioUtil/MtxUtil.hpp @@ -179,12 +179,14 @@ public: void moveHeadAndTail(const JGeometry::TVec3&, const JGeometry::TVec3&); -private: /* 0x00 */ u16 mNumPoints; /* 0x04 */ TRopePoint* mPoints; /* 0x08 */ f32 unk8; /* 0x0C */ f32 unkC; /* 0x10 */ f32 unk10; + +public: + // Made attributes public to match bosswanwan.cpp }; void SMS_GetActorMtx(const THitActor&, MtxPtr); diff --git a/include/Strategic/ActorTypes.hpp b/include/Strategic/ActorTypes.hpp index 6cb78c2e2..32e1f2509 100644 --- a/include/Strategic/ActorTypes.hpp +++ b/include/Strategic/ActorTypes.hpp @@ -336,7 +336,7 @@ enum TActorType { ACTOR_TYPE_BOSS_GESSO_BEAK = HIT_CATEGORY_BOSS | 0x9, ACTOR_TYPE_BOSS_UNKB = HIT_CATEGORY_BOSS | 0xB, ACTOR_TYPE_BOSS_UNKC = HIT_CATEGORY_BOSS | 0xC, - ACTOR_TYPE_BOSS_UNKD = HIT_CATEGORY_BOSS | 0xD, + ACTOR_TYPE_BOSS_WANWAN = HIT_CATEGORY_BOSS | 0xD, ACTOR_TYPE_BOSS_UNKE = HIT_CATEGORY_BOSS | 0xE, ACTOR_TYPE_BOSS_PAKKUN = HIT_CATEGORY_BOSS | 0xF, ACTOR_TYPE_BOSS_PAKKUN_ATTACK = HIT_CATEGORY_BOSS | 0x10, diff --git a/include/System/Particles.hpp b/include/System/Particles.hpp index d8d4b8566..995c542c6 100644 --- a/include/System/Particles.hpp +++ b/include/System/Particles.hpp @@ -169,6 +169,10 @@ enum E_SMS_EFFECT_ONETIME_NORMAL /* 0 */ { SCENE_BOSSPAKKUN_JPA_MS_BOPA_DOWN = 0xAA, SCENE_BOSSPAKKUN_JPA_MS_BOPA_SWING1 = 0xAB, SCENE_BOSSPAKKUN_JPA_MS_BOPA_SWING2 = 0xAC, + BWAN_JPA_JUMP_ROCK = 0xAD, + BWAN_JPA_JUMP_SMOKE = 0xAE, + BWAN_JPA_MS_HIBANA = 0xAF, + BWAN_JPA_MS_DOWNYUGE = 0xB0, PARTICLE_MS_SMB_KAFUN_OW = 0xB2, PARTICLE_MS_SMB_KAFUN_OA = 0xB3, PARTICLE_MS_SMB_AP_ROCK = 0xB6, @@ -330,6 +334,7 @@ enum E_SMS_EFFECT_LOOP_NORMAL /* 1 */ { SCENE_BOSSPAKKUN_JPA_MS_BOPA_TR_WEED = 0x164, PARTICLE_MS_GESO_KISEKI = 0x165, CANNON_JPA_MS_CANNON_SMOKE = 0x166, + BWANWAN_JPA_MS_BWAN_HITYUGE = 0x167, BWANWAN_JPA_MS_BWAN_KIRA = 0x168, PARTICLE_MS_BOHA_JOUKI2_R_A = 0x169, PARTICLE_MS_BOHA_JOUKI2_R_B = 0x16A, @@ -464,6 +469,7 @@ enum E_SMS_EFFECT_LOOP_INDIRECT /* 3 */ { PARTICLE_MS_GESOSURF_A = 0x1E7, PARTICLE_MS_M_WATSLIDE_A = 0x1EA, PARTICLE_MS_MOE_FIRE_C = 0x1ED, + BWAN_JPA_MS_YUGAMI = 0x1EE, }; // NOTE: doing this makes inlining work out, specializations DON'T. diff --git a/src/Enemy/bosseel.cpp b/src/Enemy/bosseel.cpp index 5e1847094..c1491b66e 100644 --- a/src/Enemy/bosseel.cpp +++ b/src/Enemy/bosseel.cpp @@ -828,9 +828,9 @@ BOOL TBossEelTooth::receiveMessage(THitActor* sender, u32 message) actor->setFrameRate(SMSGetAnmFrameRate(), ANM_TYPE_BCK); mOwner->mToothDamaged = true; mColor.a = static_cast( - static_cast(mHitPoints * 0xFF) - / static_cast( - mOwner->getBossEelParams().mSLToothMaxHitPoint.get())); + static_cast(mHitPoints * 0xFF) + / static_cast( + mOwner->getBossEelParams().mSLToothMaxHitPoint.get())); if (mToothType == 1 && mHitPoints % 20 == 1) mOwner->forceShedTears(mCanShedTears); @@ -1043,7 +1043,7 @@ void TBossEelVortex::perform(u32 cue, JDrama::TGraphics* graphics) f32 power = mOwner->mSaveParams->mSLBreathInPower.get(); f32 wave = fabsf(JMASSin( - static_cast(182.04445f * (0.9f * mTimer)))); + static_cast(182.04445f * (0.9f * mTimer)))); if (wave > 1.0f) wave = 1.0f; else if (wave < 0.1f) @@ -1187,15 +1187,13 @@ void TBossEelHeartCoin::perform(u32 cue, JDrama::TGraphics* graphics) u32 calcAnim = cue & CUE_CALC_ANIM; if (calcAnim) { - s32 jointIndex = mOwner->getMActor() - ->getModel() - ->getModelData() - ->getJointName() - ->getIndex("ha7"); - MtxPtr jointMtx - = mOwner->getMActor()->getModel()->getAnmMtx(jointIndex); - if (mOwner->getMActor()->checkCurBckFromIndex(3) - && mOwner->getMActor()->getFrameCtrl(ANM_TYPE_BCK)->getFrame() + s32 jointIndex = mOwner->mMActor->getModel() + ->getModelData() + ->getJointName() + ->getIndex("ha7"); + MtxPtr jointMtx = mOwner->mMActor->getModel()->getAnmMtx(jointIndex); + if (mOwner->mMActor->checkCurBckFromIndex(3) + && mOwner->mMActor->getFrameCtrl(ANM_TYPE_BCK)->getFrame() < 700.0f) { mPosition.set(jointMtx[0][3], jointMtx[1][3], jointMtx[2][3]); } @@ -1531,7 +1529,7 @@ void TBossEel::init(TLiveManager* manager) = { "headcol1", "headcol2", "sebone5", "sebone5" }; static const char* sCollisionFileTable[] = { "/bosseel/meoto_head", "/bosseel/meoto_head2", - "/bosseel/meoto_camera", "/bosseel/meoto_out_loop" }; + "/bosseel/meoto_camera", "/bosseel/meoto_out_loop" }; for (s32 i = 0; i < 4; ++i) { mMapCollisionJointIndices[i] diff --git a/src/Enemy/bosswanwan.cpp b/src/Enemy/bosswanwan.cpp index 8b1378917..4b1754f42 100644 --- a/src/Enemy/bosswanwan.cpp +++ b/src/Enemy/bosswanwan.cpp @@ -1 +1,1906 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// to match __sinit__ +#include +#include + +static const char* bwanwan_bastable[] = { + "/scene/bwanwan/bas/bwanwan_bark.bas", + nullptr, + "/scene/bwanwan/bas/bwanwan_shake.bas", + nullptr, + "/scene/bwanwan/bas/bwanwan_wait.bas", + "/scene/bwanwan/bas/bwanwan_wait2.bas", + nullptr, +}; + +static JGeometry::TVec3 BW_BATH_POS(-1000.0f, 4.5f, -6217.2f); +static JGeometry::TVec3 BW_PICKET_START(6012.84f, 0.0f, 7323.15f); +static JGeometry::TVec3 BW_HEAD_START(5741.72f, -100.0f, 6311.62f); + +TBWParams::TBWParams(const char* path) + : TSpineEnemyParams(path) + , PARAM_INIT(mSLMarchSpeed, 6.0f) + , PARAM_INIT(mSLShakeLengthMax, 3000.0f) + , PARAM_INIT(mSLShakeLengthMaxHP0, 2000.0f) + , PARAM_INIT(mSLTurnSpeed, 1.0f) + , PARAM_INIT(mSLLeashNodeLen, 120.0f) + , PARAM_INIT(mSLPicketHeight, 100.0f) + , PARAM_INIT(mSLPicketRadius, 100.0f) + , PARAM_INIT(mSLChainHitHeight, 100.0f) + , PARAM_INIT(mSLChainHitRadius, 100.0f) + , PARAM_INIT(mSLChainGroundRadius, 60.0f) + , PARAM_INIT(mSLPullLimit, 1.0f) + , PARAM_INIT(mSLAttackSpeed, 10.0f) + , PARAM_INIT(mSLStunTimer, 4000) + , PARAM_INIT(mSLSearchLength, 10000.0f) + , PARAM_INIT(mSLSearchAngle, 60.0f) + , PARAM_INIT(mSLBWHitPointMax, 255) + , PARAM_INIT(mSLHeadGap, 150.0f) +{ + TParams::load(mPrmPath); +} + +TBWLeashNode::TBWLeashNode(TBWLeash* leash, int index, const char* name) + : THitActor(name) + , mLeash(leash) + , mMActor(nullptr) + , mTemperature(0.0f) + , mIndex(index) +{ + TBossWanwan* owner = mLeash->mOwner; + mMActor = owner->getActorKeeper()->createMActor("bwanwan_chain.bmd", 0); + mMActor->setBrkFromIndex(2); + + f32 hitRadius = owner->getSaveParam()->mSLChainHitRadius.value; + f32 hitHeight = owner->getSaveParam()->mSLChainHitHeight.value; + initHitActor(0x800000c, 1, -0x80000000, hitRadius * 2.0f, hitHeight * 2.0f, + hitRadius, hitHeight); +} + +void TBWLeashNode::calcTemperature() +{ + if (mIndex == 0) { + return; + } + + f32 delta = mLeash->getNode(mIndex - 1)->mTemperature - mTemperature; + f32 step; + + if (delta < 0.0f) { + if (delta < 0.1f) { + step = 0.02f; + } else { + step = 0.005f; + } + } else { + if (delta > -0.1f) { + step = -0.02f; + } else { + step = -0.005f; + } + } + + this->mTemperature += step; + + if (this->mTemperature < 0.0f) { + this->mTemperature = 0.0f; + } + + if (this->mTemperature > 1.0f) { + this->mTemperature = 1.0f; + } +} + +void TBWLeashNode::calcMatrix() +{ + TRope* rope = mLeash->mRope; + TRopePoint* points = rope->mPoints; + MtxPtr mtx = mMActor->getModel()->getBaseTRMtx(); + + JGeometry::TVec3 pos = points[mIndex].unkC; + JGeometry::TVec3 dir; + + if (mIndex < rope->mNumPoints - 1) { + dir = points[mIndex + 1].unkC; + dir -= pos; + + } else { + dir = points[mIndex - 1].unkC; + dir -= pos; + dir.negate(); + } + + PSVECNormalize(&dir, &dir); + + JGeometry::TVec3 n(0.0f, 1.0f, 0.0f); + JGeometry::TVec3 side; + + side.cross2(n, dir); + PSVECNormalize(&side, &side); + + n.cross2(dir, side); + PSVECNormalize(&n, &n); + + mtx[0][2] = dir.x; + mtx[1][2] = dir.y; + mtx[2][2] = dir.z; + + if (mIndex & 1) { + mtx[0][0] = side.x; + mtx[1][0] = side.y; + mtx[2][0] = side.z; + mtx[0][1] = n.x; + mtx[1][1] = n.y; + mtx[2][1] = n.z; + } else { + mtx[0][0] = n.x; + mtx[1][0] = n.y; + mtx[2][0] = n.z; + mtx[0][1] = side.x; + mtx[1][1] = side.y; + mtx[2][1] = side.z; + } + + mtx[0][3] = pos.x; + mtx[1][3] = pos.y + 30.0f; + mtx[2][3] = pos.z; + + mPosition = pos; +} + +void TBWLeashNode::perform(u32 cue, JDrama::TGraphics* graphics) +{ + + if (cue & CUE_MOVE) { + calcTemperature(); + calcMatrix(); + if (this->mLeash->mOwner->mHitPoints != 0 && mIndex < 8) { + u32 i = 0; + for (int i = 0; i < mColCount; i++) { + THitActor* elem = mCollisions[i]; + if (elem->mActorType == ACTOR_TYPE_MARIO) { + elem->receiveMessage(this, HIT_MESSAGE_BURN); + } + } + } + } + + if ((cue & CUE_CALC_ANIM)) { + J3DFrameCtrl* frameCtrl = this->mMActor->getFrameCtrl(5); + if (frameCtrl) { + f32 animDuration = (f32)(frameCtrl->getEnd() - 1) * mTemperature; + + f32 progress; + if (mIndex < 5) { + progress = (f32)mIndex * 0.25f + + ((f32)mLeash->mOwner->mHitPoints + / (f32)mLeash->mOwner->getSaveParam() + ->mSLHitPointMax.get()); + } else if (mIndex < (this->mLeash->mRope->mNumPoints - 10)) { + progress = 1.0f; + } else { + progress = (f32)(this->mLeash->mRope->mNumPoints - mIndex) / 10; + } + + progress = MsClamp(progress, 0.0f, 1.0f); + frameCtrl->setFrame(animDuration * progress); + frameCtrl->setRate(0.0f); + } + } + + if (mIndex < this->mLeash->mRope->mNumPoints - 1) { + this->mMActor->perform(cue, graphics); + } +} + +TBWLeash::TBWLeash(TBossWanwan* owner, int nodeCount, const char* name) + : JDrama::TViewObj(name) + , mOwner(owner) + , mRope(nullptr) + , mNodes(nullptr) +{ + f32 nodeLen = mOwner->getSaveParam()->mSLLeashNodeLen.get(); + f32 groundRadius = mOwner->getSaveParam()->mSLChainGroundRadius.get(); + + mRope = new TRope(nodeCount, mOwner->mPosition, nodeLen, groundRadius, 0.7, + -2.0f); + + mNodes = new TBWLeashNode*[nodeCount]; + for (int i = 0; i < nodeCount; i++) { + mNodes[i] = new TBWLeashNode(this, i, "鎖部品"); + } + + TIdxGroupObj* group + = static_cast(JDrama::TNameRefGen::search("敵グループ")); + + for (int i = 0; i < nodeCount; i++) { + group->getChildren().push_back(mNodes[i]); + + if (i < (nodeCount - 2)) { + mNodes[i]->offHitFilter(HIT_FILTER_NO_COLLISION); + + } else { + mNodes[i]->onHitFilter(HIT_FILTER_NO_COLLISION); + } + + if (i < 2) { + mRope->mPoints[i].unk28 |= 0x1; + } else { + mRope->mPoints[i].unk28 &= -0x1; + } + } +} + +void TBWLeash::invalidateAllCollision() +{ + // TODO +} + +void TBWLeash::pullTail(const JGeometry::TVec3& pos) +{ + // TODO +} + +void TBWLeash::perform(u32 cue, JDrama::TGraphics* graphics) +{ + if (cue & CUE_MOVE) { + JGeometry::TVec3 headPos; + mOwner->getJointTransByIndex(5, &headPos); + mRope->moveHead(headPos); + mOwner->unk188 = 0; + + if (mOwner->unk17C != 0) { + TRope* rope = mRope; + const JGeometry::TVec3& picketPos = mOwner->mPicket->mPosition; + + JGeometry::TVec3 pullDelta = rope->mPoints[0].unkC; + rope->constraintTail(picketPos); + + pullDelta -= rope->mPoints[0].unkC; + pullDelta.negate(); + mOwner->mPicketPullDelta = pullDelta; + + JGeometry::TVec3 diff = rope->mPoints[0].unkC; + JGeometry::TVec3 centerPos = mOwner->mPosition; + centerPos.y += 500.0f; + diff -= centerPos; + + if (PSVECMag(&diff) > 650.0f) { + PSVECNormalize(&diff, &diff); + diff.scale(20.0f); + diff.y = 0.0f; + mOwner->mPosition.add(diff); + mOwner->unk188 = 1; + } + + f32 targetAngle + = MsWrap(MsGetRotFromZaxisY(diff) + 180.0f, 0.0f, 360.0f); + f32 angleDiff = MsAngleDiff(targetAngle, mOwner->mRotation.y); + + f32 turnStep; + if (angleDiff > 0.0f) { + f32 maxTurn = 1.5f * mOwner->mTurnSpeed; + turnStep = angleDiff > maxTurn ? maxTurn : angleDiff; + } else { + f32 minTurn = 1.5f * -mOwner->mTurnSpeed; + turnStep = angleDiff <= minTurn ? minTurn : angleDiff; + } + + mOwner->mRotation.y + = MsWrap(mOwner->mRotation.y + turnStep, 0.0f, 360.0f); + } + } + + if (cue & CUE_CALC_ANIM) { + JGeometry::TVec3 bodyPos; + mOwner->getJointTransByIndex(1, &bodyPos); + + for (int i = 0; i < 15; i++) { + TRopePoint* point = &mRope->mPoints[i]; + JGeometry::TVec3 pointDiff = point->unkC; + pointDiff -= bodyPos; + + f32 dist = PSVECMag(&pointDiff); + if (dist < 500.0f) { + pointDiff.scale(500.0f / dist); + pointDiff += bodyPos; + + point->unk18.zero(); + point->unkC = pointDiff; + point->unk0 = point->unkC; + } + } + } + + for (int i = 0; i < mRope->mNumPoints; i++) { + mNodes[i]->testPerform(cue, graphics); + } +} + +TBWPicket::TBWPicket(TBossWanwan* owner, const char* name) + : TTakeActor(name) + , mOwner(owner) + , mActor(nullptr) +{ + PSMTXIdentity(unk74); + + initHitActor(ACTOR_TYPE_BOSS_WANWAN, 1, HIT_CATEGORY_PLAYER, + ((TBWParams*)mOwner->getSaveParam())->mSLPicketRadius.get(), + ((TBWParams*)mOwner->getSaveParam())->mSLPicketHeight.get(), + ((TBWParams*)mOwner->getSaveParam())->mSLPicketRadius.get(), + ((TBWParams*)mOwner->getSaveParam())->mSLPicketHeight.get()); + + TIdxGroupObj* hitGroup = static_cast( + JDrama::TNameRefGen::search("ボスワンワンつかみ")); + if (hitGroup != nullptr) { + hitGroup->getChildren().push_back(this); + } + + offHitFilter(1); + mActor = mOwner->mMActorKeeper->createMActor("bwanwan_picket.bmd", 0); +} + +BOOL TBWPicket::receiveMessage(THitActor* sender, u32 message) +{ + if (sender->mActorType == 0x80000001) { + if (message == HIT_MESSAGE_HIP_DROP) { + TBossWanwan* owner = mOwner; + owner->unk17C = 1; + owner->unk184 = 0; + SMSGetMSound()->startSoundActor(MSD_SE_BS_WANWAN_LOCK, &mPosition, + 0, nullptr, 0, 4); + return TRUE; + } + if (message == HIT_MESSAGE_TAKE) { + TBossWanwan* owner = mOwner; + + owner->releasePicket(); + owner->unk194 = 0; + owner->unk17C = 0; + mHolder = (TTakeActor*)sender; + return TRUE; + } + if (message == HIT_MESSAGE_THROWN || message == HIT_MESSAGE_DETACH) { + mHolder = nullptr; + return TRUE; + } + } + + return FALSE; +} + +BOOL TBWPicket::moveRequest(const JGeometry::TVec3& pos) +{ + // TODO: getLatestNerve requires 2 levels of inlining to get emitted + // out-of-line, fabricated the getLatestNerve() + // function in TBossWanwan to temporarely avoid this issue. + if (mOwner->getLatestNerve() == &TNerveBWJumpToBath::theNerve() + || mOwner->getLatestNerve() == &TNerveBWDie::theNerve() + || mOwner->mHitPoints != 0) { + return FALSE; + } + + TBWLeash* leash = mOwner->mLeash; + TRope* rope = leash->mRope; + + JGeometry::TVec3 delta = rope->mPoints->unkC; + + rope->constraintTail(pos); + + delta.sub(rope->mPoints->unkC); + delta.negate(); + + leash->mOwner->mPicketPullDelta = delta; + + return TRUE; +} + +MtxPtr TBWPicket::getTakingMtx() { return unk74; } + +void TBWPicket::perform(u32 cue, JDrama::TGraphics* graphics) +{ + if (cue & CUE_MOVE) { + mPosition = mOwner->mLeash->mRope + ->mPoints[mOwner->mLeash->mRope->mNumPoints - 1] + .unkC; + + if (mHolder != nullptr) { + JGeometry::TVec3 side, up, dir; + + dir.set(mHolder->mPosition); + dir.sub(mOwner->mLeash->mRope->mPoints[0].unkC); + up.set(0.0f, 1.0f, 0.0f); + + side.cross2(up, dir); + PSVECNormalize(&side, &side); + up.cross2(dir, side); + PSVECNormalize(&up, &up); + dir.cross2(side, up); + PSVECNormalize(&dir, &dir); + + unk74.mMtx[0][0] = side.x; + unk74.mMtx[1][0] = side.y; + unk74.mMtx[2][0] = side.z; + unk74.mMtx[0][1] = up.x; + unk74.mMtx[1][1] = up.y; + unk74.mMtx[2][1] = up.z; + unk74.mMtx[0][2] = dir.x; + unk74.mMtx[1][2] = dir.y; + unk74.mMtx[2][2] = dir.z; + unk74.mMtx[0][3] = mPosition.x; + unk74.mMtx[1][3] = mPosition.y; + unk74.mMtx[2][3] = mPosition.z; + + mHolder->moveRequest(mPosition); + } + } + + if (cue & CUE_CALC_ANIM) { + MtxPtr mtx = mOwner->getModel()->getBaseTRMtx(); + PSMTXIdentity(mtx); + JGeometry::TVec3 side, up, dir; + + dir.x = mPosition.x; + dir.y = mPosition.y; + dir.z = mPosition.z; + dir.sub(mOwner->mLeash->mRope + ->mPoints[mOwner->mLeash->mRope->mNumPoints - 3] + .unkC); + + if (dir.squared() <= JGeometry::TUtil::epsilon()) { + dir.set(0.0f, 0.0f, 1.0f); + } + + up.set(0.0f, 1.0f, 0.0f); + side.cross2(up, dir); + PSVECNormalize(&side, &side); + up.cross2(dir, side); + PSVECNormalize(&up, &up); + dir.cross2(side, up); + PSVECNormalize(&dir, &dir); + + mtx[0][0] = side.x; + mtx[1][0] = side.y; + mtx[2][0] = side.z; + mtx[0][1] = up.x; + mtx[1][1] = up.y; + mtx[2][1] = up.z; + mtx[0][2] = dir.x; + mtx[1][2] = dir.y; + mtx[2][2] = dir.z; + mtx[0][3] = mPosition.x; + mtx[1][3] = mPosition.y; + mtx[2][3] = mPosition.z; + + if (mOwner->unk17C == 0) { + mtx[1][3] += 70.0f; + } + + if (isHolding()) { + mtx[0][3] -= dir.x * 50.0f; + mtx[2][3] -= dir.z * 50.0f; + } else { + mtx[0][3] -= dir.x * 60.0f; + mtx[2][3] -= dir.z * 60.0f; + } + } + + mActor->perform(cue, graphics); + THitActor::perform(cue, graphics); +} + +TBWHit::TBWHit(TBossWanwan* owner, int jointIndex, const char* name) + : THitActor(name) + , mOwner(owner) + , mJointIndex(jointIndex) +{ +} + +BOOL TBWHit::receiveMessage(THitActor* sender, u32 message) +{ + return mOwner->receiveMessage(sender, message); +} + +void TBWHit::perform(u32 cue, JDrama::TGraphics* graphics) +{ + // stack frame shenanigans + if (cue & CUE_MOVE) { + if (mJointIndex >= 0) { + mOwner->getJointTransByIndex(mJointIndex, &mPosition); + } + for (int i = 0; i < mColCount; i++) { + THitActor* elem = mCollisions[i]; + if (mOwner->mHitPoints != 0 && elem->mActorType == -0x7fffffff) { + elem->receiveMessage(mOwner, HIT_MESSAGE_BURN); + } + } + } + + THitActor::perform(cue, graphics); +} + +TBWBinder::TBWBinder() + : TBinder() +{ +} + +void TBWBinder::bind(TLiveActor* actor) +{ + TBossWanwan* boss = (TBossWanwan*)actor; + + JGeometry::TVec3 vel = boss->mVelocity; + JGeometry::TVec3 nextPos = boss->mPosition; + nextPos += vel; + + if (boss->isAirborne()) { + JGeometry::TVec3 v = boss->mVelocity; + nextPos += v; + v.y -= boss->getGravityY(); + if (v.y < TLiveActor::mVelocityMinY) { + v.y = TLiveActor::mVelocityMinY; + } + boss->mVelocity = v; + } + + if (&TNerveBWJumpToBath::theNerve() == boss->mSpine->getLatestNerve() + || &TNerveBWDie::theNerve() == boss->mSpine->getLatestNerve()) { + + boss->mVelocity = nextPos - boss->mPosition; + return; + } + + if (boss->isAirborne()) { + const TBGCheckData* bgData; + f32 groundY = gpMap->checkGround( + nextPos.x, nextPos.y + boss->getHeadHeight(), nextPos.z, &bgData); + + groundY += 1.0f; + + if (boss->mPosition.y > nextPos.y) { + if (!bgData->isEnemyThrough()) { + const TBGCheckData* bgData2; + f32 groundY2 = gpMap->checkGround( + nextPos.x, + boss->mPosition.y + boss->mHeadHeight * boss->mBodyScale, + nextPos.z, &bgData2); + if (groundY2 + 1.0f > groundY) { + groundY = groundY2 + 1.0f; + bgData = bgData2; + } + } + } + + if (nextPos.y <= groundY && !bgData->checkFlag(0x10) + && !bgData->isEnemyThrough()) { + nextPos.y = groundY; + boss->mVelocity = JGeometry::TVec3(0.0f, 0.0f, 0.0f); + boss->offLiveFlag(LIVE_FLAG_AIRBORNE); + boss->offLiveFlag(0x8000); + } else { + boss->onLiveFlag(LIVE_FLAG_AIRBORNE); + } + + boss->mGroundHeight = groundY; + boss->mGroundPlane = bgData; + } + + vel = nextPos - boss->mPosition; + + if (!boss->isAirborne()) { + TGraphTracer* tracer = boss->unk124; + if (tracer->unk0 != nullptr) { + int curIdx = tracer->mCurrIdx; + int prevIdx = tracer->mPrevIdx; + if (curIdx >= 0 && prevIdx >= 0 && curIdx != prevIdx) { + TGraphNode& curNode = tracer->unk0->getGraphNode(curIdx); + TGraphNode& prevNode = tracer->unk0->getGraphNode(prevIdx); + + JGeometry::TVec3 curPoint; + JGeometry::TVec3 prevPoint; + curNode.getPoint(&curPoint); + prevNode.getPoint(&prevPoint); + + curPoint -= prevPoint; + PSVECNormalize(&curPoint, &curPoint); + + f32 proj = 0.0f; + f32 lenSq = curPoint.squared(); + if (lenSq == 0.0f) { + } else { + proj = vel.dot(curPoint) / lenSq; + } + + proj = MsClamp(proj, -3.0f, 3.0f); + + vel = curPoint; + vel.scale(proj); + } + } + } + + if (!boss->isAirborne()) { + JGeometry::TVec3 velCopy = vel; + f32 speed = PSVECMag(&velCopy); + if (speed != 0.0f) { + f32 deg = (speed / 3141.5928f) * 360.0f; + + JGeometry::TVec3 dirCopy = boss->getRotDirY(boss->mRotation.y); + + if (dirCopy.dot(vel) < 0.0f) { + deg = -deg; + } + + f32 deltaAngle = deg * 2.0f; + if (boss->unk16C != 0) { + boss->unk168 = MsAngleWrap(deltaAngle + boss->unk168); + } else if (boss->unk168 != 0.0f) { + f32 nextAngle = boss->unk168 + deltaAngle; + if (nextAngle > 360.0f) { + nextAngle = 0.0f; + } + boss->unk168 = nextAngle; + } + } + } + + if (boss->unk17C != 0) { + JGeometry::TVec3 pos = boss->getPosition(); + JGeometry::TVec3 diff = pos; + diff += vel; + + JGeometry::TVec3 anchorPos = boss->mLeash->mRope->mPoints[3].unkC; + diff -= anchorPos; + + if (PSVECMag(&diff) > 860.0f) { + PSVECNormalize(&diff, &diff); + diff.scale(860.0f); + diff += anchorPos; + diff -= pos; + vel = diff; + } + } + + if (!boss->isAirborne()) { + TGraphTracer* tracer = boss->unk124; + int prevIdx = tracer->mPrevIdx; + TGraphNode& curNode = tracer->unk0->getGraphNode(tracer->mCurrIdx); + TGraphNode& prevNode = tracer->unk0->getGraphNode(prevIdx); + + JGeometry::TVec3 curPoint; + JGeometry::TVec3 prevPoint; + curNode.getPoint(&curPoint); + prevNode.getPoint(&prevPoint); + + JGeometry::TVec3 pull; + pull = MsPerpendicFootToLineR(prevPoint, curPoint, boss->mPosition); + curPoint -= prevPoint; + pull -= boss->mPosition; + + f32 dist = PSVECMag(&pull); + if (dist > 10.0f) { + dist = 10.0f; + } + + if (dist < 0.0001f) { + pull.zero(); + } else { + PSVECNormalize(&pull, &pull); + pull.scale(dist); + } + + vel += pull; + } + + boss->mVelocity = vel; +} + +TBossWanwanMtxCalc::TBossWanwanMtxCalc(TBossWanwan* owner) + : M3UMtxCalcSIAnmBlendQuat(false) + , mOwner(owner) +{ + mMotionBlendRatio = 0.0f; +} + +void TBossWanwanMtxCalc::joinAnm(int i) +{ + J3DAnmTransformKey* newAnm + = mOwner->getActorKeeper()->getMActorAnmData()->mBckAnms->getAnmPtr(i); + + if (mNewAnm != newAnm) { + mOldAnm = mNewAnm; + mNewAnm = newAnm; + mMotionBlendRatio = 1.0f; + } +} + +void TBossWanwanMtxCalc::calc(u16 index) +{ + J3DTransformInfo info; + Mtx rotMtx; + MtxPtr pRotMtx = rotMtx; + + if (index == 0 && this->mOwner->isAirborne()) { + j3dSys.setCurrentMtxCalc(this); + + if (mNewAnm != nullptr) { + mNewAnm->getTransform(index, &info); + } else { + J3DJoint* joint + = j3dSys.getModel()->getModelData()->getJointNodePointer(index); + info = joint->getTransformInfo(); + } + + info.mTranslate.x = 0.0f; + info.mTranslate.y = 0.0f; + info.mTranslate.z = 0.0f; + + calcTransform(index, info); + } else { + M3UMtxCalcSIAnmBlendQuat::calc(index); + + if (index == 1) { + + rotMtx[2][3] = 0.0f; + rotMtx[1][3] = 0.0f; + rotMtx[0][3] = 0.0f; + + rotMtx[2][2] = 0.0f; + rotMtx[1][2] = 0.0f; + rotMtx[0][2] = 0.0f; + + rotMtx[2][1] = 0.0f; + rotMtx[1][1] = 0.0f; + rotMtx[0][1] = 0.0f; + + rotMtx[2][0] = 0.0f; + rotMtx[1][0] = 0.0f; + rotMtx[0][0] = 0.0f; + + MsMtxSetRotX(pRotMtx, mOwner->getUnk168()); + + MtxPtr jointMtx = mOwner->getModel()->getAnmMtx(index); + PSMTXConcat(jointMtx, pRotMtx, jointMtx); + PSMTXCopy(jointMtx, J3DSys::mCurrentMtx); + } + } +} + +TBossWanwan::TBossWanwan(const char* name) + : TSpineEnemy(name) + , mPicket(nullptr) + , mLeash(nullptr) + , unk16C(0) + , unk168(0.0f) + , mMtxCalc(nullptr) + , unk17C(0) + , unk180(0) + , unk184(0) + , unk188(0) + , msInvincible(0) + , unk18D(0) + , unk190(0) + , unk194(true) + , mHasSpawnedWater(0) + , unk198(0) + , unk19C(0) + , unk1A0(0) + , unk1A4(0.0f) + , mWaterHitCount(0.0f) + , unk1AC(0.0f) + , mParams(nullptr) + , unk1B4(0) +{ + mBinder = new TBWBinder; +} + +void TBossWanwan::init(TLiveManager* manager) +{ + mManager = manager; + mManager->manageActor(this); + + mMActorKeeper = new TMActorKeeper(mManager, 0x11); + mMActor = mMActorKeeper->createMActor("bwanwan_body.bmd", 0); + + TGraphWeb* graph = gpConductor->getGraphByName("bwanwan"); + graph->initGoalIndex(BW_BATH_POS); + unk124->unk0 = graph; + + mSpine->initWith(&TNerveBWGraphWander::theNerve()); + + mMarchSpeed = getSaveParam()->mSLMarchSpeed.get(); + mTurnSpeed = getSaveParam()->mSLTurnSpeed.get(); + + mPosition = BW_HEAD_START; + calcRootMatrix(); + + mLeash = new TBWLeash(this, 15); + mPicket = new TBWPicket(this); + mPicket->mPosition = BW_PICKET_START; + unk17C = 1; + unk184 = 0; + goToRandomNextGraphNode(); + + initHitActor(ACTOR_TYPE_BOSS_UNKB, 1, HIT_CATEGORY_PLAYER, 0.0f, 0.0f, 0.0f, + 0.0f); + + mHits[0] = new TBWHit(this, 3); + mHits[0]->initHitActor(ACTOR_TYPE_BOSS_UNKB, 3, + HIT_CATEGORY_PLAYER | HIT_CATEGORY_ITEM, 500.0f, + 500.0f, 450.0f, 500.0f); + + mHits[1] = new TBWHit(this, -1); + mHits[1]->initHitActor(ACTOR_TYPE_BOSS_UNKB, 3, + HIT_CATEGORY_PLAYER | HIT_CATEGORY_ITEM, 300.0f, + 500.0f, 270.0f, 500.0f); + + TIdxGroupObj* hitGroup + = static_cast(JDrama::TNameRefGen::search("敵グループ")); + for (int i = 0; i < 2; i++) { + hitGroup->getChildren().push_back(mHits[i]); + mHits[i]->offHitFilter(1); + } + + initAnmSound(); + mScaledBodyRadius = 500.0f; + + mMtxCalc = new TBossWanwanMtxCalc(this); + J3DMtxCalc* calc = mMtxCalc; + MActorAnmBck* bck = mMActor->getAnmBck(); + + if (bck != nullptr) { + bck->setCalc(mMtxCalc); + bck->setCalcType(MActorMtxCalcType_User); + } + mMActor->calc(); + offHitFilter(0x100); + changeBck(4); + + mMActor->setBrkFromIndex(0); + mMActor->setLightType(1); + + J3DModel* model = mMActor->getModel(); + if (model->getSkinDeform() == nullptr) { + J3DSkinDeform* skinDeform = new J3DSkinDeform; + model->setSkinDeform(skinDeform, J3D_DEFORM_ATTACH_FLAG_UNK_1); + } + + mHitPoints = getSaveParam()->mSLBWHitPointMax.get(); + mPicketPullDelta.x = mPicketPullDelta.y = mPicketPullDelta.z = 0.0f; + + mMapCollisionManager = new TMapCollisionManager(1, "/scene/bwanwan", this); + mMapCollisionManager->init("bwanwan_ofuro_col.col", 2, nullptr); + + TMapCollisionManager* col = mMapCollisionManager; + Mtx mtx; + MsMtxSetTRS(mtx, mPosition.x, mPosition.y, mPosition.z, mRotation.x, + mRotation.y, mRotation.z, mScaling.x, mScaling.y, mScaling.z); + TMapCollisionBase* base = col->getActiveCollision(); + base->setUpMtx(mtx); + + if (mMapCollisionManager->getActiveCollision() != nullptr) { + mMapCollisionManager->getActiveCollision()->remove(); + } +} + +void TBossWanwan::shakeCamera(int shakeType) +{ + if (!SMS_IsMarioTouchGround4cm()) { + return; + } + + f32 dist = this->mDistToMarioSquared; + + dist = MsSqrtf(dist); + + TBWParams* params = this->getSaveParam(); + f32 shakeLengthMax = params->mSLShakeLengthMax.get(); + + TBWParams* params2 = this->getSaveParam(); + f32 shakeLengthMaxHP0 = params2->mSLShakeLengthMaxHP0.get(); + + f32 ratio; + if (this->mMActor->checkCurBckFromIndex(0)) { + ratio = 1.0f; + } else { + TBWParams* params3 = (TBWParams*)this->getSaveParam(); + ratio = (f32)this->mHitPoints / (f32)params3->mSLBWHitPointMax.get(); + } + + f32 effectiveDist + = shakeLengthMax * ratio + shakeLengthMaxHP0 * (1.0f - ratio); + + f32 delta = effectiveDist - dist; + + if (delta < 0.0f) { + return; + } + + f32 power = delta / effectiveDist; + if (power > 1.0f) { + power = 1.0f; + } + + gpCameraShake->startShake((EnumCamShakeMode)shakeType, power * ratio); + SMSRumbleMgr->start(8, &this->mPosition); +} + +BOOL TBossWanwan::receiveMessage(THitActor* sender, u32 message) +{ + u32 actorType = sender->getActorType(); + if (actorType == ACTOR_TYPE_MARIO) { + return false; + } else if (actorType == ACTOR_TYPE_WATER) { + if (this->msInvincible) { + return TRUE; + } + + gpMarioParticleManager->emit(PARTICLE_MS_ENM_WATHIT, &sender->mPosition, + 0, 0); + if (this->mHitPoints == 0) { + SMSGetMSound()->startSoundActor(MSD_SE_BS_WANWAN_COOL_MORE, + &this->mPosition, 0, nullptr, 0, 4); + } else if (this->mHitPoints == 1) { + MtxPtr iVar3 = this->getModel()->getAnmMtx(1); + gpMarioParticleManager->emitAndBindToMtxPtr( + BWAN_JPA_MS_DOWNYUGE, (iVar3 + 0x58) + 0x30, 0, 0); + SMSGetMSound()->startSoundActor(MSD_SE_BS_WANWAN_TO_COOL, + &this->mPosition, 0, nullptr, 0, 4); + } else { + SMSGetMSound()->startSoundActor(MSD_SE_BS_WANWAN_COOL, + &this->mPosition, 0, nullptr, 0, 4); + } + + decHitPoints(); + unk190++; + return TRUE; + } + + if (isActorType(0x4000005a)) { + sender->receiveMessage(this, HIT_MESSAGE_HIP_DROP); + this->mHitPoints = 0; + this->unk190++; + if (this->unk1A0 == 0) { + this->unk1A0++; + } + + gpMarioParticleManager->emitAndBindToMtxPtr( + BWAN_JPA_MS_DOWNYUGE, this->getModel()->getAnmMtx(1), 0, nullptr); + + SMSGetMSound()->startSoundActor(MSD_SE_BS_WANWAN_TO_COOL, + &this->mPosition, 0, nullptr, 0, 4); + } + + return TSpineEnemy::receiveMessage(sender, message); +} + +void TBossWanwan::takeBath() +{ + // TODO +} + +void TBossWanwan::releasePicket() +{ + if (unk17C != 0) { + JPABaseEmitter* emit = gpMarioParticleManager->emit( + BWAN_JPA_JUMP_SMOKE, &mPicket->mPosition, 0, nullptr); + if (emit != nullptr) { + JGeometry::TVec3 scale(0.3f, 0.5f, 0.3f); + emit->setGlobalScale(scale); + } + } +} + +void TBossWanwan::changeBck(int index) +{ + setBckFromIndex(index); + J3DFrameCtrl* ctrl = getMActor()->getFrameCtrl(0); + unk178 = 10.0f / ctrl->getEnd(); + setAnmSound(bwanwan_bastable[index]); +} + +void TBossWanwan::startGoldBrk() +{ + // TODO +} + +bool TBossWanwan::isBurning() +{ + // TODO: +} + +bool TBossWanwan::isTailBurning() +{ + // TODO +} + +bool TBossWanwan::isMarioInSight() +{ + // TODO +} + +BOOL TBossWanwan::isHeadPulled() +{ + return mPicketPullDelta.squared() < getSaveParam()->mSLPullLimit.get() + ? FALSE + : TRUE; +}; + +void TBossWanwan::calcRootMatrix() +{ + getModel()->setBaseScale(mScaling); + + MsMtxSetXYZRPH(getModel()->getBaseTRMtx(), mPosition.x, + mPosition.y + 500.0f, mPosition.z, mRotation.x, mRotation.y, + mRotation.z); +} + +void TBossWanwan::reverseNextGraphNode() +{ + int prev = unk124->mPrevIdx; + int curr = unk124->mCurrIdx; + TGraphWeb* graph = unk124->unk0; + + JGeometry::TVec3 toMario = *gpMarioPos; + toMario -= mPosition; + + int next = graph->getAimToDirNextIndex(prev, curr, toMario, mPosition, -1); + unk124->mPrevIdx = next; + unk124->mCurrIdx = prev; + + setGoalPathFromGraph(); + unk128 = 0; + unk12C = 0.0f; +} + +void TBossWanwan::rollNextGraphNode() +{ + int prev = unk124->mPrevIdx; + int curr = unk124->mCurrIdx; + TGraphWeb* graph = unk124->unk0; + + if (prev >= 0 + && graph->getGraphNode(prev).getRailNode()->mConnectionNum >= 2 + && gpMarDirector->getMoveTickCount() >= 14400) { + showMessage(0x1EEC); + } + + int next = graph->getEscapeDirLimited(curr, prev, getRotDirY(mRotation.y), + mPosition, 100.0f, -1); + + unk124->moveTo(next); + setGoalPathFromGraph(); + unk128 = 0; + unk12C = 0.0f; +} + +void TBossWanwan::slideToCurPathNode(float speed, float turnSpeed) +{ + JGeometry::TVec3 pos = unkF4.getPoint(); + + pos.sub(mPosition); + + f32 dist = PSVECMag(&pos); + f32 targetAngle = MsGetRotFromYaxisZ(pos); + targetAngle = MsWrap(targetAngle, 0.0f, 360.0f); + + f32 diff = MsAngleDiff(targetAngle, mRotation.y); + + if (diff > 0.0f) { + diff = (diff > turnSpeed) ? turnSpeed : diff; + } else { + diff = (diff > -turnSpeed) ? diff : -turnSpeed; + } + + mRotation.y = MsWrap(mRotation.y + diff, 0.0f, 360.0f); + + JGeometry::TVec3 vel = mVelocity; + + if (dist > 0.0f) { + pos.scale(speed / dist); + } + + vel.add(pos); + mVelocity = vel; +} + +void TBossWanwan::control() +{ + TLiveActor::control(); + + if (this->unk17C != 0 || (mPicket->isHolding() && isHeadPulled())) { + mVelocity += mPicketPullDelta; + + JGeometry::TVec3 pos = mPosition; + + pos -= this->mLeash->mRope->mPoints[3].unkC; + + f32 targetAngle = MsGetRotFromYaxisZ(pos); + targetAngle = MsWrap(targetAngle, 0.0f, 360.0f); + + f32 diff = MsAngleDiff(targetAngle, mRotation.y); + f32 maxStep = mTurnSpeed * 4.0f; + + if (diff > 0.0f) { + diff = (diff > maxStep) ? maxStep : diff; + } else { + diff = (diff > -maxStep) ? diff : -maxStep; + } + + mRotation.y = MsWrap(mRotation.y + diff, 0.0f, 360.0f); + } + + mPicketPullDelta.zero(); + this->updateSquareToMario(); +} + +void TBossWanwan::emitEffects() +{ + int emit = false; + + if (mMActor->checkCurBckFromIndex(4) != 0 + || mMActor->checkCurBckFromIndex(5) != 0) { + if (mMActor->checkBckPass(8.0f) != 0) { + emit = true; + } + } else { + if (mMActor->checkCurBckFromIndex(2) != 0 + && mMActor->checkBckPass(38.0f) != 0) { + emit = true; + } + } + + if (emit) { + gpMarioParticleManager->emit(BWAN_JPA_JUMP_ROCK, &mPosition, 0, + nullptr); + gpMarioParticleManager->emit(BWAN_JPA_JUMP_SMOKE, &mPosition, 0, + nullptr); + + if (mHitPoints == 0) { + SMSGetMSound()->startSoundActor(MSD_SE_M_BOSW_CHAIN_CAL_F, + &mLeash->mRope->mPoints->unkC, 0, + nullptr, 0, 4); + SMSGetMSound()->startSoundActor(MSD_SE_M_BOSW_CHAIN_CAL_R, + &mPicket->mPosition, 0, nullptr, 0, + 4); + } else { + SMSGetMSound()->startSoundActor(MSD_SE_M_BOSW_CHAIN_ANG_F, + &mLeash->mRope->mPoints->unkC, 0, + nullptr, 0, 4); + SMSGetMSound()->startSoundActor(MSD_SE_M_BOSW_CHAIN_ANG_R, + &mPicket->mPosition, 0, nullptr, 0, + 4); + } + } + + int emit2 = false; + + if (mMActor->checkCurBckFromIndex(0)) { + if (mMActor->checkBckPass(72.0f)) { + emit2 = true; + } + } else if (mMActor->checkCurBckFromIndex(4) + || mMActor->checkCurBckFromIndex(5)) { + if (mMActor->checkBckPass(6.0f) || mMActor->checkBckPass(12.0f)) { + emit2 = true; + } + } else if (mMActor->checkCurBckFromIndex(2)) { + if (mMActor->checkBckPass(4.0f)) { + emit2 = true; + } + } + + if (emit2) { + MtxPtr jointMtx = getModel()->getAnmMtx(1); + gpMarioParticleManager->emitAndBindToMtxPtr(BWAN_JPA_MS_HIBANA, + jointMtx, 0, this); + } + + if ((mMActor->checkCurBckFromIndex(4) || mMActor->checkCurBckFromIndex(5)) + && mMActor->checkBckPass(10.0f)) { + shakeCamera(0x16); + } + + if (mMActor->checkCurBckFromIndex(0)) { + J3DFrameCtrl* frameCtrl = mMActor->getFrameCtrl(0); + + if (frameCtrl->checkPass(60.0f) || frameCtrl->checkPass(127.0f)) { + shakeCamera(0x16); + gpMarioParticleManager->emit(BWAN_JPA_JUMP_ROCK, &mPosition, 0, + nullptr); + gpMarioParticleManager->emit(BWAN_JPA_JUMP_SMOKE, &mPosition, 0, + nullptr); + } + + if (frameCtrl->checkPass(202.0f)) { + shakeCamera(0x17); + gpMarioParticleManager->emit(BWAN_JPA_JUMP_ROCK, &mPosition, 0, + nullptr); + gpMarioParticleManager->emit(BWAN_JPA_JUMP_SMOKE, &mPosition, 0, + nullptr); + } + } + + if (mMActor->checkCurBckFromIndex(2) && mMActor->checkBckPass(40.0f)) { + shakeCamera(0x16); + } + + if (mHitPoints != 0) { + MtxPtr jointMtx = getModel()->getAnmMtx(1); + gpMarioParticleManager->emitAndBindToMtxPtr(BWAN_JPA_MS_YUGAMI, + jointMtx, 3, this); + } + + if (this->unk190 != 0 && mHitPoints != 0) { + MtxPtr jointMtx = getModel()->getAnmMtx(1); + gpMarioParticleManager->emitAndBindToMtxPtr(BWANWAN_JPA_MS_BWAN_HITYUGE, + jointMtx, 1, this); + this->unk190 = 0; + } +} + +void TBossWanwan::showMessage(u32 msg) +{ + if (!(unk198 & 4)) { + gpMarDirector->getConsole()->startAppearBalloon(msg, true); + } + unk198 |= 4; +} + +void TBossWanwan::perform(u32 cue, JDrama::TGraphics* graphics) +{ + if (msInvincible != 0) { + mHits[0]->perform(cue, graphics); + TSpineEnemy::perform(cue, graphics); + + if (cue & CUE_MOVE) { + mMtxCalc->advanceMotionBlend(-unk178); + } + + if (cue & CUE_ENTRY) { + J3DFrameCtrl* ctrl = mMActor->getFrameCtrl(5); + if (ctrl != nullptr + && ctrl->getFrame() > 0.5f * (f32)ctrl->getEnd()) { + unk1A4 = mPosition; + unk1A4.y += 500.0f; + gpMarioParticleManager->emitAndBindToPosPtr( + BWANWAN_JPA_MS_BWAN_KIRA, &unk1A4, 1, this); + } + } + return; + } + + if (cue & CUE_CALC_ANIM) { + J3DFrameCtrl* ctrl = mMActor->getFrameCtrl(5); + TBWParams* params = getSaveParam(); + f32 hpRatio = (f32)mHitPoints / (f32)params->mSLBWHitPointMax.value; + + ctrl->setFrame(hpRatio * (f32)(ctrl->getEnd() - 1)); + ctrl->setRate(0.0f); + + if (mHitPoints == 0 + || mHitPoints == getSaveParam()->mSLBWHitPointMax.value) { + mLeash->mNodes[0]->mTemperature = hpRatio; + } + + emitEffects(); + } + + mHits[0]->perform(cue, graphics); + + if (cue & CUE_MOVE) { + mHits[1]->mPosition.set(mHits[0]->mPosition.x, + mHits[0]->mPosition.y - 500.0f, + mHits[0]->mPosition.z); + } + + mHits[1]->perform(cue, graphics); + + if ((cue & CUE_MOVE) && unk194 == 0) { + unk19C++; + + if (unk19C < 0) { + unk19C = 1; + } + + if (unk19C > 600) { + if (!(unk198 & 1)) { + gpMarDirector->getConsole()->startAppearBalloon( + TBossWanwan::BOSS_WANWAN_FLAGS_UNK1, true); + } + unk198 |= 1; + } + } + + if ((cue & CUE_MOVE) && unk1A0 == 0 + && gpMarDirector->getMoveTickCount() >= 28800) { + if (!(unk198 & 8)) { + gpMarDirector->getConsole()->startAppearBalloon( + TBossWanwan::BOSS_WANWAN_FLAGS_UNK4, true); + } + unk198 |= 8; + } + + if (cue & CUE_MOVE) { + if (&TNerveBWDie::theNerve() != mSpine->getLatestNerve() + && &TNerveBWJumpToBath::theNerve() != mSpine->getLatestNerve()) { + if (mHitPoints != 0) { + if (unk17C != 0 && unk194 == 0) { + unk184++; + if (unk184 > 600) { + mSpine->pushNerve(&TNerveBWShake::theNerve()); + unk184 = 0; + } + } + } else { + unk184 = 0; + } + + if (mHitPoints == 0 + && mSpine->getLatestNerve() != &TNerveBWBark::theNerve()) { + unk180++; + if (unk180 > 2400) { + if (mSpine->getLatestNerve() != &TNerveBWBark::theNerve()) { + mSpine->setNext(&TNerveBWBark::theNerve()); + } + } + } else { + if (gpMarDirector->getMoveTickCount() % 20 == 0) { + if (mHitPoints < getSaveParam()->mSLBWHitPointMax.value) { + mHitPoints++; + } + } + unk180 = 0; + } + } + } + + if (cue & CUE_MOVE) { + if (&TNerveBWGraphWander::theNerve() == mSpine->getLatestNerve() + && mPicket->isHolding()) { + f32 pullDist = MsSqrtf(mPicketPullDelta.squared()); + TRopePoint* points = mLeash->mRope->mPoints; + SMSGetMSound()->startSoundActorWithInfo( + MSD_SE_BS_WANWAN_M_DRAG, &points[6].unkC, nullptr, pullDist, 0, + 0, nullptr, 0, 4); + } + } + + TSpineEnemy::perform(cue, graphics); + mLeash->testPerform(cue, graphics); + mPicket->testPerform(cue, graphics); + + if (cue & CUE_MOVE) { + mMtxCalc->advanceMotionBlend(-unk178); + } +} + +TBossWanwanManager::TBossWanwanManager(const char* name) + : TEnemyManager(name) +{ +} + +void TBossWanwanManager::initJParticle() +{ + SMS_LoadParticle("/scene/bwanwan/jpa/ms_bwan_jump_rock.jpa", 0xad); + SMS_LoadParticle("/scene/bwanwan/jpa/ms_bwan_jump_smoke.jpa", 0xae); + SMS_LoadParticle("/scene/bwanwan/jpa/ms_bwan_downyuge.jpa", 0xb0); + SMS_LoadParticle("/scene/bwanwan/jpa/ms_bwan_hibana.jpa", 0xaf); + SMS_LoadParticle("/scene/bwanwan/jpa/ms_bwan_deadyuge.jpa", 0xb1); + SMS_LoadParticle("/scene/bwanwan/jpa/ms_bwan_yugami.jpa", 0x1ee); + SMS_LoadParticle("/scene/bwanwan/jpa/ms_bwan_hityuge.jpa", 0x167); + SMS_LoadParticle("/scene/bwanwan/jpa/ms_bwan_kira.jpa", 0x168); +} + +TSpineEnemy* TBossWanwanManager::createEnemyInstance() +{ + return new TBossWanwan; +} + +void TBossWanwanManager::createModelData() +{ + static const TModelDataLoadEntry entry[] = { + { "bwanwan_body.bmd", 0, 0 }, + { "bwanwan_chain.bmd", 0, 0 }, + { "bwanwan_picket.bmd", 0, 0 }, + }; + + createModelDataArray(entry); +} + +void TBossWanwanManager::load(JSUMemoryInputStream& stream) +{ + unk38 = new TBWParams("/enemy/bosswanwan.prm"); + TEnemyManager::load(stream); + initJParticle(); +} + +DEFINE_NERVE(TNerveBWGraphWander, TLiveActor) +{ + TBossWanwan* boss = static_cast(spine->getBody()); + + if (spine->getTime() == 0) { + boss->unk16C = 0; + + if (!boss->getMActor()->checkCurBckFromIndex(4)) { + if (boss->mHitPoints == 0) { + boss->changeBck(5); + } else { + boss->changeBck(4); + } + J3DFrameCtrl* ctrl = boss->mMActor->getFrameCtrl(0); + ctrl->setFrame(0.0f); + ctrl->setRate(SMSGetAnmFrameRate()); + } + + boss->mMActor->setBtpFromIndex(0); + J3DFrameCtrl* btpCtrl = boss->mMActor->getFrameCtrl(3); + btpCtrl->setFrame(0.0f); + btpCtrl->setRate(0.0f); + } + + if (boss->mMActor->curAnmEndsNext(ANM_TYPE_BCK, nullptr) + && boss->mHitPoints == 0 && !boss->mMActor->checkCurBckFromIndex(5)) { + boss->changeBck(5); + J3DFrameCtrl* ctrl = boss->mMActor->getFrameCtrl(0); + ctrl->setFrame(0.0f); + ctrl->setRate(SMSGetAnmFrameRate()); + } + + if (boss->unk17C == 0 && boss->mPicket->isHolding() + && boss->isHeadPulled()) { + TGraphTracer* tracer = boss->unk124; + TGraphWeb* graph = tracer->unk0; + int prevIdx = tracer->mPrevIdx; + + JGeometry::TVec3 nodePos; + graph->getGraphNode(prevIdx).getPoint(&nodePos); + nodePos -= boss->mPosition; + + if (PSVECMag(&nodePos) < 400.0f && prevIdx == graph->unk10) { + spine->pushAfterCurrent(&TNerveBWJumpToBath::theNerve()); + return true; + } + + if (PSVECMag(&nodePos) < 400.0f) { + boss->reverseNextGraphNode(); + } + + u8 maxHp = boss->getSaveParam()->mSLBWHitPointMax.value; + boss->slideToCurPathNode( + 3.0f * (((f32)boss->mHitPoints / (f32)maxHp) * boss->mMarchSpeed), + boss->mTurnSpeed); + return false; + } + + if (boss->isReachedToGoal()) { + if (boss->jumpToNextGraphNode() >= 0) { + spine->pushAfterCurrent(&TNerveBWGraphWander::theNerve()); + spine->pushAfterCurrent(&TNerveBWJump::theNerve()); + } else { + spine->pushAfterCurrent(&TNerveBWGraphWander::theNerve()); + boss->rollNextGraphNode(); + } + return true; + } + + boss->unk16C = 0; + f32 hpRatio = (f32)boss->mHitPoints + / (f32)boss->getSaveParam()->mSLBWHitPointMax.value; + + if (boss->mPicket->isHolding()) { + f32 marioYaw = (f32)gpMarioOriginal->mIntendedYaw * (180.0f / 32768.0f); + + JGeometry::TVec3 marioDir = boss->getRotDirY(marioYaw); + + JGeometry::TVec3 diff = boss->mPosition; + diff -= gpMarioOriginal->mPosition; + PSVECNormalize(&diff, &diff); + + f32 pullFactor = 1.0f; + pullFactor -= (f32)((gpMarioOriginal->mIntendedMag / 32.0f) * 0.75) + * -diff.dot(marioDir); + + if (pullFactor < 0.0f) { + pullFactor = 0.0f; + } else if (1.5f < pullFactor) { + pullFactor = 1.5f; + } + + boss->slideToCurPathNode(pullFactor * (hpRatio * boss->mMarchSpeed), + boss->mTurnSpeed); + } else { + boss->slideToCurPathNode(hpRatio * boss->mMarchSpeed + 0.2f, + boss->mTurnSpeed); + } + + return false; +} + +DEFINE_NERVE(TNerveBWRoll, TLiveActor) +{ + TBossWanwan* boss = static_cast(spine->getBody()); + + if (spine->getTime() == 0) { + J3DFrameCtrl* ctrl = boss->mMActor->getFrameCtrl(0); + ctrl->setFrame(0.0f); + ctrl->setRate(0.0f); + boss->unk16C = 1; + } + + if (boss->isReachedToGoal()) { + spine->pushAfterCurrent(&TNerveBWGraphWander::theNerve()); + J3DFrameCtrl* ctrl = boss->mMActor->getFrameCtrl(0); + ctrl->setRate(SMSGetAnmFrameRate()); + return true; + } else { + f32 speed = boss->getSaveParam()->mSLAttackSpeed.get(); + boss->walkToCurPathNode(speed, boss->mTurnSpeed, 0.0f); + return false; + } +} + +DEFINE_NERVE(TNerveBWBark, TLiveActor) +{ + TBossWanwan* boss = static_cast(spine->getBody()); + + if (spine->getTime() == 0) { + + boss->changeBck(0); + boss->unk16C = 0; + boss->unk168 = 0.0f; + + if (!boss->unk194) { + if (boss->unk17C) { + JPABaseEmitter* emit = gpMarioParticleManager->emit( + BWAN_JPA_JUMP_SMOKE, &boss->mPicket->mPosition, 0, 0); + if (emit != nullptr) { + JGeometry::TVec3 scale(0.3f, 0.5f, 0.3f); + emit->setGlobalScale(scale); + } + + boss->unk194 = 0; + boss->unk17C = 0; + SMSGetMSound()->startSoundActor(MSD_SE_BS_WANWAN_ESCAPE1, + &boss->mPicket->mPosition, 0, + nullptr, 0, 4); + } + } + } + + if (spine->getTime() == 280) { + boss->mHitPoints = boss->getSaveParam()->mSLBWHitPointMax.get(); + } + + if (boss->mMActor->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { + spine->pushAfterCurrent(&TNerveBWGraphWander::theNerve()); + + if (!(boss->unk198 & 2)) { + gpMarDirector->getConsole()->startAppearBalloon( + TBossWanwan::BOSS_WANWAN_FLAGS_UNK2, true); + } + boss->unk198 |= 2; + return true; + } + return false; +} + +DEFINE_NERVE(TNerveBWJump, TLiveActor) +{ + TBossWanwan* boss = static_cast(spine->getBody()); + + if (spine->getTime() == 0) { + const JGeometry::TVec3& targetPos + = boss->unk104.unk0 != nullptr ? boss->unk104.unk0->mPosition + : boss->unk104.unk4; + + f32 speed = boss->unk124->unkC; + boss->mVelocity = boss->calcVelocityToJumpToY(targetPos, speed, + boss->getGravityY()); + + boss->onLiveFlag(LIVE_FLAG_AIRBORNE); + boss->unk16C = 0; + } + + if (boss->isReachedToGoal()) { + spine->pushAfterCurrent(&TNerveBWGraphWander::theNerve()); + return true; + } + + boss->walkToCurPathNode(0.0f, boss->mTurnSpeed, 0.0f); + return false; +} + +DEFINE_NERVE(TNerveBWStun, TLiveActor) +{ + TBossWanwan* boss = static_cast(spine->getBody()); + + if (boss->mPicket->isHolding() && boss->isHeadPulled()) { + TGraphWeb* graph = boss->unk124->unk0; + int prevIdx = boss->unk124->mPrevIdx; + + TGraphNode& prevNode = graph->getGraphNode(prevIdx); + JGeometry::TVec3 prevPoint; + prevNode.getPoint(&prevPoint); + prevPoint -= boss->mPosition; + + if (PSVECMag(&prevPoint) < 100.0f) { + if (prevIdx == graph->unk10) { + spine->pushAfterCurrent(&TNerveBWJumpToBath::theNerve()); + return true; + } + boss->reverseNextGraphNode(); + } + + return false; + } else if (spine->getTime() > boss->getSaveParam()->mSLStunTimer.get()) { + boss->mHitPoints = boss->getSaveParam()->mSLBWHitPointMax.get(); + spine->pushAfterCurrent(&TNerveBWWakeup::theNerve()); + return true; + } + + return false; +} + +DEFINE_NERVE(TNerveBWWakeup, TLiveActor) +{ + TBossWanwan* boss = static_cast(spine->getBody()); + + if (spine->getTime() == 0) { + boss->changeBck(6); + + boss->mMActor->setBtpFromIndex(2); + boss->unk16C = 0; + boss->unk168 = 0.0f; + } + + if (boss->mMActor->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { + spine->pushAfterCurrent(&TNerveBWGraphWander::theNerve()); + return true; + } + + return false; +} + +DEFINE_NERVE(TNerveBWJumpToBath, TLiveActor) +{ + TBossWanwan* boss = static_cast(spine->getBody()); + + if (spine->getTime() == 0) { + JGeometry::TVec3 vel = boss->calcVelocityToJumpToY( + BW_BATH_POS, 10.0f, boss->getGravityY()); + + TPathNode goal(BW_HEAD_START); + boss->unkF4 = goal; + boss->unk104 = goal; + boss->unk114.clear(); + + boss->mVelocity = vel; + boss->mLiveFlag |= 0x80; + boss->unk16C = 0; + } + + if (spine->getTime() > 120 && !boss->mHasSpawnedWater + && boss->mPosition.y < (BW_BATH_POS.y + 10.0f)) { + + TEffectColumWater* effectBase + = (TEffectColumWater*)gpConductor->makeOneEnemyAppear( + boss->mPosition, "エフェクト水柱マネージャー", 1); + + if (effectBase != nullptr) { + JGeometry::TVec3 scale(5.0f, 5.0f, 5.0f); + JGeometry::TVec3 effectPos; + effectPos.x = boss->mPosition.x; + effectPos.y = (boss->mPosition.y + 500.0f) - 30.0f; + effectPos.z = boss->mPosition.z; + effectBase->generate(effectPos, scale); + boss->mHasSpawnedWater = true; + } + + SMSGetMSound()->startSoundActor(MSD_SE_BS_WANWAN_DIVE, &boss->mPosition, + 0, nullptr, 0, 4); + } + + JGeometry::TVec3 targetPos = BW_BATH_POS; + JGeometry::TVec3 diff = targetPos; + diff.sub(boss->mPosition); + + if (diff.squared() < 10000.0f && boss->mPosition.y > BW_BATH_POS.y) { + boss->mPosition = BW_BATH_POS; + spine->pushAfterCurrent(&TNerveBWDie::theNerve()); + return true; + } + + boss->walkToCurPathNode(0.0f, boss->mTurnSpeed, 0.0f); + return false; +} + +DEFINE_NERVE(TNerveBWDie, TLiveActor) +{ + TBossWanwan* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + + if (spine->getTime() == 0) { + boss->mVelocity.zero(); + boss->onLiveFlag(LIVE_FLAG_UNK10); + boss->offLiveFlag(LIVE_FLAG_AIRBORNE); + gpMarioParticleManager->emit(0xB1, &boss->mPosition, 0, nullptr); + } + + if (boss->mHitPoints != 0) { + boss->decHitPoints(); + J3DFrameCtrl* ctrl = actor->getFrameCtrl(0); + ctrl->setFrame(0.0f); + ctrl->setRate(0.0f); + spine->pushAfterCurrent(&TNerveBWDie::theNerve()); + return true; + } + + if (spine->getTime() == 0) { + gpMarDirector->fireStartDemoCamera("bwanwan_down_camera", nullptr, -1, + 0.0f, true, nullptr, 0, nullptr, + JDrama::TFlagT(0)); + + boss->unk16C = 0; + boss->unk168 = 0.0f; + boss->msInvincible = 1; + boss->mPosition = BW_BATH_POS; + + JGeometry::TVec3 colPos = boss->mPosition; + colPos.y += 500.0f; + + JGeometry::TVec3 colScale = boss->mScaling; + colScale.scale(1.1f); + + TMapCollisionManager* col = boss->mMapCollisionManager; + Mtx mtx; + MsMtxSetTRS(mtx, colPos.x, colPos.y, colPos.z, boss->mRotation.x, + boss->mRotation.y, boss->mRotation.z, colScale.x, + colScale.y, colScale.z); + TMapCollisionBase* base = col->getActiveCollision(); + base->setMtx(mtx); + base->setUp(); + + boss->mHits[0]->mHitFilter |= 1; + boss->mHits[1]->mHitFilter |= 1; + + TBWLeash* leash = boss->mLeash; + for (int i = 0; i < leash->mRope->mNumPoints; i++) { + leash->mNodes[i]->onHitFilter(1); + } + boss->mPicket->onHitFilter(1); + + boss->changeBck(1); + actor->setBtpFromIndex(0); + actor->setBrkFromIndex(1); + } + + if (spine->getTime() > 60 && boss->unk18D == 0 + && gpMarDirector->getGameMode() != TMarDirector::GAME_MODE_INNER_DEMO) { + gpItemManager->makeShineAppearWithDemo( + "シャイン(ボスワンワン用)", "ボスワンワンシャインカメラ", + boss->mPosition.x, boss->mPosition.y, boss->mPosition.z); + boss->unk18D = 1; + } + + if (actor->curAnmEndsNext(5, nullptr)) { + actor->getFrameCtrl(5)->setRate(0.0f); + } + + return false; +} + +DEFINE_NERVE(TNerveBWJumpAway, TLiveActor) +{ + TBossWanwan* boss = static_cast(spine->getBody()); + + if (spine->getTime() == 0) { + JGeometry::TVec3 vel = boss->calcVelocityToJumpToY( + BW_HEAD_START, 40.0f, boss->getGravityY()); + + TPathNode goal(BW_HEAD_START); + boss->unkF4 = goal; + boss->unk104 = goal; + boss->unk114.clear(); + + boss->mVelocity = vel; + boss->mLiveFlag |= 0x80; + boss->unk16C = 0; + } + + if (boss->unk188) { + spine->pushAfterCurrent(&TNerveBWFall::theNerve()); + return true; + } + + if (boss->isReachedToGoal()) { + boss->mPosition = BW_HEAD_START; + + boss->unk124->mPrevIdx = -1; + boss->unk124->mCurrIdx = -1; + boss->goToShortestNextGraphNode(); + + spine->pushAfterCurrent(&TNerveBWGraphWander::theNerve()); + return true; + } + + boss->walkToCurPathNode(0.0f, boss->mTurnSpeed, 0.0f); + return false; +} + +DEFINE_NERVE(TNerveBWShake, TLiveActor) +{ + TBossWanwan* boss = static_cast(spine->getBody()); + MActor* actor = boss->mMActor; + + if (spine->getTime() == 0) { + boss->changeBck(2); + } + + if (actor->curAnmEndsNext(ANM_TYPE_BCK, nullptr)) { + boss->releasePicket(); + + boss->unk194 = 0; + boss->unk17C = 0; + SMSGetMSound()->startSoundActor(MSD_SE_BS_WANWAN_ESCAPE2, + &boss->mPicket->mPosition, 0, nullptr, + 0, 4); + return true; + } + return false; +} + +DEFINE_NERVE(TNerveBWFall, TLiveActor) +{ + TBossWanwan* boss = static_cast(spine->getBody()); + if (spine->getTime() == 0) { + JGeometry::TVec3 vel = boss->calcVelocityToJumpToY( + boss->unk104.unk4, 5.0f, boss->getGravityY()); + + TPathNode goal(boss->unk104.unk4); + boss->unkF4 = goal; + boss->unk104 = goal; + boss->unk114.clear(); + + boss->mVelocity = vel; + boss->mLiveFlag |= 0x80; + boss->unk1C = 0; + } + + if (boss->isReachedToGoal()) { + + boss->mPosition = boss->unk104.unk4; + boss->unk124->mPrevIdx = -1; + boss->unk124->mCurrIdx = -1; + boss->goToShortestNextGraphNode(); + + spine->pushAfterCurrent(&TNerveBWGraphWander::theNerve()); + return true; + } + + return false; +} diff --git a/src/Enemy/tempCodeRunnerFile.cpp b/src/Enemy/tempCodeRunnerFile.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/src/Player/MarioCheckCol.cpp b/src/Player/MarioCheckCol.cpp index 6574773b3..6ca86465b 100644 --- a/src/Player/MarioCheckCol.cpp +++ b/src/Player/MarioCheckCol.cpp @@ -411,7 +411,7 @@ void TMario::checkCollision() case ACTOR_TYPE_BOSS_GESSO_TENTACLE: case ACTOR_TYPE_BOSS_UNK8: - case ACTOR_TYPE_BOSS_UNKD: + case ACTOR_TYPE_BOSS_WANWAN: case ACTOR_TYPE_YOSHI_TONGUE: case ACTOR_TYPE_FIRE_WANWAN_TAIL_HIT: hitNoKeepPull(mCollisions[i]); diff --git a/src/Player/MarioReceiveMsg.cpp b/src/Player/MarioReceiveMsg.cpp index b29166734..843c62a10 100644 --- a/src/Player/MarioReceiveMsg.cpp +++ b/src/Player/MarioReceiveMsg.cpp @@ -665,7 +665,7 @@ BOOL TMario::receiveMessage(THitActor* sender, u32 message) case ACTOR_TYPE_BOSS_UNKB: case ACTOR_TYPE_BOSS_UNKC: - case ACTOR_TYPE_BOSS_UNKD: + case ACTOR_TYPE_BOSS_WANWAN: case ACTOR_TYPE_BOSS_UNKE: if (message == HIT_MESSAGE_BURN && !isInvincible()) { damageExec(sender, mDmgParamsFire.mDamage.get(), diff --git a/src/Player/MarioSpecial.cpp b/src/Player/MarioSpecial.cpp index cccb50dd5..4385b5b01 100644 --- a/src/Player/MarioSpecial.cpp +++ b/src/Player/MarioSpecial.cpp @@ -537,7 +537,7 @@ BOOL TMario::hanging() for (int j = 0; j < record4.mResultWallsNum; j++) { TBGCheckData* w = record4.mResultWalls[j]; s16 ang = matan(w->mNormal.z, w->mNormal.x) - - (mFaceAngle.y + 0x8000); + - (mFaceAngle.y + 0x8000); if (ang > -0x2000 && ang < 0x2000) { JGeometry::TVec3 pos = mPosition; f32 d = w->mNormal.x * pos.x + w->mNormal.y * pos.y @@ -1144,7 +1144,7 @@ void TMario::getCurrentPullParams(f32* outV, f32* outH) case ACTOR_TYPE_BOSS_GESSO_TENTACLE: params = &mPullParamsBGTentacle; break; - case ACTOR_TYPE_BOSS_UNKD: + case ACTOR_TYPE_BOSS_WANWAN: params = &mPullParamsBGFireWanWanBossTail; break; case ACTOR_TYPE_FIRE_WANWAN_TAIL_HIT: @@ -1233,7 +1233,7 @@ BOOL TMario::pulling() JGeometry::TVec3 delta; if ((mHeldObject->getActorType() == ACTOR_TYPE_BOSS_GESSO_TENTACLE ? true - : false) + : false) || (mHeldObject->getActorType() == ACTOR_TYPE_BOSS_UNK8 ? true : false)) { delta = pos - mPrevPosition;