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
7 changes: 7 additions & 0 deletions Client/mods/deathmatch/logic/CClientEntity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1334,6 +1334,13 @@ bool CClientEntity::IsOnScreen()
{
return pEntity->IsOnScreen();
}

if (GetType() == CCLIENTMARKER)
{
CClientMarker* marker = static_cast<CClientMarker*>(this);
return marker->IsClientSideOnScreen();
}

return false;
}

Expand Down
1 change: 1 addition & 0 deletions Client/mods/deathmatch/logic/CClientEntity.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ enum eClientEntityType

class CEntity;
class CClientColShape;
class CClientMarker;
class CClientPed;
class CCustomData;
class CElementGroup;
Expand Down
95 changes: 85 additions & 10 deletions Client/mods/deathmatch/logic/CClientGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ CVector g_vecBulletFireEndPosition;
#define DOUBLECLICK_TIMEOUT 330
#define DOUBLECLICK_MOVE_THRESHOLD 10.0f

// Ray casting constants for click detection
constexpr float CLICK_RAY_DEPTH = 300.0f; // Screen-to-world ray projection depth
constexpr float MAX_CLICK_DISTANCE = 6000.0f; // Maximum distance for closest marker comparison

static constexpr long long TIME_DISCORD_UPDATE_RATE = 15000;

CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo())
Expand Down Expand Up @@ -2325,6 +2329,58 @@ void CClientGame::ProcessServerControlBind(CControlFunctionBind* pBind)
m_pNetAPI->RPC(KEY_BIND, bitStream.pBitStream);
}

CClientMarker* CClientGame::CheckMarkerClick(float screenX, float screenY, float& distance) noexcept
{
if (!m_pMarkerManager)
return nullptr;

CCamera* camera = g_pGame->GetCamera();
CMatrix cameraMatrix;
camera->GetMatrix(&cameraMatrix);
CVector origin = cameraMatrix.vPos;

CVector target;
CVector screen(screenX, screenY, CLICK_RAY_DEPTH);
g_pCore->GetGraphics()->CalcWorldCoors(&screen, &target);

CVector rayDirection = target - origin;
rayDirection.Normalize();

CClientMarker* closestMarker = nullptr;
float closestDistance = MAX_CLICK_DISTANCE;

for (auto* marker : m_pMarkerManager->m_Markers)
{
if (!marker || !marker->IsStreamedIn() || !marker->IsVisible())
continue;

if (!marker->IsClientSideOnScreen())
continue;

CSphere boundingSphere = marker->GetWorldBoundingSphere();

CVector toSphere = boundingSphere.vecPosition - origin;
float projection = toSphere.DotProduct(&rayDirection);

if (projection <= 0.0f)
continue;

CVector closestPoint = origin + rayDirection * projection;
float distanceToRay = (boundingSphere.vecPosition - closestPoint).Length();

if (distanceToRay <= boundingSphere.fRadius && projection < closestDistance)
{
closestDistance = projection;
closestMarker = marker;
}
}

if (closestMarker)
distance = closestDistance;

return closestMarker;
}

