diff --git a/config/GM8E01_00/symbols.txt b/config/GM8E01_00/symbols.txt index bfbefa90..bac8230e 100644 --- a/config/GM8E01_00/symbols.txt +++ b/config/GM8E01_00/symbols.txt @@ -1891,7 +1891,7 @@ ForceClosed__11CScriptDoorFR13CStateManager = .text:0x8007E0BC; // type:function IsConnectedToArea__11CScriptDoorCFRC13CStateManager7TAreaId = .text:0x8007E1C4; // type:function size:0xD8 scope:global Think__11CScriptDoorFfR13CStateManager = .text:0x8007E29C; // type:function size:0x2B4 scope:global AcceptScriptMsg__11CScriptDoorF20EScriptObjectMessage9TUniqueIdR13CStateManager = .text:0x8007E550; // type:function size:0x480 scope:global -SetDoorAnimation__11CScriptDoorFQ211CScriptDoor13EDoorAnimType = .text:0x8007E9D0; // type:function size:0x94 scope:global +SetDoorAnimation__11CScriptDoorFQ211CScriptDoor10EDoorState = .text:0x8007E9D0; // type:function size:0x94 scope:global OpenDoor__11CScriptDoorF9TUniqueIdR13CStateManager = .text:0x8007EA64; // type:function size:0x2E8 scope:global GetDoorOpenCondition__11CScriptDoorFR13CStateManager = .text:0x8007ED4C; // type:function size:0x308 scope:global GetOrbitPosition__11CScriptDoorCFRC13CStateManager = .text:0x8007F054; // type:function size:0x34 scope:global @@ -21626,8 +21626,8 @@ lbl_805A8E2C = .sbss:0x805A8E2C; // type:object size:0x4 data:4byte lbl_805A8E30 = .sbss:0x805A8E30; // type:object size:0x8 data:byte lbl_805A8E38 = .sbss:0x805A8E38; // type:object size:0x4 data:4byte lbl_805A8E3C = .sbss:0x805A8E3C; // type:object size:0x4 data:4byte -lbl_805A8E40 = .sbss:0x805A8E40; // type:object size:0x4 data:4byte -lbl_805A8E44 = .sbss:0x805A8E44; // type:object size:0x1 data:byte +i$486 = .sbss:0x805A8E40; // type:object size:0x4 data:4byte +init$487 = .sbss:0x805A8E44; // type:object size:0x1 data:byte kLineOfSightIncludeList = .sbss:0x805A8E48; // type:object size:0x4 scope:local data:4byte lbl_805A8E4C = .sbss:0x805A8E4C; // type:object size:0x4 data:4byte kLineOfSightExcludeList = .sbss:0x805A8E50; // type:object size:0x4 scope:local data:4byte diff --git a/config/GM8E01_01/symbols.txt b/config/GM8E01_01/symbols.txt index ac701f7c..7fdff567 100644 --- a/config/GM8E01_01/symbols.txt +++ b/config/GM8E01_01/symbols.txt @@ -1891,7 +1891,7 @@ ForceClosed__11CScriptDoorFR13CStateManager = .text:0x8007E138; // type:function IsConnectedToArea__11CScriptDoorCFRC13CStateManager7TAreaId = .text:0x8007E240; // type:function size:0xD8 scope:global Think__11CScriptDoorFfR13CStateManager = .text:0x8007E318; // type:function size:0x2B4 scope:global AcceptScriptMsg__11CScriptDoorF20EScriptObjectMessage9TUniqueIdR13CStateManager = .text:0x8007E5CC; // type:function size:0x480 scope:global -SetDoorAnimation__11CScriptDoorFQ211CScriptDoor13EDoorAnimType = .text:0x8007EA4C; // type:function size:0x94 scope:global +SetDoorAnimation__11CScriptDoorFQ211CScriptDoor10EDoorState = .text:0x8007EA4C; // type:function size:0x94 scope:global OpenDoor__11CScriptDoorF9TUniqueIdR13CStateManager = .text:0x8007EAE0; // type:function size:0x2E8 scope:global GetDoorOpenCondition__11CScriptDoorFR13CStateManager = .text:0x8007EDC8; // type:function size:0x308 scope:global GetOrbitPosition__11CScriptDoorCFRC13CStateManager = .text:0x8007F0D0; // type:function size:0x34 scope:global diff --git a/include/MetroidPrime/CMapWorldInfo.hpp b/include/MetroidPrime/CMapWorldInfo.hpp index 782de631..22f2f725 100644 --- a/include/MetroidPrime/CMapWorldInfo.hpp +++ b/include/MetroidPrime/CMapWorldInfo.hpp @@ -7,6 +7,7 @@ class CMapWorldInfo { public: bool IsAreaVisible(const TAreaId areaId) const; bool IsDoorVisited(const TEditorId eid) const; + void SetDoorVisited(TEditorId eid, const bool visited); }; #endif // _CMAPWORLDINFO diff --git a/include/MetroidPrime/CStateManager.hpp b/include/MetroidPrime/CStateManager.hpp index 28ce2410..2b06b14b 100644 --- a/include/MetroidPrime/CStateManager.hpp +++ b/include/MetroidPrime/CStateManager.hpp @@ -291,6 +291,7 @@ public: bool GetInMapScreen() const { return xf94_27_inMapScreen; } void SetIsFullThreat(bool v) { xf94_30_fullThreat = v; } uint GetInputFrameIdx() const { return x8d4_inputFrameIdx; } + CMapWorldInfo* MapWorldInfo() const { return x8c0_mapWorldInfo.GetPtr(); } private: enum EInitPhase { kIP_LoadWorld, kIP_LoadFirstArea, kIP_Done }; diff --git a/include/MetroidPrime/CWorld.hpp b/include/MetroidPrime/CWorld.hpp index 8b1ff355..f96ccbb9 100644 --- a/include/MetroidPrime/CWorld.hpp +++ b/include/MetroidPrime/CWorld.hpp @@ -143,6 +143,9 @@ public: static CGameArea::CConstChainIterator GetAliveAreasEnd(); void StopGlobalSound(ushort soundId); + int GetNumAreas() const { return x18_areas.size(); } + bool AreSkyNeedsMet() const; + private: enum Phase { kP_Loading, diff --git a/include/MetroidPrime/Cameras/CBallCamera.hpp b/include/MetroidPrime/Cameras/CBallCamera.hpp index 775b06d8..0c85cf21 100644 --- a/include/MetroidPrime/Cameras/CBallCamera.hpp +++ b/include/MetroidPrime/Cameras/CBallCamera.hpp @@ -168,6 +168,8 @@ public: void SetClampVelTimer(float v) { x470_clampVelTimer = v; } void SetClampVelRange(float v) { x474_clampVelRange = v; } + void DoorClosing(TUniqueId uid); + private: struct SFailsafeState { CTransform4f x0_playerXf; diff --git a/include/MetroidPrime/ScriptObjects/CScriptDoor.hpp b/include/MetroidPrime/ScriptObjects/CScriptDoor.hpp new file mode 100644 index 00000000..bff7f8e2 --- /dev/null +++ b/include/MetroidPrime/ScriptObjects/CScriptDoor.hpp @@ -0,0 +1,63 @@ +#ifndef _CSCRIPTDOOR +#define _CSCRIPTDOOR + +#include "MetroidPrime/CPhysicsActor.hpp" + +class CScriptDoor : public CPhysicsActor { +public: + enum EDoorState { + kDS_Open, + kDS_Close, + kDS_Ready, + }; + + enum EDoorOpenCondition { + kDOC_NotReady, + kDOC_Loading, + kDOC_Ready, + }; + + CScriptDoor(TUniqueId uid, const rstl::string& name, const CEntityInfo& info, + const CTransform4f& xf, const CModelData& modelData, + const CActorParameters& actorParameters, const CVector3f& orbitPosition, + const CAABox& bounds, bool active, bool open, bool projectilesCollide, + float animationLength, bool ballDoor); + + void SetDoorAnimation(EDoorState state); + + rstl::optional_object< CAABox > GetTouchBounds() const override; + + rstl::optional_object< CAABox > GetProjectileBounds() const; + CVector3f GetOrbitPosition(const CStateManager& mgr) const override; + + EDoorOpenCondition GetDoorOpenCondition(CStateManager& mgr); + void OpenDoor(TUniqueId uid, CStateManager& mgr); + + void AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId other, CStateManager& mgr) override; + + const TUniqueId GetDockID() const { return mDockId; } + bool IsOpen() const { return mIsOpen; } + void SetDoClose(const bool close) { mDoClose = close; } + +private: + float mAnimLength; + float mAnimTime; + EDoorState mDoorState; + CAABox x264_; + TUniqueId mPartner1; + TUniqueId mPartner2; + TUniqueId mPrevDoor; + TUniqueId mDockId; + CAABox mModelBounds; + CVector3f mOrbitPos; + + bool mClosing : 1; + bool mWasOpen : 1; + bool mIsOpen : 1; + bool mConditionsMet : 1; + bool mProjectilesCollide : 1; + bool mBallDoor : 1; + bool mDoClose : 1; +}; + +#endif // _CSCRIPTDOOR diff --git a/src/MetroidPrime/ScriptObjects/CScriptDoor.cpp b/src/MetroidPrime/ScriptObjects/CScriptDoor.cpp new file mode 100644 index 00000000..49ed9b05 --- /dev/null +++ b/src/MetroidPrime/ScriptObjects/CScriptDoor.cpp @@ -0,0 +1,248 @@ +#include "MetroidPrime/ScriptObjects/CScriptDoor.hpp" + +#include "MetroidPrime/CAnimData.hpp" +#include "MetroidPrime/CAnimPlaybackParms.hpp" +#include "MetroidPrime/CEntity.hpp" +#include "MetroidPrime/CEntityInfo.hpp" +#include "MetroidPrime/CGameArea.hpp" +#include "MetroidPrime/CMapWorldInfo.hpp" +#include "MetroidPrime/CObjectList.hpp" +#include "MetroidPrime/CPhysicsActor.hpp" +#include "MetroidPrime/CWorld.hpp" +#include "MetroidPrime/Cameras/CBallCamera.hpp" +#include "MetroidPrime/Cameras/CCameraManager.hpp" +#include "MetroidPrime/ScriptObjects/CScriptDock.hpp" +#include "MetroidPrime/TCastTo.hpp" +#include "MetroidPrime/TGameTypes.hpp" + +CScriptDoor::CScriptDoor(TUniqueId uid, const rstl::string& name, const CEntityInfo& info, + const CTransform4f& xf, const CModelData& modelData, + const CActorParameters& actorParameters, const CVector3f& orbitPosition, + const CAABox& bounds, const bool active, const bool open, + const bool projectilesCollide, const float animationLength, + const bool ballDoor) +: CPhysicsActor(uid, active, name, info, xf, modelData, + open ? CMaterialList(kMT_Solid, kMT_Immovable, kMT_Orbit) + : CMaterialList(kMT_Solid, kMT_Immovable, kMT_Orbit, kMT_Occluder), + bounds, SMoverData(1.f), actorParameters, 0.3f, 0.1f) +, mAnimLength(animationLength) +, mAnimTime(0.f) +, mDoorState(kDS_Open) +, x264_(GetBoundingBox()) +, mPartner1(kInvalidUniqueId) +, mPartner2(kInvalidUniqueId) +, mPrevDoor(kInvalidUniqueId) +, mDockId(kInvalidUniqueId) +, mModelBounds(modelData.GetBounds(xf.GetRotation())) +, mOrbitPos(orbitPosition) +, mClosing(false) +, mWasOpen(open) +, mIsOpen(open) +, mConditionsMet(false) +, mProjectilesCollide(projectilesCollide) +, mBallDoor(ballDoor) +, mDoClose(false) { + SetThermalFlags(kTF_Cold); + + if (open) { + SetDoorAnimation(kDS_Open); + } + + SetMass(0.f); +} + +rstl::optional_object< CAABox > CScriptDoor::GetTouchBounds() const { + if (GetActive() && GetMaterialList().HasMaterial(kMT_Solid)) { + return CPhysicsActor::GetBoundingBox(); + } + + return rstl::optional_object_null(); +} + +rstl::optional_object< CAABox > CScriptDoor::GetProjectileBounds() const { + if (mProjectilesCollide) { + return CAABox(mModelBounds.GetMinPoint() + GetTranslation(), + mModelBounds.GetMaxPoint() + GetTranslation()); + } + + return rstl::optional_object_null(); +} + +CVector3f CScriptDoor::GetOrbitPosition(const CStateManager& mgr) const { + return GetTranslation() + mOrbitPos; +} + +CScriptDoor::EDoorOpenCondition CScriptDoor::GetDoorOpenCondition(CStateManager& mgr) { + return kDOC_Loading; +} + +void CScriptDoor::OpenDoor(TUniqueId uid, CStateManager& mgr) { + mgr.MapWorldInfo()->SetDoorVisited(mgr.GetEditorIdForUniqueId(uid), true); + mIsOpen = true; + mWasOpen = true; + mConditionsMet = false; + mPartner1 = kInvalidUniqueId; + mPartner2 = kInvalidUniqueId; + + if (const CScriptDoor* door = TCastToConstPtr< CScriptDoor >(mgr.GetObjectById(uid))) { + mPartner1 = door->GetUniqueId(); + } + + SetDoorAnimation(kDS_Open); + + if (mPartner1 != kInvalidUniqueId) { + SendScriptMsgs(kSS_MaxReached, mgr, kSM_None); + } else { + SendScriptMsgs(kSS_Open, mgr, kSM_None); + } + + if (const CScriptDock* dock1 = TCastToConstPtr< CScriptDock >(mgr.GetObjectById(mDockId))) { + CObjectList& list = mgr.ObjectListById(kOL_PlatformAndDoor); + for (int idx = list.GetFirstObjectIndex(); idx != -1; idx = list.GetNextObjectIndex(idx)) { + if (CScriptDoor* door = TCastToPtr< CScriptDoor >(list[idx])) { + if (door->GetUniqueId() == uid) { + continue; + } + + if (const CScriptDock* dock2 = + TCastToConstPtr< CScriptDock >(mgr.GetObjectById(door->GetDockID()))) { + if (dock2->GetAreaId() == dock1->GetCurrentConnectedAreaId(mgr) && + dock2->GetCurrentConnectedAreaId(mgr) == dock1->GetAreaId()) { + mPartner2 = door->GetUniqueId(); + mgr.DeliverScriptMsg(door, GetUniqueId(), kSM_Open); + break; + } + } + } + } + } + + if (mPartner1 == kInvalidUniqueId && mPartner2 == kInvalidUniqueId) { + + rstl::vector< SConnection >::const_iterator it = GetConnectionList().begin(); + for (; it != GetConnectionList().end(); ++it) { + if (it->x4_msg != kSM_Open) { + continue; + } + + if (const CScriptDoor* door = + TCastToConstPtr< CScriptDoor >(mgr.GetObjectById(mgr.GetIdForScript(it->x8_objId)))) { + mPartner2 = door->GetUniqueId(); + break; + } + } + } +} + +void CScriptDoor::SetDoorAnimation(EDoorState state) { + mDoorState = state; + if (HasAnimation()) { + AnimationData()->SetAnimation(CAnimPlaybackParms(static_cast< int >(state), -1, 1.f, true), + false); + } +} + +void CScriptDoor::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) { + switch (msg) { + case kSM_Close: { + if (!GetActive()) { + return; + } + + if (mPartner1 == kInvalidUniqueId || mPartner1 == uid) { + if (mIsOpen) { + if (mPartner2 != kInvalidUniqueId) { + // lol, this is its actual name + static int i = 0; + if (CEntity* ent = mgr.ObjectById(mPartner2)) { + ++i; + mgr.DeliverScriptMsg(ent, GetUniqueId(), kSM_Close); + --i; + } + } + mIsOpen = false; + SetDoorAnimation(kDS_Close); + mgr.GetCameraManager()->BallCamera()->DoorClosing(GetUniqueId()); + } else if (mConditionsMet) { + mConditionsMet = false; + SendScriptMsgs(kSS_Closed, mgr, kSM_None); + } + } + break; + } + case kSM_Action: { + if (mPartner1 != kInvalidUniqueId) { + if (CScriptDoor* door = TCastToPtr< CScriptDoor >(mgr.ObjectById(mPartner1))) { + if (door->IsOpen()) { + mDoClose = true; + mgr.DeliverScriptMsg(door, GetUniqueId(), kSM_Close); + door->SetDoClose(true); + } + } + } else if (mIsOpen) { + mDoClose = true; + if (CScriptDoor* door = TCastToPtr< CScriptDoor >(mgr.ObjectById(mPartner2))) { + mgr.DeliverScriptMsg(door, GetUniqueId(), kSM_Close); + door->SetDoClose(true); + } + mIsOpen = false; + SetDoorAnimation(kDS_Close); + mgr.GetCameraManager()->BallCamera()->DoorClosing(GetUniqueId()); + } + break; + } + case kSM_Open: { + if (!GetActive()) { + return; + } + + if (!mIsOpen) { + + const EDoorOpenCondition cond = TCastToConstPtr< CScriptDoor >(mgr.GetObjectById(uid)) + ? kDOC_Ready + : GetDoorOpenCondition(mgr); + + switch (cond) { + case kDOC_Loading: + mConditionsMet = true; + mPrevDoor = uid; + break; + case kDOC_Ready: + OpenDoor(uid, mgr); + break; + case kDOC_NotReady: + default: + mWasOpen = false; + mClosing = true; + break; + } + } + break; + } + case kSM_InitializedInArea: { + rstl::vector< SConnection >::const_iterator it = GetConnectionList().begin(); + for (; it != GetConnectionList().end(); ++it) { + if (it->x4_msg != kSM_Increment) { + continue; + } + + if (const CScriptDock* dock = + TCastToConstPtr< CScriptDock >(mgr.GetObjectById(mgr.GetIdForScript(it->x8_objId)))) { + mDockId = dock->GetUniqueId(); + break; + } + } + break; + } + case kSM_SetToZero: + mProjectilesCollide = true; + mgr.MapWorldInfo()->SetDoorVisited(mgr.GetEditorIdForUniqueId(GetUniqueId()), true); + break; + case kSM_SetToMax: + mProjectilesCollide = false; + break; + default: + CPhysicsActor::AcceptScriptMsg(msg, uid, mgr); + break; + } +}