diff --git a/include/Enemy/HanaSambo.hpp b/include/Enemy/HanaSambo.hpp new file mode 100644 index 000000000..3317a6039 --- /dev/null +++ b/include/Enemy/HanaSambo.hpp @@ -0,0 +1,324 @@ +#ifndef ENEMY_HANASAMBO_HPP +#define ENEMY_HANASAMBO_HPP + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class JSUMemoryInputStream; +class SDLModelData; +class THitActor; +class TLiveManager; +class TSamboHead; +class TSamboHeadManager; +class TSamboHeadSaveLoadParams; +class THanaSambo; +class THanaSamboHead; +class THanaSamboManager; +class THanaSamboSaveLoadParams; +class TSamboFlower; +class TSamboFlowerManager; +class TSamboFlowerSaveLoadParams; +class TSamboLeaf; +class TSamboFlowerCoinUnit; + +DECLARE_NERVE(TNerveSamboHeadHitWall, TLiveActor); + +DECLARE_NERVE(TNerveSamboHeadRecoverWater, TLiveActor); + +DECLARE_NERVE(TNerveSamboHeadHitWater, TLiveActor); + +DECLARE_NERVE(TNerveSamboHeadAttack, TLiveActor); + +DECLARE_NERVE(TNerveSamboHeadHide, TLiveActor); + +DECLARE_NERVE(TNerveSamboHeadAppear, TLiveActor); + +class TSamboHead : public TWalkerEnemy { +public: + TSamboHead(const char* name = "サンボヘッド"); + + virtual void load(JSUMemoryInputStream&); + virtual void init(TLiveManager*); + virtual void calcRootMatrix(); + virtual void kill(); + virtual f32 getGravityY() const; + virtual const char** getBasNameTable() const; + virtual void reset(); + virtual void genEventCoin(); + virtual void behaveToWater(THitActor*); + virtual void setDeadAnm(); + virtual void attackToMario(); + virtual void setMActorAndKeeper(); + virtual void setAfterDeadEffect(); + + bool isUseCallBack(); + void setCrashAnm(); + void initFlower(); + static u8 mBodyJntIndex; + +public: + /* 0x194 */ TSamboHeadSaveLoadParams* unk194; + /* 0x198 */ TSamboFlower* unk198; + /* 0x19C */ s32 unk19C; + /* 0x1A0 */ JGeometry::TVec3 unk1A0; + /* 0x1AC */ f32 unk1AC; + /* 0x1B0 */ u8 unk1B0; +}; + +class TSamboHeadManager : public TSmallEnemyManager { +public: + TSamboHeadManager(const char* name = "サンボヘッドマネージャー"); + + virtual void load(JSUMemoryInputStream&); + virtual void createModelData(); + virtual TSpineEnemy* createEnemyInstance(); +}; + +class TSamboHeadSaveLoadParams : public TWalkerEnemyParams { +public: + TSamboHeadSaveLoadParams(const char*); + +public: + /* 0x32C */ TParamRT mSLAppearDist; + /* 0x340 */ TParamRT mSLHideDist; + /* 0x354 */ TParamRT mSLMoveDist; + /* 0x368 */ TParamRT mSLMoveGravity; + /* 0x37C */ TParamRT mSLJumpSp; + /* 0x390 */ TParamRT mSLJumpPrepareTime; + /* 0x3A4 */ TParamRT mSLHitJumpSpXZ; + /* 0x3B8 */ TParamRT mSLHitJumpSpY; + /* 0x3CC */ TParamRT mSLHitJumpGravity; + /* 0x3E0 */ TParamRT mSLHitJumpSpRateXZ; + /* 0x3F4 */ TParamRT mSLHitJumpSpRateY; + /* 0x408 */ TParamRT mSLJumpAngY; +}; + +DECLARE_NERVE(TNerveHanaSamboFreeze, TLiveActor); + +DECLARE_NERVE(TNerveHanaSamboDie, TLiveActor); + +DECLARE_NERVE(TNerveHanaSamboHide, TLiveActor); + +DECLARE_NERVE(TNerveHanaSamboAppear, TLiveActor); + +DECLARE_NERVE(TNerveHanaSamboAttack, TLiveActor); + +DECLARE_NERVE(TNerveHanaSamboWait, TLiveActor); + +class THanaSambo : public TSmallEnemy { +public: + THanaSambo(const char* name = "ハナサンボ"); + + virtual void load(JSUMemoryInputStream&); + virtual void perform(u32, JDrama::TGraphics*); + virtual void init(TLiveManager*); + virtual void moveObject(); + virtual void drawObject(JDrama::TGraphics*); + virtual void kill(); + virtual const char** getBasNameTable() const; + virtual void reset(); + virtual void behaveToWater(THitActor*) { } + virtual void setDeadAnm(); + virtual void setWaitAnm(); + virtual void setMActorAndKeeper(); + virtual bool isHitValid(u32 message) + { + if (message == 0xB) + return true; + return false; + } + virtual bool isCollidMove(THitActor*) { return false; } + + void initFlower(); + void createPollen(); + void waterDamage(); + void setAttackAnm(); + static u8 mHeadJntIndex; + static u8 mPollenJntIndex; + +public: + /* 0x194 */ THanaSamboHead* unk194; + /* 0x198 */ THanaSamboSaveLoadParams* unk198; + /* 0x19C */ JGeometry::TVec3 unk19C; + /* 0x1A8 */ TSamboFlower* unk1A8; + /* 0x1AC */ class TMBindShadowBody* unk1AC; + /* 0x1B0 */ u8 unk1B0; + /* 0x1B4 */ JGeometry::TVec3 unk1B4[4]; +}; + +class THanaSamboHead : public THitActor { +public: + THanaSamboHead() + : THitActor("ハナサンボ頭あたり") + { + } + + virtual BOOL receiveMessage(THitActor*, u32); + + void kill(); + void checkHit(); + +public: + /* 0x68 */ THanaSambo* unk68; +}; + +class THanaSamboManager : public TSmallEnemyManager { +public: + THanaSamboManager(const char* name = "ハナサンボマネージャー"); + + virtual void load(JSUMemoryInputStream&); + virtual void createModelData(); + virtual TSpineEnemy* createEnemyInstance(); +}; + +class THanaSamboSaveLoadParams : public TSmallEnemyParams { +public: + THanaSamboSaveLoadParams(const char*); + +public: + /* 0x2D4 */ TParamRT mSLAttackDist; + /* 0x2E8 */ TParamRT mSLAttackInterval; + /* 0x2FC */ TParamRT mSLAppearDist; + /* 0x310 */ TParamRT mSLHideDist; + /* 0x324 */ TParamRT mSLAttackingTime; + /* 0x338 */ TParamRT mSLHeadAttackRadius; + /* 0x34C */ TParamRT mSLHeadAttackHeight; + /* 0x360 */ TParamRT mSLHeadDamageRadius; + /* 0x374 */ TParamRT mSLHeadDamageHeight; +}; + +class TSamboFlower : public TSpineEnemy { +public: + TSamboFlower(const char* name = "サンボフラワー") + : TSpineEnemy(name) + , unk150(0) + , unk154(0) + , unk158(-1) + , unk15C(-1) + , unk160(0) + , unk164(nullptr) + , unk168(nullptr) + { + } + + virtual void load(JSUMemoryInputStream&); + virtual void loadAfter(); + virtual BOOL receiveMessage(THitActor*, u32); + virtual void init(TLiveManager*); + virtual void control() { } + virtual void moveObject(); + virtual void drawObject(JDrama::TGraphics*); + virtual void reset(); + virtual void setMActorAndKeeper(); + + void hide(); + bool isBloomEnd(); + void bloom(); + +public: + /* 0x150 */ u8 unk150; + /* 0x151 */ char unk151[0x3]; + /* 0x154 */ s32 unk154; + /* 0x158 */ s32 unk158; + /* 0x15C */ s32 unk15C; + /* 0x160 */ u8 unk160; + /* 0x161 */ char unk161[0x3]; + /* 0x164 */ s32* unk164; + /* 0x168 */ class TMapObjBase* unk168; + /* 0x16C */ TSamboFlowerSaveLoadParams* unk16C; +}; + +class TSamboFlowerManager : public TEnemyManager { +public: + TSamboFlowerManager(const char* name = "サンボフラワーマネージャー") + : TEnemyManager(name) + , unk54(0) + , unk58(0) + , unk5C(0) + , unk60(0) + { + } + + virtual void load(JSUMemoryInputStream&); + virtual void loadAfter(); + virtual void perform(u32, JDrama::TGraphics*); + virtual void createModelData(); + virtual TSpineEnemy* createEnemyInstance(); + virtual void dropLeaf(JGeometry::TVec3&, JGeometry::TVec3&); + +public: + /* 0x54 */ TSamboFlowerCoinUnit** unk54; + /* 0x58 */ s32 unk58; + /* 0x5C */ s32 unk5C; + /* 0x60 */ TSamboLeaf** unk60; + /* 0x64 */ class J3DMaterialTable* unk64; +}; + +class TSamboFlowerSaveLoadParams : public TSpineEnemyParams { +public: + TSamboFlowerSaveLoadParams(const char*); + +public: + /* 0xA8 */ TParamRT mSLLeafVelocityXZ; + /* 0xBC */ TParamRT mSLLeafVelocityY; + /* 0xD0 */ TParamRT mSLLeafGravity; + /* 0xE4 */ TParamRT mSLBudDist; + /* 0xF8 */ TParamRT mSLBloomTimer; + /* 0x10C */ TParamRT mSLCoinCircleR; + /* 0x120 */ TParamRT mSLCoinVelocityXZ; + /* 0x134 */ TParamRT mSLCoinVelocityY; + /* 0x148 */ TParamRT mSLSeedShootRange; + /* 0x15C */ TParamRT mSLSeedShootInterval; + /* 0x170 */ TParamRT mSLSeedGravity; + /* 0x184 */ TParamRT mSLSeedSpeedXZ; + /* 0x198 */ TParamRT mSLSeedSpeedY; +}; + +class TSamboLeaf : public JDrama::TViewObj { +public: + TSamboLeaf(TSamboFlowerManager*, SDLModelData*, + const char* name = "サンボリーフ"); + + virtual void perform(u32, JDrama::TGraphics*); + + void generate(JGeometry::TVec3&); + +public: + /* 0x10 */ class SDLModel* unk10; + /* 0x14 */ JGeometry::TVec3 unk14; + /* 0x20 */ JGeometry::TVec3 unk20; + /* 0x2C */ JGeometry::TVec3 unk2C; + /* 0x38 */ JGeometry::TVec3 unk38; + /* 0x44 */ u8 unk44; + /* 0x45 */ char unk45[0x3]; + /* 0x48 */ TSamboFlowerManager* unk48; +}; + +class TSamboFlowerCoinUnit { +public: + TSamboFlowerCoinUnit(int); + + void checkGenCoin(); + void add(TSamboFlower*); + +public: + /* 0x0 */ TSamboFlower** unk0; + /* 0x4 */ JGeometry::TVec3 unk4; + /* 0x10 */ s32 unk10; + /* 0x14 */ s32 unk14; + /* 0x18 */ class TFlowerCoin* unk18; + /* 0x1C */ s32 unk1C; +}; + +#endif // ENEMY_HANASAMBO_HPP diff --git a/include/MoveBG/Item.hpp b/include/MoveBG/Item.hpp index e717577c2..e4f56a67f 100644 --- a/include/MoveBG/Item.hpp +++ b/include/MoveBG/Item.hpp @@ -62,7 +62,7 @@ class TFlowerCoin : public TCoin { virtual void load(JSUMemoryInputStream&); public: - /* 0x158 */ u32 unk158; + /* 0x158 */ s32 unk158; }; class TCoinEmpty : public TCoin { diff --git a/include/System/Particles.hpp b/include/System/Particles.hpp index 0e38660c9..2bd1b5843 100644 --- a/include/System/Particles.hpp +++ b/include/System/Particles.hpp @@ -150,8 +150,12 @@ 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, + PARTICLE_MS_SMB_KAFUN_OW = 0xB2, + PARTICLE_MS_SMB_KAFUN_OA = 0xB3, PARTICLE_MS_SMB_AP_ROCK = 0xB6, PARTICLE_MS_SMB_AP_SMOKE = 0xB7, + PARTICLE_MS_SMB_HD_ROCK = 0xB8, + PARTICLE_MS_SMB_HD_SMOKE = 0xB9, PARTICLE_MS_GESO_OSENHIT_A = 0xBC, PARTICLE_MS_GESO_OSENHIT_B = 0xBD, PARTICLE_MS_POI_DEAD = 0xC0, diff --git a/src/Enemy/hanasambo.cpp b/src/Enemy/hanasambo.cpp index 8b1378917..0c573bd2c 100644 --- a/src/Enemy/hanasambo.cpp +++ b/src/Enemy/hanasambo.cpp @@ -1 +1,1531 @@ +#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 +#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 +// rogue includes needed for matching sinit & bss +#include +#include + +static const char* sambo_bastable[] = { + "/scene/sambo/bas/sambo_down.bas", + 0, + 0, + 0, + "/scene/sambo/bas/sambo_Fhide.bas", + 0, + "/scene/sambo/bas/sambo_Fset.bas", + "/scene/sambo/bas/sambo_Fwait.bas", + "/scene/sambo/bas/sambo_hit.bas", + 0, + "/scene/sambo/bas/sambo_Ydown.bas", +}; + +static const char* sambohead_bastable[] = { + "/scene/sambohead/bas/flower_shoot.bas", + "/scene/sambohead/bas/samboHead_crash.bas", + "/scene/sambohead/bas/samboHead_dance.bas", + "/scene/sambohead/bas/samboHead_down.bas", + "/scene/sambohead/bas/samboHead_Fhide.bas", + "/scene/sambohead/bas/samboHead_hit.bas", + "/scene/sambohead/bas/samboHead_hit_end.bas", + "/scene/sambohead/bas/samboHead_jump_end.bas", + "/scene/sambohead/bas/samboHead_jump_start.bas", + 0, + "/scene/sambohead/bas/samboHead_set.bas", + "/scene/sambohead/bas/samboHead_turn.bas", + 0, +}; + +TSamboFlowerCoinUnit::TSamboFlowerCoinUnit(int param_1) + : unk0(nullptr) + , unk10(0) + , unk14(param_1) + , unk18(nullptr) + , unk1C(param_1) +{ + unk0 = new TSamboFlower*[param_1]; +} + +void TSamboFlowerCoinUnit::add(TSamboFlower* param_1) +{ + if (unk10 < unk14) { + unk0[unk10] = param_1; + param_1->unk164 = &unk1C; + unk10++; + } +} + +void TSamboFlowerCoinUnit::checkGenCoin() +{ + if (!unk18) + return; + + bool bloomed = true; + for (int i = 0; i < unk10; ++i) { + bool bloomEnd = unk0[i]->isBloomEnd(); + if (!bloomEnd) + bloomed = false; + } + + if (!bloomed) + return; + + int total = 0; + for (int i = 0; i < unk10; ++i) { + TSamboFlower* flower = unk0[i]; + if (flower->unk168) + total++; + + flower->unk160 = false; + } + + if (total > 0) { + Mtx mtx; + int spawned = 0; + for (int i = 0; i < unk10; ++i) { + if (!unk0[i]->unk168) + continue; + + f32 rate = (f32)spawned / (f32)total; + + JGeometry::TVec3 offset(0.0f, 0.0f, + unk0[i]->unk16C->mSLCoinCircleR.get()); + + MsMtxSetRotY(mtx, 360.0f * rate); + MTXMultVec(mtx, &offset, &offset); + + TMapObjBase* coin = unk0[i]->unk168; + if (coin->isActorType(0x2000000E)) + coin = gpItemManager->makeObjAppear(0x2000000E); + + if (coin) { + coin->appear(); + + JGeometry::TVec3 pos = unk4; + pos += offset; + coin->mPosition = pos; + + MsVECNormalize(&offset, &offset); + + TSamboFlowerSaveLoadParams* prm = unk0[i]->unk16C; + coin->mVelocity.set(offset.x * prm->mSLCoinVelocityXZ.get(), + 8.0f * rate + prm->mSLCoinVelocityY.get(), + offset.z * prm->mSLCoinVelocityXZ.get()); + coin->offLiveFlag(LIVE_FLAG_UNK10); + spawned++; + } + } + + SMSGetMSound()->startSoundSystemSE(MSD_SE_SY_COIN_APPEAR, spawned, + nullptr, 0); + } + + unk18 = nullptr; +} + +TSamboLeaf::TSamboLeaf(TSamboFlowerManager* param_1, SDLModelData* param_2, + const char* param_3) + : JDrama::TViewObj(param_3) + , unk10(nullptr) + , unk44(0) + , unk48(param_1) +{ + unk10 = new SDLModel(param_2, 3, 1); +} + +void TSamboLeaf::perform(u32 param_1, JDrama::TGraphics* param_2) +{ + if (!unk44) + return; + + if (param_1 & CUE_MOVE) { + unk14 += unk38; + + if (unk38.y > -20.0f) + unk38.y -= ((TSamboFlowerSaveLoadParams*)unk48->unk38) + ->mSLLeafGravity.get(); + + const TBGCheckData* ground; + if (unk14.y + < gpMap->checkGround(unk14.x, 20.0f + unk14.y, unk14.z, &ground)) + unk44 = false; + } + + if (param_1 & CUE_CALC_ANIM) { + Mtx mtx; + MsMtxSetXYZRPH(mtx, unk14.x, unk14.y, unk14.z, 0.0f, unk20.y, 0.0f); + + const JGeometry::TVec3& rot = MsGetRotFromZaxis(unk38); + Mtx rotMtx; + MsMtxSetRotZ(rotMtx, -rot.x); + MTXConcat(mtx, rotMtx, mtx); + + unk10->setBaseTRMtx(mtx); + unk10->setBaseScale(unk2C); + unk10->calc(); + } + + if (param_1 & CUE_CALC_VIEW) + unk10->viewCalc(); + + if (param_1 & CUE_ENTRY) + unk10->entry(); +} + +void TSamboLeaf::generate(JGeometry::TVec3& param_1) +{ + unk14 = param_1; + unk14.y += 10.0f; + unk44 = true; +} + +TSamboFlowerSaveLoadParams::TSamboFlowerSaveLoadParams(const char* param_1) + : TSpineEnemyParams(param_1) + , PARAM_INIT(mSLLeafVelocityXZ, 4.0f) + , PARAM_INIT(mSLLeafVelocityY, 6.0f) + , PARAM_INIT(mSLLeafGravity, 0.2f) + , PARAM_INIT(mSLBudDist, 2000.0f) + , PARAM_INIT(mSLBloomTimer, 300) + , PARAM_INIT(mSLCoinCircleR, 100.0f) + , PARAM_INIT(mSLCoinVelocityXZ, 12.0f) + , PARAM_INIT(mSLCoinVelocityY, 10.0f) + , PARAM_INIT(mSLSeedShootRange, 500.0f) + , PARAM_INIT(mSLSeedShootInterval, 200) + , PARAM_INIT(mSLSeedGravity, 0.1f) + , PARAM_INIT(mSLSeedSpeedXZ, 10.0f) + , PARAM_INIT(mSLSeedSpeedY, 10.0f) +{ + TParams::load(mPrmPath); +} + +void TSamboFlowerManager::load(JSUMemoryInputStream& param_1) +{ + TEnemyManager::load(param_1); + unk38 = new TSamboFlowerSaveLoadParams("/enemy/samboflower.prm"); + unk64 = J3DModelLoaderDataBase::loadMaterialTable( + JKRFileLoader::getGlbResource("/scene/samboflower/flower_orange.bmt")); +} + +void TSamboFlowerManager::createModelData() +{ + static TModelDataLoadEntry entry[] = { + { "flower.bmd", 0x10220000, 0 }, + { nullptr, 0, 0 }, + }; + createModelDataArray(entry); +} + +TSpineEnemy* TSamboFlowerManager::createEnemyInstance() +{ + return new TSamboFlower; +} + +void TSamboFlowerManager::loadAfter() +{ + void* leafRes + = JKRFileLoader::getGlbResource("/scene/samboflower/leaf.bmd"); + SDLModelData* leafData + = new SDLModelData(J3DModelLoaderDataBase::load(leafRes, 0x10210000)); + + unk60 = new TSamboLeaf*[0x12]; + for (int i = 0; i < 0x12; i++) + unk60[i] = new TSamboLeaf(this, leafData); + + unk58 = 0; + for (int i = 0; i < gpItemManager->getObjNum(); i++) { + if (strstr(gpItemManager->getObj(i)->getName(), "コイン(フラワー用)")) + unk58++; + } + + unk54 = new TSamboFlowerCoinUnit*[unk58]; + + int* counts = new int[unk58]; + for (int i = 0; i < unk58; i++) + counts[i] = 0; + + for (int i = 0; i < getObjNum(); i++) { + if (strstr(getObj(i)->getName(), "フラワー(コイン用)")) { + TSamboFlower* flower = (TSamboFlower*)getObj(i); + if (flower->unk158 < unk58) + counts[flower->unk158]++; + } + } + + for (int i = 0; i < unk58; i++) + unk54[i] = new TSamboFlowerCoinUnit(counts[i]); + + for (int i = 0; i < gpItemManager->getObjNum(); i++) { + if (strstr(gpItemManager->getObj(i)->getName(), + "コイン(フラワー用)")) { + TFlowerCoin* coin = (TFlowerCoin*)gpItemManager->getObj(i); + int idx = coin->unk158; + if (idx < unk58) { + unk54[idx]->unk18 = coin; + unk54[idx]->unk4 = coin->mPosition; + coin->kill(); + } + } + } + + for (int i = 0; i < getObjNum(); i++) { + if (strstr(getObj(i)->getName(), "フラワー(コイン用)")) { + TSamboFlower* flower = (TSamboFlower*)getObj(i); + if (flower->unk158 < unk58) + unk54[flower->unk158]->add(flower); + } + } + + JDrama::TNameRef::loadAfter(); +} + +void TSamboFlowerManager::perform(u32 param_1, JDrama::TGraphics* param_2) +{ + if (unk58 > 0 && param_1 & 2) { + for (int i = 0; i < unk58; i++) + unk54[i]->checkGenCoin(); + } + + TEnemyManager::perform(param_1, param_2); + + for (int i = 0; i < 0x12; ++i) + unk60[i]->perform(param_1, param_2); +} + +void TSamboFlowerManager::dropLeaf(JGeometry::TVec3& param_1, + JGeometry::TVec3& param_2) +{ + f32 rotY[] = { 0.0f, 120.0f, 240.0f }; + + int dropped = 0; + for (int i = 0; i < 0x12; i++) { + TSamboLeaf* leaf = unk60[i]; + if (!leaf->unk44) { + leaf->generate(param_1); + + TSamboFlowerSaveLoadParams* prm + = (TSamboFlowerSaveLoadParams*)unk38; + f32 velXZ = prm->mSLLeafVelocityXZ.get(); + f32 velY = prm->mSLLeafVelocityY.get(); + TMsRange speedXZ(velXZ, 1.2f * velXZ); + TMsRange speedY(velY, 1.2f * velY); + + JGeometry::TVec3 velocity(0.0f, speedY.rand(), speedXZ.rand()); + + Mtx mtx; + MsMtxSetRotRPH(mtx, 0.0f, rotY[i], 0.0f); + MTXMultVec(mtx, &velocity, &velocity); + + unk60[i]->unk20.set(0.0f, rotY[i] - 90.0f, 0.0f); + unk60[i]->unk38 = velocity; + unk60[i]->unk2C = param_2; + dropped++; + } + + if (dropped >= 3) + break; + } +} + +void TSamboFlower::load(JSUMemoryInputStream& param_1) +{ + TSpineEnemy::load(param_1); + param_1 >> unk15C; + param_1 >> unk158; + unk160 = true; + reset(); + offLiveFlag(LIVE_FLAG_UNK800); +} + +void TSamboFlower::loadAfter() +{ + JDrama::TNameRef::loadAfter(); + if (unk15C < 0) + return; + + TMapObjBase* obj = TMapObjBaseManager::newAndRegisterObj("coin"); + if (obj) + unk168 = obj; +} + +void TSamboFlower::init(TLiveManager* param_1) +{ + mManager = param_1; + param_1->manageActor(this); + setMActorAndKeeper(); + unk130 = 1; + unk16C = (TSamboFlowerSaveLoadParams*)getSaveParam(); + if (unk16C) { + mBodyRadius = unk16C->mSLBodyRadius.get(); + mWallRadius = unk16C->mSLWallRadius.get(); + mHeadHeight = unk16C->mSLHeadHeight.get(); + mScaledBodyRadius = mBodyScale * mBodyRadius; + } + + initHitActor(0x10000027, 1, 0x80000000, mBodyRadius, mHeadHeight, + mBodyRadius, mHeadHeight); + onHitFlag(HIT_FLAG_NO_COLLISION); + offLiveFlag(LIVE_FLAG_UNK400); + initAnmSound(); + mActorType = 0x10000027; + unk150 = false; + onLiveFlag(LIVE_FLAG_DEAD); +} + +void TSamboFlower::setMActorAndKeeper() +{ + mMActorKeeper = new TMActorKeeper(mManager, 1); + mMActor = mMActorKeeper->createMActor("flower.bmd", 3); + + MActor* actor = mMActor; + J3DMaterialTable* table = ((TSamboFlowerManager*)mManager)->unk64; + J3DModelData* model_data = actor->getModel()->getModelData(); + model_data->setMaterialTable(table, J3DMatCopyFlag_All); + actor->initDL(); + actor->getModel()->lock(); +} + +BOOL TSamboFlower::receiveMessage(THitActor* param_1, u32 param_2) +{ + if (param_2 == HIT_MESSAGE_SPRAYED_BY_WATER) { + if (!unk150) + bloom(); + return 1; + } + + return 0; +} + +void TSamboFlower::reset() +{ + TSpineEnemy::reset(); + mMActor->setBck("flower_wait"); + offHitFlag(HIT_FLAG_NO_COLLISION); + offLiveFlag(LIVE_FLAG_UNK800); + offLiveFlag(LIVE_FLAG_DEAD); + offLiveFlag(LIVE_FLAG_UNK10); +} + +void TSamboFlower::moveObject() +{ + if (unk150) { + if (checkCurAnmEnd(0) && getMActor()->checkCurAnm("flower_hit", 0)) + getMActor()->setBck("flower_fwait"); + + if (unk160) { + unk154++; + if (unk154 > unk16C->mSLBloomTimer.get()) { + if (getMActor()->checkCurAnm("flower_fwait", 0)) { + unk150 = false; + if (unk164) + *unk164 += 1; + + getMActor()->setBck("flower_hit"); + getMActor() + ->getFrameCtrl(ANM_TYPE_BCK) + ->setFrame( + getMActor()->getFrameCtrl(ANM_TYPE_BCK)->getEnd()); + getMActor()->setFrameRate(-SMSGetAnmFrameRate(), + ANM_TYPE_BCK); + } else if (getMActor()->getFrameCtrl(ANM_TYPE_BCK)->getFrame() + < 1.0f) { + unk154 = 0; + getMActor()->setBck("flower_wait"); + } + } + } + } + + if (!checkLiveFlag(LIVE_FLAG_UNK10)) + mPosition.y = gpMap->checkGround(mPosition.x, mPosition.y + 100.0f, + mPosition.z, &mGroundPlane); +} + +void TSamboFlower::drawObject(JDrama::TGraphics* param_1) +{ + if (checkLiveFlag(LIVE_FLAG_DEAD)) + return; + + TCircleShadowRequest request; + request.mPosition = mPosition; + request.mRadiusX = request.mRadiusZ = 120.0f; + gpBindShadowManager->request(request, getActorType() & ACTOR_TYPE_MASK); + mMActor->setLightData(mGroundPlane, mPosition); + mMActor->entry(); +} + +void TSamboFlower::bloom() +{ + unk150 = true; + unk154 = 0; + gpMarioParticleManager->emit(PARTICLE_MS_SMB_KAFUN_OW, &mPosition, 0, + nullptr); + mMActor->setBck("flower_hit"); + if (unk160 && unk164) { + *unk164 -= 1; + u32 id = MSD_SE_OBJ_FLOWER_OPEN_0 + *unk164; + SMSGetMSound()->startSoundActor(id, &mPosition, 0, nullptr, 0, 4); + } +} + +bool TSamboFlower::isBloomEnd() +{ + if (getMActor()->checkCurAnm("flower_fwait", 0)) + return true; + + return false; +} + +void TSamboFlower::hide() +{ + onHitFlag(HIT_FLAG_NO_COLLISION); + mMActor->setBck("flower_fwait"); + onLiveFlag(LIVE_FLAG_DEAD); +} + +THanaSamboSaveLoadParams::THanaSamboSaveLoadParams(const char* param_1) + : TSmallEnemyParams(param_1) + , PARAM_INIT(mSLAttackDist, 200.0f) + , PARAM_INIT(mSLAttackInterval, 200) + , PARAM_INIT(mSLAppearDist, 800.0f) + , PARAM_INIT(mSLHideDist, 1000.0f) + , PARAM_INIT(mSLAttackingTime, 30) + , PARAM_INIT(mSLHeadAttackRadius, 60.0f) + , PARAM_INIT(mSLHeadAttackHeight, 20.0f) + , PARAM_INIT(mSLHeadDamageRadius, 80.0f) + , PARAM_INIT(mSLHeadDamageHeight, 40.0f) +{ + TParams::load(mPrmPath); +} + +THanaSamboManager::THanaSamboManager(const char* param_1) + : TSmallEnemyManager(param_1) +{ +} + +void THanaSamboManager::load(JSUMemoryInputStream& param_1) +{ + TSmallEnemyManager::load(param_1); + unk38 = new THanaSamboSaveLoadParams("/enemy/hanasambo.prm"); +} + +TSpineEnemy* THanaSamboManager::createEnemyInstance() { return new THanaSambo; } + +void THanaSamboManager::createModelData() +{ + static TModelDataLoadEntry entry[] = { + { "sambo.bmd", 0x10220000, 0 }, + { "samboD.bmd", 0x10220000, 0 }, + { nullptr, 0, 0 }, + }; + createModelDataArray(entry); +} + +BOOL THanaSamboHead::receiveMessage(THitActor* param_1, u32 param_2) +{ + if (param_2 == HIT_MESSAGE_TRAMPLE || param_2 == HIT_MESSAGE_HIP_DROP) { + unk68->kill(); + return 1; + } + + if (param_2 == HIT_MESSAGE_SPRAYED_BY_WATER) { + unk68->waterDamage(); + return 1; + } + + return 0; +} + +void THanaSamboHead::checkHit() +{ + for (int i = 0; i < mColCount; ++i) { + if (mCollisions[i]->isActorType(0x80000001)) + SMS_SendMessageToMario(this, HIT_MESSAGE_ATTACK); + } +} + +void THanaSamboHead::kill() { onHitFlag(HIT_FLAG_NO_COLLISION); } + +THanaSambo::THanaSambo(const char* param_1) + : TSmallEnemy(param_1) + , unk194(nullptr) + , unk198(0) + , unk1A8(nullptr) + , unk1AC(0) +{ +} + +void THanaSambo::load(JSUMemoryInputStream& param_1) +{ + TSmallEnemy::load(param_1); + reset(); + setGoalPath(TPathNode((THitActor*)gpMarioAddress)); +} + +void THanaSambo::init(TLiveManager* param_1) +{ + TSmallEnemy::init(param_1); + mActorType = 0x1000001A; + unk150 = 0x11; + unk198 = (THanaSamboSaveLoadParams*)getSaveParam(); + mSpine->initWith(&TNerveHanaSamboHide::theNerve()); + unk1AC = new TMBindShadowBody(this, getModel(), 1.5f); + setGoalPath(TPathNode((THitActor*)gpMarioAddress)); + + if (mInstanceIndex == 0) { + // TODO: what this loop did is unknown + for (u8 i = 0; i < getActorKeeper() + ->getMActor("samboD.bmd") + ->getModel() + ->getModelData() + ->getJointNum(); + ++i) { } + } + + unk194 = new THanaSamboHead; + ((TIdxGroupObj*)JDrama::TNameRefGen::search("敵グループ"))->add(unk194); + unk194->initHitActor(0x1000001B, 2, 0x80000000, + unk198->mSLHeadAttackRadius.get() * mBodyScale, + unk198->mSLHeadAttackHeight.get() * mBodyScale, + unk198->mSLHeadDamageRadius.get() * mBodyScale, + unk198->mSLHeadDamageHeight.get() * mBodyScale); + unk194->unk68 = this; +} + +void THanaSambo::setMActorAndKeeper() +{ + mMActorKeeper = new TMActorKeeper(mManager, 2); + mMActor = mMActorKeeper->createMActor("sambo.bmd", 3); + mMActorKeeper->createMActor("samboD.bmd", 3); +} + +u8 THanaSambo::mHeadJntIndex = 3; +u8 THanaSambo::mPollenJntIndex = 6; + +void THanaSambo::moveObject() +{ + TSmallEnemy::moveObject(); + + if (checkLiveFlag(LIVE_FLAG_CLIPPED_OUT)) { + unk194->mPosition = mPosition; + } else { + MtxPtr mtx = getModel()->getAnmMtx(mHeadJntIndex); + unk194->mPosition.x = mtx[0][3]; + unk194->mPosition.y = mtx[1][3]; + unk194->mPosition.z = mtx[2][3]; + } + + unk194->checkHit(); + + mPosition.x = unk19C.x; + mPosition.z = unk19C.z; +} + +void THanaSambo::drawObject(JDrama::TGraphics* param_1) +{ + TLiveActor::drawObject(param_1); + if (!checkLiveFlag(LIVE_FLAG_DEAD | LIVE_FLAG_HIDDEN | LIVE_FLAG_UNK8)) + unk1AC->entryDrawShadow(); +} + +void THanaSambo::kill() +{ + if (checkLiveFlag(LIVE_FLAG_DEAD)) + return; + + mHitPoints = 1; + if (isBckAnm(7)) + unk18C = 3; + + if (mSpine->getCurrentNerve() != &TNerveHanaSamboDie::theNerve()) { + mSpine->reset(); + mSpine->setNext(&TNerveHanaSamboDie::theNerve()); + mSpine->pushAfterCurrent(&TNerveHanaSamboDie::theNerve()); + } + + unk194->kill(); + onLiveFlag(LIVE_FLAG_UNK40); +} + +void THanaSambo::reset() +{ + TSmallEnemy::reset(); + unk165 = false; + unk19C = mPosition; +} + +void THanaSambo::setWaitAnm() +{ + setBckAnm(7); + unk1B0 = 0; +} + +void THanaSambo::setDeadAnm() +{ + mMActor = getActorKeeper()->getMActor("samboD.bmd"); + if (unk1B0) + setBckAnm(10); + else + setBckAnm(0); + + unk194->kill(); + unk1B0 = 0; + onLiveFlag(LIVE_FLAG_UNK8); +} + +void THanaSambo::setAttackAnm() +{ + setBckAnm(3); + unk1B0 = 1; +} + +void THanaSambo::waterDamage() +{ + unk165 = true; + + if (changeByJuice()) + return; + + if (mSpine->getCurrentNerve() != &TNerveHanaSamboWait::theNerve()) + return; + + mSpine->pushNerve(&TNerveHanaSamboFreeze::theNerve()); +} + +const char** THanaSambo::getBasNameTable() const { return sambo_bastable; } + +void THanaSambo::perform(u32 param_1, JDrama::TGraphics* param_2) +{ + TSmallEnemy::perform(param_1, param_2); + if (!checkLiveFlag(LIVE_FLAG_DEAD | LIVE_FLAG_HIDDEN + | LIVE_FLAG_CLIPPED_OUT)) + unk194->THitActor::perform(param_1, param_2); +} + +void THanaSambo::createPollen() +{ + MtxPtr jointMtx = getMActor()->getModel()->getAnmMtx(mPollenJntIndex); + JGeometry::TVec3 pos; + pos.x = jointMtx[0][3]; + pos.y = jointMtx[1][3]; + pos.z = jointMtx[2][3]; + + Mtx mtx; + MsMtxSetRotRPH(mtx, 0.0f, 0.0f, -90.0f); + MTXConcat(jointMtx, mtx, mtx); + + JPABaseEmitter* emitter; + if (mSpine->getCurrentNerve() == &TNerveHanaSamboWait::theNerve()) { + emitter = gpMarioParticleManager->emit(PARTICLE_MS_SMB_KAFUN_OW, &pos, + 0, nullptr); + mtx[1][3] += 200.0f; + } else { + emitter = gpMarioParticleManager->emit(PARTICLE_MS_SMB_KAFUN_OA, &pos, + 0, nullptr); + + JGeometry::TVec3 offset(0.0f, 0.0f, 200.0f); + Mtx rot; + MsMtxSetRotRPH(rot, mRotation.x, mRotation.y, mRotation.z); + MTXMultVec(rot, &offset, &offset); + mtx[0][3] += offset.x; + mtx[1][3] += offset.y; + mtx[2][3] += offset.z; + } + + if (emitter) + emitter->setGlobalSRTMatrix(mtx); +} + +void THanaSambo::initFlower() +{ + if (!unk1A8) { + unk1A8 = (TSamboFlower*)gpConductor->makeOneEnemyAppear( + mPosition, "サンボフラワーマネージャー", 1); + unk1A8->reset(); + } + + unk1A8->onLiveFlag(LIVE_FLAG_UNK10); + unk1A8->offLiveFlag(LIVE_FLAG_DEAD); + unk1A8->mPosition = mPosition; + unk1A8->mPosition.y = mGroundHeight; +} + +DEFINE_NERVE(TNerveHanaSamboAppear, TLiveActor) +{ + THanaSambo* self = (THanaSambo*)spine->getBody(); + + if (spine->getTime() == 0) { + self->offLiveFlag(LIVE_FLAG_HIDDEN); + self->offHitFlag(HIT_FLAG_NO_COLLISION); + self->setBckAnm(6); + gpMarioParticleManager->emit(PARTICLE_MS_SMB_AP_ROCK, &self->mPosition, + 0, nullptr); + gpMarioParticleManager->emit(PARTICLE_MS_SMB_AP_SMOKE, &self->mPosition, + 0, nullptr); + self->unk1A8->hide(); + } + + if (self->checkCurAnmEnd(0)) { + spine->pushAfterCurrent(&TNerveHanaSamboWait::theNerve()); + self->setWaitAnm(); + if (gpMarioPos->y < 100.0f + self->mPosition.y) { + self->updateSquareToMario(); + if (self->mDistToMarioSquared + < self->unk198->mSLAttackDist.get() + * self->unk198->mSLAttackDist.get()) + spine->pushAfterCurrent(&TNerveHanaSamboAttack::theNerve()); + } + return true; + } + + self->walkToCurPathNode(0.0f, self->getTurnSpeed() * 3.0f, 0.0f); + return false; +} + +DEFINE_NERVE(TNerveHanaSamboWait, TLiveActor) +{ + THanaSambo* self = (THanaSambo*)spine->getBody(); + + if (spine->getTime() == 0) + self->setWaitAnm(); + + if (!self->checkLiveFlag(LIVE_FLAG_CLIPPED_OUT) + && self->getMActor()->getFrameCtrl(ANM_TYPE_BCK)->checkPass(16.0f)) + self->createPollen(); + + if (spine->getTime() > self->unk198->mSLAttackInterval.get() + && gpMarioPos->y < 100.0f + self->mPosition.y) { + self->updateSquareToMario(); + if (self->mDistToMarioSquared + < self->unk198->mSLAttackDist.get() + * self->unk198->mSLAttackDist.get()) { + spine->pushAfterCurrent(&TNerveHanaSamboAttack::theNerve()); + return true; + } + } + + self->updateSquareToMario(); + if (self->mDistToMarioSquared + > self->unk198->mSLHideDist.get() * self->unk198->mSLHideDist.get()) { + spine->pushAfterCurrent(&TNerveHanaSamboHide::theNerve()); + return true; + } + + self->walkToCurPathNode(0.0f, self->getTurnSpeed(), 0.0f); + return false; +} + +DEFINE_NERVE(TNerveHanaSamboAttack, TLiveActor) +{ + THanaSambo* self = (THanaSambo*)spine->getBody(); + + if (spine->getTime() == 0) { + self->setAttackAnm(); + } else if (self->checkCurAnmEnd(0)) { + if (self->isBckAnm(3)) { + self->createPollen(); + SMSGetMSound()->startSoundActor(MSD_SE_EN_SAMBO_ATTACK, + &self->mPosition, 0, nullptr, 0, 4); + + self->setBckAnm(1); + } else if (self->isBckAnm(1)) { + if (spine->getTime() > self->unk198->mSLAttackingTime.get() + && !self->unsetUnk165()) + self->setBckAnm(2); + else + self->setBckAnm(1); + } else { + spine->pushAfterCurrent(&TNerveHanaSamboWait::theNerve()); + return true; + } + } + + return false; +} + +DEFINE_NERVE(TNerveHanaSamboHide, TLiveActor) +{ + THanaSambo* self = (THanaSambo*)spine->getBody(); + + if (spine->getTime() == 0) { + self->setBckAnm(4); + gpMarioParticleManager->emit(PARTICLE_MS_SMB_HD_ROCK, &self->mPosition, + 0, nullptr); + gpMarioParticleManager->emit(PARTICLE_MS_SMB_HD_SMOKE, &self->mPosition, + 0, nullptr); + } + + if (spine->getTime() == 75) + self->initFlower(); + + if (self->checkCurAnmEnd(0)) { + self->initFlower(); + self->onHitFlag(HIT_FLAG_NO_COLLISION); + self->setBckAnm(6); + self->getMActor()->setFrameRate(0.0f, ANM_TYPE_BCK); + self->onLiveFlag(LIVE_FLAG_HIDDEN); + } + + self->updateSquareToMario(); + if (self->mDistToMarioSquared < self->unk198->mSLAppearDist.get() + * self->unk198->mSLAppearDist.get()) { + spine->pushAfterCurrent(&TNerveHanaSamboAppear::theNerve()); + return true; + } + + return false; +} + +DEFINE_NERVE(TNerveHanaSamboDie, TLiveActor) +{ + THanaSambo* self = (THanaSambo*)spine->getBody(); + + if (spine->getTime() == 0) { + self->onHitFlag(HIT_FLAG_NO_COLLISION); + self->setDeadAnm(); + } else if (self->checkCurAnmEnd(0) || spine->getTime() > 300) { + static int jIndexTable[] = { 1, 3, 4, 5 }; + + for (int i = 0; i < 4; ++i) { + MtxPtr mtx + = self->getMActor()->getModel()->getAnmMtx(jIndexTable[i]); + self->unk1B4[i].set(mtx[0][3], mtx[1][3], mtx[2][3]); + + if (JPABaseEmitter* emitter = gpMarioParticleManager->emit( + PARTICLE_MS_ENM_DISAP_A, &self->unk1B4[i], 0, nullptr)) + emitter->setGlobalScale(self->mScaling); + + if (JPABaseEmitter* emitter = gpMarioParticleManager->emit( + PARTICLE_MS_ENM_DISAP_B, &self->unk1B4[i], 0, nullptr)) + emitter->setGlobalScale(self->mScaling); + } + + self->onLiveFlag(LIVE_FLAG_DEAD); + self->onLiveFlag(LIVE_FLAG_UNK8); + self->offLiveFlag(LIVE_FLAG_HIDDEN); + self->offLiveFlag(TSmallEnemy::LIVE_FLAG_MELT_ON_DEATH); + self->mHolder = nullptr; + self->stopAnmSound(); + self->onHitFlag(HIT_FLAG_NO_COLLISION); + spine->reset(); + spine->setNext(&TNerveSmallEnemyDie::theNerve()); + spine->pushAfterCurrent(spine->getDefault()); + self->genRandomItem(); + } + + return false; +} + +DEFINE_NERVE(TNerveHanaSamboFreeze, TLiveActor) +{ + THanaSambo* self = (THanaSambo*)spine->getBody(); + + if (spine->getTime() == 0) + self->setBckAnm(5); + + if (self->checkCurAnmEnd(0)) { + if (self->isBckAnm(5)) { + if (self->unsetUnk165()) + self->setBckAnm(8); + } else if (self->unsetUnk165()) { + self->setBckAnm(8); + } else { + return true; + } + } + + return false; +} + +u8 TSamboHead::mBodyJntIndex; + +static TSamboHead* gpCurSamboHead; + +static int SamboHeadRollCallback(J3DNode* param_1, int param_2) +{ + if (param_2 == 0) { + if (gpCurSamboHead == nullptr || !gpCurSamboHead->isUseCallBack()) + return true; + + J3DJoint* joint = (J3DJoint*)param_1; + MtxPtr anmMtx + = gpCurSamboHead->getModel()->getAnmMtx(joint->getJntNo()); + + JGeometry::TVec3 velocity = gpCurSamboHead->mVelocity; + if (0.0f == velocity.x && 0.0f == velocity.z) + velocity.x = 0.001f; + + JGeometry::TVec3 side; + JGeometry::TVec3 up(0.0f, 1.0f, 0.0f); + VECCrossProduct(&up, &velocity, &side); + + JGeometry::TVec3 zAxis(anmMtx[0][2], anmMtx[1][2], anmMtx[2][2]); + JGeometry::TVec3 yAxis(anmMtx[0][1], anmMtx[1][1], anmMtx[2][1]); + JGeometry::TVec3 xAxis(anmMtx[0][0], anmMtx[1][0], anmMtx[2][0]); + + f32 rateZ = 0.0f; + if (0.0f != zAxis.squared()) + rateZ = side.dot(zAxis) / zAxis.squared(); + + f32 rateY = 0.0f; + if (0.0f != yAxis.squared()) + rateY = side.dot(yAxis) / yAxis.squared(); + + f32 rateX = 0.0f; + if (0.0f != xAxis.squared()) + rateX = side.dot(xAxis) / xAxis.squared(); + + JGeometry::TVec3 axis(rateX, rateY, rateZ); + + Mtx rotMtx; + MTXRotAxisRad(rotMtx, &axis, 0.017453292f * gpCurSamboHead->unk1AC); + MTXConcat(anmMtx, rotMtx, anmMtx); + MTXConcat(J3DSys::mCurrentMtx, rotMtx, J3DSys::mCurrentMtx); + } + return true; +} + +TSamboHeadSaveLoadParams::TSamboHeadSaveLoadParams(const char* param_1) + : TWalkerEnemyParams(param_1) + , PARAM_INIT(mSLAppearDist, 800.0f) + , PARAM_INIT(mSLHideDist, 1000.0f) + , PARAM_INIT(mSLMoveDist, 100.0f) + , PARAM_INIT(mSLMoveGravity, 0.1f) + , PARAM_INIT(mSLJumpSp, 10.0f) + , PARAM_INIT(mSLJumpPrepareTime, 20) + , PARAM_INIT(mSLHitJumpSpXZ, 12.0f) + , PARAM_INIT(mSLHitJumpSpY, 10.0f) + , PARAM_INIT(mSLHitJumpGravity, 1.0f) + , PARAM_INIT(mSLHitJumpSpRateXZ, 0.3f) + , PARAM_INIT(mSLHitJumpSpRateY, 0.3f) + , PARAM_INIT(mSLJumpAngY, 30.0f) +{ + TParams::load(mPrmPath); +} + +TSamboHeadManager::TSamboHeadManager(const char* param_1) + : TSmallEnemyManager(param_1) +{ + gpCurSamboHead = nullptr; +} + +void TSamboHeadManager::load(JSUMemoryInputStream& param_1) +{ + TSmallEnemyManager::load(param_1); + unk38 = new TSamboHeadSaveLoadParams("/enemy/sambohead.prm"); +} + +void TSamboHeadManager::createModelData() +{ + static TModelDataLoadEntry entry[] = { + { "samboHead.bmd", 0x10220000, 0 }, + { nullptr, 0, 0 }, + }; + createModelDataArray(entry); +} + +TSpineEnemy* TSamboHeadManager::createEnemyInstance() { return new TSamboHead; } + +TSamboHead::TSamboHead(const char* param_1) + : TWalkerEnemy(param_1) + , unk194(nullptr) + , unk198(nullptr) + , unk19C(0) + , unk1AC(0.0f) + , unk1B0(0) +{ +} + +void TSamboHead::load(JSUMemoryInputStream& param_1) +{ + TSmallEnemy::load(param_1); + reset(); + setGoalPath(TPathNode((THitActor*)gpMarioAddress)); +} + +void TSamboHead::init(TLiveManager* param_1) +{ + TWalkerEnemy::init(param_1); + mActorType = 0x1000001B; + unk150 = 0x11; + unk194 = (TSamboHeadSaveLoadParams*)getSaveParam(); + mSpine->initWith(&TNerveSamboHeadHide::theNerve()); + setGoalPath(TPathNode((THitActor*)gpMarioAddress)); + mMActor->setJointCallback(mBodyJntIndex, SamboHeadRollCallback); +} + +void TSamboHead::setMActorAndKeeper() +{ + mMActorKeeper = new TMActorKeeper(mManager, 1); + mMActor = mMActorKeeper->createMActor("samboHead.bmd", 3); +} + +void TSamboHead::reset() +{ + gpCurSamboHead = this; + TWalkerEnemy::reset(); + unk165 = false; + unk1B0 = 0; + unk19C = 0; + unk1AC = 0.0f; + offLiveFlag(LIVE_FLAG_UNK8); +} + +void TSamboHead::kill() +{ + mHitPoints = 1; + if (mSpine->getCurrentNerve() != &TNerveSmallEnemyDie::theNerve()) { + mSpine->reset(); + mSpine->setNext(&TNerveSmallEnemyDie::theNerve()); + mSpine->pushAfterCurrent(&TNerveSmallEnemyDie::theNerve()); + } + onLiveFlag(LIVE_FLAG_UNK40); +} + +void TSamboHead::behaveToWater(THitActor* param_1) +{ + if (mSpine->getCurrentNerve() == &TNerveSamboHeadHide::theNerve()) + return; + + if (mSpine->getCurrentNerve() == &TNerveSamboHeadAppear::theNerve()) + return; + + if (mSpine->getCurrentNerve() == &TNerveSamboHeadHitWall::theNerve() + || mSpine->getCurrentNerve() == &TNerveSmallEnemyDie::theNerve()) + return; + + JGeometry::TVec3 velocity = mLinearVelocity; + velocity.y = 0.0f; + + JGeometry::TVec3 jump(mPosition.x - gpMarioPos->x, 0.0f, + mPosition.z - gpMarioPos->z); + MsVECNormalize(&jump, &jump); + jump.scale(unk194->mSLHitJumpSpXZ.get()); + jump.y = unk194->mSLHitJumpSpY.get(); + + if (mSpine->getCurrentNerve() != &TNerveSamboHeadHitWater::theNerve()) + mSpine->pushNerve(&TNerveSamboHeadHitWater::theNerve()); + else + jump.add(velocity); + + setVelocity(jump); + mPosition.y += 2.0f; + onLiveFlag(LIVE_FLAG_AIRBORNE); +} + +void TSamboHead::attackToMario() +{ + sendAttackMsgToMario(); + + if (isAirborne()) { + JGeometry::TVec3 velocity(mPosition.x - gpMarioPos->x, 10.0f, + mPosition.z - gpMarioPos->z); + MsVECNormalize(&velocity, &velocity); + velocity.scale(8.0f); + setVelocity(velocity); + } else if (mSpine->getCurrentNerve() + == &TNerveSamboHeadAttack::theNerve()) { + mSpine->pushNerve(&TNerveSmallEnemyFreeze::theNerve()); + } +} + +f32 TSamboHead::getGravityY() const +{ + f32 gravity = mGravity; + + if (mSpine->getCurrentNerve() == &TNerveSamboHeadAttack::theNerve()) + gravity = unk194->mSLMoveGravity.get(); + + if (mSpine->getCurrentNerve() == &TNerveSamboHeadHitWater::theNerve()) + gravity = unk194->mSLHitJumpGravity.get(); + + return gravity; +} + +void TSamboHead::initFlower() +{ + if (!unk198) { + unk198 = (TSamboFlower*)gpConductor->makeOneEnemyAppear( + mPosition, "サンボフラワーマネージャー", 1); + unk198->reset(); + } + + unk198->offHitFlag(HIT_FLAG_NO_COLLISION); + unk198->onLiveFlag(LIVE_FLAG_UNK10); + unk198->offLiveFlag(LIVE_FLAG_DEAD); + unk198->mPosition.y = mGroundHeight; + unk198->mPosition = mPosition; +} + +void TSamboHead::setDeadAnm() { setBckAnm(3); } + +void TSamboHead::setAfterDeadEffect() +{ + JGeometry::TVec3 scale(1.5f); + + if (isBckAnm(1)) { + if (JPABaseEmitter* emitter = gpMarioParticleManager->emit( + PARTICLE_MS_ENM_DISAP_A_W, &mPosition, 0, nullptr)) + emitter->setGlobalScale(scale); + } else { + if (JPABaseEmitter* emitter = gpMarioParticleManager->emit( + PARTICLE_MS_ENM_DISAP_A, &mPosition, 0, nullptr)) + emitter->setGlobalScale(scale); + } + + if (JPABaseEmitter* emitter = gpMarioParticleManager->emit( + PARTICLE_MS_ENM_DISAP_B, &mPosition, 0, nullptr)) + emitter->setGlobalScale(scale); + + SMSGetMSound()->startSoundActor(MSD_SE_EN_COMMON_SMOKE, &mPosition, 0, + nullptr, 0, 4); +} + +void TSamboHead::setCrashAnm() +{ + setBckAnm(1); + + JGeometry::TVec3 scale(1.5f); + + if (JPABaseEmitter* emitter = gpMarioParticleManager->emitWithRotate( + PARTICLE_MS_ENM_WALLHIT, &mPosition, 0, DEG2SHORTANGLE(mRotation.y), + 0, 0, nullptr)) + emitter->setGlobalScale(scale); + + if (JPABaseEmitter* emitter = gpMarioParticleManager->emitWithRotate( + PARTICLE_MS_ENM_WALLHIT_O, &mPosition, 0, + DEG2SHORTANGLE(mRotation.y), 0, 0, nullptr)) { + emitter->setGlobalScale(scale); + SMSSetEmitterPolColor(emitter, 6); + } +} + +void TSamboHead::calcRootMatrix() +{ + gpCurSamboHead = this; + TSpineEnemy::calcRootMatrix(); +} + +void TSamboHead::genEventCoin() +{ + if (isBckAnm(1)) { + Mtx mtx; + MtxPtr matrix = mtx; + for (int i = 0; i < 3; ++i) { + MsMtxSetRotY(matrix, mRotation.y - 60.0f + 60.0f * i); + JGeometry::TVec3 offset(0.0f, 0.0f, 100.0f); + MTXMultVec(matrix, &offset, &offset); + + TMapObjBase* coin; + if (i == 1 && mCoin) { + coin = mCoin; + if (coin->isActorType(0x2000000E)) + coin = gpItemManager->makeObjAppear(0x2000000E); + + if (coin) { + coin->appear(); + coin->mPosition = mPosition; + } + } else { + coin = gpItemManager->makeObjAppear( + mPosition.x + offset.x, mPosition.y, mPosition.z + offset.z, + 0x2000000E, true); + } + + if (coin) { + coin->mPosition.y = mPosition.y; + MsVECNormalize(&offset, &offset); + coin->mVelocity.set(offset.x * 4.0f, + TMsRange(8.0f, 16.0f).rand(), + offset.z * 4.0f); + coin->offLiveFlag(LIVE_FLAG_UNK10); + } + } + } else { + TSmallEnemy::genEventCoin(); + } +} + +bool TSamboHead::isUseCallBack() +{ + if (mSpine->getCurrentNerve() == &TNerveSamboHeadAttack::theNerve() + || mSpine->getCurrentNerve() == &TNerveSamboHeadHitWater::theNerve() + || mSpine->getCurrentNerve() + == &TNerveSamboHeadRecoverWater::theNerve()) + return true; + + return false; +} + +const char** TSamboHead::getBasNameTable() const { return sambohead_bastable; } + +DEFINE_NERVE(TNerveSamboHeadAppear, TLiveActor) +{ + TSamboHead* self = (TSamboHead*)spine->getBody(); + + if (spine->getTime() == 0) { + self->offLiveFlag(LIVE_FLAG_HIDDEN); + self->offHitFlag(HIT_FLAG_NO_COLLISION); + if (self->unk198->unk150) { + self->unk198->bloom(); + self->setBckAnm(10); + } else { + self->setBckAnm(10); + } + gpMarioParticleManager->emit(PARTICLE_MS_SMB_AP_ROCK, &self->mPosition, + 0, nullptr); + gpMarioParticleManager->emit(PARTICLE_MS_SMB_AP_SMOKE, &self->mPosition, + 0, nullptr); + self->unk198->hide(); + } + + if (spine->getTime() == 20) + ((TSamboFlowerManager*)self->unk198->mManager) + ->dropLeaf(self->mPosition, self->mScaling); + + if (self->checkCurAnmEnd(0)) { + self->setBckAnm(12); + spine->pushAfterCurrent(&TNerveSamboHeadAttack::theNerve()); + return true; + } + + return false; +} + +DEFINE_NERVE(TNerveSamboHeadAttack, TLiveActor) +{ + TSamboHead* self = (TSamboHead*)spine->getBody(); + + if (!self->isAirborne()) { + if (self->unk19C > self->unk194->mSLJumpPrepareTime.get() + && self->checkCurAnmEnd(0)) { + self->unk19C = 0; + self->updateSquareToMario(); + + JGeometry::TVec3 goal = self->getUnk104().getPoint(); + goal.set(gpMarioPos->x - self->mPosition.x, 0.0f, + gpMarioPos->z - self->mPosition.z); + if (goal.x == 0.0f && goal.y == 0.0f && goal.z == 0.0f) + goal.x += 1.0f; + + MsVECNormalize(&goal, &goal); + + f32 moveDist = self->unk194->mSLMoveDist.get(); + goal.x = goal.x * moveDist + self->mPosition.x; + goal.z = goal.z * moveDist + self->mPosition.z; + goal.y = self->mPosition.y; + + f32 jumpSp = self->unk194->mSLJumpSp.get(); + self->setVelocity( + self->calcVelocityToJumpToY(goal, jumpSp, self->getGravityY())); + self->mPosition.y += 2.0f; + self->onLiveFlag(LIVE_FLAG_AIRBORNE); + self->setBckAnm(8); + } else { + self->unk19C++; + } + + if (self->checkCurAnmEnd(0) && self->isBckAnm(7)) + self->setBckAnm(12); + + self->getMActor()->setFrameRate(SMSGetAnmFrameRate(), ANM_TYPE_BCK); + } else { + JGeometry::TVec3 velocity = self->mLinearVelocity; + if (velocity.y < 0.0f && self->isBckAnm(8)) { + self->setBckAnm(7); + self->getMActor()->setFrameRate(0.0f, ANM_TYPE_BCK); + } + } + + if (self->mPosition.y > 30.0f + self->mGroundHeight) { + f32 limit = self->unk194->mSLJumpAngY.get(); + JGeometry::TVec3 velocity = self->mLinearVelocity; + self->unk1AC = MsClamp(MsGetRotFromZaxis(velocity).x, -limit, limit); + } else { + self->unk1AC *= 0.8f; + } + + f32 turnSpeed = self->getTurnSpeed(); + if (self->isAirborne()) + turnSpeed = 5.0f; + + self->walkToCurPathNode(0.0f, turnSpeed, 0.0f); + return false; +} + +DEFINE_NERVE(TNerveSamboHeadHide, TLiveActor) +{ + TSamboHead* self = (TSamboHead*)spine->getBody(); + + if (spine->getTime() == 0) { + self->setBckAnm(4); + self->onHitFlag(HIT_FLAG_NO_COLLISION); + gpMarioParticleManager->emit(PARTICLE_MS_SMB_HD_ROCK, &self->mPosition, + 0, nullptr); + gpMarioParticleManager->emit(PARTICLE_MS_SMB_HD_SMOKE, &self->mPosition, + 0, nullptr); + } else if (self->checkCurAnmEnd(0)) { + self->onLiveFlag(LIVE_FLAG_HIDDEN); + self->setBckAnm(12); + self->initFlower(); + } else if (self->isFindMario(1.0f)) { + self->updateSquareToMario(); + if (self->mDistToMarioSquared + < self->unk194->mSLAppearDist.get() + * self->unk194->mSLAppearDist.get()) { + spine->pushAfterCurrent(&TNerveSamboHeadAppear::theNerve()); + return true; + } + } + + self->walkToCurPathNode(0.0f, self->getTurnSpeed(), 0.0f); + return false; +} + +DEFINE_NERVE(TNerveSamboHeadHitWater, TLiveActor) +{ + TSamboHead* self = (TSamboHead*)spine->getBody(); + + if (spine->getTime() == 0) { + self->setBckAnm(5); + self->unk1A0 = self->mLinearVelocity; + } + + if (self->isHitWallInBound()) { + self->unk1AC = 0.0f; + if (self->unk1B0) + self->mRotation.y += 180.0f; + + spine->reset(); + spine->setNext(&TNerveSamboHeadHitWall::theNerve()); + spine->pushAfterCurrent(&TNerveSamboHeadHitWall::theNerve()); + return true; + } + + f32 limit = self->unk194->mSLJumpAngY.get(); + if (self->isBckAnm(6)) { + if (spine->getTime() < 100) + self->unk1AC = MsClamp(self->unk1AC - 3.0f, -limit, limit); + else + self->unk1AC = MsClamp(3.0f + self->unk1AC, -limit, 0.0f); + } else { + self->unk1AC = MsClamp(3.0f + self->unk1AC, -limit, limit); + } + + if (self->isBckAnm(5) && !self->isAirborne()) + self->setBckAnm(7); + + if (self->isBckAnm(6)) { + self->onLiveFlag(LIVE_FLAG_AIRBORNE); + self->mPosition.y = 1.0f + self->mGroundHeight; + self->setVelocity(self->unk1A0); + } + + if (!self->isAirborne()) + self->setBckAnm(6); + + if (self->checkCurAnmEnd(0) && self->isBckAnm(6)) { + f32 rate = self->unk194->mSLHitJumpSpRateXZ.get(); + self->setBckAnm(6); + self->unk1A0.x *= rate; + self->unk1A0.z *= rate; + self->unk1A0.y = 0.0f; + self->setVelocity(self->unk1A0); + self->mPosition.y = self->mGroundHeight; + self->offLiveFlag(LIVE_FLAG_AIRBORNE); + self->setBckAnm(12); + spine->setNext(&TNerveSamboHeadAttack::theNerve()); + spine->pushAfterCurrent(&TNerveSamboHeadRecoverWater::theNerve()); + return true; + } + + self->walkToCurPathNode(0.0f, self->getTurnSpeed(), 0.0f); + return false; +} + +DEFINE_NERVE(TNerveSamboHeadRecoverWater, TLiveActor) +{ + TSamboHead* self = static_cast(spine->getBody()); + if (spine->getTime() == 0) + self->setBckAnm(12); + + self->unk1AC *= 0.99f; + if (self->checkCurAnmEnd(0) && self->unk1AC < 1.0f) + return true; + return false; +} + +DEFINE_NERVE(TNerveSamboHeadHitWall, TLiveActor) +{ + TSamboHead* self = (TSamboHead*)spine->getBody(); + + if (spine->getTime() == 0) + self->setCrashAnm(); + + int waitTime = ((TSmallEnemyManager*)self->getManager())->unk5C; + if (self->checkCurAnmEnd(0) + && spine->getTime() > waitTime + + self->getMActor() + ->getFrameCtrl(ANM_TYPE_BCK) + ->getEnd()) { + self->onLiveFlag(LIVE_FLAG_DEAD); + self->onLiveFlag(LIVE_FLAG_UNK8); + self->onLiveFlag(LIVE_FLAG_UNK20000); + self->offLiveFlag(TSmallEnemy::LIVE_FLAG_MELT_ON_DEATH); + self->mHolder = nullptr; + self->stopAnmSound(); + spine->reset(); + spine->setNext(&TNerveSmallEnemyDie::theNerve()); + spine->pushAfterCurrent(&TNerveSmallEnemyDie::theNerve()); + self->genRandomItem(); + return true; + } + + return false; +}