bool CClientGame::ProcessMessageForCursorEvents(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
bool bCursorForcedVisible = g_pCore->IsCursorForcedVisible();
Expand Down Expand Up @@ -2374,7 +2430,7 @@ bool CClientGame::ProcessMessageForCursorEvents(HWND hwnd, UINT uMsg, WPARAM wPa

CVector2D vecCursorPosition((float)iX, (float)iY);

CVector vecOrigin, vecTarget, vecScreen((float)iX, (float)iY, 300.0f);
CVector vecOrigin, vecTarget, vecScreen((float)iX, (float)iY, CLICK_RAY_DEPTH);
g_pCore->GetGraphics()->CalcWorldCoors(&vecScreen, &vecTarget);

// Grab the camera position
Expand All @@ -2392,7 +2448,8 @@ bool CClientGame::ProcessMessageForCursorEvents(HWND hwnd, UINT uMsg, WPARAM wPa

CVector vecCollision;
ElementID CollisionEntityID = INVALID_ELEMENT_ID;
CClientEntity* pCollisionEntity = NULL;
CClientEntity* collisionEntity = nullptr;

if (bCollision && pColPoint)
{
vecCollision = pColPoint->GetPosition();
Expand All @@ -2402,7 +2459,7 @@ bool CClientGame::ProcessMessageForCursorEvents(HWND hwnd, UINT uMsg, WPARAM wPa
CClientEntity* pEntity = pPools->GetClientEntity((DWORD*)pGameEntity->GetInterface());
if (pEntity)
{
pCollisionEntity = pEntity;
collisionEntity = pEntity;
if (!pEntity->IsLocalEntity())
CollisionEntityID = pEntity->GetID();
}
Expand All @@ -2415,9 +2472,7 @@ bool CClientGame::ProcessMessageForCursorEvents(HWND hwnd, UINT uMsg, WPARAM wPa

// Destroy the colpoint so we don't get a leak
if (pColPoint)
{
pColPoint->Destroy();
}

const char* szButton = NULL;
const char* szState = NULL;
Expand Down Expand Up @@ -2457,6 +2512,25 @@ bool CClientGame::ProcessMessageForCursorEvents(HWND hwnd, UINT uMsg, WPARAM wPa
if (std::isnan(vecCollision.fZ))
vecCollision.fZ = 0;

float markerDistance = 0.0f;
CClientMarker* clickedMarker = CheckMarkerClick(static_cast<float>(iX), static_cast<float>(iY), markerDistance);
if (clickedMarker)
{
CVector markerPosition;
clickedMarker->GetPosition(markerPosition);

CLuaArguments MarkerArguments;
MarkerArguments.PushString(szButton);
MarkerArguments.PushString(szState);
MarkerArguments.PushNumber(vecCursorPosition.fX);
MarkerArguments.PushNumber(vecCursorPosition.fY);
MarkerArguments.PushNumber(markerPosition.fX);
MarkerArguments.PushNumber(markerPosition.fY);
MarkerArguments.PushNumber(markerPosition.fZ);
MarkerArguments.PushNumber(markerDistance);
clickedMarker->CallEvent("onClientMarkerClick", MarkerArguments, false);
}

// Call the event for the client
CLuaArguments Arguments;
Arguments.PushString(szButton);
Expand All @@ -2466,8 +2540,8 @@ bool CClientGame::ProcessMessageForCursorEvents(HWND hwnd, UINT uMsg, WPARAM wPa
Arguments.PushNumber(vecCollision.fX);
Arguments.PushNumber(vecCollision.fY);
Arguments.PushNumber(vecCollision.fZ);
if (pCollisionEntity)
Arguments.PushElement(pCollisionEntity);
if (collisionEntity)
Arguments.PushElement(collisionEntity);
else
Arguments.PushBoolean(false);
m_pRootEntity->CallEvent("onClientClick", Arguments, false);
Expand Down Expand Up @@ -2510,8 +2584,8 @@ bool CClientGame::ProcessMessageForCursorEvents(HWND hwnd, UINT uMsg, WPARAM wPa
DoubleClickArguments.PushNumber(vecCollision.fX);
DoubleClickArguments.PushNumber(vecCollision.fY);
DoubleClickArguments.PushNumber(vecCollision.fZ);
if (pCollisionEntity)
DoubleClickArguments.PushElement(pCollisionEntity);
if (collisionEntity)
DoubleClickArguments.PushElement(collisionEntity);
else
DoubleClickArguments.PushBoolean(false);
m_pRootEntity->CallEvent("onClientDoubleClick", DoubleClickArguments, false);
Expand Down Expand Up @@ -2540,7 +2614,7 @@ bool CClientGame::ProcessMessageForCursorEvents(HWND hwnd, UINT uMsg, WPARAM wPa
CVector2D vecResolution = g_pCore->GetGUI()->GetResolution();
CVector2D vecCursorPosition(((float)iX) / vecResolution.fX, ((float)iY) / vecResolution.fY);

CVector vecTarget, vecScreen((float)iX, (float)iY, 300.0f);
CVector vecTarget, vecScreen((float)iX, (float)iY, CLICK_RAY_DEPTH);
g_pCore->GetGraphics()->CalcWorldCoors(&vecScreen, &vecTarget);

// Call the onClientCursorMove event
Expand Down Expand Up @@ -2741,6 +2815,7 @@ void CClientGame::AddBuiltInEvents()
// Marker events
m_Events.AddEvent("onClientMarkerHit", "entity, matchingDimension", nullptr, false);
m_Events.AddEvent("onClientMarkerLeave", "entity, matchingDimension", nullptr, false);
m_Events.AddEvent("onClientMarkerClick", "button, state, screenX, screenY, worldX, worldY, worldZ, distance", nullptr, false);

m_Events.AddEvent("onClientPlayerMarkerHit", "marker, matchingDimension", nullptr, false);
m_Events.AddEvent("onClientPlayerMarkerLeave", "marker, matchingDimension", nullptr, false);
Expand Down
1 change: 1 addition & 0 deletions Client/mods/deathmatch/logic/CClientGame.h
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ class CClientGame
void ProcessServerControlBind(CControlFunctionBind* pBind);

bool ProcessMessageForCursorEvents(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
CClientMarker* CheckMarkerClick(float screenX, float screenY, float& distance) noexcept;
bool AreCursorEventsEnabled() { return m_bCursorEventsEnabled; }
void SetCursorEventsEnabled(bool bCursorEventsEnabled) { m_bCursorEventsEnabled = bCursorEventsEnabled; }

Expand Down
40 changes: 39 additions & 1 deletion Client/mods/deathmatch/logic/CClientMarker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ extern CClientGame* g_pClientGame;
#define M_PI 3.14159265358979323846
#endif

// Threshold for determining if a marker is considered on-screen.
// The value 0.1f represents the minimum depth (Z value in screen coordinates) at which a marker is visible.
// Markers with a screen Z value below this threshold are considered off-screen.
constexpr float CLIENT_MARKER_ONSCREEN_THRESHOLD = 0.1f;

unsigned int CClientMarker::m_uiStreamedInMarkers = 0;

CClientMarker::CClientMarker(CClientManager* pManager, ElementID ID, int iMarkerType) : ClassInit(this), CClientStreamElement(pManager->GetMarkerStreamer(), ID)
Expand Down Expand Up @@ -322,7 +327,7 @@ void CClientMarker::SetSize(float fSize)
break;
}
}

m_pMarker->SetSize(fSize);
}

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

bool CClientMarker::IsClientSideOnScreen() noexcept
{
if (!IsStreamedIn() || !IsVisible())
return false;

CVector position;
GetPosition(position);

CVector screen;
g_pCore->GetGraphics()->CalcScreenCoors(&position, &screen);

if (screen.fZ <= CLIENT_MARKER_ONSCREEN_THRESHOLD)
return false;

float resWidth = static_cast<float>(g_pCore->GetGraphics()->GetViewportWidth());
float resHeight = static_cast<float>(g_pCore->GetGraphics()->GetViewportHeight());

CSphere boundingSphere = GetWorldBoundingSphere();
CVector edgePos = boundingSphere.vecPosition;
edgePos.fX += boundingSphere.fRadius;

CVector edgeScreen;
g_pCore->GetGraphics()->CalcScreenCoors(&edgePos, &edgeScreen);

if (edgeScreen.fZ <= CLIENT_MARKER_ONSCREEN_THRESHOLD)
return true;

float screenRadius = fabs(edgeScreen.fX - screen.fX);

return (screen.fX + screenRadius) >= 0.0f && (screen.fX - screenRadius) <= resWidth &&
(screen.fY + screenRadius) >= 0.0f && (screen.fY - screenRadius) <= resHeight;
}
2 changes: 2 additions & 0 deletions Client/mods/deathmatch/logic/CClientMarker.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ class CClientMarker final : public CClientStreamElement, private CClientColCallb

static bool IsLimitReached();

bool IsClientSideOnScreen() noexcept;

CClientColShape* GetColShape() { return m_pCollision; }

void Callback_OnCollision(CClientColShape& Shape, CClientEntity& Entity);
Expand Down