- 68
- 4
- Версия SA-MP
-
- Любая
Пытаюсь создать CEF chatbubbles, но столкнулся с проблемой, что они не прикрепляются к персонажу, а пытаются его догнать. Если не двигать мышкой и не бегать, то все нормально. Но стоит сделать хоть какое-то движение, то chatbubbles начинает догонять голову персонажа. Возможно ли это как-то фиксануть?
Lua:
function sampev.onPlayerChatBubble(playerId, color, distance, duration, message)
if not query[playerId] then
query[playerId] = {}
end
local a, r, g, b = explode_argb(color)
local newMessage = {
message = u8:encode(message),
create_time = os.clock(),
duration = duration / 1000,
distance = distance,
r = r,
g = g,
b = b,
a = a
}
table.insert(query[playerId], 1, newMessage)
while #query[playerId] > 1 do
table.remove(query[playerId])
end
lua_thread.create(function ()
while true do
wait(0)
if not browser then return end
local current_time = os.clock()
local bubblesData = {}
for playerId, messages in pairs(query) do
local res, handle = sampGetCharHandleBySampPlayerId(tonumber(playerId))
if handle and doesCharExist(handle) and wallPlayer(handle, messages[1] and messages[1].distance or 50) then
local x, y, z = getNameTagPosForText(handle)
local screenX, screenY = convert3DCoordsToScreen(x, y, z)
local playerBubbles = {}
for i, data in ipairs(messages) do
local elapsed = current_time - data.create_time
if elapsed > data.duration then
table.remove(messages, i)
else
table.insert(playerBubbles, {
text = data.message,
r = data.r,
g = data.g,
b = data.b,
alpha = 1.0,
index = i - 1
})
end
end
if #playerBubbles > 0 then
table.insert(bubblesData, {
playerId = playerId,
x = screenX,
y = screenY,
messages = playerBubbles
})
end
end
end
if browser then
local jsonData = encodeJson(bubblesData)
browser:execute_js(string.format("updateBubbles(%s)", jsonData))
end
end
end)
return false
end
local getBonePosition = ffi.cast("int (__thiscall*)(void*, float*, int, bool)", 0x5E4280)
function getBodyPartCoordinates(id, handle)
local pedptr = getCharPointer(handle)
local vec = ffi.new("float[3]")
getBonePosition(ffi.cast("void*", pedptr), vec, id, true)
return vec[0], vec[1], vec[2]
end
function getRealCameraCoordinates()
local CCamera = ffi.cast("float*", 0xB6F028)
return CCamera[0x20F], CCamera[0x210], CCamera[0x211]
end
function getNameTagPosForText(handle)
local localPlayerPos = vector3d(getRealCameraCoordinates())
local pPlayerPos = vector3d(getBodyPartCoordinates(5, handle))
return pPlayerPos.x, pPlayerPos.y,
pPlayerPos.z + 0.37 +
(getDistanceBetweenCoords3d(localPlayerPos.x, localPlayerPos.y, localPlayerPos.z,
pPlayerPos.x, pPlayerPos.y, pPlayerPos.z) * 0.025)
end
function wallPlayer(handle, distance)
if doesCharExist(handle) then
local camX, camY, camZ = getRealCameraCoordinates()
local x, y, z = getCharCoordinates(handle)
local maxDistance = false and distance or 50
local withinDistance = getDistanceBetweenCoords3d(camX, camY, camZ, x, y, z) <= maxDistance
if not (withinDistance and isCharOnScreen(handle)) then
return false
end
return false or isLineOfSightClear(camX, camY, camZ, x, y, z, true, false, false, true, false)
end
end
JavaScript:
const bubblesContainer = document.getElementById('bubbles-container');
const activeBubbles = new Map();
function getMessageDiv(wrapper, index) {
let el = wrapper.children[index];
if (!el) {
el = document.createElement('div');
el.className = 'bubble-message';
wrapper.appendChild(el);
}
return el;
}
function updateBubbles(bubblesData) {
const currentIds = new Set();
bubblesData.forEach(playerData => {
const key = `player_${playerData.playerId}`;
currentIds.add(key);
let wrapper = activeBubbles.get(key);
if (!wrapper) {
wrapper = document.createElement('div');
wrapper.className = 'bubble-wrapper';
bubblesContainer.appendChild(wrapper);
activeBubbles.set(key, wrapper);
}
wrapper.style.left = playerData.x + 'px';
wrapper.style.top = playerData.y + 'px';
const count = playerData.messages.length;
wrapper.dataset.count = count;
for (let i = 0; i < count; i++) {
const msg = playerData.messages[i];
const messageDiv = getMessageDiv(wrapper, i);
messageDiv.style.display = 'block';
messageDiv.style.color = `rgb(${msg.r}, ${msg.g}, ${msg.b})`;
messageDiv.style.opacity = msg.alpha;
messageDiv.innerHTML = msg.text;
messageDiv.style.marginBottom = (i < count - 1) ? '8px' : '0';
messageDiv.style.textShadow = `-1px -1px 0 #000`;
}
const children = wrapper.children;
for (let i = count; i < children.length; i++) {
children[i].style.display = 'none';
}
});
activeBubbles.forEach((wrapper, key) => {
if (!currentIds.has(key)) {
wrapper.remove();
activeBubbles.delete(key);
}
});
}