Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Client/mods/deathmatch/StdInc.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@
#include <CClientIMG.h>
#include <CClientIFP.h>
#include <CClientWater.h>
#include <CClientWaterCannon.h>
#include <CClientWeapon.h>
#include <CClientRenderElement.h>
#include <CClientDxFont.h>
Expand Down Expand Up @@ -143,6 +144,7 @@
#include <luadefs/CLuaTeamDefs.h>
#include <luadefs/CLuaTimerDefs.h>
#include <luadefs/CLuaVehicleDefs.h>
#include <luadefs/CLuaWaterCannonDefs.h>
#include <luadefs/CLuaWaterDefs.h>
#include <luadefs/CLuaWeaponDefs.h>
#include <luadefs/CLuaWorldDefs.h>
Expand Down
2 changes: 2 additions & 0 deletions Client/mods/deathmatch/logic/CClientEntity.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ enum eClientEntityType
CCLIENTUNKNOWN,
CCLIENTIMG,
CCLIENTBUILDING,
CCLIENTWATERCANNON,
};

class CEntity;
Expand Down Expand Up @@ -146,6 +147,7 @@ enum eCClientEntityClassTypes
CLASS_CClientSearchLight,
CLASS_CClientIMG,
CLASS_CClientBuilding,
CLASS_CClientWaterCannon,
};

class CClientEntity : public CClientEntityBase
Expand Down
33 changes: 30 additions & 3 deletions Client/mods/deathmatch/logic/CClientGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3714,9 +3714,9 @@ bool CClientGame::StaticObjectBreakHandler(CObjectSAInterface* pObjectInterface,
return g_pClientGame->ObjectBreakHandler(pObjectInterface, pAttackerInterface);
}

bool CClientGame::StaticWaterCannonHandler(CVehicleSAInterface* pCannonVehicle, CPedSAInterface* pHitPed)
bool CClientGame::StaticWaterCannonHandler(CVehicleSAInterface* pCannonVehicle, CPedSAInterface* pHitPed, void* pWaterCannonInterface)
{
return g_pClientGame->WaterCannonHitHandler(pCannonVehicle, pHitPed);
return g_pClientGame->WaterCannonHitHandler(pCannonVehicle, pHitPed, pWaterCannonInterface);
}

bool CClientGame::StaticVehicleFellThroughMapHandler(CVehicleSAInterface* pVehicle)
Expand Down Expand Up @@ -4938,8 +4938,35 @@ bool CClientGame::ObjectBreakHandler(CObjectSAInterface* pObjectInterface, CEnti
return true;
}

