Skip to content

Commit eb6b18a

Browse files
authored
Add removeAllGameBuildings / restoreAllGameBuildings API (#3275)
1 parent aa932d2 commit eb6b18a

20 files changed

+743
-277
lines changed

Client/game_sa/CBuildingsPoolSA.cpp

+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*****************************************************************************
2+
*
3+
* PROJECT: Multi Theft Auto v1.0
4+
* LICENSE: See LICENSE in the top level directory
5+
* FILE: game_sa/CBuildingsPoolSA.cpp
6+
* PURPOSE: Buildings pool class
7+
*
8+
* Multi Theft Auto is available from http://www.multitheftauto.com/
9+
*
10+
*****************************************************************************/
11+
12+
#include "StdInc.h"
13+
#include "CBuildingsPoolSA.h"
14+
15+
#include "CFileLoaderSA.h"
16+
#include <game/CWorld.h>
17+
#include "CGameSA.h"
18+
#include "CPtrNodeSingleListSA.h"
19+
20+
extern CGameSA* pGame;
21+
22+
class CClientEntity;
23+
24+
CBuildingsPoolSA::CBuildingsPoolSA() : m_pOriginalBuildingsBackup(nullptr)
25+
{
26+
m_ppBuildingPoolInterface = (CPoolSAInterface<CBuildingSAInterface>**)0xB74498;
27+
}
28+
29+
inline bool CBuildingsPoolSA::AddBuildingToPool(CClientBuilding* pClientBuilding, CBuildingSA* pBuilding)
30+
{
31+
// Grab the new object interface
32+
CBuildingSAInterface* pInterface = pBuilding->GetBuildingInterface();
33+
34+
if (!pInterface)
35+
return false;
36+
37+
uint32_t dwElementIndexInPool = (*m_ppBuildingPoolInterface)->GetObjectIndexSafe(pInterface);
38+
if (dwElementIndexInPool == UINT_MAX)
39+
return false;
40+
41+
m_buildingPool.arrayOfClientEntities[dwElementIndexInPool] = {pBuilding, (CClientEntity*)pClientBuilding};
42+
43+
// Increase the count of objects
44+
++m_buildingPool.ulCount;
45+
46+
return true;
47+
}
48+
49+
CBuilding* CBuildingsPoolSA::AddBuilding(CClientBuilding* pClientBuilding, uint16_t modelId, CVector* vPos, CVector4D* vRot, uint8_t interior)
50+
{
51+
if (!HasFreeBuildingSlot())
52+
return nullptr;
53+
54+
// Load building
55+
SFileObjectInstance instance;
56+
instance.modelID = modelId;
57+
instance.lod = -1;
58+
instance.interiorID = interior;
59+
instance.position = *vPos;
60+
instance.rotation = *vRot;
61+
62+
// Fix strange SA rotation
63+
instance.rotation.fW = -instance.rotation.fW;
64+
65+
auto pBuilding = static_cast<CBuildingSAInterface*>(CFileLoaderSA::LoadObjectInstance(&instance));
66+
67+
// Disable lod and ipl
68+
pBuilding->m_pLod = nullptr;
69+
pBuilding->m_iplIndex = 0;
70+
71+
// Always stream model collosion
72+
// TODO We can setup collison bounding box and use GTA streamer for it
73+
auto modelInfo = pGame->GetModelInfo(modelId);
74+
modelInfo->AddColRef();
75+
76+
// Add building in world
77+
auto pBuildingSA = new CBuildingSA(pBuilding);
78+
pGame->GetWorld()->Add(pBuildingSA, CBuildingPool_Constructor);
79+
80+
// Add CBuildingSA object in pool
81+
AddBuildingToPool(pClientBuilding, pBuildingSA);
82+
83+
return pBuildingSA;
84+
}
85+
86+
void CBuildingsPoolSA::RemoveBuilding(CBuilding* pBuilding)
87+
{
88+
assert(NULL != pBuilding);
89+
90+
CBuildingSAInterface* pInterface = pBuilding->GetBuildingInterface();
91+
92+
uint32_t dwElementIndexInPool = (*m_ppBuildingPoolInterface)->GetObjectIndexSafe(pInterface);
93+
if (dwElementIndexInPool == UINT_MAX)
94+
return;
95+
96+
// Remove building from world
97+
pGame->GetWorld()->Remove(pInterface, CBuildingPool_Destructor);
98+
99+
// Remove building from cover list
100+
CPtrNodeSingleListSAInterface<CBuildingSAInterface>* coverList = reinterpret_cast<CPtrNodeSingleListSAInterface<CBuildingSAInterface>*>(0xC1A2B8);
101+
coverList->RemoveItem(pInterface);
102+
103+
// Remove plant
104+
using CPlantColEntry_Remove = CEntitySAInterface* (*)(CEntitySAInterface*);
105+
((CPlantColEntry_Remove)0x5DBEF0)(pInterface);
106+
107+
// Remove col reference
108+
auto modelInfo = pGame->GetModelInfo(pBuilding->GetModelIndex());
109+
modelInfo->RemoveColRef();
110+
111+
// Remove from BuildingSA pool
112+
auto* pBuildingSA = m_buildingPool.arrayOfClientEntities[dwElementIndexInPool].pEntity;
113+
m_buildingPool.arrayOfClientEntities[dwElementIndexInPool] = {nullptr, nullptr};
114+
115+
// Delete it from memory
116+
delete pBuildingSA;
117+
118+
// Remove building from SA pool
119+
(*m_ppBuildingPoolInterface)->Release(dwElementIndexInPool);
120+
121+
// Decrease the count of elements in the pool
122+
--m_buildingPool.ulCount;
123+
}
124+
125+
void CBuildingsPoolSA::RemoveAllBuildings()
126+
{
127+
if (m_pOriginalBuildingsBackup)
128+
return;
129+
130+
m_pOriginalBuildingsBackup = std::make_unique<std::array<std::pair<bool, CBuildingSAInterface>, MAX_BUILDINGS>>();
131+
132+
auto pBuildsingsPool = (*m_ppBuildingPoolInterface);
133+
for (size_t i = 0; i < MAX_BUILDINGS; i++)
134+
{
135+
if (pBuildsingsPool->IsContains(i))
136+
{
137+
auto building = pBuildsingsPool->GetObject(i);
138+
139+
pGame->GetWorld()->Remove(building, CBuildingPool_Destructor);
140+
141+
pBuildsingsPool->Release(i);
142+
143+
(*m_pOriginalBuildingsBackup)[i].first = true;
144+
(*m_pOriginalBuildingsBackup)[i].second = *building;
145+
}
146+
else
147+
{
148+
(*m_pOriginalBuildingsBackup)[i].first = false;
149+
}
150+
}
151+
}
152+
153+
void CBuildingsPoolSA::RestoreAllBuildings()
154+
{
155+
if (!m_pOriginalBuildingsBackup)
156+
return;
157+
158+
auto& originalData = *m_pOriginalBuildingsBackup;
159+
auto pBuildsingsPool = (*m_ppBuildingPoolInterface);
160+
for (size_t i = 0; i < MAX_BUILDINGS; i++)
161+
{
162+
if (originalData[i].first)
163+
{
164+
pBuildsingsPool->AllocateAt(i);
165+
auto building = pBuildsingsPool->GetObject(i);
166+
*building = originalData[i].second;
167+
168+
pGame->GetWorld()->Add(building, CBuildingPool_Constructor);
169+
}
170+
}
171+
172+
m_pOriginalBuildingsBackup.release();
173+
}
174+
175+
bool CBuildingsPoolSA::HasFreeBuildingSlot()
176+
{
177+
return (*m_ppBuildingPoolInterface)->GetFreeSlot() != -1;
178+
}

Client/game_sa/CBuildingsPoolSA.h

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/*****************************************************************************
2+
*
3+
* PROJECT: Multi Theft Auto v1.0
4+
* LICENSE: See LICENSE in the top level directory
5+
* FILE: game_sa/CBuildingsPoolSA.h
6+
* PURPOSE: Buildings pool class
7+
*
8+
* Multi Theft Auto is available from http://www.multitheftauto.com/
9+
*
10+
*****************************************************************************/
11+
12+
#include <game/CBuildingsPool.h>
13+
#include <CVector.h>
14+
#include "CPoolSAInterface.h"
15+
#include "CBuildingSA.h"
16+
17+
class CBuildingsPoolSA : public CBuildingsPool
18+
{
19+
public:
20+
CBuildingsPoolSA();
21+
~CBuildingsPoolSA() = default;
22+
23+
CBuilding* AddBuilding(CClientBuilding*, uint16_t modelId, CVector* vPos, CVector4D* vRot, uint8_t interior);
24+
void RemoveBuilding(CBuilding* pBuilding);
25+
bool HasFreeBuildingSlot();
26+
27+
void RemoveAllBuildings();
28+
void RestoreAllBuildings();
29+
30+
private:
31+
bool AddBuildingToPool(CClientBuilding* pClientBuilding, CBuildingSA* pBuilding);
32+
33+
private:
34+
SPoolData<CBuildingSA, CBuildingSAInterface, MAX_BUILDINGS> m_buildingPool;
35+
CPoolSAInterface<CBuildingSAInterface>** m_ppBuildingPoolInterface;
36+
37+
std::unique_ptr<std::array<std::pair<bool, CBuildingSAInterface>, MAX_BUILDINGS>> m_pOriginalBuildingsBackup;
38+
};

Client/game_sa/CGameSA.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@
5656
#include "CWeatherSA.h"
5757
#include "CWorldSA.h"
5858
#include "D3DResourceSystemSA.h"
59+
#include "CIplStoreSA.h"
5960

6061
extern CGameSA* pGame;
6162

@@ -137,6 +138,7 @@ CGameSA::CGameSA()
137138
m_pWeaponStatsManager = new CWeaponStatManagerSA();
138139
m_pPointLights = new CPointLightsSA();
139140
m_collisionStore = new CColStoreSA();
141+
m_pIplStore = new CIplStoreSA();
140142

141143
// Normal weapon types (WEAPONSKILL_STD)
142144
for (int i = 0; i < NUM_WeaponInfosStdSkill; i++)
@@ -273,6 +275,7 @@ CGameSA::~CGameSA()
273275
delete reinterpret_cast<CAudioContainerSA*>(m_pAudioContainer);
274276
delete reinterpret_cast<CPointLightsSA*>(m_pPointLights);
275277
delete static_cast<CColStoreSA*>(m_collisionStore);
278+
delete static_cast<CIplStore*>(m_pIplStore);
276279

277280
delete[] ModelInfo;
278281
delete[] ObjectGroupsInfo;
@@ -425,6 +428,9 @@ void CGameSA::Reset()
425428

426429
// Restore changed TXD IDs
427430
CModelInfoSA::StaticResetTextureDictionaries();
431+
432+
// Restore default world state
433+
RestoreGameBuildings();
428434
}
429435
}
430436

