From 0de4f9332609fec28dd5eb3ec7d56d02b16f6383 Mon Sep 17 00:00:00 2001
From: John Magdy Lotfy Kamel <19735243+Zorono@users.noreply.github.com>
Date: Fri, 16 Jan 2026 16:38:49 +0000
Subject: [PATCH 01/11] Create samp_chatbubbles.lua
---
amx/client/samp_chatbubbles.lua | 100 ++++++++++++++++++++++++++++++++
1 file changed, 100 insertions(+)
create mode 100644 amx/client/samp_chatbubbles.lua
diff --git a/amx/client/samp_chatbubbles.lua b/amx/client/samp_chatbubbles.lua
new file mode 100644
index 0000000..6d67dee
--- /dev/null
+++ b/amx/client/samp_chatbubbles.lua
@@ -0,0 +1,100 @@
+local g_Players = {}
+local screenW, screenH = guiGetScreenSize()
+
+local function getPlayerData(player)
+ if not g_Players[player] then
+ g_Players[player] = { pvars = {} }
+ end
+ return g_Players[player]
+end
+
+addEvent("onChatBubbleRequested", true)
+addEventHandler("onChatBubbleRequested", root, function(player, text, colour, dist, exptime)
+ if not isElement(player) then return end
+
+ local data = getPlayerData(player)
+
+ local bubbleID = "chatbubble_" .. tostring(getElementID(player) or "temp")
+ data.pvars[bubbleID] = {
+ text = text,
+ color = colour,
+ expires = getTickCount() + (exptime or 5000),
+ zOffset = 0.7
+ drawDist = dist
+ }
+end)
+
+addEventHandler("onClientRender", root, function()
+ local text, colour, dist, exptime, zOffset = nil
+ for targetPlayer, data in pairs(g_Players) do
+ if isElement(targetPlayer) and isElementStreamedIn(targetPlayer) then
+ for key, bubble in pairs(data.pvars) do
+ if now > bubble.expires then
+ data.pvars[key] = nil
+ else
+ text = bubble.text
+ colour = bubble.colour
+ zOffset = bubble.zOffset
+ exptime = bubble.expires
+ dist = bubble.drawDist
+ end
+ end
+ end
+ end
+ local chatBubbleTimer = nil
+ local function destroyDisplay(serverDisplay, dspText, targetPlayer, forPlayer)
+ textDisplayRemoveObserver(serverDisplay, forPlayer)
+ textDisplayRemoveText(serverDisplay, dspText)
+ textDestroyDisplay(serverDisplay)
+ g_Players[getElemID(forPlayer)].pvars["_chtbbl" .. getElemID(targetPlayer)] = nil
+ g_Players[getElemID(forPlayer)].pvars["_chtbbl2" .. getElemID(targetPlayer)] = nil
+ if chatBubbleTimer and isTimer(chatBubbleTimer) then
+ killTimer(chatBubbleTimer)
+ end
+ end
+
+ chatBubbleTimer = setTimer(function()
+ for _, plr in ipairs(getElementsByType('player')) do
+ if not isElementStreamedIn(plr) then
+ goto continue
+ end
+ local cx, cy, cz = getCameraMatrix(plr)
+ local camPos = Vector3(cx, cy, cz)
+ local bx, by, bz = getPedBonePosition(player, 8)
+ local sx, sy = getScreenFromWorldPosition(bx, by, bz + zOffset)
+ local targetPos = Vector3(sx, sy, sz)
+
+ g_Players[getElemID(plr)].pvars = {}
+ local txtDisplay = g_Players[getElemID(plr)].pvars["_chtbbl" .. getElemID(player)]
+ local serverText = g_Players[getElemID(plr)].pvars["_chtbbl2" .. getElemID(player)]
+ if targetPos.x and targetPos.y then
+ local distance = (camPos - targetPos).length)
+ if distance <= dist and isLineOfSightClear(camPos.x, camPos.y, camPos.z, targetPos.x, targetPos.y, targetPos.z, true, true, true, true, true, false, false) then
+ local scale = math.max(0.6, (15 / distance * 1.0)
+ if not txtDisplay == nil then
+ txtDisplay = textCreateDisplay()
+ serverText = textCreateTextItem(text, 0.5, 0.5)
+ textItemSetColor(serverText, (colour >> 24) & 0xFF, (colour >> 16) & 0xFF, (colour >> 8) & 0xFF, colour & 0xFF)
+ textDisplayAddText(txtDisplay, serverText)
+ local normX = sx / screenW
+ local normY = sy / screenH
+ textItemSetPosition(serverText, normX, normY)
+
+ textItemSetScale(serverText, scale)
+ end
+
+ if not textDisplayIsObserver(txtDisplay, plr) then
+ textDisplayAddObserver(txtDisplay, plr)
+ end
+ end
+ else
+ if textDisplayIsObserver(txtDisplay) then
+ destroyDisplay(txtDisplay, serverText, plr)
+ end
+ end
+ setTimer(destroyDisplay, exptime, 1, txtDisplay, serverText, plr)
+
+ ::continue::
+ end
+ end, 500, 0)
+end)
From c0e7dc88c91f9b6ae1a9bc8220549759a8f71689 Mon Sep 17 00:00:00 2001
From: John Magdy Lotfy Kamel <19735243+Zorono@users.noreply.github.com>
Date: Fri, 16 Jan 2026 16:42:29 +0000
Subject: [PATCH 02/11] Update a_players.lua
---
amx/server/natives/a_players.lua | 72 ++++++++++++++++++--------------
1 file changed, 40 insertions(+), 32 deletions(-)
diff --git a/amx/server/natives/a_players.lua b/amx/server/natives/a_players.lua
index a03b7ec..abcae6c 100644
--- a/amx/server/natives/a_players.lua
+++ b/amx/server/natives/a_players.lua
@@ -21,9 +21,9 @@ function SetPlayerPosFindZ(amx, player, x, y, z)
end
function GetPlayerPos(amx, player, refX, refY, refZ)
- if not player then
- return false
- end
+ if not isElement(player) or getElementType(player) ~= "player" then
+ return false
+ end
local x, y, z
@@ -46,9 +46,9 @@ function SetPlayerFacingAngle(amx, player, angle)
end
function GetPlayerFacingAngle(amx, player, refAng)
- if not player then
- return false
- end
+ if not isElement(player) or getElementType(player) ~= "player" then
+ return false
+ end
local rX, rY, rZ = getElementRotation(player)
writeMemFloat(amx, refAng, rZ)
return true
@@ -115,9 +115,9 @@ function SetPlayerHealth(amx, player, health)
end
function GetPlayerHealth(amx, player, refHealth)
- if not player then
- return false
- end
+ if not isElement(player) or getElementType(player) ~= "player" then
+ return false
+ end
writeMemFloat(amx, refHealth, getElementHealth(player))
return true
end
@@ -127,9 +127,9 @@ function SetPlayerArmour(amx, player, armor)
end
function GetPlayerArmour(amx, player, refArmor)
- if not player then
- return false
- end
+ if not isElement(player) or getElementType(player) ~= "player" then
+ return false
+ end
writeMemFloat(amx, refArmor, getPedArmor(player))
return true
end
@@ -410,9 +410,9 @@ function SetPlayerVelocity(amx, player, vx, vy, vz)
end
function GetPlayerVelocity(amx, player, refVX, refVY, refVZ)
- if not player then
- return false
- end
+ if not isElement(player) or getElementType(player) ~= "player" then
+ return false
+ end
local vx, vy, vz = getElementVelocity(player)
writeMemFloat(amx, refVX, vx)
writeMemFloat(amx, refVY, vy)
@@ -824,14 +824,22 @@ function GetPVarType(amx, player, varname)
end
function SetPlayerChatBubble(amx, player, text, color, dist, exptime)
- notImplemented('SetPlayerChatBubble')
- return false
+ if not isElement(player) or getElementType(player) ~= "player" then
+ return false
+ end
+
+ if not text or string.len(text) == 0 then
+ return false
+ end
+
+ triggerClientEvent(root, "onChatBubbleRequested", player, player, text, color, dist, exptime)
+ return true
end
function PutPlayerInVehicle(amx, player, vehicle, seat)
- if not player then
- return false
- end
+ if not isElement(player) or getElementType(player) ~= "player" then
+ return false
+ end
warpPedIntoVehicle(player, vehicle, seat)
if g_RCVehicles[getElementModel(vehicle)] then
setPedWeaponSlot(player, 0)
@@ -1037,18 +1045,18 @@ function SetPlayerDisabledWeapons(amx, player, ...)
end
function SetPlayerCameraPos(amx, player, x, y, z)
- if not player then
- return false
- end
+ if not isElement(player) or getElementType(player) ~= "player" then
+ return false
+ end
fadeCamera(player, true)
setCameraMatrix(player, x, y, z)
return true
end
function SetPlayerCameraLookAt(amx, player, lx, ly, lz, cut)
- if not player then
- return false
- end
+ if not isElement(player) or getElementType(player) ~= "player" then
+ return false
+ end
local x, y, z = getCameraMatrix(player)
if not x then
return false
@@ -1065,9 +1073,9 @@ function SetCameraBehindPlayer(amx, player)
end
function GetPlayerCameraPos(amx, player, refX, refY, refZ)
- if not player then
- return false
- end
+ if not isElement(player) or getElementType(player) ~= "player" then
+ return false
+ end
local x, y, z = getCameraMatrix(player)
writeMemFloat(amx, refX, x)
writeMemFloat(amx, refY, y)
@@ -1076,9 +1084,9 @@ function GetPlayerCameraPos(amx, player, refX, refY, refZ)
end
function GetPlayerCameraFrontVector(amx, player, refX, refY, refZ)
- if not player then
- return false
- end
+ if not isElement(player) or getElementType(player) ~= "player" then
+ return false
+ end
local x, y, z, lx, ly, lz = getCameraMatrix(player)
local vx, vy, vz = 0.0, 0.0, 0.0
From d2f773ad8d924425e02685ab7d9e467765946add Mon Sep 17 00:00:00 2001
From: John Magdy Lotfy Kamel <19735243+Zorono@users.noreply.github.com>
Date: Fri, 16 Jan 2026 16:45:06 +0000
Subject: [PATCH 03/11] Update meta.xml
---
amx/meta.xml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/amx/meta.xml b/amx/meta.xml
index 0dfef0a..74566cc 100644
--- a/amx/meta.xml
+++ b/amx/meta.xml
@@ -32,6 +32,7 @@
+