diff --git a/Client/game_sa/CFxSA.h b/Client/game_sa/CFxSA.h index efb7d0403f..06653b201b 100644 --- a/Client/game_sa/CFxSA.h +++ b/Client/game_sa/CFxSA.h @@ -32,6 +32,21 @@ class FxSystem_c; #define FUNC_CFx_TriggerFootSplash 0x4a1150 #define FUNC_FXSystem_c_AddParticle 0x4AA440 +struct FxPrtMult_c +{ + struct + { + float red; + float green; + float blue; + float alpha; + } m_color; + + float m_fSize; + float unk; + float m_fLife; +}; + class CFxSAInterface { public: @@ -61,6 +76,8 @@ class CFxSA : public CFx public: CFxSA(CFxSAInterface* pInterface) { m_pInterface = pInterface; } + CFxSAInterface* GetInterface() noexcept { return m_pInterface; } + void AddBlood(CVector& vecPosition, CVector& vecDirection, int iCount, float fBrightness); void AddWood(CVector& vecPosition, CVector& vecDirection, int iCount, float fBrightness); void AddSparks(CVector& vecPosition, CVector& vecDirection, float fForce, int iCount, CVector vecAcrossLine, unsigned char ucBlurIf0, float fSpread, @@ -80,19 +97,4 @@ class CFxSA : public CFx private: CFxSAInterface* m_pInterface; - - struct FxPrtMult_c - { - struct - { - float red; - float green; - float blue; - float alpha; - } m_color; - - float m_fSize; - float unk; - float m_fLife; - }; }; diff --git a/Client/game_sa/CGameSA.cpp b/Client/game_sa/CGameSA.cpp index 7cac177f42..3d00a9f3a1 100644 --- a/Client/game_sa/CGameSA.cpp +++ b/Client/game_sa/CGameSA.cpp @@ -250,6 +250,8 @@ CGameSA::CGameSA() CFireSA::StaticSetHooks(); CPtrNodeSingleLinkPoolSA::StaticSetHooks(); CVehicleAudioSettingsManagerSA::StaticSetHooks(); + CAutomobileSA::StaticSetHooks(); + CProjectileInfoSA::StaticSetHooks(); } catch (const std::bad_alloc& e) { diff --git a/Client/game_sa/CMatrixSA.cpp b/Client/game_sa/CMatrixSA.cpp index dc7fc6db6b..ee0f6991fc 100644 --- a/Client/game_sa/CMatrixSA.cpp +++ b/Client/game_sa/CMatrixSA.cpp @@ -24,7 +24,10 @@ CMatrixSAInterface::CMatrixSAInterface(RwMatrix* matrix, bool temporary) // destructor detaches matrix if attached CMatrixSAInterface::~CMatrixSAInterface() { - ((void(__thiscall*)(CMatrixSAInterface*))0x59ACD0)(this); + if (m_bOwnsAttachedMatrix && m_pAttachMatrix) + ((void(__cdecl*)(RwMatrix*))0x7F2A20)(m_pAttachMatrix); + + //((void(__thiscall*)(CMatrixSAInterface*))0x59ACD0)(this); } void CMatrixSAInterface::ConvertToEulerAngles(float& x, float& y, float& z, std::int32_t flags) @@ -36,7 +39,67 @@ void CMatrixSAInterface::ConvertFromEulerAngles(float x, float y, float z, std:: ((void(__thiscall*)(CMatrixSAInterface*, float, float, float, std::int32_t))0x59AA40)(this, x, y, z, flags); } +void CMatrixSAInterface::UpdateRwMatrix(RwMatrix* rwMatrix) +{ + rwMatrix->right.x = m_right.fX; + rwMatrix->right.y = m_right.fY; + rwMatrix->right.z = m_right.fZ; + + rwMatrix->up.x = m_forward.fX; + rwMatrix->up.y = m_forward.fY; + rwMatrix->up.z = m_forward.fZ; + + rwMatrix->at.x = m_up.fX; + rwMatrix->at.y = m_up.fY; + rwMatrix->at.z = m_up.fZ; + + rwMatrix->pos.x = m_pos.fX; + rwMatrix->pos.y = m_pos.fY; + rwMatrix->pos.z = m_pos.fZ; + + ((void(__cdecl*)(RwMatrix*))0x7F18A0)(rwMatrix); + //RwMatrixUpdate(rwMatrix); +} + void CMatrixSAInterface::UpdateRW() { - ((void(__thiscall*)(CMatrixSAInterface*))0x59BBB0)(this); + if (!m_pAttachMatrix) + return; + + UpdateRwMatrix(m_pAttachMatrix); +} + +void CMatrixSAInterface::SetTranslate(const CVector& position) +{ + m_right.fX = 1.0f; + m_right.fY = 0; + m_right.fZ = 0; + + m_forward.fX = 0; + m_forward.fY = 1.0f; + m_forward.fZ = 0; + + m_up.fX = 0; + m_up.fY = 0; + m_up.fZ = 1.0f; + + m_pos = position; +} + +void CMatrixSAInterface::RotateZ(float angle) +{ + float cos = std::cos(angle); + float sin = std::sin(angle); + + m_right.fX = cos * m_right.fX - sin * m_right.fY; + m_right.fY = cos * m_right.fY + sin * m_right.fX; + + m_forward.fX = cos * m_forward.fX - sin * m_forward.fY; + m_forward.fY = cos * m_forward.fY + sin * m_forward.fX; + + m_up.fX = cos * m_up.fX - sin * m_up.fY; + m_up.fY = cos * m_up.fY + sin * m_up.fX; + + m_pos.fX = cos * m_pos.fX - sin * m_pos.fY; + m_pos.fY = cos * m_pos.fY + sin * m_pos.fX; } diff --git a/Client/game_sa/CMatrixSA.h b/Client/game_sa/CMatrixSA.h index 7b41c3ea75..402573a58a 100644 --- a/Client/game_sa/CMatrixSA.h +++ b/Client/game_sa/CMatrixSA.h @@ -36,8 +36,21 @@ class CMatrixSAInterface void ConvertToEulerAngles(float& x, float& y, float& z, std::int32_t flags); void ConvertFromEulerAngles(float x, float y, float z, std::int32_t flags); + + void UpdateRwMatrix(RwMatrix* rwMatrix); void UpdateRW(); + + void SetTranslate(const CVector& position); // 0x59AF40 (CMatrix::Translate) + + void RotateZ(float angle); + void SetTranslateOnly(CVector position) { m_pos = position; } + + CVector GetPosition() const noexcept { return m_pos; } + CVector GetRight() const noexcept { return m_right; } + CVector GetForward() const noexcept { return m_forward; } + CVector GetUp() const noexcept { return m_up; } + void SetMatrix(const CVector& right, const CVector& forward, const CVector& up, const CVector& pos) { m_right = right; @@ -45,4 +58,15 @@ class CMatrixSAInterface m_up = up; m_pos = pos; } + + CMatrixSAInterface* operator=(const void* m) + { + RwMatrix* attachedMatrix = m_pAttachMatrix; + std::memcpy(this, m, 64); + + if (attachedMatrix) + UpdateRwMatrix(attachedMatrix); + + return this; + } }; diff --git a/Client/game_sa/CPedSA.cpp b/Client/game_sa/CPedSA.cpp index ef718d32d1..47b219aeaa 100644 --- a/Client/game_sa/CPedSA.cpp +++ b/Client/game_sa/CPedSA.cpp @@ -79,15 +79,6 @@ void CPedSA::SetModelIndex(std::uint32_t modelIndex) GetPedInterface()->pedSound.m_bIsFemale = type == 5 || type == 22; } -bool CPedSA::AddProjectile(eWeaponType weaponType, CVector origin, float force, CVector* target, CEntity* targetEntity) -{ - CProjectileInfo* projectileInfo = pGame->GetProjectileInfo(); - if (!projectileInfo) - return false; - - return projectileInfo->AddProjectile(static_cast(this), weaponType, origin, force, const_cast(target), const_cast(targetEntity)); -} - void CPedSA::DetachPedFromEntity() { // void __thiscall CPed::DettachPedFromEntity(CPed *this) @@ -546,12 +537,12 @@ void CPedSA::Say(const ePedSpeechContext& speechId, float probability) void CPedSA::GetAttachedSatchels(std::vector& satchelsList) const { // Array of projectiles objects - auto** projectilesArray = reinterpret_cast(ARRAY_CProjectile); - CProjectileSAInterface* projectileInterface = nullptr; + auto** projectilesArray = reinterpret_cast(ARRAY_CProjectile); + CProjectileSA* projectileInterface = nullptr; // Array of projectiles infos - auto* projectilesInfoArray = reinterpret_cast(ARRAY_CProjectileInfo); - CProjectileInfoSAInterface* projectileInfoInterface = nullptr; + auto* projectilesInfoArray = reinterpret_cast(ARRAY_CProjectileInfo); + CProjectileInfoSA* projectileInfoInterface = nullptr; satchelsList.reserve(PROJECTILE_COUNT); @@ -561,18 +552,18 @@ void CPedSA::GetAttachedSatchels(std::vector& satchelsList) const projectileInterface = projectilesArray[i]; // is attached to our ped? - if (!projectileInterface || projectileInterface->m_pAttachedEntity != m_pInterface) + if (!projectileInterface || projectileInterface->m_object->m_pAttachedEntity != m_pInterface) continue; // index is always the same for both arrays projectileInfoInterface = &projectilesInfoArray[i]; // We are only interested in satchels - if (!projectileInfoInterface || projectileInfoInterface->dwProjectileType != eWeaponType::WEAPONTYPE_REMOTE_SATCHEL_CHARGE) + if (!projectileInfoInterface || projectileInfoInterface->GetType() != eWeaponType::WEAPONTYPE_REMOTE_SATCHEL_CHARGE) continue; // Push satchel into the array. There is no need to check the counter because for satchels it restarts until the player detonates the charges - satchelsList.emplace_back(projectileInterface, &projectileInterface->m_vecAttachedOffset, &projectileInterface->m_vecAttachedRotation); + satchelsList.emplace_back(projectileInterface, &projectileInterface->m_object->m_vecAttachedOffset, &projectileInterface->m_object->m_vecAttachedRotation); } } diff --git a/Client/game_sa/CPedSA.h b/Client/game_sa/CPedSA.h index a17ae306e6..37c547eab8 100644 --- a/Client/game_sa/CPedSA.h +++ b/Client/game_sa/CPedSA.h @@ -374,7 +374,6 @@ class CPedSA : public virtual CPed, public virtual CPhysicalSA float GetOxygenLevel() const override; void SetOxygenLevel(float oxygen) override; - bool AddProjectile(eWeaponType weaponType, CVector origin, float force, CVector* target, CEntity* targetEntity) override; CWeapon* GiveWeapon(eWeaponType weaponType, std::uint32_t ammo, eWeaponSkill skill) override; CWeapon* GetWeapon(eWeaponSlot weaponSlot) const noexcept override; CWeapon* GetWeapon(eWeaponType weaponType) const override; diff --git a/Client/game_sa/CPhysicalSA.h b/Client/game_sa/CPhysicalSA.h index c1de359937..ecce7968b0 100644 --- a/Client/game_sa/CPhysicalSA.h +++ b/Client/game_sa/CPhysicalSA.h @@ -68,6 +68,7 @@ class CPhysicalSAInterface : public CEntitySAInterface uint32 bAttachedToEntity : 1; uint32 b0x4000000 : 1; uint32 bTouchingWater : 1; + uint32 bEnableCollision : 1; uint32 bDestroyed : 1; uint32 b0x40000000 : 1; @@ -107,7 +108,8 @@ class CPhysicalSAInterface : public CEntitySAInterface CVector m_vecAttachedRotation; // 268 CVector m_vecUnk; // 280 uint32 m_pad4; // 292 - CPtrNodeDoubleLink* m_pControlCodeNodeLink; // 296 + //CPtrNodeDoubleLink* m_pControlCodeNodeLink; // 296 + CEntitySAInterface* m_entityIgnoredCollision; float m_fLighting; // 300 surface brightness float m_fLighting2; // 304 dynamic lighting (unused, always set to 0 in the GTA code) class CShadowDataSA* m_pShadowData; // 308 diff --git a/Client/game_sa/CPlaceableSA.h b/Client/game_sa/CPlaceableSA.h index d2b713da73..16dc1ec902 100644 --- a/Client/game_sa/CPlaceableSA.h +++ b/Client/game_sa/CPlaceableSA.h @@ -9,6 +9,7 @@ #include #include +#include "CMatrixSA.h" class CSimpleTransformSAInterface // 16 bytes { @@ -29,6 +30,12 @@ class CPlaceableSAInterface bool HasMatrix() const noexcept { return matrix != nullptr; } void RemoveMatrix() { ((void(__thiscall*)(void*))0x54F3B0)(this); } + // Since the m_matrix field has an incorrect type, we currently call the GetMatrix method directly. + // In the future, after a refactor, we can reimplement this method here without needing to call the external function. + CMatrixSAInterface* GetMatrix() { return ((CMatrixSAInterface*(__thiscall*)(CPlaceableSAInterface*))0x411990)(this); } + + void SetMatrix(CMatrixSAInterface* matrix) { ((void(__thiscall*)(CPlaceableSAInterface*, CMatrixSAInterface*))0x54F610)(this, matrix); } + void SetOrientation(float x, float y, float z) { ((void(__thiscall*)(CPlaceableSAInterface * pEntity, float, float, float))0x439A80)(this, x, y, z); } public: diff --git a/Client/game_sa/CPoolsSA.cpp b/Client/game_sa/CPoolsSA.cpp index b0ce14b296..c3913cdbab 100644 --- a/Client/game_sa/CPoolsSA.cpp +++ b/Client/game_sa/CPoolsSA.cpp @@ -1089,3 +1089,20 @@ void CPoolsSA::InvalidateLocalPlayerClientEntity() { m_pedPool.arrayOfClientEntities[0] = {m_pedPool.arrayOfClientEntities[0].pEntity, nullptr}; } + +std::uint16_t CPoolsSA::GetObjectHandle(CObjectSAInterface* object) const +{ + auto pool = *reinterpret_cast**>(CLASS_CPool_Object); + if (!pool || !pool->m_pObjects || !pool->m_byteMap) + return 0xFFFF; + + int index = static_cast((reinterpret_cast(object) - reinterpret_cast(pool->m_pObjects)) / (sizeof(CObjectSAInterface) + 32)); + if (index < 0 || index >= pool->m_nSize) + return 0xFFFF; + + const tPoolObjectFlags& flags = pool->m_byteMap[index]; + if (flags.bEmpty) + return 0xFFFF; + + return static_cast((index << 8) | flags.nId); +} diff --git a/Client/game_sa/CPoolsSA.h b/Client/game_sa/CPoolsSA.h index 524b7035c5..3cc6804407 100644 --- a/Client/game_sa/CPoolsSA.h +++ b/Client/game_sa/CPoolsSA.h @@ -100,6 +100,9 @@ class CPoolsSA : public CPools CTxdPool& GetTxdPool() noexcept { return m_TxdPool; }; CPtrNodeSingleLinkPool& GetPtrNodeSingleLinkPool() noexcept override { return m_PtrNodeSingleLinkPool; }; + [[nodiscard]] + std::uint16_t GetObjectHandle(CObjectSAInterface* object) const override; + private: // Pools SPoolData m_vehiclePool; diff --git a/Client/game_sa/CProjectileInfoSA.cpp b/Client/game_sa/CProjectileInfoSA.cpp index ec5897b514..f88b216a17 100644 --- a/Client/game_sa/CProjectileInfoSA.cpp +++ b/Client/game_sa/CProjectileInfoSA.cpp @@ -14,129 +14,930 @@ #include "CProjectileInfoSA.h" #include "CVehicleSA.h" #include "CWorldSA.h" +#include "enums/ExplosionType.h" +#include "CMatrixSA.h" +#include "CMatrixLinkSA.h" +#include "game/CCamera.h" +#include "CCamSA.h" +#include "CColModelSA.h" + +#include "CExplosionManagerSA.h" +#include "CFxSA.h" extern CGameSA* pGame; -CProjectile* CProjectileInfoSA::GetProjectile(void* projectilePointer) +ProjectileHandler CProjectileInfoSA::m_projectileCreationHandler; +GameProjectileDestructHandler* CProjectileInfoSA::m_projectileDestructionHandler; + +std::array CProjectileInfoSA::ms_projectileInfo{}; + +void CProjectileInfoSA::Initialise() { - // This must be destroyed later - return new CProjectileSA((CProjectileSAInterface*)projectilePointer); + for (auto& info : ms_projectileInfo) + { + info.m_weaponType = eWeaponType::WEAPONTYPE_GRENADE; + info.m_creator = nullptr; + info.m_target = nullptr; + info.m_particle = nullptr; + info.m_projectile = nullptr; + info.m_counter = 0; + } +} + +void CProjectileInfoSA::Shutdown() +{ + for (auto& info : ms_projectileInfo) + { + if (!info.m_particle) + continue; + + RemoveParticle(&info, true); + } +} + +CObjectSAInterface* CProjectileInfoSA::CreateProjectileObject(std::uint32_t modelIndex, CProjectileInfoSA* info) +{ + auto* mem = ((CObjectSAInterface * (__cdecl*)(int))0x5A1EE0)(sizeof(CObjectSAInterface)); + auto* object = ((CObjectSAInterface * (__thiscall*)(CObjectSAInterface*, std::uint32_t))0x5A4030)(mem, modelIndex); + + info->m_projectile = new CProjectileSA(object, info); + object->bStreamingDontDelete = true; + object->bDontStream = true; + object->bRemoveFromWorld = false; + + return object; +} + +void CProjectileInfoSA::DestroyProjectileObject(CObjectSAInterface* object) +{ + if (!object) + return; + + if (m_projectileDestructionHandler) + m_projectileDestructionHandler(object); + + for (auto& info : ms_projectileInfo) + { + if (!info.m_projectile) + continue; + + if (info.m_projectile->m_object == object) + { + delete info.m_projectile; + info.m_projectile = nullptr; + break; + } + } + + // CRadar::ClearBlipForEntity + ((void(__cdecl*)(int, int))0x587C60)(3, pGame->GetPools()->GetObjectHandle(object)); + + pGame->GetWorld()->Remove(object, eDebugCaller::CObject_Destructor); + + // CProjectile destructor + ((void(__thiscall*)(CObjectSAInterface*, bool))0x5A40F0)(object, true); } -CProjectileInfo* CProjectileInfoSA::GetProjectileInfo(void* projectileInfoInterface) +void CProjectileInfoSA::RemoveNotAdd(CEntitySAInterface* entity, eWeaponType weaponType, CVector pos) { - return projectileInfo[((DWORD)projectileInfoInterface - ARRAY_CProjectileInfo) / sizeof(CProjectileInfoSAInterface)]; + ExplosionType::Enum explosionType{}; + + switch (weaponType) + { + case WEAPONTYPE_GRENADE: + case WEAPONTYPE_REMOTE_SATCHEL_CHARGE: + explosionType = ExplosionType::Enum::EXPLOSION_GRENADE; + break; + case WEAPONTYPE_MOLOTOV: + explosionType = ExplosionType::Enum::EXPLOSION_MOLOTOV; + break; + case WEAPONTYPE_ROCKET: + case WEAPONTYPE_ROCKET_HS: + explosionType = ExplosionType::Enum::EXPLOSION_ROCKET; + break; + default: + explosionType = ExplosionType::Enum::EXPLOSION_SMALL; + break; + } + + // Call CExplosion::AddExplosion + ((void(__cdecl*)(CEntitySAInterface*, CEntitySAInterface*, ExplosionType::Enum, CVector, bool, float, bool))0x736A50)(nullptr, entity, explosionType, pos, true, -1.0f, false); } -void CProjectileInfoSA::RemoveProjectile(CProjectileInfo* pProjectileInfo, CProjectile* pProjectile, bool bBlow) +void CProjectileInfoSA::RemoveDetonatorProjectiles() { - CProjectileInfoSAInterface* projectileInfoInterface = ((CProjectileInfoSA*)pProjectileInfo)->internalInterface; + for (std::size_t i = 0; i < MAX_PROJECTILES_COUNT; i++) + { + CProjectileInfoSA& info = ms_projectileInfo[i]; + + if (!info.m_isActive || info.m_weaponType != eWeaponType::WEAPONTYPE_REMOTE_SATCHEL_CHARGE) + continue; + + CObjectSAInterface* object = info.m_projectile->m_object; + CVector pos = object->matrix ? object->matrix->vPos : object->m_transform.m_translate; + + object->bRemoveFromWorld = true; + + // Call CExplosion::AddExplosion + ((void(__cdecl*)(CEntitySAInterface*, CEntitySAInterface*, ExplosionType::Enum, CVector, bool, float, bool))0x736A50)(nullptr, info.m_creator, ExplosionType::Enum::EXPLOSION_GRENADE, pos, true, -1.0f, false); + + info.m_isActive = false; + + if (info.m_particle) + { + // Call FxManager_c::DestroyFxSystem + ((void(__thiscall*)(void*, void*))0x4A9810)((void*)0xA9AE80, info.m_particle); + info.m_particle = nullptr; + } + } +} + +void CProjectileInfoSA::RemoveIfThisIsAProjectile(CObjectSAInterface* object) +{ + for (auto& info : ms_projectileInfo) + { + if (!info.m_projectile || !info.m_projectile->m_object) + continue; + + RemoveParticle(&info, false); + DestroyProjectileObject(info.m_projectile->m_object); + + delete info.m_projectile; + info.m_projectile = nullptr; + } +} + +void CProjectileInfoSA::RemoveAllProjectiles() +{ + for (auto& info : ms_projectileInfo) + { + if (!info.m_isActive) + continue; + + if (!info.m_projectile || !info.m_projectile->m_object) + continue; + + RemoveParticle(&info, true); + DestroyProjectileObject(info.m_projectile->m_object); + + info.m_isActive = false; + + delete info.m_projectile; + info.m_projectile = nullptr; + } +} + +void CProjectileInfoSA::StaticRemoveProjectile(CProjectileInfoSA* info, CProjectileSA* projectile, bool blow) +{ + CObjectSAInterface* object = projectile->m_object; + if (!object) + return; + + if (!blow) + { + RemoveIfThisIsAProjectile(object); + return; + } + + CVector objectPosition = object->matrix ? object->matrix->vPos : object->m_transform.m_translate; + + switch (info->m_weaponType) + { + case eWeaponType::WEAPONTYPE_MOLOTOV: + { + // Call CExplosion::AddExplosion + ((void(__cdecl*)(CEntitySAInterface*, CEntitySAInterface*, int, CVector, bool, float, bool))0x736A50)(nullptr, info->m_creator, 1, objectPosition, true, -1.0f, false); + + // Call CAudioEngine::ReportObjectDestruction + ((void(__thiscall*)(CAudioEngineSAInterface*, CObjectSAInterface*))0x506ED0)((CAudioEngineSAInterface*)0xB6BC90, object); + break; + } + case eWeaponType::WEAPONTYPE_ROCKET: + { + if (info->m_creator->nType == ENTITY_TYPE_VEHICLE) + { + // Call CExplosion::AddExplosion + ((void(__cdecl*)(CEntitySAInterface*, CEntitySAInterface*, int, CVector, bool, float, bool))0x736A50)(nullptr, static_cast(info->m_creator)->pDriver, 2, objectPosition, true, -1.0f, false); + } + else + { + // Call CExplosion::AddExplosion + ((void(__cdecl*)(CEntitySAInterface*, CEntitySAInterface*, int, CVector, bool, float, bool))0x736A50)(nullptr, info->m_creator, 2, objectPosition, true, -1.0f, false); + } + break; + } + case eWeaponType::WEAPONTYPE_ROCKET_HS: + { + CPedSAInterface* localPlayer = pGame->GetPedContext()->GetPedInterface(); + // Call CExplosion::AddExplosion + ((void(__cdecl*)(CEntitySAInterface*, CEntitySAInterface*, int, CVector, bool, float, bool))0x736A50)(nullptr, info->m_creator, info->m_creator == localPlayer ? 2 : 3, objectPosition, true, -1.0f, false); + + break; + } + case eWeaponType::WEAPONTYPE_FREEFALL_BOMB: + case eWeaponType::WEAPONTYPE_GRENADE: + { + // Call CExplosion::AddExplosion + ((void(__cdecl*)(CEntitySAInterface*, CEntitySAInterface*, int, CVector, bool, float, bool))0x736A50)(nullptr, info->m_creator, 0, objectPosition, true, -1.0f, false); + break; + } + default: + break; + } + + RemoveParticle(info, false); + DestroyProjectileObject(object); - CProjectileSA* pProjectileSA = dynamic_cast(pProjectile); - if (!pProjectileSA) + delete projectile; + info->m_isActive = false; +} + +void CProjectileInfoSA::RemoveParticle(CProjectileInfoSA* info, bool instantly) +{ + if (!info->m_particle) return; - CProjectileSAInterface* projectileInterface = pProjectileSA->GetProjectileInterface(); + if (instantly) + // Call FxManager_c::DestroyFxSystem + ((void(__thiscall*)(void*, void*))0x4A9810)((void*)0xA9AE80, info->m_particle); + else + // Call FxSystem_c::Kill + ((void(__thiscall*)(void*))0x4AA3F0)(info->m_particle); - // Check that this infact is a CProjectile - // This is perhaps the fix for a crash where it jumps to 0x42480000 - // The proper cause should be figured out instead though as this is a rather unsafe fix. - if (projectileInterface->IsProjectableVTBL()) + info->m_particle = nullptr; +} + +bool CProjectileInfoSA::IsProjectileInRange(float x1, float x2, float y1, float y2, float z1, float z2, bool destroy) +{ + bool isInRange = false; + + for (std::size_t i = 0; i < MAX_PROJECTILES_COUNT; i++) { - // Has it not already been removed by GTA? - if (pProjectileInfo->IsActive()) + if (!ms_projectileInfo[i].m_isActive) + continue; + + auto projectileType = ms_projectileInfo[i].m_weaponType; + if (projectileType == eWeaponType::WEAPONTYPE_ROCKET || projectileType == eWeaponType::WEAPONTYPE_ROCKET_HS || projectileType == eWeaponType::WEAPONTYPE_MOLOTOV || projectileType == eWeaponType::WEAPONTYPE_TEARGAS || projectileType == eWeaponType::WEAPONTYPE_GRENADE) { - if (bBlow) + CVector pos = ms_projectileInfo[i].m_projectile->m_object->matrix ? ms_projectileInfo[i].m_projectile->m_object->matrix->vPos : ms_projectileInfo[i].m_projectile->m_object->m_transform.m_translate; + + if ((pos.fX >= x1 && pos.fX <= x2) && (pos.fY >= y1 && pos.fY <= y2) && (pos.fZ >= z1 && pos.fZ <= z2)) { - DWORD dwFunc = FUNC_RemoveProjectile; - _asm + isInRange = true; + + if (destroy) { - push projectileInterface - push projectileInfoInterface - call dwFunc - add esp, 8 + RemoveParticle(&ms_projectileInfo[i], false); + DestroyProjectileObject(ms_projectileInfo[i].m_projectile->m_object); } + + break; } - else + } + } + + return isInRange; +} + +void CProjectileInfoSA::Update() +{ + for (std::size_t i = 0; i < MAX_PROJECTILES_COUNT; i++) + { + if (!ms_projectileInfo[i].m_isActive) + continue; + + CProjectileInfoSA* projectileInfo = &ms_projectileInfo[i]; + CProjectileSA* projectileInstance = projectileInfo->m_projectile; + if (!projectileInstance) + continue; + + CObjectSAInterface* projectile = projectileInstance->m_object; + if (!projectile) + continue; + + if (projectile->bSubmergedInWater && projectileInfo->m_particle) + RemoveParticle(projectileInfo, false); + + // Call CPed::IsPointerValid + if (projectileInfo->m_creator && projectileInfo->m_creator->nType == ENTITY_TYPE_PED && !((bool(__thiscall*)(CPedSAInterface*))0x5E4220)((CPedSAInterface*)projectileInfo->m_creator)) + projectileInfo->m_creator = nullptr; + + if (projectileInfo->m_weaponType == eWeaponType::WEAPONTYPE_REMOTE_SATCHEL_CHARGE || projectileInfo->m_weaponType == eWeaponType::WEAPONTYPE_GRENADE || projectileInfo->m_weaponType == eWeaponType::WEAPONTYPE_TEARGAS) + { + if (projectile->m_fElasticity > 0.1f && (std::fabs(projectile->m_vecLinearVelocity.fX) < 0.05f && std::fabs(projectile->m_vecLinearVelocity.fY) < 0.05f && std::fabs(projectile->m_vecLinearVelocity.fZ) < 0.05f)) + projectile->m_fElasticity = 0.03f; + + if (projectileInfo->m_weaponType == eWeaponType::WEAPONTYPE_TEARGAS && pGame->GetSystemTime() > (projectileInfo->m_counter - 17500) && GetRandomNumberInRange(0, 100) < 10) { - DWORD dwFunc = FUNC_RemoveIfThisIsAProjectile; - _asm + // Call CWorld::SetPedsChoking + const CVector& pos = projectile->matrix ? projectile->matrix->vPos : projectile->m_transform.m_translate; + ((void(__cdecl*)(CVector, float, CPedSAInterface*))0x565800)(pos, 6.0f, (CPedSAInterface*)projectileInfo->m_creator); + } + } + + if (projectileInfo->m_weaponType == eWeaponType::WEAPONTYPE_ROCKET || projectileInfo->m_weaponType == eWeaponType::WEAPONTYPE_ROCKET_HS) + { + FxPrtMult_c fxPrt{{0.3f, 0.3f, 0.3f, 0.3f}, 0.5f, 1.0f, 0.08f}; + const CVector& pos = projectile->matrix ? projectile->matrix->vPos : projectile->m_transform.m_translate; + + CVector velocity = projectile->m_vecLinearVelocity * pGame->GetTimeStep(); + float len = velocity.Length(); + int particleCount = 1; + if (len >= 1.0f) + particleCount = static_cast(len); + + for (std::size_t i = 0; i < particleCount; i++) + { + fxPrt.m_color.red = rand() * 0.000030518509f * 0.25f + 0.25f; + fxPrt.m_color.green = fxPrt.m_color.red; + fxPrt.m_color.blue = fxPrt.m_color.red; + + fxPrt.m_fLife = rand() * 0.000030518509f * 0.04f + 0.08f; + + float t = 1.0f - (static_cast(i) / static_cast(particleCount)); + + CVector prtPos = pos - velocity * t; + + int rand1 = rand(); + int rand2 = rand(); + int rand3 = rand(); + CVector prtVel = CVector(rand1 * 0.000030518509f + rand1 * 0.000030518509f - 1.0f, rand2 * 0.000030518509f + rand2 * 0.000030518509f - 1.0f, rand3 * 0.000030518509f + rand3 * 0.000030518509f - 1.0f); + prtVel.Normalize(); + + CVector objectVelocity = projectile->m_vecLinearVelocity; + objectVelocity.Normalize(); + + objectVelocity.CrossProduct(&prtVel); + objectVelocity *= 1.5f; + + // Call FxSystem_c::AddParticle + ((int(__thiscall*)(FxSystem_c*, const CVector*, const CVector*, float, FxPrtMult_c*, float, float, float, int))FUNC_FXSystem_c_AddParticle)(pGame->GetFx()->GetInterface()->m_fxSysSmokeHuge, &prtPos, &objectVelocity, 0, &fxPrt, -1.0f, 1.2f, 0.6f, 0); + } + } + + if (projectileInfo->m_counter > 0 && pGame->GetSystemTime() > projectileInfo->m_counter) + { + if (projectileInfo->m_weaponType == eWeaponType::WEAPONTYPE_REMOTE_SATCHEL_CHARGE) + { + if (projectileInfo->m_creator->nType == ENTITY_TYPE_PED && static_cast(projectileInfo->m_creator)->bPedType < 2) { - push projectileInterface - call dwFunc - add esp, 4 + CPedSAInterface* ped = static_cast(projectileInfo->m_creator); + if (ped->Weapons[12].m_eWeaponType != eWeaponType::WEAPONTYPE_DETONATOR || ped->Weapons[12].m_ammoTotal == 0) + projectileInfo->m_counter = 0; } + + UpdateLastPos(projectileInfo); + continue; + } + + UpdateLastPos(projectileInfo); + StaticRemoveProjectile(projectileInfo, projectileInfo->m_projectile); + continue; + } + + if (projectileInfo->m_weaponType == eWeaponType::WEAPONTYPE_ROCKET) + { + float time = pGame->GetTimeStep() * 0.008f; + projectile->m_vecLinearVelocity += (projectile->matrix ? projectile->matrix->vFront : CVector(0, 0 ,0)) * time; + + float length = projectile->m_vecLinearVelocity.Length(); + if (length > 9.9) + { + projectile->m_vecLinearVelocity.Normalize(); + projectile->m_vecLinearVelocity *= 9.9f; + } + + if (CheckIsLineOfSightClear(projectileInfo)) + continue; + + UpdateLastPos(projectileInfo); + StaticRemoveProjectile(projectileInfo, projectileInfo->m_projectile); + continue; + } + + switch (projectileInfo->m_weaponType) + { + case eWeaponType::WEAPONTYPE_FLARE: + { + CVector pos = projectile->matrix ? projectile->matrix->vPos : projectile->m_transform.m_translate; + + *(void**)0xB7CD68 = projectileInfo->m_creator; + projectile->bUsesCollision = false; + + // Call CWorld::GetIsLineOfSightClear + bool clear = ((bool(__cdecl*)(CVector*, CVector*, bool, bool, bool, bool, bool, bool, bool))0x56A490)(&projectileInfo->m_lastPos, &pos, true, true, true, true, false, false, false); + + projectile->bUsesCollision = true; + *(void**)0xB7CD68 = nullptr; + + if (!clear) + { + projectile->m_vecLinearVelocity = CVector(0, 0, 0); + + if (projectile->matrix) + projectile->matrix->vPos = projectileInfo->m_lastPos; + else + projectile->m_transform.m_translate = projectileInfo->m_lastPos; + } + + projectileInfo->m_lastPos = pos; + continue; + } + case eWeaponType::WEAPONTYPE_FREEFALL_BOMB: + case eWeaponType::WEAPONTYPE_MOLOTOV: + { + CVector pos = projectile->matrix ? projectile->matrix->vPos : projectile->m_transform.m_translate; + + *(void**)0xB7CD68 = projectileInfo->m_creator; + projectile->bUsesCollision = false; + + // Call CWorld::GetIsLineOfSightClear + bool clear = ((bool(__cdecl*)(CVector*, CVector*, bool, bool, bool, bool, bool, bool, bool))0x56A490)(&projectileInfo->m_lastPos, &pos, true, true, true, true, false, false, false); + + CVector diff = projectileInfo->m_lastPos - pos; + if ((!projectileInfo->m_creator || diff.LengthSquared() >= 2.0f) && (projectile->bOnSolidSurface || !clear)) + StaticRemoveProjectile(projectileInfo, projectileInfo->m_projectile); + + if (projectile) + projectile->bUsesCollision = true; + + *(void**)0xB7CD68 = nullptr; + break; + } + case eWeaponType::WEAPONTYPE_ROCKET_HS: + { + if (projectileInfo->m_target) + { + CPedSAInterface* localPlayer = pGame->GetPedContext()->GetPedInterface(); + if (projectileInfo->m_target == localPlayer->pVehicle) + // CAudioEngine::ReportFrontendAudioEvent + ((void(__thiscall*)(CAudioEngineSAInterface*, int, float, float))0x506EA0)((CAudioEngineSAInterface*)0xB6BC90, 101, 0, 1.0f); + + const CVector& pos = projectile->matrix ? projectile->matrix->vPos : projectile->m_transform.m_translate; + const CVector& forward = projectile->matrix ? projectile->matrix->vFront : CVector(0,0,0); + + CVector startPoint = pos + forward; + // CWeapon::EvaluateTargetForHeatSeekingMissile + double evaluate1 = ((double(__cdecl*)(CEntitySAInterface*, CVector*, const CVector*, float, bool, CEntitySAInterface*))0x73E560)(projectileInfo->m_target, &startPoint, &pos, 1.2f, true, nullptr); + double evaluate2 = 0.0; + + CPhysicalSAInterface* targetProjectile = nullptr; + + for (std::size_t j = 0; j < MAX_PROJECTILES_COUNT; j++) + { + if (ms_projectileInfo[j].m_isActive && ms_projectileInfo[j].m_weaponType == eWeaponType::WEAPONTYPE_FLARE && ms_projectileInfo[j].m_projectile && ms_projectileInfo[j].m_projectile->m_object) + { + // CWeapon::EvaluateTargetForHeatSeekingMissile + double ev = ((double(__cdecl*)(CEntitySAInterface*, CVector*, const CVector*, float, bool, CEntitySAInterface*))0x73E560)(ms_projectileInfo[j].m_projectile->m_object, &startPoint, &pos, 1.2f, true, nullptr); + if (ev > evaluate2) + { + evaluate2 = ev; + targetProjectile = ms_projectileInfo[j].m_projectile->m_object; + } + } + } + + if (!targetProjectile || evaluate2 <= evaluate1) + targetProjectile = (CPhysicalSAInterface*)projectileInfo->m_target; + + bool isPlaneTargetFromLocalPlayer = false; + + if (targetProjectile->nType == ENTITY_TYPE_VEHICLE) + { + if ((projectileInfo->m_creator == localPlayer || projectileInfo->m_creator == localPlayer->pVehicle) && static_cast(targetProjectile)->m_vehicleSubClass == (uint32_t)VehicleClass::PLANE) + isPlaneTargetFromLocalPlayer = true; + } + + CVector startPos = projectile->m_vecLinearVelocity * 100.0f + pos; + + if (isPlaneTargetFromLocalPlayer) + startPos = pos; + + CVector targetProjectilePos = targetProjectile->matrix ? targetProjectile->matrix->vPos : targetProjectile->m_transform.m_translate; + + CVector diff = pos - targetProjectilePos; + float vecLen = diff.Length(); + float maxDist = isPlaneTargetFromLocalPlayer ? std::min(vecLen, 1.5f) : std::min(vecLen, 50.0f); + + CVector targetVelocityCompensation = targetProjectile->m_vecLinearVelocity * maxDist; + CVector projectileDir = projectile->m_vecLinearVelocity; + projectileDir.Normalize(); + + CVector aimAdjustment = CVector((targetVelocityCompensation .fX + targetProjectilePos.fX) - startPos.fX, (targetVelocityCompensation .fY + targetProjectilePos.fY) - startPos.fY, (targetVelocityCompensation .fZ + targetProjectilePos.fZ) - startPos.fZ); + + float dotToAimDir = aimAdjustment.fX * projectileDir.fX + aimAdjustment.fY * projectileDir.fY + aimAdjustment.fZ * projectileDir.fZ; + if (dotToAimDir < 0.0f) + aimAdjustment -= projectileDir * dotToAimDir; + + aimAdjustment.Normalize(); + + float steeringStrength = 0.0f; + float velocityScale = 1.0f; + + if (projectileInfo->m_creator == localPlayer || projectileInfo->m_creator == localPlayer->pVehicle) + steeringStrength = 0.011f; + else + steeringStrength = 0.009f; + + if (targetProjectile->m_vecLinearVelocity.Length() > 0.8f) + steeringStrength *= 1.2f; + + if (isPlaneTargetFromLocalPlayer) + { + steeringStrength = 0.15f; + velocityScale = pGame->GetTimeStep() * 0.95f; + } + + projectile->m_vecLinearVelocity *= velocityScale; + projectile->m_vecLinearVelocity += aimAdjustment * (pGame->GetTimeStep() * steeringStrength); + + if (projectile->m_vecLinearVelocity.Length() > 9.9f) + { + projectile->m_vecLinearVelocity.Normalize(); + projectile->m_vecLinearVelocity *= 9.9f; + } + + projectile->matrix->vFront = projectileDir; + + if (CheckIsLineOfSightClear(projectileInfo)) + continue; + + projectileInfo->m_lastPos = pos; + StaticRemoveProjectile(projectileInfo, projectileInfo->m_projectile); + continue; + } + + break; + } + default: + { + if (projectileInfo->m_weaponType != eWeaponType::WEAPONTYPE_REMOTE_SATCHEL_CHARGE || projectile->m_fDamageImpulseMagnitude <= 0.0f || !projectile->m_pCollidedEntity || projectile->m_pAttachedEntity) + { + UpdateLastPos(projectileInfo); + continue; + } + + // CPhysical::AttachEntityToEntity + ((void(__thiscall*)(CPhysicalSAInterface*, CPhysicalSAInterface*, float, float))0x54D690)(projectile, (CPhysicalSAInterface*)projectile->m_pCollidedEntity, 0, 0); + projectile->bUsesCollision = false; + break; } } } } -CProjectileInfo* CProjectileInfoSA::GetProjectileInfo(DWORD dwIndex) +int CProjectileInfoSA::FindFreeIndex() { - return projectileInfo[dwIndex]; + int freeIndex = -1; + + for (std::size_t i = 0; i < MAX_PROJECTILES_COUNT; i++) + { + if (ms_projectileInfo[i].m_isActive) + continue; + + freeIndex = i; + break; + } + + return freeIndex; } -/** - * Creates a projectile based on an entity's position at a specified offset from the entity's position (VC) - * @param creator The entity that is emmitting the projectile - * @param eWeapon The type of projectile - * @param vecOffset How far away from the creator entity the projectile is created - * @param fForce How fast the projectile is moving when it is created - * \note The projectile is created at the same angle as the creator entity. If the creator entity is the player - * and the camera is in standard 3rd person mode, then the camera's rotation is used as the basis for the angle. - * SA: public: static bool __cdecl CProjectileInfo::AddProjectile(class CEntity *,enum eWeaponType,class CVector,float,class CVector *,class CEntity *) - */ +void CProjectileInfoSA::InitCollision(CObjectSAInterface* projectile) +{ + CModelInfo* modelInfo = pGame->GetModelInfo(projectile->m_nModelIndex); + if (modelInfo) + { + CColModelSAInterface* col = modelInfo->GetInterface()->pColModel; + if (CColDataSA* colData = col->m_data) + { + if (!colData->m_spheres && colData->m_numSpheres == 0) + { + colData->m_numSpheres = 1; + colData->m_spheres = ((CColSphereSA * (__cdecl*)(int))0x72F420)(20); // CMemoryMgr::Malloc + + // CColSphere::Set + ((void(__thiscall*)(CColSphereSA*, float, CVector&, unsigned char, char, unsigned char))(0x40FD10))(colData->m_spheres, col->m_sphere.m_radius * 0.75f, col->m_sphere.m_center, 56, 0, 255); + } + } + else + { + // CColModel::AllocateData + ((void(__thiscall*)(CColModelSAInterface*, int, int, int, int, int, int))(0x40F870))(col, 1, 0, 0, 0, 0, 0); -bool CProjectileInfoSA::AddProjectile(CEntity* creator, eWeaponType eWeapon, CVector vecOrigin, float fForce, CVector* target, CEntity* targetEntity) + if (col->m_data) + // CColSphere::Set + ((void(__thiscall*)(CColSphereSA*, float, CVector&, unsigned char, char, unsigned char))(0x40FD10))(col->m_data->m_spheres, col->m_sphere.m_radius * 0.75f, col->m_sphere.m_center, 56, 0, 255); + } + } +} + +void CProjectileInfoSA::UpdateLastPos(CProjectileInfoSA* info) { - DWORD dwFunction = FUNC_AddProjectile; - DWORD dwReturn = 0; - CEntitySAInterface* creatorVC = nullptr; - if (creator != nullptr) + if (!info) + return; + + CObjectSAInterface* object = info->m_projectile ? info->m_projectile->m_object : nullptr; + if (!object) + return; + + info->m_lastPos = object->matrix ? object->matrix->vPos : object->m_transform.m_translate; +} + +bool CProjectileInfoSA::CheckIsLineOfSightClear(CProjectileInfoSA* info) +{ + if (!info) + return false; + + CObjectSAInterface* object = info->m_projectile ? info->m_projectile->m_object : nullptr; + if (!object) + return false; + + if (!object->bOnSolidSurface) { - CEntitySA* pCreatorSA = dynamic_cast(creator); - if (pCreatorSA) + CVector pos = object->matrix ? object->matrix->vPos : object->m_transform.m_translate; + + *(void**)0xB7CD68 = info->m_creator; + object->bUsesCollision = false; + + // CWorld::GetIsLineOfSightClear + bool clear = ((bool(__cdecl*)(CVector*, CVector*, bool, bool, bool, bool, bool, bool, bool))0x56A490)(&info->m_lastPos, &pos, true, true, true, true, false, false, false); + + object->bUsesCollision = true; + *(void**)0xB7CD68 = nullptr; + + object->m_entityIgnoredCollision = info->m_creator; + if (clear) + { + UpdateLastPos(info); + return true; + } + } + + if (object->m_ucCollisionState > 0) + { + if (object->pLastContactedEntity[0] && (object->pLastContactedEntity[0]->GetInterface() == info->m_creator || object->pLastContactedEntity[0]->GetModelIndex() == 345)) { - creatorVC = pCreatorSA->GetInterface(); - pGame->GetWorld()->IgnoreEntity(creator); + UpdateLastPos(info); + return true; } } - CEntitySAInterface* targetVC = nullptr; + return false; +} + +CProjectileInfo* CProjectileInfoSA::StaticAddProjectile(CEntitySAInterface* creator, eWeaponType projectileType, CVector pos, float force, CVector* target, CEntitySAInterface* targetEntity) +{ + int freeIndex = FindFreeIndex(); + if (freeIndex == -1) + return nullptr; + + CProjectileInfoSA* projectileInfo = &ms_projectileInfo[freeIndex]; + projectileInfo->m_weaponType = projectileType; + projectileInfo->m_creator = creator; + projectileInfo->m_target = targetEntity; + + std::uint32_t timeToDestroy = 2000; + auto* matrix = creator->matrix; + float heading = matrix ? std::atan2(-matrix->vFront.fX, matrix->vFront.fY) : creator->m_transform.m_heading; + float elasticity = 1.0f; + + float offset = force == 0.0f ? 0.0f : force * 0.22f + 0.15f; + + CMatrixSAInterface projectileMatrix; + projectileMatrix.SetTranslate(CVector(0, 0, 0)); + projectileMatrix.RotateZ(heading); + + CVector matrixPos = projectileMatrix.GetPosition(); + projectileMatrix.SetTranslateOnly(matrixPos + pos); - if (targetEntity != nullptr) + auto ComputeOffsetVector = [](float heading, float offset, float force) -> CVector { + return CVector( + std::sin(heading) * offset * -1.0f, + std::cos(heading) * offset, + (force + 1.0f) * 0.4f * offset + ); + }; + + CVector projectileVelocity = ComputeOffsetVector(heading, offset, force); + + bool useStaticObjectInfo = false; + bool applyGravity = true; + + std::uint32_t model; + + if (projectileType == eWeaponType::WEAPONTYPE_FLARE) { - CEntitySA* pTargetEntitySA = dynamic_cast(targetEntity); - if (pTargetEntitySA) - targetVC = pTargetEntitySA->GetInterface(); + pGame->GetStreaming()->RequestModel(354, 0); // 354 = MI_FLARE + model = 354; } + else // GetWeaponInfo returns null for flare + model = pGame->GetWeaponInfo(projectileType, WEAPONSKILL_STD)->GetModel(); + + CObjectSAInterface* projectile = CreateProjectileObject(model, projectileInfo); + if (!projectile) + return nullptr; - _asm + switch (projectileType) { - push eax + case eWeaponType::WEAPONTYPE_GRENADE: + case eWeaponType::WEAPONTYPE_REMOTE_SATCHEL_CHARGE: + { + elasticity = 0.5f; + + if (projectileType == eWeaponType::WEAPONTYPE_REMOTE_SATCHEL_CHARGE) + { + offset *= 0.5f; + elasticity = 0.03f; + } + + if (creator->nType == ENTITY_TYPE_VEHICLE) // Call CGeneral::LimitRadianAngle + heading = ((float(__cdecl*)(float))0x53CB50)(heading + PI); + + projectileVelocity = ComputeOffsetVector(heading, offset, force); + + if (creator->m_nModelIndex == 405) // ??? + projectileVelocity += static_cast(creator)->m_vecLinearVelocity; + + InitCollision(projectile); + useStaticObjectInfo = true; + break; + } + case eWeaponType::WEAPONTYPE_TEARGAS: + { + useStaticObjectInfo = true; + elasticity = 0.5f; + timeToDestroy = 20000; + + InitCollision(projectile); + break; + } + case eWeaponType::WEAPONTYPE_MOLOTOV: + { + if (offset < 0.2f) + offset = 0.2f; + + InitCollision(projectile); + + projectileVelocity = ComputeOffsetVector(heading, offset, force); + projectileVelocity.fZ = (force * 0.2f + 0.4f) * offset; + break; + } + case eWeaponType::WEAPONTYPE_ROCKET: + case eWeaponType::WEAPONTYPE_ROCKET_HS: + { + timeToDestroy = projectileType == eWeaponType::WEAPONTYPE_ROCKET ? 3000 : 100000; + + CVector in = CVector(0, projectileType == eWeaponType::WEAPONTYPE_ROCKET ? 0.4f : 0.2f, 0); + + if (creator->nType == ENTITY_TYPE_VEHICLE) + { + projectileMatrix = creator->GetMatrix(); + projectileMatrix.SetTranslateOnly(pos); + + in.fY += static_cast(creator)->m_vecLinearVelocity.Length(); + } + else if (creator->nType == ENTITY_TYPE_PED && static_cast(creator)->bPedType < 2) + { + CCamera* camera = pGame->GetCamera(); + CCam* cam = camera->GetCam(camera->GetActiveCam()); + + CVector* camUp = cam->GetUp(); + CVector* camFront = cam->GetFront(); + + CVector right = camUp->Clone(); + right.CrossProduct(camFront); + + projectileMatrix.SetMatrix(right, *camFront, *camUp, pos); + } + else if (target) + { + CMatrixSAInterface* matrix = creator->GetMatrix(); + + CVector right = matrix->GetRight(); + CVector up = matrix->GetRight(); + up.CrossProduct(target); + + projectileMatrix.SetMatrix(right, *target, up, pos); + } + else + projectileMatrix = creator->GetMatrix(); + + projectileVelocity = CVector::Multiply3x3(projectileMatrix.GetRight(), projectileMatrix.GetForward(), projectileMatrix.GetUp(), in); + applyGravity = false; + break; + } + case eWeaponType::WEAPONTYPE_FREEFALL_BOMB: + case eWeaponType::WEAPONTYPE_FLARE: + { + timeToDestroy = projectileType == eWeaponType::WEAPONTYPE_FREEFALL_BOMB ? 2000000 : 10000; + + if (projectileType == eWeaponType::WEAPONTYPE_FLARE) + projectile->m_fAirResistance = 0.9f; + + projectileMatrix = creator->GetMatrix(); + projectileMatrix.SetTranslateOnly(pos); + + if (creator->nType > ENTITY_TYPE_BUILDING && creator->nType < ENTITY_TYPE_DUMMY) + projectileVelocity = static_cast(creator)->m_vecLinearVelocity; + else + projectileVelocity = CVector(0, 0, 0); + + break; + } + default: + break; + } + + projectileInfo->m_counter = pGame->GetSystemTime() + timeToDestroy; + projectileInfo->m_lastPos = projectileMatrix.GetPosition(); + + projectile->SetMatrix(&projectileMatrix); + projectile->m_vecLinearVelocity = projectileVelocity; + projectile->m_fElasticity = elasticity; + projectile->m_entityIgnoredCollision = creator; + projectile->bEnableCollision = true; + projectile->bApplyGravity = applyGravity; + + // CObjectData::Initialise (0x5B5420) + if (useStaticObjectInfo) + projectile->pObjectInfo = (CObjectInfo*)0xBB4BD0; + + pGame->GetWorld()->Add(projectile, eDebugCaller::CObject_Constructor); + + // CEntity::RegisterReference + ((void(__thiscall*)(CEntitySAInterface*, CEntitySAInterface**))0x571B70)(creator, &projectileInfo->m_creator); + ((void(__thiscall*)(CEntitySAInterface*, CObjectSAInterface**))0x571B70)(projectile, &projectileInfo->m_projectile->m_object); - push targetVC - push target - push fForce - lea eax, vecOrigin - push [eax+8] - push [eax+4] - push [eax] - push eWeapon - push creatorVC - call dwFunction - add esp, 32 - mov dwReturn, eax + if (targetEntity) // CEntity::RegisterReference + ((void(__thiscall*)(CEntitySAInterface*, CEntitySAInterface**))0x571B70)(targetEntity, &projectileInfo->m_target); - pop eax + if (creator && (creator->nType == ENTITY_TYPE_VEHICLE || creator->nType == ENTITY_TYPE_PED || creator->nType == ENTITY_TYPE_OBJECT) && !static_cast(creator)->m_entityIgnoredCollision) + static_cast(creator)->m_entityIgnoredCollision = creator; + + if (projectileType == eWeaponType::WEAPONTYPE_TEARGAS) + { + CVector point = CVector(0, 0, 0); + // CEntity::getModellingMatrix + void* modellingMatrix = ((void*(__thiscall*)(CEntitySAInterface*))0x46A2D0)(projectile); // RwMatrixTag* + if (modellingMatrix) + { + // FxManager_c::CreateFxSystem + void* fx = ((void*(__thiscall*)(void*, const char*, CVector*, void*, bool))0x4A9BE0)((void*)0xA9AE80, "teargasAD", &point, modellingMatrix, false); + + if (fx) + { + projectileInfo->m_particle = fx; + ((void(__thiscall*)(void*))0x4AA2F0)(fx); // FxSystem_c::Play + } + } + } + + if (projectileType == eWeaponType::WEAPONTYPE_ROCKET_HS) + { + // CRadar::SetEntityBlip + int blipIndex = ((int(__cdecl*)(int, int, int, int))0x5839A0)(3, pGame->GetPools()->GetObjectHandle(projectile), 0xFF0000FF, 2); + ((void(__cdecl*)(int, std::int16_t))0x583CC0)(blipIndex, 1); // CRadar::ChangeBlipScale + + CPedSAInterface* localPlayer = pGame->GetPedContext()->GetPedInterface(); + // CRadar::ChangeBlipColour + if (creator == localPlayer || static_cast(creator)->pVehicle == localPlayer->pVehicle) + ((void(__cdecl*)(int, int))0x583AB0)(blipIndex, -1); + else + ((void(__cdecl*)(int, int))0x583AB0)(blipIndex, 0xFF0000FF); } - pGame->GetWorld()->IgnoreEntity(nullptr); - return dwReturn != 0; + + // CAudioEngine::ReportWeaponEvent + ((void(__thiscall*)(CAudioEngineSAInterface*, int, eWeaponType, CEntitySAInterface*))0x506F40)((CAudioEngineSAInterface*)0xB6BC90, 0x94, projectileType, projectile); + + projectileInfo->m_isActive = true; + + if (m_projectileCreationHandler) + { + CPools* pools = pGame->GetPools(); + + CEntity* creatorEntity = pools->GetEntity((DWORD*)creator); + CEntity* targetGameEntity = targetEntity ? pools->GetEntity((DWORD*)targetEntity) : nullptr; + + if (!m_projectileCreationHandler(creatorEntity, projectileInfo->m_projectile, projectileInfo, projectileType, &pos, force, target, targetGameEntity)) + return nullptr; + } + + return projectileInfo; +} + +CProjectileInfo* CProjectileInfoSA::AddProjectile(CEntity* creator, eWeaponType projectileType, CVector pos, float force, CVector* target, CEntity* targetEntity) const +{ + return CProjectileInfoSA::StaticAddProjectile(creator->GetInterface(), projectileType, pos, force, target, targetEntity ? targetEntity->GetInterface() : nullptr); +} + +void CProjectileInfoSA::RemoveProjectile(CProjectileInfo* info, CProjectile* object, bool blow) const +{ + CProjectileInfoSA::StaticRemoveProjectile(dynamic_cast(info), dynamic_cast(object), blow); } CEntity* CProjectileInfoSA::GetTarget() { - CEntitySAInterface* pTargetInterface = internalInterface->pEntProjectileTarget; + CEntitySAInterface* pTargetInterface = m_target; CEntity* pTarget = nullptr; if (pTargetInterface) { @@ -163,22 +964,22 @@ void CProjectileInfoSA::SetTarget(CEntity* pEntity) { CEntitySA* pEntitySA = dynamic_cast(pEntity); if (pEntitySA) - internalInterface->pEntProjectileTarget = pEntitySA->GetInterface(); + m_target = pEntitySA->GetInterface(); } bool CProjectileInfoSA::IsActive() { - return (internalInterface->bProjectileActive == 1 && internalInterface->dwProjectileType != 0); + return (m_isActive == 1 && m_weaponType != 0); } void CProjectileInfoSA::SetCounter(DWORD dwCounter) { - internalInterface->dwCounter = dwCounter + pGame->GetSystemTime(); + m_counter = dwCounter + pGame->GetSystemTime(); } DWORD CProjectileInfoSA::GetCounter() { - return internalInterface->dwCounter - pGame->GetSystemTime(); + return m_counter - pGame->GetSystemTime(); } void CProjectileInfoSA::RemoveEntityReferences(CEntity* entity) @@ -186,12 +987,41 @@ void CProjectileInfoSA::RemoveEntityReferences(CEntity* entity) const CEntitySAInterface* entityInterface = entity->GetInterface(); for (int i = 0; i < PROJECTILE_INFO_COUNT; i++) { - auto projectileInterface = projectileInfo[i]->internalInterface; + auto projectileInterface = ms_projectileInfo[i]; - if (projectileInterface->pEntProjectileOwner == entityInterface) - projectileInterface->pEntProjectileOwner = nullptr; + if (projectileInterface.m_creator == entityInterface) + projectileInterface.m_creator = nullptr; - if (projectileInterface->pEntProjectileTarget == entityInterface) - projectileInterface->pEntProjectileTarget = nullptr; + if (projectileInterface.m_creator == entityInterface) + projectileInterface.m_creator = nullptr; } } + +void CProjectileInfoSA::StaticSetHooks() +{ + HookInstallCall(0x73A30F, (DWORD)CProjectileInfoSA::Initialise); + HookInstallCall(0x73A33F, (DWORD)CProjectileInfoSA::Shutdown); + + HookInstallCall(0x429763, (DWORD)CProjectileInfoSA::StaticAddProjectile); + HookInstallCall(0x42B45B, (DWORD)CProjectileInfoSA::StaticAddProjectile); + HookInstallCall(0x5A0B3B, (DWORD)CProjectileInfoSA::StaticAddProjectile); + HookInstallCall(0x6E0749, (DWORD)CProjectileInfoSA::StaticAddProjectile); + HookInstallCall(0x6E08BF, (DWORD)CProjectileInfoSA::StaticAddProjectile); + HookInstallCall(0x6E35D7, (DWORD)CProjectileInfoSA::StaticAddProjectile); + HookInstallCall(0x741A53, (DWORD)CProjectileInfoSA::StaticAddProjectile); + + HookInstallCall(0x4808C0, (DWORD)CProjectileInfoSA::IsProjectileInRange); + + HookInstall(0x56612E, (void*)CProjectileInfoSA::RemoveDetonatorProjectiles, 5); + + HookInstallCall(0x73A36A, (DWORD)CProjectileInfoSA::Update); + + HookInstallCall(0x741928, (DWORD)CProjectileInfoSA::RemoveNotAdd); + HookInstallCall(0x741A11, (DWORD)CProjectileInfoSA::RemoveNotAdd); + + HookInstallCall(0x5500E8, (DWORD)CProjectileInfoSA::RemoveIfThisIsAProjectile); + + HookInstallCall(0x564372, (DWORD)CProjectileInfoSA::RemoveAllProjectiles); + HookInstallCall(0X56A46E, (DWORD)CProjectileInfoSA::RemoveAllProjectiles); + HookInstallCall(0X56E975, (DWORD)CProjectileInfoSA::RemoveAllProjectiles); +} diff --git a/Client/game_sa/CProjectileInfoSA.h b/Client/game_sa/CProjectileInfoSA.h index 5bcb33c629..3f01fea7d8 100644 --- a/Client/game_sa/CProjectileInfoSA.h +++ b/Client/game_sa/CProjectileInfoSA.h @@ -25,46 +25,32 @@ #define ARRAY_CProjectile 0xC89110 //##SA## #define ARRAY_CProjectileInfo 0xC891A8 //##SA## -// #pragma pack(push,1) -class CProjectileInfoSAInterface -{ -public: - eWeaponType dwProjectileType; - CEntitySAInterface* pEntProjectileOwner; - CEntitySAInterface* pEntProjectileTarget; - DWORD dwCounter; - BYTE bProjectileActive; - BYTE bPad[3]; - CVector OldCoors; - DWORD dwUnk; -}; -// #pragma pack(pop) +static constexpr std::size_t MAX_PROJECTILES_COUNT = 32; -// TODO extract manager class class CProjectileInfoSA final : public CProjectileInfo { private: - CProjectileInfoSA* projectileInfo[PROJECTILE_INFO_COUNT]; - CProjectileInfoSAInterface* internalInterface; + // CProjectileInfoSAInterface + eWeaponType m_weaponType; + CEntitySAInterface* m_creator; + CEntitySAInterface* m_target; + std::uint32_t m_counter; // time to destroy + bool m_isActive; + std::uint8_t m_field_0x11[3]; // pad + CVector m_lastPos; + void* m_particle; // FxSystem_cSAInterface* + // + + CProjectileSA* m_projectile; + + static ProjectileHandler m_projectileCreationHandler; + static GameProjectileDestructHandler* m_projectileDestructionHandler; + + static std::array ms_projectileInfo; // gaProjectileInfo public: - CProjectileInfoSA() - { - for (int i = 0; i < PROJECTILE_INFO_COUNT; i++) - { - projectileInfo[i] = new CProjectileInfoSA((CProjectileInfoSAInterface*)(ARRAY_CProjectileInfo + i * sizeof(CProjectileInfoSAInterface))); - } - } - - CProjectileInfoSA(CProjectileInfoSAInterface* projectileInfoInterface) { internalInterface = projectileInfoInterface; } - - void RemoveAllProjectiles(); - void RemoveProjectile(CProjectileInfo* pProjectileInfo, CProjectile* pProjectile, bool bBlow = true); - CProjectileInfo* GetProjectileInfo(void* projectileInfoInterface); - CProjectileInfo* GetProjectileInfo(DWORD dwIndex); - bool AddProjectile(CEntity* creator, eWeaponType eWeapon, CVector vecOrigin, float fForce, CVector* target, CEntity* targetEntity); - CProjectile* GetProjectile(void* projectilePointer); - void RemoveEntityReferences(CEntity* entity); + CProjectileInfo* AddProjectile(CEntity* creator, eWeaponType projectileType, CVector pos, float force, CVector* target, CEntity* targetEntity) const override; + void RemoveProjectile(CProjectileInfo* info, CProjectile* object, bool blow = true) const override; CEntity* GetTarget(); void SetTarget(CEntity* pEntity); @@ -73,4 +59,43 @@ class CProjectileInfoSA final : public CProjectileInfo void SetCounter(DWORD dwCounter); DWORD GetCounter(); + + eWeaponType GetType() const override { return m_weaponType; } + + CProjectile* GetProjectileObject() override { return m_projectile; } + CProjectileSA* GetProjectile() { return m_projectile; } + + CProjectileInfoSA* GetProjectileInfo(std::uint32_t index) const noexcept override { return index >= MAX_PROJECTILES_COUNT ? nullptr : &ms_projectileInfo[index]; } + + void SetProjectileCreationHandler(ProjectileHandler handler) override { m_projectileCreationHandler = handler; } + void SetGameProjectileDestructHandler(GameProjectileDestructHandler* handler) override { m_projectileDestructionHandler = handler; } + + void RemoveEntityReferences(CEntity* entity) override; + + static void StaticSetHooks(); + +private: + static void Initialise(); + static void Shutdown(); + + static CObjectSAInterface* CreateProjectileObject(std::uint32_t modelIndex, CProjectileInfoSA* info); + static void DestroyProjectileObject(CObjectSAInterface* object); + + static CProjectileInfo* StaticAddProjectile(CEntitySAInterface* creator, eWeaponType projectileType, CVector pos, float force, CVector* target, CEntitySAInterface* targetEntity); + static void StaticRemoveProjectile(CProjectileInfoSA* info, CProjectileSA* projectile, bool blow = true); + + static void RemoveNotAdd(CEntitySAInterface* entity, eWeaponType weaponType, CVector pos); + + static void RemoveDetonatorProjectiles(); + + static void RemoveIfThisIsAProjectile(CObjectSAInterface* object); + static void RemoveAllProjectiles(); + static void RemoveParticle(CProjectileInfoSA* info, bool instantly); + static bool IsProjectileInRange(float x1, float x2, float y1, float y2, float z1, float z2, bool destroy); + static void Update(); + + static int FindFreeIndex(); + static void InitCollision(CObjectSAInterface* projectile); + static void UpdateLastPos(CProjectileInfoSA* info); + static bool CheckIsLineOfSightClear(CProjectileInfoSA* info); }; diff --git a/Client/game_sa/CProjectileSA.cpp b/Client/game_sa/CProjectileSA.cpp index 939b87232b..002d1126a7 100644 --- a/Client/game_sa/CProjectileSA.cpp +++ b/Client/game_sa/CProjectileSA.cpp @@ -17,16 +17,13 @@ extern CGameSA* pGame; -CProjectileSA::CProjectileSA(CProjectileSAInterface* projectileInterface) : CObjectSA(projectileInterface) +CProjectileSA::CProjectileSA(CObjectSAInterface* object, CProjectileInfoSA* info) : CObjectSA(object), m_object(object), m_projectileInfo(info) { - internalInterface = projectileInterface; - projectileInfo = NULL; BeingDeleted = false; DoNotRemoveFromGame = false; - internalInterface->bStreamingDontDelete = true; - internalInterface->bDontStream = true; - internalInterface->bRemoveFromWorld = false; - m_bDestroyed = false; + m_destroyed = false; + + SetInterface(object); } CProjectileSA::~CProjectileSA() @@ -63,15 +60,14 @@ CProjectileSA::~CProjectileSA() //delete this; //OutputDebugString("Destroying Object\n"); }*/ - Destroy(); } void CProjectileSA::Destroy(bool bBlow) { - if (m_bDestroyed == false) + if (m_destroyed == false) { - pGame->GetProjectileInfo()->RemoveProjectile(projectileInfo, this, bBlow); - m_bDestroyed = true; + pGame->GetProjectileInfo()->RemoveProjectile(m_projectileInfo, this, bBlow); + m_destroyed = true; } } diff --git a/Client/game_sa/CProjectileSA.h b/Client/game_sa/CProjectileSA.h index 3a9d2caaf6..8dcaa524b7 100644 --- a/Client/game_sa/CProjectileSA.h +++ b/Client/game_sa/CProjectileSA.h @@ -1,6 +1,6 @@ /***************************************************************************** * - * PROJECT: Multi Theft Auto v1.0 + * PROJECT: Multi Theft Auto * LICENSE: See LICENSE in the top level directory * FILE: game_sa/CProjectileSA.h * PURPOSE: Header file for projectile entity class @@ -14,27 +14,19 @@ #include #include "CObjectSA.h" -class CProjectileSAInterface : public CObjectSAInterface // entirely inherited from CObject -{ -public: - bool IsProjectableVTBL() const { return GetVTBL() == (void*)0x867030; }; -}; - class CProjectileSA : public virtual CProjectile, public virtual CObjectSA { private: - CProjectileSAInterface* internalInterface; - class CProjectileInfo* projectileInfo; - bool m_bDestroyed; - + class CProjectileInfo* m_projectileInfo; + bool m_destroyed; + public: - CProjectileSA(class CProjectileSAInterface* projectileInterface); + CProjectileSA(CObjectSAInterface* object, class CProjectileInfoSA* info); ~CProjectileSA(); - void Destroy(bool bBlow = true); - CProjectileSAInterface* GetProjectileInterface() const { return static_cast(m_pInterface); }; - bool CalculateImpactPosition(CEntitySAInterface* pCollidedWith, CVector vecInputStart, CVector& vecInputEnd); + void Destroy(bool blow = true) override; + bool CorrectPhysics() override; - void SetProjectileInfo(CProjectileInfo* pProjectileInfo) { projectileInfo = pProjectileInfo; } - bool CorrectPhysics(); +public: + CObjectSAInterface* m_object; }; diff --git a/Client/game_sa/CVehicleSA.cpp b/Client/game_sa/CVehicleSA.cpp index f143c1784e..126f7d93aa 100644 --- a/Client/game_sa/CVehicleSA.cpp +++ b/Client/game_sa/CVehicleSA.cpp @@ -398,11 +398,6 @@ CVehicle* CVehicleSA::GetNextTrainCarriage() return nullptr; } -bool CVehicleSA::AddProjectile(eWeaponType eWeapon, CVector vecOrigin, float fForce, CVector* target, CEntity* targetEntity) -{ - return ((CProjectileInfoSA*)pGame->GetProjectileInfo())->AddProjectile((CEntitySA*)this, eWeapon, vecOrigin, fForce, target, targetEntity); -} - void CVehicleSA::SetNextTrainCarriage(CVehicle* pNext) { if (pNext) diff --git a/Client/game_sa/CVehicleSA.h b/Client/game_sa/CVehicleSA.h index ceaba79e49..798241d410 100644 --- a/Client/game_sa/CVehicleSA.h +++ b/Client/game_sa/CVehicleSA.h @@ -459,8 +459,6 @@ class CVehicleSA : public virtual CVehicle, public virtual CPhysicalSA // Override of CPhysicalSA::SetMoveSpeed to take trains into account void SetMoveSpeed(const CVector& vecMoveSpeed) noexcept; - bool AddProjectile(eWeaponType eWeapon, CVector vecOrigin, float fForce, CVector* target, CEntity* targetEntity); - CTrainSAInterface* GetNextCarriageInTrain(); CVehicle* GetNextTrainCarriage(); void SetNextTrainCarriage(CVehicle* pNext); diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index 49343f0c9e..0a03fd3cb8 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -270,8 +270,6 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo()) g_pMultiplayer->SetDamageHandler(CClientGame::StaticDamageHandler); g_pMultiplayer->SetDeathHandler(CClientGame::StaticDeathHandler); g_pMultiplayer->SetFireHandler(CClientGame::StaticFireHandler); - g_pMultiplayer->SetProjectileStopHandler(CClientProjectileManager::Hook_StaticProjectileAllow); - g_pMultiplayer->SetProjectileHandler(CClientProjectileManager::Hook_StaticProjectileCreation); g_pMultiplayer->SetRender3DStuffHandler(CClientGame::StaticRender3DStuffHandler); g_pMultiplayer->SetPreRenderSkyHandler(CClientGame::StaticPreRenderSkyHandler); g_pMultiplayer->SetRenderHeliLightHandler(CClientGame::StaticRenderHeliLightHandler); @@ -300,7 +298,7 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo()) g_pMultiplayer->SetGameObjectDestructHandler(CClientGame::StaticGameObjectDestructHandler); g_pMultiplayer->SetGameVehicleDestructHandler(CClientGame::StaticGameVehicleDestructHandler); g_pMultiplayer->SetGamePlayerDestructHandler(CClientGame::StaticGamePlayerDestructHandler); - g_pMultiplayer->SetGameProjectileDestructHandler(CClientGame::StaticGameProjectileDestructHandler); + //g_pMultiplayer->SetGameProjectileDestructHandler(CClientGame::StaticGameProjectileDestructHandler); g_pMultiplayer->SetGameModelRemoveHandler(CClientGame::StaticGameModelRemoveHandler); g_pMultiplayer->SetGameRunNamedAnimDestructorHandler(CClientGame::StaticGameRunNamedAnimDestructorHandler); g_pMultiplayer->SetGameEntityRenderHandler(CClientGame::StaticGameEntityRenderHandler); @@ -318,6 +316,9 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo()) g_pCore->GetKeyBinds()->SetCharacterKeyHandler(CClientGame::StaticCharacterKeyHandler); g_pNet->RegisterPacketHandler(CClientGame::StaticProcessPacket); + g_pGame->GetProjectileInfo()->SetProjectileCreationHandler(CClientProjectileManager::Hook_StaticProjectileCreation); + g_pGame->GetProjectileInfo()->SetGameProjectileDestructHandler(CClientGame::StaticGameProjectileDestructHandler); + m_pLuaManager = new CLuaManager(this); m_pScriptDebugging = new CScriptDebugging(m_pLuaManager); m_pScriptDebugging->SetLogfile(CalcMTASAPath("mta\\logs\\clientscript.log"), 3); @@ -477,8 +478,6 @@ CClientGame::~CClientGame() g_pMultiplayer->SetDrawRadarAreasHandler(NULL); g_pMultiplayer->SetDamageHandler(NULL); g_pMultiplayer->SetFireHandler(NULL); - g_pMultiplayer->SetProjectileStopHandler(NULL); - g_pMultiplayer->SetProjectileHandler(NULL); g_pMultiplayer->SetProcessCamHandler(nullptr); g_pMultiplayer->SetRender3DStuffHandler(NULL); g_pMultiplayer->SetPreRenderSkyHandler(NULL); @@ -3855,14 +3854,6 @@ void CClientGame::ProjectileInitiateHandler(CClientProjectile* pProjectile) pProjectile->SetInterior(pProjectile->GetCreator()->GetInterior()); pProjectile->SetDimension(pProjectile->GetCreator()->GetDimension()); } - - // Validate the projectile for our element tree - pProjectile->SetParent(m_pRootEntity); - - // Call our creation event - CLuaArguments Arguments; - Arguments.PushElement(pProjectile->GetCreator()); - pProjectile->CallEvent("onClientProjectileCreation", Arguments, true); } void CClientGame::Render3DStuffHandler() @@ -4934,15 +4925,14 @@ void CClientGame::GamePlayerDestructHandler(CEntitySAInterface* pPlayer) // m_pGameEntityXRefManager->OnGameEntityDestruct(pPlayer); } -void CClientGame::GameProjectileDestructHandler(CEntitySAInterface* pProjectile) +void CClientGame::GameProjectileDestructHandler(CEntitySAInterface* projectileObject) { - CClientProjectile* pClientProjectile = m_pManager->GetProjectileManager()->Get(pProjectile); - // Happens when destroyElement is called rather than letting the projectile expire - // Normal code path is destruction from CProjectileSAInterface -> CProjectileSA -> CClientProjectile - // destroyElement is CClientProjectile -> CProjectileSA -> CProjectileSAInterface - // which means the CClientProjectile element is deleted when we get here - if (pClientProjectile) - CStaticFunctionDefinitions::DestroyElement(*pClientProjectile); + CClientProjectile* clientProjectile = m_pManager->GetProjectileManager()->Get(projectileObject); + // Called BEFORE the projectile is destroyed (e.g., explodes or expires) (CProjectile::~CProjectile) + // First, we destroy the client-side element; then the game object itself will be destroyed. + // (This is possible thanks to our custom projectile implementation.) + if (clientProjectile) + CStaticFunctionDefinitions::DestroyElement(*clientProjectile); } void CClientGame::GameModelRemoveHandler(ushort usModelId) diff --git a/Client/mods/deathmatch/logic/CClientProjectile.cpp b/Client/mods/deathmatch/logic/CClientProjectile.cpp index 048334da6a..bb6df739e7 100644 --- a/Client/mods/deathmatch/logic/CClientProjectile.cpp +++ b/Client/mods/deathmatch/logic/CClientProjectile.cpp @@ -100,8 +100,8 @@ CClientProjectile::~CClientProjectile() if (m_pProjectile) { // Make sure we're destroyed - delete m_pProjectile; + //m_pProjectile->Destroy(true); m_pProjectile = NULL; } @@ -118,7 +118,7 @@ void CClientProjectile::Unlink() if (m_pProjectile) { // Make sure we're destroyed - delete m_pProjectile; + //m_pProjectile->Destroy(true); m_pProjectile = NULL; } @@ -127,59 +127,26 @@ void CClientProjectile::Unlink() void CClientProjectile::DoPulse() { - // We use initiate data to set values on creation (as it doesn't exist until a frame after our projectile hook) - if (m_bInitiate) - { - if (m_pInitiateData) - { - if (m_pInitiateData->pvecPosition) - SetPosition(*m_pInitiateData->pvecPosition); - if (m_pInitiateData->pvecRotation) - SetRotationRadians(*m_pInitiateData->pvecRotation); - if (m_pInitiateData->pvecVelocity) - SetVelocity(*m_pInitiateData->pvecVelocity); - if (m_pInitiateData->usModel) - SetModel(m_pInitiateData->usModel); - } - - // Handle net sync and script event - g_pClientGame->ProjectileInitiateHandler(this); - - m_bInitiate = false; - } - // Update our position/rotation if we're attached DoAttaching(); if (m_bCorrected == false && m_pProjectile != NULL && GetWeaponType() == eWeaponType::WEAPONTYPE_REMOTE_SATCHEL_CHARGE) - { m_bCorrected = m_pProjectile->CorrectPhysics(); - } } void CClientProjectile::Initiate(CVector& vecPosition, CVector& vecRotation, CVector& vecVelocity, unsigned short usModel) { - dassert(!m_pInitiateData); - - // Store our initiation data - m_pInitiateData = new CProjectileInitiateData; - m_pInitiateData->pvecPosition = new CVector(vecPosition); + SetPosition(vecPosition); if (vecRotation != CVector(0, 0, 0)) - { - m_pInitiateData->pvecRotation = new CVector(vecRotation); - } - else - { - m_pInitiateData->pvecRotation = NULL; - } + SetRotationRadians(vecRotation); if (vecVelocity != CVector(0, 0, 0)) - m_pInitiateData->pvecVelocity = new CVector(vecVelocity); - else - m_pInitiateData->pvecVelocity = NULL; + SetVelocity(vecVelocity); + + SetModel(usModel); - m_pInitiateData->usModel = usModel; + g_pClientGame->ProjectileInitiateHandler(this); } void CClientProjectile::Destroy(bool bBlow) @@ -187,6 +154,7 @@ void CClientProjectile::Destroy(bool bBlow) if (m_pProjectile) { m_pProjectile->Destroy(bBlow); + m_pProjectile = nullptr; } } diff --git a/Client/mods/deathmatch/logic/CClientProjectileManager.cpp b/Client/mods/deathmatch/logic/CClientProjectileManager.cpp index 862ebc68c4..26b5ae0b48 100644 --- a/Client/mods/deathmatch/logic/CClientProjectileManager.cpp +++ b/Client/mods/deathmatch/logic/CClientProjectileManager.cpp @@ -1,6 +1,6 @@ /***************************************************************************** * - * PROJECT: Multi Theft Auto v1.0 + * PROJECT: Multi Theft Auto * (Shared logic for modifications) * LICENSE: See LICENSE in the top level directory * FILE: mods/shared_logic/CClientProjectileManager.cpp @@ -9,14 +9,12 @@ *****************************************************************************/ #include "StdInc.h" +#include "game/CProjectileInfo.h" using std::list; CClientProjectileManager* g_pProjectileManager = NULL; -/* This class is used to manage/control projectiles created from game-layer hooks. - Projectile creation should eventually be server-requested. -*/ CClientProjectileManager::CClientProjectileManager(CClientManager* pManager) { CClientEntityRefManager::AddEntityRefs(ENTITY_REF_DEBUG(this, "CClientProjectileManager"), &m_pCreator, &m_pLastCreated, NULL); @@ -81,17 +79,19 @@ bool CClientProjectileManager::Exists(CClientProjectile* pProjectile) return false; } -CClientProjectile* CClientProjectileManager::Get(CEntitySAInterface* pProjectile) +CClientProjectile* CClientProjectileManager::Get(CEntitySAInterface* projectileObject) { int iCount = m_List.size(); - assert(iCount <= 32); + //assert(iCount <= 32); list::iterator iter = m_List.begin(); for (; iter != m_List.end(); iter++) { - if ((*iter)->GetGameEntity()->GetInterface() == pProjectile) - { + CEntity* projectile = (*iter)->GetGameEntity(); + if (!projectile) + continue; + + if (projectile->GetInterface() == projectileObject) return (*iter); - } } return NULL; } @@ -101,75 +101,58 @@ void CClientProjectileManager::RemoveFromList(CClientProjectile* pProjectile) m_List.remove(pProjectile); } -bool CClientProjectileManager::Hook_StaticProjectileAllow(CEntity* pGameCreator, eWeaponType weaponType, CVector* origin, float fForce, CVector* target, - CEntity* targetEntity) -{ - return g_pProjectileManager->Hook_ProjectileAllow(pGameCreator, weaponType, origin, fForce, target, targetEntity); -} - -bool CClientProjectileManager::Hook_ProjectileAllow(CEntity* pGameCreator, eWeaponType weaponType, CVector* origin, float fForce, CVector* target, - CEntity* targetEntity) -{ - // Called before projectile creation, we need to decide to allow or cancel it here - - // Is this a local projectile? - CClientPlayer* pLocalPlayer = m_pManager->GetPlayerManager()->GetLocalPlayer(); - - CPools* pPools = g_pGame->GetPools(); - - // Store the creator/local variables for the next stage - m_pCreator = pGameCreator ? pPools->GetClientEntity((DWORD*)pGameCreator->GetInterface()) : nullptr; - m_bIsLocal = (m_pCreator == pLocalPlayer || (pLocalPlayer->GetOccupiedVehicleSeat() == 0 && m_pCreator == pLocalPlayer->GetOccupiedVehicle())); - - return (m_bCreating || m_bIsLocal); -} - -void CClientProjectileManager::Hook_StaticProjectileCreation(CEntity* pGameCreator, CProjectile* pGameProjectile, CProjectileInfo* pProjectileInfo, +bool CClientProjectileManager::Hook_StaticProjectileCreation(CEntity* pGameCreator, CProjectile* pGameProjectile, CProjectileInfo* pProjectileInfo, eWeaponType weaponType, CVector* origin, float fForce, CVector* target, CEntity* pGameTarget) { - g_pProjectileManager->Hook_ProjectileCreation(pGameCreator, pGameProjectile, pProjectileInfo, weaponType, origin, fForce, target, pGameTarget); + return g_pProjectileManager->Hook_ProjectileCreation(pGameCreator, pGameProjectile, pProjectileInfo, weaponType, origin, fForce, target, pGameTarget); } -void CClientProjectileManager::Hook_ProjectileCreation(CEntity* pGameCreator, CProjectile* pGameProjectile, CProjectileInfo* pProjectileInfo, +bool CClientProjectileManager::Hook_ProjectileCreation(CEntity* pGameCreator, CProjectile* pGameProjectile, CProjectileInfo* pProjectileInfo, eWeaponType weaponType, CVector* origin, float fForce, CVector* target, CEntity* pGameTarget) { - // Called on projectile construction (projectile doesn't actually exist until the next frame) - /* Projectiles: - WEAPONTYPE_GRENADE, WEAPONTYPE_TEARGAS, WEAPONTYPE_MOLOTOV, - WEAPONTYPE_REMOTE_SATCHEL_CHARGE, WEAPONTYPE_ROCKET, WEAPONTYPE_ROCKET_HS, - WEAPONTYPE_FLARE, WEAPONTYPE_FREEFALL_BOMB */ - CPools* pPools = g_pGame->GetPools(); CClientEntity* pTarget = pGameTarget ? pPools->GetClientEntity((DWORD*)pGameTarget->GetInterface()) : nullptr; - m_pLastCreated = new CClientProjectile(m_pManager, pGameProjectile, pProjectileInfo, m_pCreator, pTarget, weaponType, origin, target, fForce, m_bIsLocal); + CClientEntity* creator = pGameCreator ? pPools->GetClientEntity((DWORD*)pGameCreator->GetInterface()) : nullptr; + + CClientPlayer* pLocalPlayer = m_pManager->GetPlayerManager()->GetLocalPlayer(); + m_bIsLocal = (creator == pLocalPlayer || (pLocalPlayer->GetOccupiedVehicleSeat() == 0 && creator == pLocalPlayer->GetOccupiedVehicle())); + + m_pLastCreated = new CClientProjectile(m_pManager, pGameProjectile, pProjectileInfo, creator, pTarget, weaponType, origin, target, fForce, m_bIsLocal); + + // Validate the projectile for our element tree + m_pLastCreated->SetParent(g_pClientGame->GetRootEntity()); + + CLuaArguments Arguments; + Arguments.PushElement(creator); + Arguments.PushNumber(weaponType); + Arguments.PushNumber(origin->fX); + Arguments.PushNumber(origin->fY); + Arguments.PushNumber(origin->fZ); + Arguments.PushNumber(fForce); + Arguments.PushNumber(target ? target->fX : 0); + Arguments.PushNumber(target ? target->fY : 0); + Arguments.PushNumber(target ? target->fZ : 0); + if (pTarget) + Arguments.PushElement(pTarget); + else + Arguments.PushNil(); + + bool continueCreation = m_pLastCreated->CallEvent("onClientProjectileCreation", Arguments, true); + if (!continueCreation) + m_pLastCreated->Destroy(false); + + return continueCreation; } -CClientProjectile* CClientProjectileManager::Create(CClientEntity* pCreator, eWeaponType eWeapon, CVector& vecOrigin, float fForce, CVector* target, - CClientEntity* pTargetEntity) +CClientProjectile* CClientProjectileManager::Create(CClientEntity* creator, eWeaponType weapon, CVector& origin, float force, CVector* target, CClientEntity* targetEntity) { - m_bCreating = true; - m_pLastCreated = NULL; - CEntity* pGameCreator = pCreator->GetGameEntity(); - CEntity* pGameTargetEntity = NULL; - if (pTargetEntity) - pGameTargetEntity = pTargetEntity->GetGameEntity(); - if (pGameCreator) - { - // Peds and players - if (pCreator->GetType() == CCLIENTPED || pCreator->GetType() == CCLIENTPLAYER) - { - CPed* pPed = dynamic_cast(pGameCreator); - if (pPed) - pPed->AddProjectile(eWeapon, vecOrigin, fForce, target, pGameTargetEntity); - } - // Vehicles - else if (pCreator->GetType() == CCLIENTVEHICLE) - { - CVehicle* pVehicle = dynamic_cast(pGameCreator); - if (pVehicle) - pVehicle->AddProjectile(eWeapon, vecOrigin, fForce, target, pGameTargetEntity); - } - } - m_bCreating = false; + CEntity* creatorGameEntity = creator->GetGameEntity(); + if (!creatorGameEntity) + return nullptr; + + CProjectileInfo* gameProjectile = g_pGame->GetProjectileInfo()->AddProjectile(creatorGameEntity, weapon, origin, force, target, targetEntity ? targetEntity->GetGameEntity() : nullptr); + if (!gameProjectile) + return nullptr; + return m_pLastCreated; } diff --git a/Client/mods/deathmatch/logic/CClientProjectileManager.h b/Client/mods/deathmatch/logic/CClientProjectileManager.h index 1992ab9d6c..be31faa1a3 100644 --- a/Client/mods/deathmatch/logic/CClientProjectileManager.h +++ b/Client/mods/deathmatch/logic/CClientProjectileManager.h @@ -35,9 +35,9 @@ class CClientProjectileManager static bool Hook_StaticProjectileAllow(CEntity* pGameCreator, eWeaponType weaponType, CVector* origin, float fForce, CVector* target, CEntity* targetEntity); bool Hook_ProjectileAllow(CEntity* pGameCreator, eWeaponType weaponType, CVector* origin, float fForce, CVector* target, CEntity* targetEntity); - static void Hook_StaticProjectileCreation(CEntity* pGameCreator, CProjectile* pGameProjectile, CProjectileInfo* pProjectileInfo, eWeaponType weaponType, + static bool Hook_StaticProjectileCreation(CEntity* pGameCreator, CProjectile* pGameProjectile, CProjectileInfo* pProjectileInfo, eWeaponType weaponType, CVector* origin, float fForce, CVector* target, CEntity* pGameTarget); - void Hook_ProjectileCreation(CEntity* pGameCreator, CProjectile* pGameProjectile, CProjectileInfo* pProjectileInfo, eWeaponType weaponType, CVector* origin, + bool Hook_ProjectileCreation(CEntity* pGameCreator, CProjectile* pGameProjectile, CProjectileInfo* pProjectileInfo, eWeaponType weaponType, CVector* origin, float fForce, CVector* target, CEntity* pGameTarget); CClientProjectile* Create(CClientEntity* pCreator, eWeaponType eWeapon, CVector& vecOrigin, float fForce, CVector* target, CClientEntity* pTargetEntity); diff --git a/Client/multiplayer_sa/CMultiplayerSA.cpp b/Client/multiplayer_sa/CMultiplayerSA.cpp index 6973495f8c..3c73a0c88a 100644 --- a/Client/multiplayer_sa/CMultiplayerSA.cpp +++ b/Client/multiplayer_sa/CMultiplayerSA.cpp @@ -396,8 +396,6 @@ BulletFireHandler* m_pBulletFireHandler = NULL; DamageHandler* m_pDamageHandler = NULL; DeathHandler* m_pDeathHandler = NULL; FireHandler* m_pFireHandler = NULL; -ProjectileHandler* m_pProjectileHandler = NULL; -ProjectileStopHandler* m_pProjectileStopHandler = NULL; ProcessCamHandler* m_pProcessCamHandler = NULL; ChokingHandler* m_pChokingHandler = NULL; ExplosionHandler* m_pExplosionHandler = NULL; @@ -591,8 +589,6 @@ CMultiplayerSA::CMultiplayerSA() m_pDrawRadarAreasHandler = NULL; m_pDamageHandler = NULL; m_pFireHandler = NULL; - m_pProjectileHandler = NULL; - m_pProjectileStopHandler = NULL; MemSetFast(&localStatsData, 0, sizeof(CStatsData)); localStatsData.StatTypesFloat[24] = 569.0f; // Max Health @@ -2604,16 +2600,6 @@ void CMultiplayerSA::SetExplosionHandler(ExplosionHandler* pExplosionHandler) m_pExplosionHandler = pExplosionHandler; } -void CMultiplayerSA::SetProjectileHandler(ProjectileHandler* pProjectileHandler) -{ - m_pProjectileHandler = pProjectileHandler; -} - -void CMultiplayerSA::SetProjectileStopHandler(ProjectileStopHandler* pProjectileHandler) -{ - m_pProjectileStopHandler = pProjectileHandler; -} - void CMultiplayerSA::SetBreakTowLinkHandler(BreakTowLinkHandler* pBreakTowLinkHandler) { m_pBreakTowLinkHandler = pBreakTowLinkHandler; diff --git a/Client/multiplayer_sa/CMultiplayerSA.h b/Client/multiplayer_sa/CMultiplayerSA.h index 75206d8211..4248a5dcc6 100644 --- a/Client/multiplayer_sa/CMultiplayerSA.h +++ b/Client/multiplayer_sa/CMultiplayerSA.h @@ -108,8 +108,6 @@ class CMultiplayerSA : public CMultiplayer void SetExplosionHandler(ExplosionHandler* pExplosionHandler); void SetDamageHandler(DamageHandler* pDamageHandler); void SetDeathHandler(DeathHandler* pDeathHandler); - void SetProjectileHandler(ProjectileHandler* pProjectileHandler); - void SetProjectileStopHandler(ProjectileStopHandler* pProjectileHandler); void SetFireHandler(FireHandler* pFireHandler); void SetBreakTowLinkHandler(BreakTowLinkHandler* pBreakTowLinkHandler); void SetProcessCamHandler(ProcessCamHandler* pProcessCamHandler); diff --git a/Client/multiplayer_sa/CMultiplayerSA_VehicleCollision.cpp b/Client/multiplayer_sa/CMultiplayerSA_VehicleCollision.cpp index 30de5308fe..1284854b04 100644 --- a/Client/multiplayer_sa/CMultiplayerSA_VehicleCollision.cpp +++ b/Client/multiplayer_sa/CMultiplayerSA_VehicleCollision.cpp @@ -40,7 +40,7 @@ void TriggerVehicleCollisionEvent() } else { - const bool isProjectile = static_cast(pEntity)->IsProjectableVTBL(); + const bool isProjectile = pEntity->GetVTBL() == (void*)0x867030; pVehicleCollisionHandler(pCollisionVehicle, pEntity, pEntity->m_nModelIndex, pCollisionVehicle->m_fDamageImpulseMagnitude, 0.0f, pCollisionVehicle->m_usPieceType, pCollisionVehicle->m_vecCollisionPosition, pCollisionVehicle->m_vecCollisionImpactVelocity, pEntity->nType == ENTITY_TYPE_OBJECT && isProjectile); diff --git a/Client/multiplayer_sa/multiplayer_shotsync.cpp b/Client/multiplayer_sa/multiplayer_shotsync.cpp index a197488e9b..f26c249f56 100644 --- a/Client/multiplayer_sa/multiplayer_shotsync.cpp +++ b/Client/multiplayer_sa/multiplayer_shotsync.cpp @@ -69,14 +69,9 @@ extern BulletFireHandler* m_pBulletFireHandler; extern DamageHandler* m_pDamageHandler; extern DeathHandler* m_pDeathHandler; extern FireHandler* m_pFireHandler; -extern ProjectileHandler* m_pProjectileHandler; -extern ProjectileStopHandler* m_pProjectileStopHandler; char szDebug[255] = {'\0'}; -DWORD RETURN_CProjectile__AddProjectile = 0x401C3D; -DWORD RETURN_CProjectile__CProjectile = 0x4037B3; - CPools* m_pools = 0; #define VAR_CWorld_IncludeCarTyres 0xb7cd70 // Used for CWorld_ProcessLineOfSight @@ -95,8 +90,6 @@ VOID InitShotsyncHooks() HookInstall(HOOKPOS_CEventDamage__AffectsPed, (DWORD)HOOK_CEventDamage__AffectsPed, 6); HookInstall(HOOKPOS_CEventVehicleExplosion__AffectsPed, (DWORD)HOOK_CEventVehicleExplosion__AffectsPed, 5); HookInstall(HOOKPOS_CFireManager__StartFire, (DWORD)HOOK_CFireManager__StartFire, 6); - HookInstall(HOOKPOS_CProjectileInfo__AddProjectile, (DWORD)HOOK_CProjectileInfo__AddProjectile, 7); - HookInstall(HOOKPOS_CProjectile__CProjectile, (DWORD)HOOK_CProjectile__CProjectile, 7); HookInstall(HOOKPOS_IKChainManager_PointArm, (DWORD)HOOK_IKChainManager_PointArm, 7); HookInstall(HOOKPOS_IKChainManager_LookAt, (DWORD)HOOK_IKChainManager_LookAt, 7); HookInstall(HOOKPOS_IKChainManager_SkipAim, (DWORD)HOOK_SkipAim, 6); @@ -937,103 +930,6 @@ static void GetProjectileTarget(CPools* pPools) } } -bool ProcessProjectileAdd() -{ - if (m_pProjectileStopHandler) - { - CPools* pPools = pGameInterface->GetPools(); - CEntity* pOwner = GetProjectileOwner(pPools); - GetProjectileTarget(pPools); - - return m_pProjectileStopHandler(pOwner, projectileWeaponType, projectileOrigin, projectileForce, projectileTarget, projectileTargetEntity); - } - return true; -} - -void ProcessProjectile() -{ - if (m_pProjectileHandler != NULL) - { - CPoolsSA* pPools = (CPoolsSA*)pGameInterface->GetPools(); - CEntity* pOwner = GetProjectileOwner(pPools); - GetProjectileTarget(pPools); - - CProjectileInfo* projectileInfo = pGameInterface->GetProjectileInfo()->GetProjectileInfo(dwProjectileInfoIndex); - CProjectile* projectile = pGameInterface->GetProjectileInfo()->GetProjectile(pProjectile); - projectile->SetProjectileInfo(projectileInfo); - m_pProjectileHandler(pOwner, projectile, projectileInfo, projectileWeaponType, projectileOrigin, projectileForce, projectileTarget, - projectileTargetEntity); - projectileTargetEntity = NULL; - } -} - -// CProjectileInfo::AddProjectile(class CEntity * owner,enum eWeaponType weapon type -// ,class CVector origin?,float 0?,class CVector * direction,class CEntity * target) -void _declspec(naked) HOOK_CProjectileInfo__AddProjectile() -{ - _asm - { - mov edx, [esp+4] - mov pProjectileOwner, edx - - mov edx, [esp+8] - mov projectileWeaponType, edx - - lea edx, [esp+12] - mov projectileOrigin, edx - - mov edx, [esp+24] - mov projectileForce, edx - - mov edx, [esp+28] - mov projectileTarget, edx - - mov edx, [esp+32] - mov projectileTargetEntityInterface, edx - - pushad - } - if (ProcessProjectileAdd()) - { // projectile should be created - _asm - { - popad - push 0xFFFFFFFF - mov edx, RETURN_CProjectile__AddProjectile - jmp edx - } - } - else - { - _asm - { - popad - xor al, al - retn - } - } -} - -void _declspec(naked) HOOK_CProjectile__CProjectile() -{ - _asm - { - mov dwProjectileInfoIndex, ebx // it happens to be in here, luckily - mov pProjectile, ecx - pushad - } - - ProcessProjectile(); - - _asm - { - popad - push 0xFFFFFFFF - mov edx, RETURN_CProjectile__CProjectile - jmp edx - } -} - static void CheckInVehicleDamage() { SClientEntity* pPedClientEntity = m_pools->GetPed((DWORD*)pShootingPed); diff --git a/Client/sdk/game/CFx.h b/Client/sdk/game/CFx.h index d575f28b83..31e4df252b 100644 --- a/Client/sdk/game/CFx.h +++ b/Client/sdk/game/CFx.h @@ -12,6 +12,7 @@ #pragma once #include "enums/FxParticleSystems.h" +class CFxSAInterface; class CEntity; class CVector; class CVehicle; @@ -20,6 +21,8 @@ struct RwColor; class CFx { public: + virtual CFxSAInterface* GetInterface() = 0; + virtual void AddBlood(CVector& vecPosition, CVector& vecDirection, int iCount, float fBrightness) = 0; virtual void AddWood(CVector& vecPosition, CVector& vecDirection, int iCount, float fBrightness) = 0; virtual void AddSparks(CVector& vecPosition, CVector& vecDirection, float fForce, int iCount, CVector vecAcrossLine, unsigned char ucBlurIf0, float fSpread, diff --git a/Client/sdk/game/CPed.h b/Client/sdk/game/CPed.h index 3219738e69..e1ba29676f 100644 --- a/Client/sdk/game/CPed.h +++ b/Client/sdk/game/CPed.h @@ -28,7 +28,7 @@ class CTaskManager; class CVehicle; class CWeapon; class CWeaponStat; -class CProjectileSAInterface; +class CProjectile; enum ePedPieceTypes { @@ -175,11 +175,11 @@ enum class EPedWeaponAudioEventType struct SSatchelsData { - CProjectileSAInterface* pProjectileInterface; + CProjectile* pProjectileInterface; CVector* vecAttachedOffsets; CVector* vecAttachedRotation; - SSatchelsData(CProjectileSAInterface* proj, CVector* offset, CVector* rotation) : pProjectileInterface(proj), vecAttachedOffsets(offset), vecAttachedRotation(rotation) {} + SSatchelsData(CProjectile* proj, CVector* offset, CVector* rotation) : pProjectileInterface(proj), vecAttachedOffsets(offset), vecAttachedRotation(rotation) {} }; inline bool IsValidMoveAnim(std::uint32_t iMoveAnim) noexcept @@ -211,7 +211,6 @@ class CPed : public virtual CPhysical virtual float GetOxygenLevel() const = 0; virtual void SetOxygenLevel(float oxygen) = 0; - virtual bool AddProjectile(eWeaponType weaponType, CVector origin, float force, CVector* target, CEntity* targetEntity) = 0; virtual CWeapon* GiveWeapon(eWeaponType weaponType, std::uint32_t ammo, eWeaponSkill weaponSkill) = 0; virtual CWeapon* GetWeapon(eWeaponSlot weaponSlot) const noexcept = 0; virtual CWeapon* GetWeapon(eWeaponType weaponType) const = 0; diff --git a/Client/sdk/game/CPools.h b/Client/sdk/game/CPools.h index 7c209ed6c4..914cdbe122 100644 --- a/Client/sdk/game/CPools.h +++ b/Client/sdk/game/CPools.h @@ -20,6 +20,7 @@ class CClientEntity; class CEntity; class CEntitySAInterface; +class CObjectSAInterface; class CObject; class CObjectSA; class CPed; @@ -113,4 +114,6 @@ class CPools virtual CDummyPool& GetDummyPool() noexcept = 0; virtual CTxdPool& GetTxdPool() noexcept = 0; virtual CPtrNodeSingleLinkPool& GetPtrNodeSingleLinkPool() noexcept = 0; + + virtual std::uint16_t GetObjectHandle(CObjectSAInterface* object) const = 0; }; diff --git a/Client/sdk/game/CProjectile.h b/Client/sdk/game/CProjectile.h index d6f9faa7c9..ee9c523034 100644 --- a/Client/sdk/game/CProjectile.h +++ b/Client/sdk/game/CProjectile.h @@ -1,6 +1,6 @@ /***************************************************************************** * - * PROJECT: Multi Theft Auto v1.0 + * PROJECT: Multi Theft Auto * LICENSE: See LICENSE in the top level directory * FILE: sdk/game/CProjectile.h * PURPOSE: Projectile entity interface @@ -13,14 +13,11 @@ #include "CObject.h" -class CProjectileInfo; - class CProjectile : public virtual CObject { public: virtual ~CProjectile(){}; - virtual void Destroy(bool bBlow) = 0; - virtual void SetProjectileInfo(CProjectileInfo* pProjectileInfo) = 0; + virtual void Destroy(bool blow) = 0; virtual bool CorrectPhysics() = 0; }; diff --git a/Client/sdk/game/CProjectileInfo.h b/Client/sdk/game/CProjectileInfo.h index 70cdc7fb2b..5e5156f88e 100644 --- a/Client/sdk/game/CProjectileInfo.h +++ b/Client/sdk/game/CProjectileInfo.h @@ -1,6 +1,6 @@ /***************************************************************************** * - * PROJECT: Multi Theft Auto v1.0 + * PROJECT: Multi Theft Auto * LICENSE: See LICENSE in the top level directory * FILE: sdk/game/CProjectileInfo.h * PURPOSE: Projectile entity information interface @@ -12,19 +12,21 @@ #pragma once class CEntity; -class CPlayerPed; class CProjectile; class CVector; +using ProjectileHandler = bool(*)(CEntity* creator, CProjectile* projectile, CProjectileInfo* projectileInfo, eWeaponType projectileType, CVector* pos, float force, CVector* target, CEntity* targetEntity); +using GameProjectileDestructHandler = void(CEntitySAInterface* entity); + class CProjectileInfo { public: - virtual bool AddProjectile(CEntity* creator, eWeaponType eWeapon, CVector vecOrigin, float fForce, CVector* target, CEntity* targetEntity) = 0; - virtual CProjectile* GetProjectile(void* projectilePointer) = 0; // hack, don't use please - virtual CProjectileInfo* GetProjectileInfo(void* projectileInfoInterface) = 0; // don't use - virtual void RemoveProjectile(CProjectileInfo* pProjectileInfo, CProjectile* pProjectile, bool bBlow = true) = 0; - virtual CProjectileInfo* GetProjectileInfo(DWORD Index) = 0; - virtual void RemoveEntityReferences(CEntity* entity) = 0; + virtual CProjectileInfo* AddProjectile(CEntity* creator, eWeaponType weapon, CVector origin, float force, CVector* target, CEntity* targetEntity) const = 0; + virtual CProjectile* GetProjectileObject() = 0; + + virtual void RemoveProjectile(CProjectileInfo* projectileInfo, CProjectile* projectile, bool blow = true) const = 0; + + virtual void RemoveEntityReferences(CEntity* entity) = 0; virtual CEntity* GetTarget() = 0; virtual void SetTarget(CEntity* pEntity) = 0; @@ -33,4 +35,11 @@ class CProjectileInfo virtual void SetCounter(DWORD dwCounter) = 0; virtual DWORD GetCounter() = 0; + + virtual eWeaponType GetType() const = 0; + + virtual CProjectileInfo* GetProjectileInfo(std::uint32_t Index) const noexcept = 0; + + virtual void SetProjectileCreationHandler(ProjectileHandler handler) = 0; + virtual void SetGameProjectileDestructHandler(GameProjectileDestructHandler* handler) = 0; }; diff --git a/Client/sdk/game/CVehicle.h b/Client/sdk/game/CVehicle.h index ce4b337d54..392332b4dd 100644 --- a/Client/sdk/game/CVehicle.h +++ b/Client/sdk/game/CVehicle.h @@ -96,8 +96,6 @@ class CVehicle : public virtual CPhysical public: virtual ~CVehicle(){}; - virtual bool AddProjectile(eWeaponType eWeapon, CVector vecOrigin, float fForce, CVector* target, CEntity* targetEntity) = 0; - virtual CPed* GetDriver() = 0; virtual CPed* GetPassenger(unsigned char ucSlot) = 0; virtual bool IsBeingDriven() = 0; diff --git a/Client/sdk/multiplayer/CMultiplayer.h b/Client/sdk/multiplayer/CMultiplayer.h index e5e591ad5d..452ab9a304 100644 --- a/Client/sdk/multiplayer/CMultiplayer.h +++ b/Client/sdk/multiplayer/CMultiplayer.h @@ -86,10 +86,6 @@ typedef void(BulletFireHandler)(class CPed* pInitiator, const CVector* pvecStart typedef bool(DamageHandler)(class CPed* pDamagePed, class CEventDamage* pEvent); typedef void(DeathHandler)(class CPed* pKilledPed, unsigned char ucDeathReason, unsigned char ucBodyPart); typedef bool(FireHandler)(class CEntitySAInterface* target, class CEntitySAInterface* creator); -typedef bool(ProjectileStopHandler)(class CEntity* owner, enum eWeaponType weaponType, class CVector* origin, float fForce, class CVector* target, - class CEntity* targetEntity); -typedef void(ProjectileHandler)(class CEntity* owner, class CProjectile* projectile, class CProjectileInfo* projectileInfo, enum eWeaponType weaponType, - class CVector* origin, float fForce, class CVector* target, class CEntity* targetEntity); typedef bool(BreakTowLinkHandler)(class CVehicle* towingVehicle); typedef bool(ProcessCamHandler)(class CCam* pCam); typedef void(DrawRadarAreasHandler)(); @@ -226,8 +222,6 @@ class CMultiplayer virtual void SetFireHandler(FireHandler* pFireHandler) = 0; virtual void SetProcessCamHandler(ProcessCamHandler* pProcessCamHandler) = 0; virtual void SetChokingHandler(ChokingHandler* pChokingHandler) = 0; - virtual void SetProjectileHandler(ProjectileHandler* pProjectileHandler) = 0; - virtual void SetProjectileStopHandler(ProjectileStopHandler* pProjectileHandler) = 0; virtual void SetPreWorldProcessHandler(PreWorldProcessHandler* pHandler) = 0; virtual void SetPostWorldProcessHandler(PostWorldProcessHandler* pHandler) = 0; virtual void SetPostWorldProcessPedsAfterPreRenderHandler(PostWorldProcessPedsAfterPreRenderHandler* pHandler) = 0; diff --git a/Shared/sdk/CVector.h b/Shared/sdk/CVector.h index ad9805077e..781b7c8138 100644 --- a/Shared/sdk/CVector.h +++ b/Shared/sdk/CVector.h @@ -194,6 +194,17 @@ class CVector return false; } + static CVector Multiply3x3(const CVector& right, const CVector& forward, const CVector& up, const CVector& in) + { + CVector out; + + out.fX = right.fX * in.fX + forward.fX * in.fY + up.fX * in.fZ; + out.fY = right.fY * in.fX + forward.fY * in.fY + up.fY * in.fZ; + out.fZ = right.fZ * in.fX + forward.fZ * in.fY + up.fZ * in.fZ; + + return out; + } + bool IsValid() const { const float values[3] = {fX, fY, fZ}; diff --git a/Shared/sdk/enums/ExplosionType.h b/Shared/sdk/enums/ExplosionType.h new file mode 100644 index 0000000000..95fb3eb236 --- /dev/null +++ b/Shared/sdk/enums/ExplosionType.h @@ -0,0 +1,32 @@ +/***************************************************************************** + * + * PROJECT: Multi Theft Auto + * LICENSE: See LICENSE in the top level directory + * FILE: sdk/ExplosionType.h + * PURPOSE: Header for common definitions + * + * Multi Theft Auto is available from https://www.multitheftauto.com/ + * + *****************************************************************************/ + +#pragma once + +namespace ExplosionType +{ + enum Enum + { + EXPLOSION_GRENADE = 0, + EXPLOSION_MOLOTOV, + EXPLOSION_ROCKET, + EXPLOSION_WEAK_ROCKET, + EXPLOSION_CAR, + EXPLOSION_QUICK_CAR, + EXPLOSION_BOAT, + EXPLOSION_AIRCRAFT, + EXPLOSION_MINE, + EXPLOSION_OBJECT, + EXPLOSION_TANK_FIRE, + EXPLOSION_SMALL, + EXPLOSION_RC_VEHICLE + }; +}