diff --git a/asm/MetroidPrime/ScriptObjects/CScriptVisorGoo.s b/asm/MetroidPrime/ScriptObjects/CScriptVisorGoo.s index 152c2e73..57607a2f 100644 --- a/asm/MetroidPrime/ScriptObjects/CScriptVisorGoo.s +++ b/asm/MetroidPrime/ScriptObjects/CScriptVisorGoo.s @@ -3,8 +3,8 @@ .section .data .balign 8 -.global lbl_803E58C0 -lbl_803E58C0: +.global __vt__15CScriptVisorGoo +__vt__15CScriptVisorGoo: # ROM: 0x3E28C0 .4byte 0 .4byte 0 @@ -482,9 +482,9 @@ __dt__15CScriptVisorGooFv: /* 801D75B8 001D4518 93 C1 00 08 */ stw r30, 8(r1) /* 801D75BC 001D451C 7C 7E 1B 79 */ or. r30, r3, r3 /* 801D75C0 001D4520 41 82 00 54 */ beq lbl_801D7614 -/* 801D75C4 001D4524 3C 60 80 3E */ lis r3, lbl_803E58C0@ha +/* 801D75C4 001D4524 3C 60 80 3E */ lis r3, __vt__15CScriptVisorGoo@ha /* 801D75C8 001D4528 34 1E 00 F0 */ addic. r0, r30, 0xf0 -/* 801D75CC 001D452C 38 03 58 C0 */ addi r0, r3, lbl_803E58C0@l +/* 801D75CC 001D452C 38 03 58 C0 */ addi r0, r3, __vt__15CScriptVisorGoo@l /* 801D75D0 001D4530 90 1E 00 00 */ stw r0, 0(r30) /* 801D75D4 001D4534 41 82 00 10 */ beq lbl_801D75E4 /* 801D75D8 001D4538 38 7E 00 F0 */ addi r3, r30, 0xf0 @@ -570,9 +570,9 @@ lbl_801D7614: /* 801D7700 001D4660 38 61 00 D0 */ addi r3, r1, 0xd0 /* 801D7704 001D4664 38 80 FF FF */ li r4, -1 /* 801D7708 001D4668 4B F3 F3 45 */ bl __dt__10CModelDataFv -/* 801D770C 001D466C 3C 60 80 3E */ lis r3, lbl_803E58C0@ha +/* 801D770C 001D466C 3C 60 80 3E */ lis r3, __vt__15CScriptVisorGoo@ha /* 801D7710 001D4670 3A BA 00 E8 */ addi r21, r26, 0xe8 -/* 801D7714 001D4674 38 03 58 C0 */ addi r0, r3, lbl_803E58C0@l +/* 801D7714 001D4674 38 03 58 C0 */ addi r0, r3, __vt__15CScriptVisorGoo@l /* 801D7718 001D4678 38 61 00 28 */ addi r3, r1, 0x28 /* 801D771C 001D467C 90 1A 00 00 */ stw r0, 0(r26) /* 801D7720 001D4680 38 80 00 00 */ li r4, 0 diff --git a/configure.py b/configure.py index 6676c724..75d9b2dc 100755 --- a/configure.py +++ b/configure.py @@ -286,7 +286,7 @@ LIBS = [ "MetroidPrime/Player/CGameState", ["MetroidPrime/ScriptObjects/CScriptVisorFlare", False], ["MetroidPrime/ScriptObjects/CScriptWorldTeleporter", False], - "MetroidPrime/ScriptObjects/CScriptVisorGoo", + ["MetroidPrime/ScriptObjects/CScriptVisorGoo", False], "MetroidPrime/Enemies/CJellyZap", ["MetroidPrime/ScriptObjects/CScriptControllerAction", False], ["MetroidPrime/Weapons/GunController/CGunMotion", False], diff --git a/include/Kyoto/Math/CMath.hpp b/include/Kyoto/Math/CMath.hpp index 0313ff2e..d13b72dc 100644 --- a/include/Kyoto/Math/CMath.hpp +++ b/include/Kyoto/Math/CMath.hpp @@ -12,6 +12,7 @@ class CMath { public: static float FastCosR(float v); static float FastSinR(float v); + static float FastArcCosR(float v); static inline float FastFmod(float x, float y) { int v = static_cast< int >(x * (1.f / y)); return x - v * y; diff --git a/include/MetroidPrime/ScriptObjects/CScriptVisorGoo.hpp b/include/MetroidPrime/ScriptObjects/CScriptVisorGoo.hpp new file mode 100644 index 00000000..3dc36354 --- /dev/null +++ b/include/MetroidPrime/ScriptObjects/CScriptVisorGoo.hpp @@ -0,0 +1,39 @@ +#ifndef _CSCRIPTVISORGOO +#define _CSCRIPTVISORGOO + +#include "MetroidPrime/CActor.hpp" + +class CGenDescription; +class CElectricDescription; + +class CScriptVisorGoo : public CActor { +public: + CScriptVisorGoo(TUniqueId uid, const rstl::string& name, const CEntityInfo& info, + const CTransform4f& xf, CAssetId particle, CAssetId electric, float minDist, + float maxDist, float nearProb, float farProb, const CColor& color, int sfx, + bool forceShow, bool active); + ~CScriptVisorGoo(); + + void Accept(IVisitor& visitor) override; + void Think(float, CStateManager& stateMgr) override; + void AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId objId, CStateManager& stateMgr) override; + void AddToRenderer(const CFrustumPlanes&, const CStateManager&) const override; + void Render(const CStateManager&) const override; + rstl::optional_object< CAABox > GetTouchBounds() const override; + void Touch(CActor&, CStateManager&) override; + +private: + TToken< CGenDescription > xe8_particleDesc; + TToken< CElectricDescription > xf0_electricDesc; + ushort xf8_sfx; + CAssetId xfc_particleId; + CAssetId x100_electricId; + float x104_minDist; + float x108_maxDist; + float x10c_nearProb; + float x110_farProb; + CColor x114_color; + bool x118_24_angleTest : 1; +}; + +#endif // _CSCRIPTVISORGOO diff --git a/src/MetroidPrime/ScriptObjects/CScriptVisorGoo.cpp b/src/MetroidPrime/ScriptObjects/CScriptVisorGoo.cpp new file mode 100644 index 00000000..0c2e1e42 --- /dev/null +++ b/src/MetroidPrime/ScriptObjects/CScriptVisorGoo.cpp @@ -0,0 +1,142 @@ +#include "MetroidPrime/ScriptObjects/CScriptVisorGoo.hpp" + +#include "MetroidPrime/CActorParameters.hpp" +#include "MetroidPrime/CStateManager.hpp" +#include "MetroidPrime/Cameras/CCameraManager.hpp" +#include "MetroidPrime/Player/CPlayer.hpp" +#include "MetroidPrime/ScriptObjects/CHUDBillboardEffect.hpp" + +#include "Kyoto/Audio/CSfxManager.hpp" + +#include "rstl/math.hpp" + +CScriptVisorGoo::CScriptVisorGoo(TUniqueId uid, const rstl::string& name, const CEntityInfo& info, + const CTransform4f& xf, CAssetId particle, CAssetId electric, + float minDist, float maxDist, float nearProb, float farProb, + const CColor& color, int sfx, bool forceShow, bool active) +: CActor(uid, active, name, info, xf, CModelData::CModelDataNull(), CMaterialList(), + CActorParameters::None(), kInvalidUniqueId) +, xe8_particleDesc(nullptr) +, xf0_electricDesc(nullptr) +, xf8_sfx(CSfxManager::TranslateSFXID(sfx)) +, xfc_particleId(particle) +, x100_electricId(electric) +, x104_minDist(minDist) +, x108_maxDist(rstl::max_val(maxDist, minDist + 0.01f)) +, x10c_nearProb(nearProb) +, x110_farProb(farProb) +, x114_color(color) { + x118_24_angleTest = !forceShow; + if (particle != kInvalidAssetId) { + xe8_particleDesc = gpSimplePool->GetObj(SObjectTag('PART', particle)); + } + if (electric != kInvalidAssetId) { + xf0_electricDesc = gpSimplePool->GetObj(SObjectTag('ELSC', electric)); + } +} + +CScriptVisorGoo::~CScriptVisorGoo() {} + +void CScriptVisorGoo::Think(float, CStateManager& mgr) { + if (GetActive()) { + bool loaded = false; + if (xfc_particleId != kInvalidAssetId) { + if (xe8_particleDesc.IsLoaded()) { + if (x100_electricId != kInvalidAssetId) { + if (xf0_electricDesc.IsLoaded()) { + loaded = true; + } + } else { + loaded = true; + } + } + } else { + if (xf0_electricDesc.IsLoaded()) { + loaded = true; + } + } + + if (loaded) { + bool showGoo = false; + if (mgr.GetPlayer()->GetCameraState() == CPlayer::kCS_FirstPerson) { + const CVector3f eyeToGoo = GetTranslation() - mgr.GetPlayer()->GetEyePosition(); + const float eyeToGooDist = eyeToGoo.Magnitude(); + if (eyeToGooDist >= x104_minDist && eyeToGooDist <= x108_maxDist) { + if (x118_24_angleTest) { + CVector3f colNorm = mgr.GetCameraManager() + ->GetCurrentCameraTransform(mgr) + .GetColumn(kDY) + .AsNormalized(); + float angleThresh = 45.f; + float angle = CMath::Rad2Deg(CMath::FastArcCosR( + CVector3f::Dot(eyeToGoo.AsNormalized(), colNorm))) * + 360.f; + if (eyeToGooDist < 4.f) { + angleThresh *= 4.f / eyeToGooDist; + angleThresh = rstl::min_val(angleThresh, 90.f); + } + if (angle <= angleThresh) { + showGoo = true; + } + } else { + showGoo = true; + } + if (showGoo) { + const float t = (x108_maxDist - eyeToGooDist) / (x108_maxDist - x104_minDist); + float prob = t * x10c_nearProb + (1.0f - t) * x110_farProb; + if (mgr.Random()->Float() * 100.f <= prob) { + mgr.AddObject(new CHUDBillboardEffect( + xfc_particleId != kInvalidAssetId + ? rstl::optional_object< TToken< CGenDescription > >(xe8_particleDesc) + : rstl::optional_object_null(), + x100_electricId != kInvalidAssetId + ? rstl::optional_object< TToken< CElectricDescription > >(xf0_electricDesc) + : rstl::optional_object_null(), + mgr.AllocateUniqueId(), true, rstl::string_l("VisorGoo"), + CHUDBillboardEffect::GetNearClipDistance(mgr), + CHUDBillboardEffect::GetScaleForPOV(mgr), x114_color, CVector3f(1.f, 1.f, 1.f), + CVector3f(0.f, 0.f, 0.f))); + CSfxManager::SfxStart(xf8_sfx, 0x7f, 0x40, false, CSfxManager::kMedPriority); + } + } + } + } + mgr.FreeScriptObject(GetUniqueId()); + } + } +} + +void CScriptVisorGoo::Touch(CActor& other, CStateManager& mgr) { + // Empty +} + +rstl::optional_object< CAABox > CScriptVisorGoo::GetTouchBounds() const { + return rstl::optional_object_null(); +} + +void CScriptVisorGoo::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId objId, + CStateManager& mgr) { + switch (msg) { + case kSM_Activate: + if (xfc_particleId != kInvalidAssetId) { + xe8_particleDesc.Lock(); + } + if (x100_electricId != kInvalidAssetId) { + xf0_electricDesc.Lock(); + } + break; + default: + break; + } + CActor::AcceptScriptMsg(msg, objId, mgr); +} + +void CScriptVisorGoo::Render(const CStateManager& mgr) const { + // Empty +} + +void CScriptVisorGoo::AddToRenderer(const CFrustumPlanes& frustum, const CStateManager& mgr) const { + // Empty +} + +void CScriptVisorGoo::Accept(IVisitor& visitor) { visitor.Visit(*this); }