Files
red-valley/resources/[framework]/[base]/[jobs]/17mov_JobCenter/client/utils.lua
Kotzu 7a1f7f24eb bubble text 3D
- Animatie tableta (prop_cs_tablet) la deschiderea Job Center NUI
- Bubble text 3D deasupra capului vizibil pt toti jucatorii (state bags)
- Job Center: icon 💼 | testbubble: icon 💬 + text custom
- Curatat locale ro.lua (~r~[E]~s~ incompatibil cu qb-target)
- Comenzi test: testanim, stopanim, testbubble [text], stopbubble
- Scos db/ din git tracking + .gitignore actualizat
- Regula 13 rulebook: commit info dupa fiecare modificare
- Fisiere: 17mov_JobCenter/client/utils.lua, locale/ro.lua, .gitignore, rulebook.md
2026-04-01 00:55:29 +03:00

188 lines
6.3 KiB
Lua

Config.TargetSystem = nil
if GetResourceState("qb-target") ~= "missing" then
Config.TargetSystem = "qb-target"
elseif GetResourceState("qtarget") ~= "missing" then
Config.TargetSystem = "qtarget"
elseif GetResourceState("ox_target") ~= "missing" then
Config.TargetSystem = "qtarget" -- OX_Target have a backward compability to qtarget
end
-- Comenzi utile pentru testare animatii + props
local testProp = nil
RegisterCommand("testanim", function()
local ped = PlayerPedId()
local dict = "amb@code_human_in_bus_passenger_idles@female@tablet@base"
RequestAnimDict(dict)
while not HasAnimDictLoaded(dict) do Wait(10) end
TaskPlayAnim(ped, dict, "base", 2.0, -2.0, -1, 49, 0, false, false, false)
local model = GetHashKey("prop_cs_tablet")
RequestModel(model)
while not HasModelLoaded(model) do Wait(10) end
local coords = GetEntityCoords(ped)
testProp = CreateObject(model, coords.x, coords.y, coords.z, true, true, true)
AttachEntityToEntity(testProp, ped, GetPedBoneIndex(ped, 60309),
0.03, 0.002, -0.0, 10.0, 160.0, 0.0, true, true, false, true, 1, true)
end, false)
RegisterCommand("stopanim", function()
ClearPedTasks(PlayerPedId())
if testProp and DoesEntityExist(testProp) then DeleteEntity(testProp) testProp = nil end
end, false)
RegisterCommand("testbubble", function(_, args)
local text = table.concat(args, " ")
if text == "" then text = "Test bubble..." end
LocalPlayer.state:set("bubbleText", text, true)
LocalPlayer.state:set("bubbleIcon", "💬", true)
LocalPlayer.state:set("browsingJobs", true, true)
end, false)
RegisterCommand("stopbubble", function()
LocalPlayer.state:set("browsingJobs", false, true)
end, false)
-- Animatie tableta pentru meniul Job Center
local tabletProp = nil
local animDict = "amb@code_human_in_bus_passenger_idles@female@tablet@base"
local animName = "base"
Utils.PlayTabletAnim = function()
local ped = PlayerPedId()
-- Incarca animatia
RequestAnimDict(animDict)
while not HasAnimDictLoaded(animDict) do
Wait(10)
end
-- Joaca animatia (flag 49 = loop + upper body + secondary)
TaskPlayAnim(ped, animDict, animName, 2.0, -2.0, -1, 49, 0, false, false, false)
-- Creeaza prop tableta
local model = GetHashKey("prop_cs_tablet")
RequestModel(model)
while not HasModelLoaded(model) do
Wait(10)
end
local coords = GetEntityCoords(ped)
tabletProp = CreateObject(model, coords.x, coords.y, coords.z, true, true, true)
-- Ataseaza la mana dreapta (bone 60309)
AttachEntityToEntity(tabletProp, ped, GetPedBoneIndex(ped, 60309),
0.03, 0.002, -0.0, 10.0, 160.0, 0.0, true, true, false, true, 1, true)
SetModelAsNoLongerNeeded(model)
RemoveAnimDict(animDict)
end
Utils.StopTabletAnim = function()
local ped = PlayerPedId()
-- Opreste animatia
ClearPedTasks(ped)
-- Sterge prop-ul
if tabletProp and DoesEntityExist(tabletProp) then
DeleteEntity(tabletProp)
tabletProp = nil
end
end
Utils.AddEntityToTarget = function(entity)
exports[Config.TargetSystem]:AddTargetEntity(entity, {
options = {
{
icon = "fas fa-briefcase",
label = _L("Interaction.Open"),
action = function()
Utils.PlayTabletAnim()
Utils.ToggleNUI(true)
end,
},
},
distance = 1.5
})
end
-- Opreste animatia cand se inchide meniul (safety net)
-- Monitorizeza NUI focus — cand se pierde, opreste animatia
-- Seteaza state bag pentru text bubble vizibil de toti jucatorii
CreateThread(function()
local wasOpen = false
while true do
Wait(500)
local nuiFocused = IsNuiFocused()
if wasOpen and not nuiFocused then
-- Meniul tocmai s-a inchis
Utils.StopTabletAnim()
LocalPlayer.state:set("browsingJobs", false, true)
wasOpen = false
elseif nuiFocused and tabletProp and DoesEntityExist(tabletProp) then
if not wasOpen then
LocalPlayer.state:set("bubbleText", "Se uita la locuri de munca...", true)
LocalPlayer.state:set("bubbleIcon", "💼", true)
LocalPlayer.state:set("browsingJobs", true, true)
end
wasOpen = true
end
end
end)
-- Deseneaza text bubble deasupra jucatorilor care cauta joburi
-- Vizibil pentru toti jucatorii din apropiere
CreateThread(function()
while true do
Wait(0)
local myPed = PlayerPedId()
local myCoords = GetEntityCoords(myPed)
for _, playerId in ipairs(GetActivePlayers()) do
local targetPed = GetPlayerPed(playerId)
local state = Player(GetPlayerServerId(playerId)).state
if state and state.browsingJobs then
local pedCoords = GetEntityCoords(targetPed)
local dist = #(myCoords - pedCoords)
if dist < 15.0 then
local headBone = GetPedBoneCoords(targetPed, 31086, 0.0, 0.0, 0.0) -- SKEL_Head
local bubbleText = state.bubbleText or "Se uita la locuri de munca..."
local bubbleIcon = state.bubbleIcon or ""
Utils.DrawJobBubble(headBone.x, headBone.y, headBone.z + 0.35, bubbleIcon .. " " .. bubbleText)
end
end
end
end
end)
-- Deseneaza text bubble stilizat deasupra capului
Utils.DrawJobBubble = function(x, y, z, text)
local onScreen, _x, _y = World3dToScreen2d(x, y, z)
if onScreen then
local camCoords = GetGameplayCamCoords()
local dist = #(vec3(x, y, z) - camCoords)
local scale = (1 / dist) * 2
local fov = (1 / GetGameplayCamFov()) * 100
scale = scale * fov
-- Limiteaza scala
if scale > 0.4 then scale = 0.4 end
if scale < 0.15 then scale = 0.15 end
-- Background box
SetTextScale(0.0, scale * 0.5)
SetTextFont(4)
SetTextProportional(true)
SetTextColour(255, 255, 255, 245)
SetTextDropshadow(1, 0, 0, 0, 200)
SetTextEdge(2, 0, 0, 0, 180)
SetTextDropShadow()
SetTextOutline()
SetTextEntry("STRING")
SetTextCentre(true)
AddTextComponentString(text)
DrawText(_x, _y)
end
end