Skip to content
2 changes: 1 addition & 1 deletion Client/game_sa/C3DMarkersSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,6 @@ class C3DMarkersSA : public C3DMarkers

C3DMarker* CreateMarker(DWORD Identifier, T3DMarkerType dwType, CVector* vecPosition, float fSize, float fPulseFraction, BYTE r, BYTE g, BYTE b, BYTE a);
C3DMarker* FindFreeMarker();
C3DMarker* FindMarker(DWORD Identifier);
C3DMarker* FindMarker(DWORD Identifier) override;
void ReinitMarkers();
};
9 changes: 9 additions & 0 deletions Client/game_sa/CCameraSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -738,3 +738,12 @@ bool CCameraSA::GetTransitionMatrix(CMatrix& matrix) const

return true;
}

bool CCameraSA::IsSphereVisible(CVector* center, float radius) const
{
CCameraSAInterface* cameraInterface = GetInterface();
if (!cameraInterface)
return false;

return ((bool(__thiscall*)(CCameraSAInterface*, CVector*, float))0x420D40)(cameraInterface, center, radius);
}
1 change: 1 addition & 0 deletions Client/game_sa/CCameraSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,7 @@ class CCameraSA : public CCamera
bool IsInTransition() const override;
float GetTransitionFOV() const override;
bool GetTransitionMatrix(CMatrix& matrix) const override;
bool IsSphereVisible(CVector* center, float radius) const override;

// Additional overload not in base interface
virtual CCam* GetCam(CCamSAInterface* camInterface);
Expand Down
1 change: 1 addition & 0 deletions Client/game_sa/CRegisteredCoronaSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ class CRegisteredCoronaSA : public CRegisteredCorona
void SetFlareType(BYTE fFlareType);
void SetReflectionType(BYTE reflectionType);
DWORD GetIdentifier() { return internalInterface->Identifier; }
float GetNearClipDistance() override { return internalInterface->PullTowardsCam; }
DWORD GetID();
void Init(DWORD Identifier);
void Disable();
Expand Down
8 changes: 8 additions & 0 deletions Client/game_sa/CVisibilityPluginsSA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,11 @@ bool CVisibilityPluginsSA::InsertEntityIntoEntityList(void* entity, float distan
{
return ((bool(_cdecl*)(void*, float, void*))FUNC_CVisibilityPlugins_InsertEntityIntoEntityList)(entity, distance, callback);
}

bool CVisibilityPluginsSA::IsAtomicVisible(RpAtomic* atomic) const
{
if (!atomic)
return false;

return ((bool(__cdecl*)(RpAtomic*))0x732990)(atomic);
}
2 changes: 2 additions & 0 deletions Client/game_sa/CVisibilityPluginsSA.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,6 @@ class CVisibilityPluginsSA : public CVisibilityPlugins
int GetAtomicId(RwObject* pAtomic);

bool InsertEntityIntoEntityList(void* entity, float distance, void* callback);

bool IsAtomicVisible(RpAtomic* atomic) const override;
};
5 changes: 5 additions & 0 deletions Client/mods/deathmatch/logic/CClient3DMarker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ bool CClient3DMarker::IsHit(const CVector& vecPosition) const
return IsPointNearPoint3D(m_Matrix.vPos, vecPosition, m_fSize + 4);
}

RpAtomic* CClient3DMarker::GetAtomic() const
{
return m_pMarker ? reinterpret_cast<RpAtomic*>(m_pMarker->GetRwObject()) : nullptr;
}

