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 @@