diff --git a/include/Kyoto/CRandom16.hpp b/include/Kyoto/CRandom16.hpp index 667207ed..7e3054d3 100644 --- a/include/Kyoto/CRandom16.hpp +++ b/include/Kyoto/CRandom16.hpp @@ -5,30 +5,35 @@ class CRandom16; class CGlobalRandom { - static CGlobalRandom* gCurrentGlobalRandom; + static CGlobalRandom* gCurrentGlobalRandom; + public: - CGlobalRandom(CRandom16& rnd); - ~CGlobalRandom(); + CGlobalRandom(CRandom16& rnd); + ~CGlobalRandom(); + private: - CRandom16& mRandom; - bool mIsFirst; - CGlobalRandom* mPrev; + CRandom16& mRandom; + bool mIsFirst; + CGlobalRandom* mPrev; }; class CRandom16 { - friend class CGlobalRandom; - static CRandom16* gRandomNumber; - static void _SetRandomNumber(CRandom16* rnd); + friend class CGlobalRandom; + static CRandom16* gRandomNumber; + static void _SetRandomNumber(CRandom16* rnd); + public: - static CRandom16* GetRandomNumber(); - CRandom16(uint seed = 99); - void SetSeed(uint seed); - int Range(int min, int max); - float Range(float min, float max); - int Next(); - float Float(); + CRandom16(uint seed = 99); + void SetSeed(uint seed); + int Range(int min, int max); + float Range(float min, float max); + int Next(); + float Float(); + + static CRandom16* GetRandomNumber(); + private: - uint mSeed; + uint mSeed; }; #endif // __CRANDOM16_HPP__ diff --git a/include/Kyoto/CResFactory.hpp b/include/Kyoto/CResFactory.hpp index 1d81509f..81c8ce18 100644 --- a/include/Kyoto/CResFactory.hpp +++ b/include/Kyoto/CResFactory.hpp @@ -9,6 +9,13 @@ #include "Kyoto/CResLoader.hpp" class IFactory { +public: + virtual ~IFactory() {} + virtual CFactoryFnReturn Build(const SObjectTag&, const CVParamTransfer&, CObjectReference*) = 0; + virtual void BuildAsync(const SObjectTag&, const CVParamTransfer&, rstl::auto_ptr< IObj >*, CObjectReference*) = 0; + virtual void CancelBuild(const SObjectTag&) = 0; + virtual bool CanBuild(const SObjectTag&) = 0; + virtual const SObjectTag* GetResourceIdByName(const char* name) const = 0; // TODO }; diff --git a/include/Kyoto/CResLoader.hpp b/include/Kyoto/CResLoader.hpp index bfcbc9f0..f04e80fa 100644 --- a/include/Kyoto/CResLoader.hpp +++ b/include/Kyoto/CResLoader.hpp @@ -25,6 +25,7 @@ public: void AddPakFileAsync(const rstl::string&, bool, bool); void AsyncIdlePakLoading(); bool AreAllPaksLoaded() const; + CInputStream* LoadNewResourceSync(const SObjectTag& tag, char* extBuf); private: rstl::list< void > x0_aramList; diff --git a/include/MetroidPrime/CEntity.hpp b/include/MetroidPrime/CEntity.hpp index 31d893a5..ba48e776 100644 --- a/include/MetroidPrime/CEntity.hpp +++ b/include/MetroidPrime/CEntity.hpp @@ -25,6 +25,7 @@ public: void SendScriptMsgs(EScriptObjectState state, CStateManager& mgr, EScriptObjectMessage msg); TUniqueId GetUniqueId() const { return x8_uid; } TAreaId GetAreaId() const; + bool GetActive() const { return x30_24_active; } static rstl::vector NullConnectionList; diff --git a/include/MetroidPrime/CStateManager.hpp b/include/MetroidPrime/CStateManager.hpp index 78fbf935..35479227 100644 --- a/include/MetroidPrime/CStateManager.hpp +++ b/include/MetroidPrime/CStateManager.hpp @@ -29,6 +29,7 @@ class CVector3f; class CWeaponMgr; class CWorld; class CWorldTransManager; +class CEntity; namespace SL { class CSortedListManager; @@ -37,8 +38,12 @@ class CSortedListManager; class CStateManager { public: void SendScriptMsg(TUniqueId uid, TEditorId target, EScriptObjectMessage msg, EScriptObjectState state); + void SendScriptMsg(CEntity* ent, TUniqueId target, EScriptObjectMessage msg); bool AddDrawableActor(const CActor& actor, const CVector3f& pos, const CAABox& bounds) const; void SetupParticleHook(const CActor& actor) const; + void FreeScriptObject(TUniqueId uid); + + CEntity* ObjectById(TUniqueId uid); CCameraManager* CameraManager() { return x870_cameraManager; } const CCameraManager* GetCameraManager() const { return x870_cameraManager; } diff --git a/include/MetroidPrime/ScriptObjects/CScriptMazeNode.hpp b/include/MetroidPrime/ScriptObjects/CScriptMazeNode.hpp index 07a98e85..87ec8f9b 100644 --- a/include/MetroidPrime/ScriptObjects/CScriptMazeNode.hpp +++ b/include/MetroidPrime/ScriptObjects/CScriptMazeNode.hpp @@ -5,9 +5,112 @@ #include "MetroidPrime/CActor.hpp" +#include "Kyoto/CRandom16.hpp" +#include "Kyoto/Math/CVector3f.hpp" + +#include "rstl/vector.hpp" + +#define skMazeCols 9 +#define skMazeRows 7 +#define skEnterCol 4 +#define skEnterRow 4 +#define skTargetCol 5 +#define skTargetRow 3 + +#define NUM_MAZE_CELLS (skMazeRows * skMazeCols) + +struct SMazeCell { + bool x0_24_openTop : 1; + bool x0_25_openRight : 1; + bool x0_26_openBottom : 1; + bool x0_27_openLeft : 1; + bool x0_28_gateTop : 1; + bool x0_29_gateRight : 1; + bool x0_30_gateBottom : 1; + bool x0_31_gateLeft : 1; + bool x1_24_puddle : 1; + bool x1_25_onPath : 1; + bool x1_26_checked : 1; + + SMazeCell() { + x0_24_openTop = x0_25_openRight = x0_26_openBottom = x0_27_openLeft = x0_28_gateTop = x0_29_gateRight = x0_30_gateBottom = + x0_31_gateLeft = x1_24_puddle = x1_25_onPath = x1_26_checked = false; + } + + inline bool IsClosed() const { return !x0_24_openTop && !x0_25_openRight && !x0_26_openBottom && !x0_27_openLeft; } +}; + +class CMazeState { + CRandom16 x0_rand; + SMazeCell x4_cells[skMazeRows * skMazeCols]; + int x84_enterCol; + int x88_enterRow; + int x8c_targetCol; + int x90_targetRow; + bool x94_24_initialized : 1; + +public: + enum ESide { + kS_Invalid = -1, + kS_Top = 0, + kS_Right = 1, + kS_Bottom = 2, + kS_Left = 3, + }; + + CMazeState(int enterCol, int enterRow, int targetCol, int targetRow); + void Reset(int seed); + void Initialize(); + void GenerateObstacles(); + + SMazeCell& GetCell(uint col, uint row); + SMazeCell& GetCell2(uint col, uint row); // ???? + SMazeCell& GetCellInline(uint col, uint row) { return x4_cells[col + row * skMazeCols]; } // ???? + inline SMazeCell& GetCell(uint idx) { return x4_cells[idx]; } +}; + class CScriptMazeNode : public CActor { public: + CScriptMazeNode(TUniqueId uid, const rstl::string& name, const CEntityInfo& info, const CTransform4f& xf, bool active, int col, int row, + int side, const CVector3f& actorPos, const CVector3f& triggerPos, const CVector3f& effectPos); + ~CScriptMazeNode() override; + + void Accept(IVisitor& visitor) override; + void AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) override; + void Think(float dt, CStateManager& mgr) override; + static void LoadMazeSeeds(); + +private: + enum ESide { + Invalid = -1, + Top = 0, + Right = 1, + Bottom = 2, + Left = 3, + }; + + int xe8_col; + int xec_row; + ESide xf0_side; + TUniqueId xf4_gateEffectId; + float xf8_msgTimer; + TUniqueId xfc_actorId; + CVector3f x100_actorPos; + TUniqueId x10c_triggerId; + CVector3f x110_triggerPos; + TUniqueId x11c_effectId; + CVector3f x120_effectPos; + rstl::vector< TUniqueId > x12c_puddleObjectIds; + bool x13c_24_hasPuddle : 1; + bool x13c_25_hasGate : 1; + bool x13c_26_gateActive : 1; + + void GenerateObjects(CStateManager& mgr); + void Reset(CStateManager& mgr); + void SendScriptMsgs(CStateManager& mgr, EScriptObjectMessage msg); + + static uint sMazeSeeds[300]; }; #endif \ No newline at end of file diff --git a/src/MetroidPrime/ScriptObjects/CScriptDebugCameraWaypoint.cpp b/src/MetroidPrime/ScriptObjects/CScriptDebugCameraWaypoint.cpp index 3ebe7ebe..0af91af7 100644 --- a/src/MetroidPrime/ScriptObjects/CScriptDebugCameraWaypoint.cpp +++ b/src/MetroidPrime/ScriptObjects/CScriptDebugCameraWaypoint.cpp @@ -9,4 +9,4 @@ CScriptDebugCameraWaypoint::CScriptDebugCameraWaypoint(TUniqueId uid, const rstl void CScriptDebugCameraWaypoint::Accept(IVisitor& visitor) { visitor.Visit(*this); } -CScriptDebugCameraWaypoint::~CScriptDebugCameraWaypoint() {} \ No newline at end of file +CScriptDebugCameraWaypoint::~CScriptDebugCameraWaypoint() {} diff --git a/src/MetroidPrime/ScriptObjects/CScriptMazeNode.cpp b/src/MetroidPrime/ScriptObjects/CScriptMazeNode.cpp new file mode 100644 index 00000000..7d06c874 --- /dev/null +++ b/src/MetroidPrime/ScriptObjects/CScriptMazeNode.cpp @@ -0,0 +1,300 @@ +#include "MetroidPrime/ScriptObjects/CScriptMazeNode.hpp" + +#include "Kyoto/CResFactory.hpp" +#include "MetroidPrime/CActorParameters.hpp" + +uint CScriptMazeNode::sMazeSeeds[300]; + +CMazeState::CMazeState(int enterCol, int enterRow, int targetCol, int targetRow) +: x0_rand(0) +, x84_enterCol(enterCol) +, x88_enterRow(enterRow) +, x8c_targetCol(targetCol) +, x90_targetRow(targetRow) +, x94_24_initialized(false) {} + +void CMazeState::Reset(int seed) { + int numCells = NUM_MAZE_CELLS - 1; + int cellIdx = 0; + + x0_rand = CRandom16(seed); + x94_24_initialized = false; + for (int i = 0; i < NUM_MAZE_CELLS; i++) { + x4_cells[i] = SMazeCell(); + } + + ESide sides[4]; + while (numCells != 0) { + int acc = 0; + if (cellIdx - skMazeCols > 0 && GetCell(cellIdx - skMazeCols).IsClosed()) { + sides[acc++] = kS_Top; + } + if (cellIdx < NUM_MAZE_CELLS - 2 && (cellIdx + 1) % skMazeCols != 0 && GetCell(cellIdx + 1).IsClosed()) { + sides[acc++] = kS_Right; + } + if (cellIdx + skMazeCols <= NUM_MAZE_CELLS - 1 && GetCell(cellIdx + skMazeCols).IsClosed()) { + sides[acc++] = kS_Bottom; + } + if (cellIdx > 0 && cellIdx % skMazeCols != 0 && GetCell(cellIdx - 1).IsClosed()) { + sides[acc++] = kS_Left; + } + + if (acc != 0) { + numCells--; + + ESide side = sides[x0_rand.Next() % static_cast< uint >(acc)]; + switch (side) { + case kS_Top: + GetCell(cellIdx).x0_24_openTop = true; + GetCell(cellIdx - skMazeCols).x0_26_openBottom = true; + cellIdx -= skMazeCols; + break; + case kS_Right: + GetCell(cellIdx).x0_25_openRight = true; + GetCell(cellIdx + 1).x0_27_openLeft = true; + cellIdx++; + break; + case kS_Bottom: + GetCell(cellIdx).x0_26_openBottom = true; + GetCell(cellIdx + skMazeCols).x0_24_openTop = true; + cellIdx += skMazeCols; + break; + case kS_Left: + GetCell(cellIdx).x0_27_openLeft = true; + GetCell(cellIdx - 1).x0_25_openRight = true; + cellIdx--; + break; + } + continue; + } + + do { + cellIdx++; + if (cellIdx > NUM_MAZE_CELLS - 1) { + cellIdx = 0; + } + } while (GetCell(cellIdx).IsClosed()); + } +} + +SMazeCell& CMazeState::GetCell(uint col, uint row) { return x4_cells[col + row * skMazeCols]; } + +// ???? +SMazeCell& CMazeState::GetCell2(uint col, uint row) { return x4_cells[col + row * skMazeCols]; } + +static inline int GetRandom(CRandom16& rand, int offset) { + int tmp = rand.Next(); + return tmp - ((tmp / 5) * 5) + offset; +} + +// TODO non-matching +void CMazeState::GenerateObstacles() { + if (!x94_24_initialized) { + Initialize(); + } + + int gate1Idx = GetRandom(x0_rand, 9); + int gate2Idx = GetRandom(x0_rand, 21); + int gate3Idx = GetRandom(x0_rand, 33); + int puddle1Idx = GetRandom(x0_rand, 13); + int puddle2Idx = GetRandom(x0_rand, 29); + + int row = x88_enterRow; + int col = x84_enterCol; + int prevRow = x88_enterRow; + int prevCol = x84_enterCol; + + ESide side = kS_Invalid; + int idx = 0; + + while (col != x8c_targetCol || row != x90_targetRow) { + if (idx == gate1Idx || idx == gate2Idx || idx == gate3Idx) { + switch (side) { + case kS_Top: + GetCellInline(col, row).x0_30_gateBottom = true; + GetCellInline(prevCol, prevRow).x0_28_gateTop = true; + break; + case kS_Right: + GetCellInline(col, row).x0_31_gateLeft = true; + GetCellInline(prevCol, prevRow).x0_29_gateRight = true; + break; + case kS_Bottom: + GetCellInline(col, row).x0_28_gateTop = true; + GetCellInline(prevCol, prevRow).x0_30_gateBottom = true; + break; + case kS_Left: + GetCellInline(col, row).x0_29_gateRight = true; + GetCellInline(prevCol, prevRow).x0_31_gateLeft = true; + break; + } + } + + int nextCol = col; + int nextRow = row; + if (row > 1 && side != kS_Bottom && GetCellInline(col, row).x0_24_openTop && GetCellInline(col, row - 1).x1_25_onPath) { + side = kS_Top; + nextRow--; + } else if (row < skMazeRows - 1 && side != kS_Top && GetCellInline(col, row).x0_26_openBottom && + GetCellInline(col, row + 1).x1_25_onPath) { + side = kS_Bottom; + nextRow++; + } else if (col > 1 && side != kS_Right && GetCellInline(col, row).x0_27_openLeft && GetCellInline(col - 1, row).x1_25_onPath) { + side = kS_Left; + nextCol--; + } else if (col < skMazeRows && side != kS_Left && GetCellInline(col, row).x0_25_openRight && GetCellInline(col + 1, row).x1_25_onPath) { + side = kS_Right; + nextCol++; + } else { + return; + } + + if (idx == puddle1Idx || idx == puddle2Idx) { + if (col == 0 || row == 0 || col == skMazeCols - 1 || row == skMazeRows - 1) { + if (idx == puddle1Idx) { + puddle1Idx++; + } else { + puddle2Idx++; + } + } else { + SMazeCell& cell = GetCellInline(col, row); + cell.x1_24_puddle = true; + switch (side) { + case kS_Top: + GetCellInline(nextCol, nextRow).x0_26_openBottom = false; + cell.x0_24_openTop = false; + break; + case kS_Right: + GetCellInline(nextCol, nextRow).x0_27_openLeft = false; + cell.x0_25_openRight = false; + break; + case kS_Bottom: + GetCellInline(nextCol, nextRow).x0_24_openTop = false; + cell.x0_26_openBottom = false; + break; + case kS_Left: + GetCellInline(nextCol, nextRow).x0_25_openRight = false; + cell.x0_27_openLeft = false; + break; + } + } + } + + idx++; + prevCol = col; + prevRow = row; + col = nextCol; + row = nextRow; + }; +} + +// TODO non-matching +void CMazeState::Initialize() { + int path[NUM_MAZE_CELLS]; + int targetRow = x90_targetRow; + int targetCol = x8c_targetCol; + int cellIdx = x84_enterCol + x88_enterRow * skMazeCols; + path[0] = cellIdx; + x4_cells[cellIdx].x1_26_checked = true; + int pathLength = 1; + while (cellIdx != targetCol + targetRow * skMazeCols) { + if (x4_cells[cellIdx].x0_24_openTop && !x4_cells[cellIdx - skMazeCols].x1_26_checked) { + path[pathLength] = cellIdx - skMazeCols; + pathLength++; + } + if (x4_cells[cellIdx].x0_25_openRight && !x4_cells[cellIdx + 1].x1_26_checked) { + path[pathLength] = cellIdx + 1; + pathLength++; + } + if (x4_cells[cellIdx].x0_26_openBottom && !x4_cells[cellIdx + skMazeCols].x1_26_checked) { + path[pathLength] = cellIdx + skMazeCols; + pathLength++; + } + if (x4_cells[cellIdx].x0_27_openLeft && !x4_cells[cellIdx - 1].x1_26_checked) { + path[pathLength] = cellIdx - 1; + pathLength++; + } + if (cellIdx == path[pathLength - 1]) { + pathLength--; + } + cellIdx = path[pathLength - 1]; + x4_cells[cellIdx].x1_26_checked = true; + } + for (; pathLength != 0; pathLength--) { + int idx = path[pathLength]; + if (x4_cells[idx].x1_26_checked) { + x4_cells[idx].x1_25_onPath = true; + } + } + x94_24_initialized = true; +} + +CScriptMazeNode::CScriptMazeNode(TUniqueId uid, const rstl::string& name, const CEntityInfo& info, const CTransform4f& xf, bool active, + int col, int row, int side, const CVector3f& actorPos, const CVector3f& triggerPos, + const CVector3f& effectPos) +: CActor(uid, active, name, info, xf, CModelData::CModelDataNull(), CMaterialList(), CActorParameters::None(), kInvalidUniqueId) +, xe8_col(col) +, xec_row(row) +, xf0_side(static_cast< ESide >(side)) +, xf4_gateEffectId(kInvalidUniqueId) +, xf8_msgTimer(1.f) +, xfc_actorId(kInvalidUniqueId) +, x100_actorPos(actorPos) +, x10c_triggerId(kInvalidUniqueId) +, x110_triggerPos(triggerPos) +, x11c_effectId(kInvalidUniqueId) +, x120_effectPos(effectPos) +, x13c_24_hasPuddle(false) +, x13c_25_hasGate(false) +, x13c_26_gateActive(true) {} + +void CScriptMazeNode::Accept(IVisitor& visitor) { visitor.Visit(*this); } + +void CScriptMazeNode::GenerateObjects(CStateManager& mgr) { + // TODO +} + +void CScriptMazeNode::Reset(CStateManager& mgr) { + mgr.FreeScriptObject(x11c_effectId); + mgr.FreeScriptObject(xfc_actorId); + mgr.FreeScriptObject(x10c_triggerId); + mgr.FreeScriptObject(xf4_gateEffectId); + x11c_effectId = xfc_actorId = x10c_triggerId = xf4_gateEffectId = kInvalidUniqueId; +} + +// TODO non-matching +void CScriptMazeNode::SendScriptMsgs(CStateManager& mgr, EScriptObjectMessage msg) { + mgr.SendScriptMsg(mgr.ObjectById(x11c_effectId), GetUniqueId(), msg); + mgr.SendScriptMsg(mgr.ObjectById(xfc_actorId), GetUniqueId(), msg); + mgr.SendScriptMsg(mgr.ObjectById(x10c_triggerId), GetUniqueId(), msg); + mgr.SendScriptMsg(mgr.ObjectById(xf4_gateEffectId), GetUniqueId(), msg); +} + +void CScriptMazeNode::AcceptScriptMsg(EScriptObjectMessage msg, TUniqueId uid, CStateManager& mgr) { + // TODO +} + +void CScriptMazeNode::Think(float dt, CStateManager& mgr) { + if (GetActive() && x13c_25_hasGate) { + xf8_msgTimer -= dt; + if (xf8_msgTimer <= 0.f) { + xf8_msgTimer = 1.f; + if (x13c_26_gateActive) { + x13c_26_gateActive = false; + SendScriptMsgs(mgr, kSM_Deactivate); + } else { + x13c_26_gateActive = true; + SendScriptMsgs(mgr, kSM_Activate); + } + } + } +} + +void CScriptMazeNode::LoadMazeSeeds() { + const SObjectTag* tag = gpResourceFactory->GetResourceIdByName("DUMB_MazeSeeds"); + rstl::auto_ptr< CInputStream > stream = gpResourceFactory->GetResLoader().LoadNewResourceSync(*tag, nullptr); + for (int i = 0; i < 300; i++) { + sMazeSeeds[i] = stream->ReadLong(); + } +} + +CScriptMazeNode::~CScriptMazeNode() {}