@@ -882,6 +888,20 @@ void CGameSA::GetShaderReplacementStats(SShaderReplacementStats& outStats)
882888
m_pRenderWare->GetShaderReplacementStats(outStats);
883889
}
884890

891+
void CGameSA::RemoveAllBuildings()
892+
{
893+
m_pIplStore->SetDynamicIplStreamingEnabled(false);
894+
895+
m_pPools->GetBuildingsPool().RemoveAllBuildings();
896+
}
897+
898+
void CGameSA::RestoreGameBuildings()
899+
{
900+
m_pPools->GetBuildingsPool().RestoreAllBuildings();
901+
902+
m_pIplStore->SetDynamicIplStreamingEnabled(true, [](CIplSAInterface* ipl) { return memcmp("barriers", ipl->name, 8) != 0; });
903+
}
904+
885905
// Ensure models have the default lod distances
886906
void CGameSA::ResetModelLodDistances()
887907
{

Client/game_sa/CGameSA.h

+5
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ class CGameSA : public CGame
152152
CColStore* GetCollisionStore() override { return m_collisionStore; }
153153
CRenderWareSA* GetRenderWareSA() { return m_pRenderWare; }
154154
CFxManagerSA* GetFxManagerSA() { return m_pFxManager; }
155+
CIplStore* GetIplStore() { return m_pIplStore; };
155156

156157
CWeaponInfo* GetWeaponInfo(eWeaponType weapon, eWeaponSkill skill = WEAPONSKILL_STD);
157158
CModelInfo* GetModelInfo(DWORD dwModelID, bool bCanBeInvalid = false);
@@ -273,6 +274,9 @@ class CGameSA : public CGame
273274
PostWeaponFireHandler* m_pPostWeaponFireHandler;
274275
TaskSimpleBeHitHandler* m_pTaskSimpleBeHitHandler;
275276

277+
void RemoveAllBuildings();
278+
void RestoreGameBuildings();
279+
276280
private:
277281
CPools* m_pPools;
278282
CPlayerInfo* m_pPlayerInfo;
@@ -320,6 +324,7 @@ class CGameSA : public CGame
320324
CGameSettings* m_pSettings;
321325
CCarEnterExit* m_pCarEnterExit;
322326
CControllerConfigManager* m_pControllerConfigManager;
327+
CIplStore* m_pIplStore;
323328

324329
eGameVersion m_eGameVersion;
325330
bool m_bAsyncScriptEnabled;

Client/game_sa/CIplSA.h

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*****************************************************************************
2+
*
3+
* PROJECT: Multi Theft Auto v1.0
4+
* LICENSE: See LICENSE in the top level directory
5+
* FILE: game_sa/CIplSA.h
6+
* PURPOSE: Header file for game IPL class
7+
*
8+
* Multi Theft Auto is available from http://www.multitheftauto.com/
9+
*
10+
*****************************************************************************/
11+
12+
#pragma once
13+
14+
#include <cstdint>
15+
#include "CRect.h"
16+
17+
class CIplSAInterface
18+
{
19+
public:
20+
CRect rect;
21+
char name[16];
22+
uint16_t unk;
23+
uint16_t minBuildId;
24+
uint16_t maxBuildId;
25+
uint16_t minBummyId;
26+
uint16_t maxDummyId;
27+
uint16_t relatedIpl;
28+
uint8_t interior;
29+
uint8_t unk2;
30+
uint8_t bLoadReq;
31+
uint8_t bDisabledStreaming;
32+
uint8_t unk3;
33+
uint8_t unk4;
34+
uint8_t unk5;
35+
uint8_t unk6;
36+
};
37+
static_assert(sizeof(CIplSAInterface) == 0x34, "Wrong CIplSAInterface size");

0 commit comments

Comments
 (0)