bool CClientGame::WaterCannonHitHandler(CVehicleSAInterface* pCannonVehicle, CPedSAInterface* pHitPed)
bool CClientGame::WaterCannonHitHandler(CVehicleSAInterface* pCannonVehicle, CPedSAInterface* pHitPed, void* pWaterCannonInterface)
{
// A script-owned standalone cannon (createWaterCannon) has no vehicle to fire
// onClientPedHitByWaterCannon/onClientPlayerHitByWaterCannon against; fire the same two events
// from the cannon element itself instead, so scripts get exactly the same notification (and the
// same chance to cancel the native knockdown) a real vehicle cannon gives them
if (!pCannonVehicle)
{
CClientWaterCannon* pWaterCannon = m_pManager->GetWaterCannonManager()->GetByNativeHandle(pWaterCannonInterface);
if (!pWaterCannon || !pWaterCannon->IsKnockdownEnabled())
return false;

CPools* pPools = g_pGame->GetPools();
CClientPed* pClientPed = nullptr;
SClientEntity<CPedSA>* pPedEntity = pPools->GetPed(reinterpret_cast<DWORD*>(pHitPed));
if (pPedEntity)
pClientPed = reinterpret_cast<CClientPed*>(pPedEntity->pClientEntity);

CLuaArguments Arguments;
if (pClientPed)
Arguments.PushElement(pClientPed);
else
Arguments.PushNil();

if (pClientPed && !IS_PLAYER(pClientPed))
return pWaterCannon->CallEvent("onClientPedHitByWaterCannon", Arguments, true);
return pWaterCannon->CallEvent("onClientPlayerHitByWaterCannon", Arguments, true);
}

if (pCannonVehicle && pHitPed)
{
CPools* pPools = g_pGame->GetPools();
Expand Down
4 changes: 2 additions & 2 deletions Client/mods/deathmatch/logic/CClientGame.h
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ class CClientGame
static bool StaticHeliKillHandler(CVehicleSAInterface* pHeli, CEntitySAInterface* pHitInterface);
static bool StaticObjectDamageHandler(CObjectSAInterface* pObjectInterface, float fLoss, CEntitySAInterface* pAttackerInterface);
static bool StaticObjectBreakHandler(CObjectSAInterface* pObjectInterface, CEntitySAInterface* pAttackerInterface);
static bool StaticWaterCannonHandler(CVehicleSAInterface* pCannonVehicle, CPedSAInterface* pHitPed);
static bool StaticWaterCannonHandler(CVehicleSAInterface* pCannonVehicle, CPedSAInterface* pHitPed, void* pWaterCannonInterface);
static bool StaticVehicleFellThroughMapHandler(CVehicleSAInterface* pVehicle);
static void StaticGameObjectDestructHandler(CEntitySAInterface* pObject);
static void StaticGameVehicleDestructHandler(CEntitySAInterface* pVehicle);
Expand Down Expand Up @@ -648,7 +648,7 @@ class CClientGame
bool HeliKillHandler(CVehicleSAInterface* pHeli, CEntitySAInterface* pHitInterface);
bool ObjectDamageHandler(CObjectSAInterface* pObjectInterface, float fLoss, CEntitySAInterface* pAttackerInterface);
bool ObjectBreakHandler(CObjectSAInterface* pObjectInterface, CEntitySAInterface* pAttackerInterface);
bool WaterCannonHitHandler(CVehicleSAInterface* pCannonVehicle, CPedSAInterface* pHitPed);
bool WaterCannonHitHandler(CVehicleSAInterface* pCannonVehicle, CPedSAInterface* pHitPed, void* pWaterCannonInterface);
bool VehicleFellThroughMapHandler(CVehicleSAInterface* pVehicle);
void GameObjectDestructHandler(CEntitySAInterface* pObject);
void GameVehicleDestructHandler(CEntitySAInterface* pVehicle);
Expand Down
5 changes: 5 additions & 0 deletions Client/mods/deathmatch/logic/CClientManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ CClientManager::CClientManager()
m_pPacketRecorder = new CClientPacketRecorder(this);
m_pImgManager = new CClientIMGManager(this);
m_pBuildingManager = new CClientBuildingManager(this);
m_pWaterCannonManager = new CClientWaterCannonManager(this);

m_bBeingDeleted = false;
m_bGameUnloadedFlag = false;
Expand Down Expand Up @@ -181,6 +182,9 @@ CClientManager::~CClientManager()

delete m_pBuildingManager;
m_pBuildingManager = nullptr;

delete m_pWaterCannonManager;
m_pWaterCannonManager = nullptr;
}

//
Expand Down Expand Up @@ -218,6 +222,7 @@ void CClientManager::DoPulse(bool bDoStandardPulses, bool bDoVehicleManagerPulse
m_pColManager->DoPulse();
m_pGUIManager->DoPulse();
m_pWeaponManager->DoPulse();
m_pWaterCannonManager->DoPulse();
}
else
{
Expand Down
3 changes: 3 additions & 0 deletions Client/mods/deathmatch/logic/CClientManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class CClientManager;
#include "CClientModelManager.h"
#include "CClientIMGManager.h"
#include "CClientBuildingManager.h"
#include "CClientWaterCannonManager.h"

class CClientProjectileManager;
class CClientExplosionManager;
Expand Down Expand Up @@ -96,6 +97,7 @@ class CClientManager
CClientWeaponManager* GetWeaponManager() { return m_pWeaponManager; }
CClientEffectManager* GetEffectManager() { return m_pEffectManager; }
CClientPointLightsManager* GetPointLightsManager() { return m_pPointLightsManager; }
CClientWaterCannonManager* GetWaterCannonManager() { return m_pWaterCannonManager; }
CClientIMGManager* GetIMGManager() { return m_pImgManager; }
CClientBuildingManager* GetBuildingManager() const noexcept { return m_pBuildingManager; }

Expand Down Expand Up @@ -150,6 +152,7 @@ class CClientManager
CClientWeaponManager* m_pWeaponManager;
CClientEffectManager* m_pEffectManager;
CClientPointLightsManager* m_pPointLightsManager;
CClientWaterCannonManager* m_pWaterCannonManager;
CClientModelManager* m_pModelManager;
CClientIMGManager* m_pImgManager;
CClientPacketRecorder* m_pPacketRecorder;
Expand Down
89 changes: 89 additions & 0 deletions Client/mods/deathmatch/logic/CClientWaterCannon.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*****************************************************************************
*
* PROJECT: Multi Theft Auto v1.0
* (Shared logic for modifications)
* LICENSE: See LICENSE in the top level directory
* FILE: mods/shared_logic/CClientWaterCannon.cpp
* PURPOSE: Water cannon entity class
*
*****************************************************************************/

#include <StdInc.h>

CClientWaterCannon::CClientWaterCannon(CClientManager* pManager, ElementID ID) : ClassInit(this), CClientEntity(ID)
{
m_pWaterCannonManager = pManager->GetWaterCannonManager();

m_vecDirection = CVector(0.0f, 1.0f, 0.0f);
m_fForce = 1.0f;
m_bEnabled = true;
m_bKnockdownEnabled = true;
m_bHasCustomColor = false;
m_Color = SColor(0xFFC8C8FF); // the native jet's light blue (R200 G200 B255), opaque
m_pNativeCannon = g_pMultiplayer->CreateCustomWaterCannon();

SetTypeName("water-cannon");

m_pWaterCannonManager->AddToList(this);
}

CClientWaterCannon::~CClientWaterCannon()
{
Unlink();
g_pMultiplayer->DestroyCustomWaterCannon(m_pNativeCannon);
}

void CClientWaterCannon::Unlink()
{
m_pWaterCannonManager->RemoveFromList(this);
}

void CClientWaterCannon::SetColor(const SColor& color)
{
m_Color = color;
m_bHasCustomColor = true;
if (m_pNativeCannon)
g_pMultiplayer->SetCustomWaterCannonColor(m_pNativeCannon, color.R, color.G, color.B, color.A);
}

void CClientWaterCannon::ResetColor()
{
m_bHasCustomColor = false;
m_Color = SColor(0xFFC8C8FF);
if (m_pNativeCannon)
g_pMultiplayer->ResetCustomWaterCannonColor(m_pNativeCannon);
}

void CClientWaterCannon::DoPulse()
{
// Update our position/rotation if we're attached, and let anything attached to us follow ours
DoAttaching();

if (!m_bEnabled || !m_pNativeCannon)
return;

// m_vecDirection is always normalized (see SetDirection), so this is purely "orientation
// times force", not the direction's own magnitude bleeding into the velocity.
const CVector vecVelocity = m_vecDirection * m_fForce;
g_pMultiplayer->UpdateCustomWaterCannon(m_pNativeCannon, m_vecPosition, vecVelocity);
}

// Same heading/pitch-from-direction convention the native water cannons use
// (CAutomobile::FireTruckControl/TankControl), so a model attached to us via attachElements
// aims consistently with how the game's own nozzles look. atan2f's second argument here is
// never negative, so pitch always comes back between -90 and 90 degrees for a normalized
// direction, which is exactly the range SetRotationRadians below expects back.
void CClientWaterCannon::GetRotationRadians(CVector& vecOutRadians) const
{
const float fHorizontalMagnitude = sqrtf(m_vecDirection.fX * m_vecDirection.fX + m_vecDirection.fY * m_vecDirection.fY);
const float fHeading = atan2f(-m_vecDirection.fX, m_vecDirection.fY);
const float fPitch = atan2f(m_vecDirection.fZ, fHorizontalMagnitude);
vecOutRadians = CVector(fPitch, 0.0f, fHeading);
}

void CClientWaterCannon::SetRotationRadians(const CVector& vecRadians)
{
const float fPitch = vecRadians.fX;
const float fHeading = vecRadians.fZ;
SetDirection(CVector(-sinf(fHeading) * cosf(fPitch), cosf(fHeading) * cosf(fPitch), sinf(fPitch)));
}
91 changes: 91 additions & 0 deletions Client/mods/deathmatch/logic/CClientWaterCannon.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/*****************************************************************************
*
* PROJECT: Multi Theft Auto v1.0
* (Shared logic for modifications)
* LICENSE: See LICENSE in the top level directory
* FILE: mods/shared_logic/CClientWaterCannon.h
* PURPOSE: Water cannon entity class header
*
*****************************************************************************/

#pragma once

#include "CClientEntity.h"

class CClientWaterCannonManager;

// A standalone water jet using the native CWaterCannons system (the same one the Firetruck and
// SWAT tank use), but not tied to any real vehicle. See CClientWaterCannon.cpp for how the native
// side treats the "owner" id as fully opaque, which is what makes that safe.
class CClientWaterCannon final : public CClientEntity
{
DECLARE_CLASS(CClientWaterCannon, CClientEntity)
friend class CClientWaterCannonManager;

public:
CClientWaterCannon(CClientManager* pManager, ElementID ID);
~CClientWaterCannon();

void Unlink();

eClientEntityType GetType() const { return CCLIENTWATERCANNON; };

void GetPosition(CVector& vecPosition) const { vecPosition = m_vecPosition; };
void SetPosition(const CVector& vecPosition) { m_vecPosition = vecPosition; };

// The generic element rotation, kept in sync with the direction below so that scripts can
// attach a model to us with the standard attachElements and have it aim correctly; see
// CClientWaterCannon.cpp for the angle convention shared with the direction accessors.
void GetRotationRadians(CVector& vecOutRadians) const;
void SetRotationRadians(const CVector& vecRadians);

// Always stored normalized, so it's a pure orientation; SetForce's value is the only thing
// that controls the velocity's actual magnitude.
const CVector& GetDirection() const { return m_vecDirection; };
void SetDirection(CVector vecDirection)
{
vecDirection.Normalize();
m_vecDirection = vecDirection;
};

float GetForce() const { return m_fForce; };
void SetForce(float fForce) { m_fForce = fForce; };

bool IsEnabled() const { return m_bEnabled; };
void SetEnabled(bool bEnabled) { m_bEnabled = bEnabled; };

// Whether getting sprayed by this cannon knocks a ped down, the same reaction a real vehicle-
// owned cannon gives; on by default to match that vanilla behaviour
bool IsKnockdownEnabled() const { return m_bKnockdownEnabled; };
void SetKnockdownEnabled(bool bEnabled) { m_bKnockdownEnabled = bEnabled; };

// A custom jet colour; when unset the native fading light-blue is used
bool HasCustomColor() const { return m_bHasCustomColor; };
const SColor& GetColor() const { return m_Color; };
void SetColor(const SColor& color);
void ResetColor();

// False if the shared custom-cannon pool (CMultiplayer::CreateCustomWaterCannon, capped
// separately from the vehicle-owned one) was full at construction time.
bool HasNativeCannon() const { return m_pNativeCannon != nullptr; };
void* GetNativeHandle() const { return m_pNativeCannon; };

protected:
void DoPulse();

private:
CClientWaterCannonManager* m_pWaterCannonManager;

CVector m_vecPosition;
CVector m_vecDirection;
float m_fForce;
bool m_bEnabled;
bool m_bKnockdownEnabled;

bool m_bHasCustomColor;
SColor m_Color;

// Opaque handle from CMultiplayer::CreateCustomWaterCannon; our own native CWaterCannon
// instance, kept apart from the vehicle-owned ones (see CClientWaterCannon.cpp).
void* m_pNativeCannon;
};
Loading
Loading