diff --git a/Client/game_sa/CBuildingsPoolSA.cpp b/Client/game_sa/CBuildingsPoolSA.cpp index 76e14cd34e..b0f5812a9d 100644 --- a/Client/game_sa/CBuildingsPoolSA.cpp +++ b/Client/game_sa/CBuildingsPoolSA.cpp @@ -16,6 +16,8 @@ #include #include "CGameSA.h" #include "CPtrNodeSingleListSA.h" +#include "MemSA.h" +#include "CVehicleSA.h" extern CGameSA* pGame; @@ -93,16 +95,13 @@ void CBuildingsPoolSA::RemoveBuilding(CBuilding* pBuilding) if (dwElementIndexInPool == UINT_MAX) return; - // Remove building from world - pGame->GetWorld()->Remove(pInterface, CBuildingPool_Destructor); - // Remove building from cover list - CPtrNodeSingleListSAInterface* coverList = reinterpret_cast*>(0xC1A2B8); - coverList->RemoveItem(pInterface); + pGame->GetCoverManager()->RemoveCover(pInterface); // Remove plant - using CPlantColEntry_Remove = CEntitySAInterface* (*)(CEntitySAInterface*); - ((CPlantColEntry_Remove)0x5DBEF0)(pInterface); + pGame->GetPlantManager()->RemovePlant(pInterface); + + RemoveBuildingFromWorld(pInterface); // Remove col reference auto modelInfo = pGame->GetModelInfo(pBuilding->GetModelIndex()); @@ -127,6 +126,13 @@ void CBuildingsPoolSA::RemoveAllBuildings() if (m_pOriginalBuildingsBackup) return; + pGame->GetCoverManager()->RemoveAllCovers(); + pGame->GetPlantManager()->RemoveAllPlants(); + + // Remove all shadows + using CStencilShadowObjects_dtorAll = void* (*)(); + ((CStencilShadowObjects_dtorAll)0x711390)(); + m_pOriginalBuildingsBackup = std::make_unique, MAX_BUILDINGS>>(); auto pBuildsingsPool = (*m_ppBuildingPoolInterface); @@ -134,9 +140,9 @@ void CBuildingsPoolSA::RemoveAllBuildings() { if (pBuildsingsPool->IsContains(i)) { - auto building = pBuildsingsPool->GetObject(i); + CBuildingSAInterface* building = pBuildsingsPool->GetObject(i); - pGame->GetWorld()->Remove(building, CBuildingPool_Destructor); + RemoveBuildingFromWorld(building); pBuildsingsPool->Release(i); @@ -162,14 +168,146 @@ void CBuildingsPoolSA::RestoreAllBuildings() if (originalData[i].first) { pBuildsingsPool->AllocateAt(i); - auto building = pBuildsingsPool->GetObject(i); - *building = originalData[i].second; + auto pBuilding = pBuildsingsPool->GetObject(i); + *pBuilding = originalData[i].second; - pGame->GetWorld()->Add(building, CBuildingPool_Constructor); + pGame->GetWorld()->Add(pBuilding, CBuildingPool_Constructor); } } - m_pOriginalBuildingsBackup.release(); + m_pOriginalBuildingsBackup = nullptr; +} + +void CBuildingsPoolSA::RemoveBuildingFromWorld(CBuildingSAInterface* pBuilding) +{ + // Remove building from world + pGame->GetWorld()->Remove(pBuilding, CBuildingPool_Destructor); + + pBuilding->DeleteRwObject(); + pBuilding->ResolveReferences(); + pBuilding->RemoveShadows(); +} + +bool CBuildingsPoolSA::Resize(int size) +{ + auto* pool = (*m_ppBuildingPoolInterface); + const int curretnSize = pool->m_nSize; + + void* oldPool = pool->m_pObjects; + + if (oldPool != nullptr) + { + MemSA::free(pool->m_pObjects); + pool->m_pObjects = nullptr; + } + + if (pool->m_byteMap != nullptr) + { + MemSA::free(pool->m_byteMap); + pool->m_byteMap = nullptr; + } + + CBuildingSAInterface* newObjects = MemSA::malloc_struct(size); + if (newObjects == nullptr) + { + Resize(curretnSize); + return false; + } + + tPoolObjectFlags* newBytemap = MemSA::malloc_struct(size); + if (newBytemap == nullptr) + { + MemSA::free(newObjects); + Resize(curretnSize); + return false; + } + + pool->m_pObjects = newObjects; + pool->m_byteMap = newBytemap; + pool->m_nSize = size; + pool->m_nFirstFree = 0; + + for (auto i = 0; i < size; i++) + { + newBytemap[i].bEmpty = true; + } + + const uint32_t offset = (uint32_t)newObjects - (uint32_t)oldPool; + if (oldPool != nullptr) + { + UpdateIplEntrysPointers(offset); + } + + if (m_pOriginalBuildingsBackup) + { + UpdateBackupLodPointers(offset); + } + + pGame->GetPools()->GetDummyPool().UpdateBuildingLods(oldPool, newObjects); + + RemoveVehicleDamageLinks(); + + return true; +} + +void CBuildingsPoolSA::UpdateIplEntrysPointers(uint32_t offset) +{ + using buildings_array_t = CBuildingSAInterface* [1000]; + using ipl_entry_array_t = buildings_array_t* [40]; + ipl_entry_array_t* iplEntryArray = (ipl_entry_array_t*)0x8E3F08; + + for (auto i = 0; i < 40; i++) + { + buildings_array_t* ppArray = (*iplEntryArray)[i]; + + if (ppArray == nullptr) + { + return; + } + size_t arraySize = MemSA::msize(*ppArray) / sizeof(CBuildingSAInterface*); + for (auto j = 0; j < arraySize; j++) + { + CBuildingSAInterface* object = (*ppArray)[j]; + + (*ppArray)[j] = (CBuildingSAInterface*)((uint32_t)object + offset); + } + } +} + +void CBuildingsPoolSA::UpdateBackupLodPointers(uint32_t offset) +{ + std::array, MAX_BUILDINGS> *arr = m_pOriginalBuildingsBackup.get(); + for (auto i = 0; i < MAX_BUILDINGS; i++) + { + std::pair* data = &(*arr)[i]; + if (data->first) + { + CBuildingSAInterface* building = &data->second; + if (building->m_pLod != nullptr) + { + building->m_pLod = (CBuildingSAInterface*)((uint32_t)building->m_pLod + offset); + } + } + } +} + +void CBuildingsPoolSA::RemoveVehicleDamageLinks() +{ + const int count = pGame->GetPools()->GetVehicleCount(); + for (int i = 0; i < count; i++) + { + auto* vehLinks = pGame->GetPools()->GetVehicle(i); + if (vehLinks->pEntity) + { + CVehicleSAInterface* vehicle = vehLinks->pEntity->GetVehicleInterface(); + vehicle->m_pCollidedEntity = nullptr; + vehicle->pLastContactedEntity[0] = nullptr; + vehicle->pLastContactedEntity[1] = nullptr; + vehicle->pLastContactedEntity[2] = nullptr; + vehicle->pLastContactedEntity[3] = nullptr; + vehicle->m_ucCollisionState = 0; + } + } } bool CBuildingsPoolSA::HasFreeBuildingSlot() diff --git a/Client/game_sa/CBuildingsPoolSA.h b/Client/game_sa/CBuildingsPoolSA.h index 25854e3b2e..0a2be522d5 100644 --- a/Client/game_sa/CBuildingsPoolSA.h +++ b/Client/game_sa/CBuildingsPoolSA.h @@ -9,6 +9,8 @@ * *****************************************************************************/ +#pragma once + #include #include #include "CPoolSAInterface.h" @@ -24,11 +26,17 @@ class CBuildingsPoolSA : public CBuildingsPool void RemoveBuilding(CBuilding* pBuilding); bool HasFreeBuildingSlot(); - void RemoveAllBuildings(); - void RestoreAllBuildings(); + void RemoveAllBuildings() override; + void RestoreAllBuildings() override; + bool Resize(int size) override; + int GetSize() const override { return (*m_ppBuildingPoolInterface)->m_nSize; }; private: - bool AddBuildingToPool(CClientBuilding* pClientBuilding, CBuildingSA* pBuilding); + void RemoveBuildingFromWorld(CBuildingSAInterface* pBuilding); + bool AddBuildingToPool(CClientBuilding* pClientBuilding, CBuildingSA* pBuilding); + void UpdateIplEntrysPointers(uint32_t offset); + void UpdateBackupLodPointers(uint32_t offset); + void RemoveVehicleDamageLinks(); private: SPoolData m_buildingPool; diff --git a/Client/game_sa/CCoverManagerSA.cpp b/Client/game_sa/CCoverManagerSA.cpp new file mode 100644 index 0000000000..dee7782ffb --- /dev/null +++ b/Client/game_sa/CCoverManagerSA.cpp @@ -0,0 +1,41 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CCoverManagerSA.cpp + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#include "StdInc.h" +#include "CCoverManagerSA.h" + +CCoverManagerSA::CCoverManagerSA() +{ + m_pCoverList = reinterpret_cast*>(0xC1A2B8); +} + +void CCoverManagerSA::RemoveAllCovers() +{ + CPtrNodeDoubleLink* pNode = m_pCoverList->m_pNode; + while (pNode) + { + RemoveCoverFromArray(pNode->pItem); + pNode = pNode->pNext; + } + m_pCoverList->RemoveAllItems(); +} + +void CCoverManagerSA::RemoveCover(CEntitySAInterface* entity) +{ + RemoveCoverFromArray(entity); + + m_pCoverList->RemoveItem(entity); +} + +void CCoverManagerSA::RemoveCoverFromArray(CEntitySAInterface* entity) +{ + using CCover_RemoveCoverPointsForThisEntity = char(__cdecl*)(CEntitySAInterface*); + ((CCover_RemoveCoverPointsForThisEntity)0x698740)(entity); +} diff --git a/Client/game_sa/CCoverManagerSA.h b/Client/game_sa/CCoverManagerSA.h new file mode 100644 index 0000000000..6944b43604 --- /dev/null +++ b/Client/game_sa/CCoverManagerSA.h @@ -0,0 +1,30 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CCoverManagerSA.h + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include "CEntitySA.h" +#include "CPtrNodeDoubleListSA.h" + +class CCoverManagerSA +{ +public: + CCoverManagerSA(); + ~CCoverManagerSA() = default; + + void RemoveAllCovers(); + void RemoveCover(CEntitySAInterface* entity); + +private: + void RemoveCoverFromArray(CEntitySAInterface* entity); + +private: + CPtrNodeDoubleListSAInterface* m_pCoverList; +}; diff --git a/Client/game_sa/CDummyPoolSA.cpp b/Client/game_sa/CDummyPoolSA.cpp new file mode 100644 index 0000000000..7f9b539a40 --- /dev/null +++ b/Client/game_sa/CDummyPoolSA.cpp @@ -0,0 +1,76 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CDummyPoolSA.cpp + * PURPOSE: Dummy pool class + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include "StdInc.h" +#include "CDummyPoolSA.h" + +CDummyPoolSA::CDummyPoolSA() +{ + m_ppDummyPoolInterface = (CPoolSAInterface**)0xB744A0; +} + +void CDummyPoolSA::RemoveAllBuildingLods() +{ + if (m_pLodBackup) + return; + + m_pLodBackup = std::make_unique>(); + + for (int i = 0; i < MAX_DUMMIES; i++) + { + CEntitySAInterface* object = (*m_ppDummyPoolInterface)->GetObject(i); + (*m_pLodBackup)[i] = object->m_pLod; + object->m_pLod = nullptr; + } +} + +void CDummyPoolSA::RestoreAllBuildingsLods() +{ + if (!m_pLodBackup) + return; + + for (int i = 0; i < MAX_DUMMIES; i++) + { + CEntitySAInterface* object = (*m_ppDummyPoolInterface)->GetObject(i); + object->m_pLod = (*m_pLodBackup)[i]; + } + + m_pLodBackup.release(); +} + +void CDummyPoolSA::UpdateBuildingLods(void* oldPool, void* newPool) +{ + const uint32_t offset = (uint32_t)newPool - (uint32_t)oldPool; + + if (m_pLodBackup) + { + for (int i = 0; i < MAX_DUMMIES; i++) + { + if ((*m_pLodBackup)[i] != nullptr) + { + (*m_pLodBackup)[i] = (CEntitySAInterface*)((uint32_t)(*m_pLodBackup)[i] + offset); + } + } + } + else + { + for (int i = 0; i < MAX_DUMMIES; i++) + { + CEntitySAInterface* object = (*m_ppDummyPoolInterface)->GetObject(i); + if (object->m_pLod) + { + object->m_pLod = (CEntitySAInterface*)((uint32_t)object->m_pLod + offset); + } + } + } +} diff --git a/Client/game_sa/CDummyPoolSA.h b/Client/game_sa/CDummyPoolSA.h new file mode 100644 index 0000000000..95d00b19f3 --- /dev/null +++ b/Client/game_sa/CDummyPoolSA.h @@ -0,0 +1,33 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CDummyPoolSA.h + * PURPOSE: Dummy pool class + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include +#include "CEntitySA.h" +#include "CPoolSAInterface.h" +#include + +class CDummyPoolSA : public CDummyPool +{ +public: + CDummyPoolSA(); + ~CDummyPoolSA() = default; + + void RemoveAllBuildingLods(); + void RestoreAllBuildingsLods(); + void UpdateBuildingLods(void* oldPool, void* newPool); + +private: + CPoolSAInterface** m_ppDummyPoolInterface; + + std::unique_ptr> m_pLodBackup; +}; diff --git a/Client/game_sa/CEntitySA.h b/Client/game_sa/CEntitySA.h index 101b88a46a..9de57edc0a 100644 --- a/Client/game_sa/CEntitySA.h +++ b/Client/game_sa/CEntitySA.h @@ -224,6 +224,24 @@ class CEntitySAInterface return numLodChildrenRendered & 0x1f; return -1; } + + void ResolveReferences() + { + using CEntity_ResolveReferences = void*(__thiscall*)(CEntitySAInterface*); + ((CEntity_ResolveReferences)0x571A40)(this); + }; + + void RemoveShadows() + { + using CStencilShadow_dtorByOwner = void*(__cdecl*)(CEntitySAInterface * pEntity); + ((CStencilShadow_dtorByOwner)0x711730)(this); + }; + + void DeleteRwObject() + { + using vtbl_DeleteRwObject = void(__thiscall*)(CEntitySAInterface * pEntity); + ((vtbl_DeleteRwObject)this->vtbl->DeleteRwObject)(this); + }; }; static_assert(sizeof(CEntitySAInterface) == 0x38, "Invalid size for CEntitySAInterface"); diff --git a/Client/game_sa/CGameSA.cpp b/Client/game_sa/CGameSA.cpp index b1127b16d3..e4029c7ecd 100644 --- a/Client/game_sa/CGameSA.cpp +++ b/Client/game_sa/CGameSA.cpp @@ -139,6 +139,8 @@ CGameSA::CGameSA() m_pPointLights = new CPointLightsSA(); m_collisionStore = new CColStoreSA(); m_pIplStore = new CIplStoreSA(); + m_pCoverManager = new CCoverManagerSA(); + m_pPlantManager = new CPlantManagerSA(); // Normal weapon types (WEAPONSKILL_STD) for (int i = 0; i < NUM_WeaponInfosStdSkill; i++) @@ -276,6 +278,8 @@ CGameSA::~CGameSA() delete reinterpret_cast(m_pPointLights); delete static_cast(m_collisionStore); delete static_cast(m_pIplStore); + delete m_pCoverManager; + delete m_pPlantManager; delete[] ModelInfo; delete[] ObjectGroupsInfo; @@ -903,16 +907,29 @@ void CGameSA::RemoveAllBuildings() { m_pIplStore->SetDynamicIplStreamingEnabled(false); + m_pPools->GetDummyPool().RemoveAllBuildingLods(); m_pPools->GetBuildingsPool().RemoveAllBuildings(); } void CGameSA::RestoreGameBuildings() { m_pPools->GetBuildingsPool().RestoreAllBuildings(); + m_pPools->GetDummyPool().RestoreAllBuildingsLods(); m_pIplStore->SetDynamicIplStreamingEnabled(true, [](CIplSAInterface* ipl) { return memcmp("barriers", ipl->name, 8) != 0; }); } +bool CGameSA::SetBuildingPoolSize(size_t size) +{ + RemoveAllBuildings(); + + bool status = m_pPools->GetBuildingsPool().Resize(size); + + RestoreGameBuildings(); + + return status; +} + // Ensure models have the default lod distances void CGameSA::ResetModelLodDistances() { diff --git a/Client/game_sa/CGameSA.h b/Client/game_sa/CGameSA.h index 7ad273350f..c668276656 100644 --- a/Client/game_sa/CGameSA.h +++ b/Client/game_sa/CGameSA.h @@ -15,6 +15,8 @@ #include "CFxManagerSA.h" #include "CModelInfoSA.h" #include "CStreamingSA.h" +#include "CCoverManagerSA.h" +#include "CPlantManagerSA.h" class CAnimBlendClumpDataSAInterface; class CObjectGroupPhysicalPropertiesSA; @@ -153,6 +155,8 @@ class CGameSA : public CGame CRenderWareSA* GetRenderWareSA() { return m_pRenderWare; } CFxManagerSA* GetFxManagerSA() { return m_pFxManager; } CIplStore* GetIplStore() { return m_pIplStore; }; + CCoverManagerSA* GetCoverManager() const noexcept { return m_pCoverManager; }; + CPlantManagerSA* GetPlantManager() const noexcept { return m_pPlantManager; }; CWeaponInfo* GetWeaponInfo(eWeaponType weapon, eWeaponSkill skill = WEAPONSKILL_STD); CModelInfo* GetModelInfo(DWORD dwModelID, bool bCanBeInvalid = false); @@ -280,6 +284,8 @@ class CGameSA : public CGame void RemoveAllBuildings(); void RestoreGameBuildings(); + bool SetBuildingPoolSize(size_t size); + private: CPools* m_pPools; CPlayerInfo* m_pPlayerInfo; @@ -314,6 +320,8 @@ class CGameSA : public CGame CPointLights* m_pPointLights; CColStore* m_collisionStore; CObjectGroupPhysicalProperties* m_pObjectGroupPhysicalProperties; + CCoverManagerSA* m_pCoverManager; + CPlantManagerSA* m_pPlantManager; CPad* m_pPad; CAERadioTrackManager* m_pCAERadioTrackManager; diff --git a/Client/game_sa/CPhysicalSA.h b/Client/game_sa/CPhysicalSA.h index 7ac72168f1..2b1234150c 100644 --- a/Client/game_sa/CPhysicalSA.h +++ b/Client/game_sa/CPhysicalSA.h @@ -14,6 +14,7 @@ #include #include "CEntitySA.h" #include +#include "CPtrNodeDoubleListSA.h" #define FUNC_GetMoveSpeed 0x404460 #define FUNC_GetTurnSpeed 0x470030 @@ -102,7 +103,7 @@ class CPhysicalSAInterface : public CEntitySAInterface CVector m_vecAttachedRotation; // 268 CVector m_vecUnk; // 280 uint32 m_pad4; // 292 - class CPtrNodeDoubleLink* m_pControlCodeNodeLink; // 296 + CPtrNodeDoubleLink* m_pControlCodeNodeLink; // 296 float m_fLighting; // 300 float m_fLighting2; // 304 class CShadowDataSA* m_pShadowData; // 308 diff --git a/Client/game_sa/CPlantManagerSA.cpp b/Client/game_sa/CPlantManagerSA.cpp new file mode 100644 index 0000000000..aef1849aae --- /dev/null +++ b/Client/game_sa/CPlantManagerSA.cpp @@ -0,0 +1,36 @@ + +#include "StdInc.h" +#include "CPlantManagerSA.h" +#include "CPtrNodeSingleListSA.h" + +class CPlantLocTri; + +class CPlantColEntEntry +{ +public: + CEntitySAInterface* m_Entity; + CPlantLocTri** m_Objects; + uint16 m_numTriangles; + CPlantColEntEntry* m_NextEntry; + CPlantColEntEntry* m_PrevEntry; + +public: + void ReleaseEntry() { + using CPlantColEntEntry_ReleaseEntry = void* ( __thiscall *)(CPlantColEntEntry*); + ((CPlantColEntEntry_ReleaseEntry)0x5DB8A0)(this); + }; +}; + +void CPlantManagerSA::RemoveAllPlants() +{ + while (true) + { + auto list = reinterpret_cast(0xC0399C); + if (*list == nullptr) + { + break; + } + + (*list)->ReleaseEntry(); + } +} diff --git a/Client/game_sa/CPlantManagerSA.h b/Client/game_sa/CPlantManagerSA.h new file mode 100644 index 0000000000..4ade022684 --- /dev/null +++ b/Client/game_sa/CPlantManagerSA.h @@ -0,0 +1,18 @@ +#pragma once + +#include "CEntitySA.h" + +class CPlantManagerSA +{ +public: + CPlantManagerSA() = default; + ~CPlantManagerSA() = default; + + void RemovePlant(CEntitySAInterface* enity) + { + using CPlantColEntry_Remove = CEntitySAInterface* (*)(CEntitySAInterface*); + ((CPlantColEntry_Remove)0x5DBEF0)(enity); + }; + + void RemoveAllPlants(); +}; diff --git a/Client/game_sa/CPoolsSA.cpp b/Client/game_sa/CPoolsSA.cpp index 2495cfcdc4..2be6742617 100644 --- a/Client/game_sa/CPoolsSA.cpp +++ b/Client/game_sa/CPoolsSA.cpp @@ -809,6 +809,54 @@ int CPoolsSA::GetPoolDefaultCapacity(ePools pool) return 0; } +int CPoolsSA::GetPoolDefaultModdedCapacity(ePools pool) +{ + switch (pool) + { + case BUILDING_POOL: + return MAX_BUILDINGS; + case PED_POOL: + return 140; + case OBJECT_POOL: + return MAX_OBJECTS; + case DUMMY_POOL: + return 2500; + case VEHICLE_POOL: + return 110; + case COL_MODEL_POOL: + return 12000; + case TASK_POOL: + return 5000; + case EVENT_POOL: + return 5000; + case TASK_ALLOCATOR_POOL: + return 16; + case PED_INTELLIGENCE_POOL: + return 140; + case PED_ATTRACTOR_POOL: + return 64; + case ENTRY_INFO_NODE_POOL: + return MAX_ENTRY_INFO_NODES; + case NODE_ROUTE_POOL: + return 64; + case PATROL_ROUTE_POOL: + return 32; + case POINT_ROUTE_POOL: + return 64; + case POINTER_DOUBLE_LINK_POOL: + return MAX_POINTER_DOUBLE_LINKS; + case POINTER_SINGLE_LINK_POOL: + return MAX_POINTER_SINGLE_LINKS; + case ENV_MAP_MATERIAL_POOL: + return 16000; + case ENV_MAP_ATOMIC_POOL: + return 4000; + case SPEC_MAP_MATERIAL_POOL: + return 16000; + } + return 0; +} + int CPoolsSA::GetPoolCapacity(ePools pool) { DWORD iPtr = NULL; @@ -816,8 +864,7 @@ int CPoolsSA::GetPoolCapacity(ePools pool) switch (pool) { case BUILDING_POOL: - iPtr = 0x55105F; - break; + return GetBuildingsPool().GetSize(); case PED_POOL: iPtr = 0x550FF2; break; diff --git a/Client/game_sa/CPoolsSA.h b/Client/game_sa/CPoolsSA.h index f3b09a4101..2e0ba7427e 100644 --- a/Client/game_sa/CPoolsSA.h +++ b/Client/game_sa/CPoolsSA.h @@ -17,6 +17,7 @@ #include "CBuildingSA.h" #include "CTextureDictonarySA.h" #include "CBuildingsPoolSA.h" +#include "CDummyPoolSA.h" #define INVALID_POOL_ARRAY_ID 0xFFFFFFFF @@ -37,11 +38,8 @@ class CPoolsSA : public CPools public: void RemoveVehicle(CVehicle* pVehicle, bool bDelete = true); SClientEntity* GetVehicle(DWORD* pGameInterface); - unsigned long GetVehicleCount() - { - return m_vehiclePool.ulCount; - ; - } + SClientEntity* GetVehicle(size_t pos) { return &m_vehiclePool.arrayOfClientEntities[pos]; }; + unsigned long GetVehicleCount() { return m_vehiclePool.ulCount; }; void DeleteAllVehicles(); // Objects pool @@ -85,6 +83,7 @@ class CPoolsSA : public CPools int GetNumberOfUsedSpaces(ePools pools); int GetPoolDefaultCapacity(ePools pool); + int GetPoolDefaultModdedCapacity(ePools pool); int GetPoolCapacity(ePools pool); void SetPoolCapacity(ePools pool, int iValue); @@ -98,6 +97,7 @@ class CPoolsSA : public CPools ushort GetFreeTextureDictonarySlot(); CBuildingsPool& GetBuildingsPool() noexcept override { return m_BuildingsPool; }; + CDummyPool& GetDummyPool() noexcept { return m_DummyPool; }; private: // Pools @@ -111,6 +111,7 @@ class CPoolsSA : public CPools CPoolSAInterface** m_ppTxdPoolInterface; CBuildingsPoolSA m_BuildingsPool; + CDummyPoolSA m_DummyPool; bool m_bGetVehicleEnabled; }; diff --git a/Client/game_sa/CPtrNodeDoubleListSA.h b/Client/game_sa/CPtrNodeDoubleListSA.h new file mode 100644 index 0000000000..fb6f6c9978 --- /dev/null +++ b/Client/game_sa/CPtrNodeDoubleListSA.h @@ -0,0 +1,41 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/CPtrNodeDoubleListSA.h + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +template +class CPtrNodeDoubleLink +{ +public: + T* pItem; + CPtrNodeDoubleLink* pNext; + CPtrNodeDoubleLink* pPrev; +}; + +template +class CPtrNodeDoubleListSAInterface +{ +public: + CPtrNodeDoubleLink* m_pNode; + + void RemoveItem(T* pItem) + { + using CPtrListDoubleLinkSAInterface_RemoveItem = void(__thiscall*)(CPtrNodeDoubleListSAInterface * pLinkList, void* item); + ((CPtrListDoubleLinkSAInterface_RemoveItem)0x5336B0)(this, pItem); + }; + + void RemoveAllItems() + { + while (m_pNode) + { + RemoveItem(m_pNode->pItem); + } + }; +}; diff --git a/Client/game_sa/CPtrNodeSingleListSA.h b/Client/game_sa/CPtrNodeSingleListSA.h index 06df6a2575..f083614421 100644 --- a/Client/game_sa/CPtrNodeSingleListSA.h +++ b/Client/game_sa/CPtrNodeSingleListSA.h @@ -2,7 +2,7 @@ * * PROJECT: Multi Theft Auto v1.0 * LICENSE: See LICENSE in the top level directory - * FILE: game_sa/CPtrNodeSingleListSA.cpp + * FILE: game_sa/CPtrNodeSingleListSA.h * * Multi Theft Auto is available from http://www.multitheftauto.com/ * diff --git a/Client/game_sa/MemSA.h b/Client/game_sa/MemSA.h new file mode 100644 index 0000000000..34d4a949ea --- /dev/null +++ b/Client/game_sa/MemSA.h @@ -0,0 +1,39 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: game_sa/MemSA.h + * PURPOSE: C memory functions in SA + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +namespace MemSA +{ + size_t msize(const void* p) + { + using gta_msize = size_t (*__cdecl)(const void*); + return ((gta_msize)0x828C4A)(p); + }; + + void* malloc(size_t count) + { + using gta_malloc = void* (*__cdecl)(size_t a1); + return ((gta_malloc)0x824257)(count); + }; + + template + T* malloc_struct(size_t count) + { + return static_cast(MemSA::malloc(sizeof(T) * count)); + } + + void free(void* p) + { + using gta_free = void (*__cdecl)(void* p); + return ((gta_free)0x82413F)(p); + }; +} diff --git a/Client/mods/deathmatch/logic/CClientBuilding.h b/Client/mods/deathmatch/logic/CClientBuilding.h index f98f8f6413..2a45673bce 100644 --- a/Client/mods/deathmatch/logic/CClientBuilding.h +++ b/Client/mods/deathmatch/logic/CClientBuilding.h @@ -43,8 +43,6 @@ class CClientBuilding : public CClientEntity eClientEntityType GetType() const { return CCLIENTBUILDING; } - void Create(); - void Destroy(); bool IsValid() const noexcept { return m_pBuilding != nullptr; }; @@ -54,6 +52,9 @@ class CClientBuilding : public CClientEntity private: + void Create(); + void Destroy(); + CClientBuilding* GetHighLodBuilding() const { return m_pHighBuilding; }; void SetHighLodBuilding(CClientBuilding* pHighBuilding = nullptr) { m_pHighBuilding = pHighBuilding; }; diff --git a/Client/mods/deathmatch/logic/CClientBuildingManager.cpp b/Client/mods/deathmatch/logic/CClientBuildingManager.cpp index 13a3246497..5fe42baf23 100644 --- a/Client/mods/deathmatch/logic/CClientBuildingManager.cpp +++ b/Client/mods/deathmatch/logic/CClientBuildingManager.cpp @@ -85,3 +85,26 @@ bool CClientBuildingManager::IsValidPosition(const CVector& pos) noexcept return (pos.fX >= -WORLD_DISTANCE_FROM_CENTER && pos.fX <= WORLD_DISTANCE_FROM_CENTER && pos.fY >= -WORLD_DISTANCE_FROM_CENTER && pos.fY <= WORLD_DISTANCE_FROM_CENTER); } + +void CClientBuildingManager::DestroyAllForABit() +{ + for (CClientBuilding* building : GetBuildings()) + { + building->Destroy(); + } +} + +void CClientBuildingManager::RestoreDestroyed() +{ + for (CClientBuilding* building : GetBuildings()) + { + building->Create(); + + if (!building->IsValid()) + { + // User creates too much buildings + // We can't restore them all + delete building; + } + } +} diff --git a/Client/mods/deathmatch/logic/CClientBuildingManager.h b/Client/mods/deathmatch/logic/CClientBuildingManager.h index e47ce1e0eb..9a649dd5dd 100644 --- a/Client/mods/deathmatch/logic/CClientBuildingManager.h +++ b/Client/mods/deathmatch/logic/CClientBuildingManager.h @@ -31,6 +31,9 @@ class CClientBuildingManager static bool IsValidModel(uint16_t modelId); static bool IsValidPosition(const CVector& pos) noexcept; + void DestroyAllForABit(); + void RestoreDestroyed(); + private: void AddToList(CClientBuilding* pBuilding) { m_List.push_back(pBuilding); } void RemoveFromList(CClientBuilding* pBuilding); diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp index 5ee0b062b1..e037494a1b 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.cpp @@ -830,6 +830,29 @@ ADD_ENUM(eRenderStage::POST_FX, "postfx") ADD_ENUM(eRenderStage::POST_GUI, "postgui") IMPLEMENT_ENUM_CLASS_END("render-stage") +IMPLEMENT_ENUM_BEGIN(ePools) +ADD_ENUM(ePools::BUILDING_POOL, "building") +ADD_ENUM(ePools::PED_POOL, "ped") +ADD_ENUM(ePools::OBJECT_POOL, "object") +ADD_ENUM(ePools::DUMMY_POOL, "dummy") +ADD_ENUM(ePools::VEHICLE_POOL, "vehicle") +ADD_ENUM(ePools::COL_MODEL_POOL, "col-model") +ADD_ENUM(ePools::TASK_POOL, "task") +ADD_ENUM(ePools::EVENT_POOL, "event") +ADD_ENUM(ePools::TASK_ALLOCATOR_POOL, "task-allocator") +ADD_ENUM(ePools::PED_INTELLIGENCE_POOL, "ped-intelligence") +ADD_ENUM(ePools::PED_ATTRACTOR_POOL, "ped-attractor") +ADD_ENUM(ePools::ENTRY_INFO_NODE_POOL, "entry-info-node") +ADD_ENUM(ePools::NODE_ROUTE_POOL, "node-route") +ADD_ENUM(ePools::PATROL_ROUTE_POOL, "patrol-route") +ADD_ENUM(ePools::POINT_ROUTE_POOL, "point-route") +ADD_ENUM(ePools::POINTER_DOUBLE_LINK_POOL, "pointer-double-link-pool") +ADD_ENUM(ePools::POINTER_SINGLE_LINK_POOL, "pointer-single-link-pool") +ADD_ENUM(ePools::ENV_MAP_MATERIAL_POOL, "env-map-material") +ADD_ENUM(ePools::ENV_MAP_ATOMIC_POOL, "env-map-atomic") +ADD_ENUM(ePools::SPEC_MAP_MATERIAL_POOL, "spec-map-material") +IMPLEMENT_ENUM_END("gta-pool") + // // CResource from userdata // diff --git a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h index 078c798934..3c8af2144c 100644 --- a/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h +++ b/Client/mods/deathmatch/logic/lua/CLuaFunctionParseHelpers.h @@ -82,6 +82,7 @@ DECLARE_ENUM_CLASS(eSoundEffectParams::Reverb); DECLARE_ENUM_CLASS(eModelIdeFlag); DECLARE_ENUM_CLASS(_D3DFORMAT); DECLARE_ENUM_CLASS(eRenderStage); +DECLARE_ENUM(ePools); class CRemoteCall; diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp index 7c0c31509b..e48121b6ec 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaBuildingDefs.cpp @@ -65,42 +65,23 @@ void CLuaBuildingDefs::RemoveAllGameBuildings() { // We do not want to remove scripted buildings // But we need remove them from the buildings pool for a bit... - for (CClientBuilding* building : m_pBuildingManager->GetBuildings()) - { - building->Destroy(); - } + m_pBuildingManager->DestroyAllForABit(); // This function makes buildings backup without scripted buildings g_pGame->RemoveAllBuildings(); // ... And restore here - for (CClientBuilding* building : m_pBuildingManager->GetBuildings()) - { - building->Create(); - } + m_pBuildingManager->RestoreDestroyed(); } void CLuaBuildingDefs::RestoreGameBuildings() { // We want to restore the game buildings to the same positions as they were before the backup. // Remove scripted buildings for a bit - for (CClientBuilding* building : m_pBuildingManager->GetBuildings()) - { - building->Destroy(); - } + m_pBuildingManager->DestroyAllForABit(); g_pGame->RestoreGameBuildings(); // Restore what we can - for (CClientBuilding* building : m_pBuildingManager->GetBuildings()) - { - building->Create(); - - if (!building->IsValid()) - { - // User creates too much buildings - // We can't restore them all - delete building; - } - } + m_pBuildingManager->RestoreDestroyed(); } diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp index 2111c41195..d97e644810 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.cpp @@ -135,9 +135,12 @@ void CLuaEngineDefs::LoadFunctions() {"engineStreamingSetBufferSize", ArgumentParser}, {"engineStreamingGetBufferSize", ArgumentParser}, {"engineStreamingRestoreBufferSize", ArgumentParser}, - {"engineRequestTXD", ArgumentParser}, {"engineFreeTXD", ArgumentParser}, + {"engineGetPoolCapacity", ArgumentParser}, + {"engineGetPoolDefaultCapacity", ArgumentParser}, + {"engineGetPoolUsedCapacity", ArgumentParser}, + {"engineSetPoolCapacity", ArgumentParser}, // CLuaCFunctions::AddFunction ( "engineReplaceMatchingAtomics", EngineReplaceMatchingAtomics ); // CLuaCFunctions::AddFunction ( "engineReplaceWheelAtomics", EngineReplaceWheelAtomics ); @@ -2458,3 +2461,52 @@ bool CLuaEngineDefs::EngineFreeTXD(uint txdID) std::shared_ptr pModel = m_pManager->GetModelManager()->FindModelByID(MAX_MODEL_DFF_ID + txdID); return pModel && pModel->Deallocate(); } + +size_t CLuaEngineDefs::EngineGetPoolCapacity(ePools pool) +{ + return g_pGame->GetPools()->GetPoolCapacity(pool); +} + +size_t CLuaEngineDefs::EngineGetPoolDefaultCapacity(ePools pool) +{ + return g_pGame->GetPools()->GetPoolDefaultModdedCapacity(pool); +} + +size_t CLuaEngineDefs::EngineGetPoolUsedCapacity(ePools pool) +{ + return g_pGame->GetPools()->GetNumberOfUsedSpaces(pool); +} + +bool CLuaEngineDefs::EngineSetPoolCapacity(lua_State* luaVM, ePools pool, size_t newSize) +{ + size_t minSize = g_pGame->GetPools()->GetPoolDefaultModdedCapacity(pool); + if (newSize < minSize) + { + m_pScriptDebugging->LogWarning(luaVM, "Cannot set the pool capacity to less than the default capacity."); + return false; + } + + minSize = g_pGame->GetPools()->GetNumberOfUsedSpaces(pool); + if (newSize < minSize) + { + m_pScriptDebugging->LogWarning(luaVM, "Cannot set the pool capacity to less than the used capacity."); + return false; + } + + switch (pool) + { + case ePools::BUILDING_POOL: + { + m_pBuildingManager->DestroyAllForABit(); + + bool success = g_pGame->SetBuildingPoolSize(newSize); + + m_pBuildingManager->RestoreDestroyed(); + + return success; + } + default: + throw std::invalid_argument("Can not change this pool capacity"); + } + return true; +} diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h index 899d725915..5426c31015 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaEngineDefs.h @@ -80,6 +80,11 @@ class CLuaEngineDefs : public CLuaDefs static bool EngineSetModelVisibleTime(std::string strModelId, char cHourOn, char cHourOff); static std::variant> EngineGetModelVisibleTime(std::string strModelId); + static size_t EngineGetPoolCapacity(ePools pool); + static size_t EngineGetPoolDefaultCapacity(ePools pool); + static size_t EngineGetPoolUsedCapacity(ePools pool); + static bool EngineSetPoolCapacity(lua_State* luaVM, ePools pool, size_t newSize); + static uint EngineRequestTXD(lua_State* const luaVM, std::string strTxdName); static bool EngineFreeTXD(uint txdID); diff --git a/Client/sdk/game/CBuildingsPool.h b/Client/sdk/game/CBuildingsPool.h index 6a08ee5372..d490c162f7 100644 --- a/Client/sdk/game/CBuildingsPool.h +++ b/Client/sdk/game/CBuildingsPool.h @@ -25,6 +25,8 @@ class CBuildingsPool virtual CBuilding* AddBuilding(class CClientBuilding*, uint16_t modelId, CVector* vPos, CVector4D* vRot, uint8_t interior) = 0; virtual void RemoveBuilding(CBuilding* pObject) = 0; virtual bool HasFreeBuildingSlot() = 0; + virtual bool Resize(int size) = 0; + virtual int GetSize() const = 0; virtual void RemoveAllBuildings() = 0; virtual void RestoreAllBuildings() = 0; diff --git a/Client/sdk/game/CDummyPool.h b/Client/sdk/game/CDummyPool.h new file mode 100644 index 0000000000..46308c304f --- /dev/null +++ b/Client/sdk/game/CDummyPool.h @@ -0,0 +1,22 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto v1.0 + * LICENSE: See LICENSE in the top level directory + * FILE: sdk/game/CDummyPool.h + * PURPOSE: Dummy pool interface + * + * Multi Theft Auto is available from http://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +#include "Common.h" + +class CDummyPool +{ +public: + virtual void RemoveAllBuildingLods() = 0; + virtual void RestoreAllBuildingsLods() = 0; + virtual void UpdateBuildingLods(void* oldPool, void* newPool) = 0; +}; diff --git a/Client/sdk/game/CGame.h b/Client/sdk/game/CGame.h index a2290af6cf..59fe69af55 100644 --- a/Client/sdk/game/CGame.h +++ b/Client/sdk/game/CGame.h @@ -262,4 +262,6 @@ class __declspec(novtable) CGame virtual void RemoveAllBuildings() = 0; virtual void RestoreGameBuildings() = 0; + + virtual bool SetBuildingPoolSize(size_t size) = 0; }; diff --git a/Client/sdk/game/CPools.h b/Client/sdk/game/CPools.h index b6374b6f75..3319f71e32 100644 --- a/Client/sdk/game/CPools.h +++ b/Client/sdk/game/CPools.h @@ -13,6 +13,7 @@ #include "Common.h" #include "CBuildingsPool.h" +#include "CDummyPool.h" class CClientEntity; class CEntity; @@ -68,6 +69,7 @@ class CPools virtual void RemoveVehicle(CVehicle* pVehicle, bool bDelete = true) = 0; virtual SClientEntity* GetVehicle(DWORD* pGameInterface) = 0; + virtual SClientEntity* GetVehicle(size_t pos) = 0; virtual unsigned long GetVehicleCount() = 0; // Objects pool @@ -97,6 +99,7 @@ class CPools virtual int GetNumberOfUsedSpaces(ePools pool) = 0; virtual int GetPoolDefaultCapacity(ePools pool) = 0; + virtual int GetPoolDefaultModdedCapacity(ePools pool) = 0; virtual int GetPoolCapacity(ePools pool) = 0; virtual void SetPoolCapacity(ePools pool, int iValue) = 0; @@ -110,4 +113,5 @@ class CPools virtual ushort GetFreeTextureDictonarySlot() = 0; virtual CBuildingsPool& GetBuildingsPool() noexcept = 0; + virtual CDummyPool& GetDummyPool() noexcept = 0; }; diff --git a/Client/sdk/game/Common.h b/Client/sdk/game/Common.h index 503f4c9021..14108f5210 100644 --- a/Client/sdk/game/Common.h +++ b/Client/sdk/game/Common.h @@ -27,6 +27,7 @@ #define MAX_PEDS ( MAX_PEDS_MTA + 30 ) // 140 #define MAX_OBJECTS ( MAX_OBJECTS_MTA + 200 ) // 1200 #define MAX_BUILDINGS 13000 +#define MAX_DUMMIES 2500 #define MAX_ENTRY_INFO_NODES ( MAX_ENTRY_INFO_NODES_MTA + 600 ) // 72600 #define MAX_POINTER_SINGLE_LINKS ( MAX_POINTER_SINGLE_LINKS_MTA + 5000 ) // 90000 #define MAX_POINTER_DOUBLE_LINKS ( MAX_POINTER_DOUBLE_LINKS_MTA + 800 ) // 74800