void CClient3DMarker::StreamIn()
{
// We're now streamed in
Expand Down
2 changes: 2 additions & 0 deletions Client/mods/deathmatch/logic/CClient3DMarker.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@ class CClient3DMarker : public CClientMarkerCommon
void SetIgnoreAlphaLimits(bool ignore) noexcept { m_ignoreAlphaLimits = ignore; };
bool AreAlphaLimitsIgnored() const noexcept override { return m_ignoreAlphaLimits; };

class RpAtomic* GetAtomic() const override;

protected:
void StreamIn();
void StreamOut();
Expand Down
7 changes: 7 additions & 0 deletions Client/mods/deathmatch/logic/CClientCheckpoint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include <StdInc.h>
#include <game/CCheckpoint.h>
#include <game/CCheckpoints.h>
#include <game/C3DMarkers.h>

CClientCheckpoint::CClientCheckpoint(CClientMarker* pThis)
{
Expand Down Expand Up @@ -409,6 +410,12 @@ void CClientCheckpoint::SetTargetArrowProperties(const SColor& arrowColor, float
ApplyCheckpointTargetArrowProperties();
}

RpAtomic* CClientCheckpoint::GetAtomic() const
{
C3DMarker* marker = g_pGame->Get3DMarkers()->FindMarker(m_dwIdentifier);
return marker ? reinterpret_cast<RpAtomic*>(marker->GetRwObject()) : nullptr;
}

void CClientCheckpoint::ApplyCheckpointTargetArrowProperties() noexcept
{
if (m_pCheckpoint && m_uiIcon == CClientCheckpoint::ICON_ARROW)
Expand Down
2 changes: 2 additions & 0 deletions Client/mods/deathmatch/logic/CClientCheckpoint.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ class CClientCheckpoint : public CClientMarkerCommon
float GetTargetArrowSize() const noexcept { return m_TargetArrowSize; };
void SetTargetArrowProperties(const SColor& arrowColor, float size) noexcept;

class RpAtomic* GetAtomic() const override;

protected:
bool IsStreamedIn() { return m_bStreamedIn; };
void StreamIn();
Expand Down
3 changes: 3 additions & 0 deletions Client/mods/deathmatch/logic/CClientCorona.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class CClientCorona : public CClientMarkerCommon
void SetIgnoreAlphaLimits(bool ignore) noexcept {};
bool AreAlphaLimitsIgnored() const noexcept override { return true; };

unsigned long GetIdentifier() const noexcept { return m_ulIdentifier; }
class RpAtomic* GetAtomic() const override { return nullptr; }

protected:
bool IsStreamedIn() { return m_bStreamedIn; };
void StreamIn();
Expand Down
63 changes: 63 additions & 0 deletions Client/mods/deathmatch/logic/CClientMarker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
*****************************************************************************/

#include "StdInc.h"
#include "game/CVisibilityPlugins.h"
#include "game/CCoronas.h"
#include "game/CRegisteredCorona.h"

extern CClientGame* g_pClientGame;

Expand Down Expand Up @@ -540,3 +543,63 @@ void CClientMarker::SetIgnoreAlphaLimits(bool ignore)
{
m_pMarker->SetIgnoreAlphaLimits(ignore);
}

bool CClientMarker::IsOnScreen() const
{
if (!m_pMarker)
return false;

bool isCorona = GetMarkerType() == MARKER_CORONA;

// Check if marker's atomic is visible on the screen
// Always false for corona (corona is just a texture)
bool isVisible = g_pGame->GetVisibilityPlugins()->IsAtomicVisible(m_pMarker->GetAtomic());

// The above check for atomic visibility may return false at certain camera angles, even though the marker is partially visible on the screen
// This happens because the sphere of its atomic is no fully visible on the screen.
// So instead, we check whether the marker's position along with its size is visible on the screen.
if (!isVisible)
{
CVector pos;
GetPosition(pos);

// A radius of 2.0 is the size that is checked when rendering a 3D marker.
// If the camera is outside this area, large markers will disappear even though
// they are partially visible on the screen.
// See C3dMarkers::Render()
isVisible = g_pGame->GetCamera()->IsSphereVisible(&pos, isCorona ? GetSize() : 2.0f);
}

// The above camera check returns a false positive even when the corona is not visible on the screen,
// because the area with that radius is within the camera's range.
// To eliminate this false positive, we check whether the corona is actually visible on the screen.
// The point is that you can move away from the corona and completely lose sight of it,
// yet the area is still within the camera's range.

// For coronas, we need to check if they are within the camera's view because simply checking the screen coordinates
// is insufficient and often returns true even when the corona is not visible on the screen.
// (GTA renders coronas with some offset outside the screen area).
if (isVisible && isCorona)
{
CVector outPos;
float w;
float h;

CVector camPos;
m_pManager->GetCamera()->GetPosition(camPos);

CRegisteredCorona* corona = g_pGame->GetCoronas()->FindCorona(static_cast<CClientCorona*>(m_pMarker)->GetIdentifier());
if (!corona)
return false;

CVector diff = m_vecPosition - camPos;
diff.Normalize();

CVector inPos = m_vecPosition - diff * corona->GetNearClipDistance();

// Call CSprite::CalcScreenCoors
isVisible = ((bool(__cdecl*)(const CVector*, CVector*, float*, float*, bool, bool))0x70CE30)(&inPos, &outPos, &w, &h, true, true);
}

return isVisible;
}
2 changes: 2 additions & 0 deletions Client/mods/deathmatch/logic/CClientMarker.h
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ class CClientMarker final : public CClientStreamElement, private CClientColCallb
void SetIgnoreAlphaLimits(bool ignore);
bool AreAlphaLimitsIgnored() const noexcept { return m_pMarker->AreAlphaLimitsIgnored(); };

bool IsOnScreen() const;

protected:
void StreamIn(bool bInstantly);
void StreamOut();
Expand Down
2 changes: 2 additions & 0 deletions Client/mods/deathmatch/logic/CClientMarkerCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,6 @@ class CClientMarkerCommon

virtual void SetIgnoreAlphaLimits(bool ignore) noexcept = 0;
virtual bool AreAlphaLimitsIgnored() const noexcept = 0;

virtual class RpAtomic* GetAtomic() const = 0;
};
6 changes: 0 additions & 6 deletions Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -725,12 +725,6 @@ bool CStaticFunctionDefinitions::GetElementAlpha(CClientEntity& Entity, unsigned
return true;
}

bool CStaticFunctionDefinitions::IsElementOnScreen(CClientEntity& Entity, bool& bOnScreen)
{
bOnScreen = Entity.IsOnScreen();
return true;
}

bool CStaticFunctionDefinitions::GetElementHealth(CClientEntity& Entity, float& fHealth)
{
switch (Entity.GetType())
Expand Down
1 change: 0 additions & 1 deletion Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@ class CStaticFunctionDefinitions
static bool GetElementDistanceFromCentreOfMassToBaseOfModel(CClientEntity& Entity, float& fDistance);
static bool GetElementAttachedOffsets(CClientEntity& Entity, CVector& vecPosition, CVector& vecRotation);
static bool GetElementAlpha(CClientEntity& Entity, unsigned char& ucAlpha);
static bool IsElementOnScreen(CClientEntity& Entity, bool& bOnScreen);
static bool GetElementHealth(CClientEntity& Entity, float& fHealth);
static bool GetElementModel(CClientEntity& Entity, unsigned short& usModel);
static bool IsElementInWater(CClientEntity& Entity, bool& bInWater);
Expand Down
35 changes: 10 additions & 25 deletions Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "StdInc.h"
#include <lua/CLuaFunctionParser.h>
#include "CLuaElementDefs.h"
using std::list;

void CLuaElementDefs::LoadFunctions()
Expand Down Expand Up @@ -53,7 +54,7 @@ void CLuaElementDefs::LoadFunctions()
{"getElementAttachedOffsets", GetElementAttachedOffsets},
{"getElementAlpha", GetElementAlpha},
{"getElementLighting", ArgumentParser<GetElementLighting>},
{"isElementOnScreen", IsElementOnScreen},
{"isElementOnScreen", ArgumentParserWarn<nullptr, IsElementOnScreen>},
{"getElementHealth", GetElementHealth},
{"getElementModel", GetElementModel},
{"isElementStreamedIn", IsElementStreamedIn},
Expand Down Expand Up @@ -1363,6 +1364,14 @@ std::variant<bool, float> CLuaElementDefs::GetElementLighting(CClientEntity* ent
return false;
}

bool CLuaElementDefs::IsElementOnScreen(CClientEntity* entity)
{
if (entity->GetType() == CCLIENTMARKER)
return static_cast<CClientMarker*>(entity)->IsOnScreen();

return entity->IsOnScreen();
}

int CLuaElementDefs::GetElementHealth(lua_State* luaVM)
{
// Verify the argument
Expand Down Expand Up @@ -1640,30 +1649,6 @@ int CLuaElementDefs::IsElementStreamable(lua_State* luaVM)
return 1;
}

int CLuaElementDefs::IsElementOnScreen(lua_State* luaVM)
{
// Verify the argument
CClientEntity* pEntity = NULL;
CScriptArgReader argStream(luaVM);
argStream.ReadUserData(pEntity);

if (!argStream.HasErrors())
{
// Return whether we're on the screen or not
bool bOnScreen;
if (CStaticFunctionDefinitions::IsElementOnScreen(*pEntity, bOnScreen))
{
lua_pushboolean(luaVM, bOnScreen);
return 1;
}
}
else
m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage());

lua_pushnil(luaVM);
return 1;
}

int CLuaElementDefs::CreateElement(lua_State* luaVM)
{
// Verify the argument
Expand Down
2 changes: 1 addition & 1 deletion Client/mods/deathmatch/logic/luadefs/CLuaElementDefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class CLuaElementDefs : public CLuaDefs
LUA_DECLARE(GetElementAttachedOffsets);
LUA_DECLARE(GetElementAlpha);
static std::variant<bool, float> GetElementLighting(CClientEntity* entity);
LUA_DECLARE(IsElementOnScreen);
static bool IsElementOnScreen(CClientEntity* entity);
LUA_DECLARE(GetElementHealth);
LUA_DECLARE(IsElementStreamedIn);
LUA_DECLARE(IsElementStreamable);
Expand Down
1 change: 1 addition & 0 deletions Client/sdk/game/C3DMarkers.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,6 @@ class C3DMarkers
virtual C3DMarker* CreateMarker(DWORD Identifier, T3DMarkerType dwType, CVector* vecPosition, float fSize, float fPulseFraction, BYTE r, BYTE g, BYTE b,
BYTE a) = 0;
virtual C3DMarker* FindFreeMarker() = 0;
virtual C3DMarker* FindMarker(DWORD Identifier) = 0;
virtual void ReinitMarkers() = 0;
};
2 changes: 2 additions & 0 deletions Client/sdk/game/CCamera.h
Original file line number Diff line number Diff line change
Expand Up @@ -154,4 +154,6 @@ class CCamera
virtual bool IsInTransition() const = 0;
virtual float GetTransitionFOV() const = 0;
virtual bool GetTransitionMatrix(CMatrix& matrix) const = 0;

virtual bool IsSphereVisible(CVector* center, float radius) const = 0;
};
1 change: 1 addition & 0 deletions Client/sdk/game/CRegisteredCorona.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,5 @@ class CRegisteredCorona
virtual DWORD GetID() = 0;
virtual void Refresh() = 0;
virtual void Disable() = 0;
virtual float GetNearClipDistance() = 0;
};
3 changes: 3 additions & 0 deletions Client/sdk/game/CVisibilityPlugins.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#define ATOMIC_ID_FLAG_TWO_VERSIONS_DAMAGED 2

struct RpClump;
struct RpAtomic;
struct RwObject;

class CVisibilityPlugins
Expand All @@ -24,4 +25,6 @@ class CVisibilityPlugins
virtual int GetAtomicId(RwObject* pAtomic) = 0;

virtual bool InsertEntityIntoEntityList(void* entity, float distance, void* callback) = 0;

virtual bool IsAtomicVisible(RpAtomic* atomic) const = 0;
};
Loading