diff --git a/[editor]/editor_gui/client/browser/objects.xml b/[editor]/editor_gui/client/browser/objects.xml index 42385acf5..26e91f490 100644 --- a/[editor]/editor_gui/client/browser/objects.xml +++ b/[editor]/editor_gui/client/browser/objects.xml @@ -760,6 +760,7 @@ + diff --git a/[editor]/editor_main/client/colpatch/patches.col b/[editor]/editor_main/client/colpatch/patches.col index 0029a8534..4d11e0233 100644 Binary files a/[editor]/editor_main/client/colpatch/patches.col and b/[editor]/editor_main/client/colpatch/patches.col differ diff --git a/[editor]/editor_main/client/colpatch/placement.list b/[editor]/editor_main/client/colpatch/placement.list index bc3ee7e22..0047d49d0 100644 --- a/[editor]/editor_main/client/colpatch/placement.list +++ b/[editor]/editor_main/client/colpatch/placement.list @@ -5626,4 +5626,5 @@ 16284,0,511.97656,1077.24219,36.76563,0.00000,-0.00000,0.00000 16286,0,557.28125,1123.08594,26.72656,0.00000,-0.00000,0.00000 16498,0,829.40625,694.06250,10.67188,-0.00000,-0.00000,20.73494 -16291,0,671.07813,1119.28125,37.03125,0.00000,-0.00000,0.00000 \ No newline at end of file +16291,0,671.07813,1119.28125,37.03125,0.00000,-0.00000,0.00000 +5993,0,1110.89844,-1328.81250,13.85156,0.00000,0.00000,0.00000 \ No newline at end of file diff --git a/[gamemodes]/[play]/play/play_config.lua b/[gamemodes]/[play]/play/play_config.lua index 0530b42bd..818de52a8 100644 --- a/[gamemodes]/[play]/play/play_config.lua +++ b/[gamemodes]/[play]/play/play_config.lua @@ -1,13 +1,13 @@ playerSkins = { - 0, 1, 2, 7, 9, 10, 11, 12, 13, 14, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, - 43, 44, 45, 46, 47, 48, 49, 50, 51, + 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, - 61, 62, 63, 64, 66, 67, 68, 69, 70, + 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 75, 76, 77, 78, 79, 80, - 81, 82, 83, 84, 85, 87, 88, 89, 90, + 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, @@ -27,9 +27,9 @@ playerSkins = { 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, - 265, 266, 267, 268, 269, 270, 271, 272, 274, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, - 284, 285, 286, 287, 288, 290, 291, 292, 293, + 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312 @@ -100,4 +100,4 @@ vehicleSpawns = { {541, 2024.7091064453, 1499.3103027344, 10.520312309265, 270.00003051758}, {429, 2025.9349365234, 1490.2434082031, 10.5703125, 270.00003051758}, {429, 2025.6474609375, 1494.671875, 10.5703125, 270.00003051758} -} \ No newline at end of file +} diff --git a/[gameplay]/voice_local/client.lua b/[gameplay]/voice_local/client.lua index ea20fb9d7..f5f8dc3c7 100644 --- a/[gameplay]/voice_local/client.lua +++ b/[gameplay]/voice_local/client.lua @@ -1,155 +1,146 @@ -local fMinDistance = 0 -local fMaxDistance = 25 -local mathexp = math.exp +-- Remote events: +addEvent("voice_local:onClientPlayerVoiceStart", true) +addEvent("voice_local:onClientPlayerVoiceStop", true) +addEvent("voice_local:updateSettings", true) + +-- Only starts handling player voices after receiving the settings from the server +local initialWaiting = true + local streamedPlayers = {} -local fDistDiff = fMinDistance - fMaxDistance +local localPlayerTalking = false local sx, sy = guiGetScreenSize() -local nx, ny = sx / 1920, sy / 1080 -local width = 108 * nx -local height = 180 * ny -local xOffset = 75 * nx -local yOffset = 50 * ny -local halfWidth = width / 2 -local halfHeight = height / 2 +local devSX, devSY = sx / 1920, sy / 1080 +local iconWidth = 108 * devSX +local iconHalfWidth = iconWidth / 2 +local iconHeight = 180 * devSY +local iconHalfHeight = iconHeight / 2 +local iconTexture = dxCreateTexture("icon.png", "dxt5", true, "clamp") + +local function drawTalkingIcon(player, camDistToPlayer) + local boneX, boneY, boneZ = getPedBonePosition(player, 8) + local screenX, screenY = getScreenFromWorldPosition(boneX, boneY, boneZ + 0.4) + if screenX and screenY then + local factor = 1 / camDistToPlayer + dxDrawImage( + screenX - iconHalfWidth * factor, + screenY - iconHalfHeight * factor, + iconWidth * factor, + iconHeight * factor, + iconTexture, 0, 0, 0, -1, false + ) + end +end + +local function handlePreRender() + local debugY = 50 + local maxDistance = settings.maxVoiceDistance.value + local cameraX, cameraY, cameraZ = getCameraMatrix() + local localPlayerX, localPlayerY, localPlayerZ = getElementPosition(localPlayer) + for player, talking in pairs(streamedPlayers) do + local otherPlayerX, otherPlayerY, otherPlayerZ = getElementPosition(player) + local realDistanceToPlayer = getDistanceBetweenPoints3D(localPlayerX, localPlayerY, localPlayerZ, otherPlayerX, otherPlayerY, otherPlayerZ) + local playerVolume + if (realDistanceToPlayer >= maxDistance) then + playerVolume = 0.0 + else + playerVolume = (1.0 - (realDistanceToPlayer / maxDistance)^2) + end -local icon = dxCreateTexture("icon.png", "dxt5", true, "clamp") + -- Voice voume is usually unfortunately very low, resulting in players + -- barely hearing others if we set the player voice volume to 1.0 + -- So we need to increase it to like 6.0 to make it audible + playerVolume = playerVolume * settings.voiceSoundBoost.value -function preRender() - local x, y, z, lx, ly, lz = getCameraMatrix() + setSoundVolume(player, playerVolume) - for player, talking in pairs(streamedPlayers) do - if player and isElement(player) and getElementType(player) == "player" then - local x1, y1, z1 = getElementPosition(player) - local fDistance = getDistanceBetweenPoints3D(x, y, z, x1, y1, z1) - - local fVolume - if (fDistance <= fMinDistance) then - fVolume = 100 - elseif (fDistance >= fMaxDistance) then - fVolume = 0.0 - else - fVolume = mathexp(-(fDistance - fMinDistance) * (5.0 / fDistDiff)) * 100 - end - - if isLineOfSightClear(x, y, z, x1, y1, z1, false, false, false, false, true, true, true, localPlayer) then - setSoundVolume(player, fVolume) - setSoundEffectEnabled(player, "compressor", false) - else - setSoundVolume(player, fVolume * 2) - setSoundEffectEnabled(player, "compressor", true) - end - - if talking and isLineOfSightClear(x, y, z, x1, y1, z1, false, false, false, false, true, true, true, localPlayer) then - local boneX, boneY, boneZ = getPedBonePosition(player, 8) - local screenX, screenY = getScreenFromWorldPosition(boneX, boneY, boneZ + 0.5) - if screenX and screenY and fDistance < fMaxDistance then - fDistance = 1 / fDistance - dxDrawImage(screenX - halfWidth * fDistance, screenY - halfHeight * fDistance, width * fDistance, height * fDistance, icon, 0, 0, 0, -1, false) - end - end + if DEBUG_MODE then + dxDrawRectangle(20, debugY - 5, 300, 25, tocolor(0, 0, 0, 200)) + dxDrawText(("%s | Distance: %.2f | Voice Volume: %.2f"):format(getPlayerName(player), realDistanceToPlayer, playerVolume), 30, debugY) + debugY = debugY + 15 end - end -end -addEventHandler("onClientPreRender", root, preRender) -function onStart() - local x, y, z = getElementPosition(localPlayer) - for _, player in ipairs(getElementsWithinRange(x, y, z, 250, "player")) do - if streamedPlayers[player] == nil then - streamedPlayers[player] = false + if talking and (settings.showTalkingIcon.value == true) + and realDistanceToPlayer < maxDistance + and isLineOfSightClear(cameraX, cameraY, cameraZ, otherPlayerX, otherPlayerY, otherPlayerZ, false, false, false, false, true, true, true, localPlayer) then + drawTalkingIcon(player, getDistanceBetweenPoints3D(cameraX, cameraY, cameraZ, otherPlayerX, otherPlayerY, otherPlayerZ)) end end - triggerServerEvent("voice:setPlayerBroadcast", resourceRoot, localPlayer, streamedPlayers) + if localPlayerTalking and (settings.showTalkingIcon.value == true) then + drawTalkingIcon(localPlayer, getDistanceBetweenPoints3D(cameraX, cameraY, cameraZ, localPlayerX, localPlayerY, localPlayerZ)) + end end -addEventHandler("onClientResourceStart", resourceRoot, onStart, false) -function playerJoin() - if getElementType(source) == "player" then - if streamedPlayers[source] == nil then - streamedPlayers[source] = false - triggerServerEvent("voice:addToPlayerBroadcast", resourceRoot, localPlayer, source) +addEventHandler("onClientResourceStart", resourceRoot, function() + for _, player in pairs(getElementsByType("player", root, true)) do + if player ~= localPlayer and streamedPlayers[player] == nil then + setSoundVolume(player, 0) + streamedPlayers[player] = false end end -end -addEventHandler("onClientPlayerJoin", root, playerJoin) + triggerServerEvent("voice_local:setPlayerBroadcast", localPlayer, streamedPlayers) +end, false) -function playerQuit() +-- Handle remote/other player quit +addEventHandler("onClientPlayerQuit", root, function() if streamedPlayers[source] ~= nil then streamedPlayers[source] = nil - setSoundVolume(source, 0) - triggerServerEvent("voice:removePlayerBroadcast", resourceRoot, localPlayer, source) + triggerServerEvent("voice_local:removeFromPlayerBroadcast", localPlayer, source) end -end -addEventHandler("onClientPlayerQuit", root, playerQuit) +end) -- Code considers this event's problem @ "Note" box: https://wiki.multitheftauto.com/wiki/OnClientElementStreamIn -- It should be modified if said behavior ever changes -function streamIn() - if (isElement(source) and getElementType(source) == "player" and isPedDead(source) == false) then - if streamedPlayers[source] == nil then - streamedPlayers[source] = false - triggerServerEvent("voice:addToPlayerBroadcast", resourceRoot, localPlayer, source) - end - end -end -addEventHandler("onClientElementStreamIn", root, streamIn) - --- To ensure table integrity, stream out & local player death into 2 separate, safer events --- See the "Note" box at https://wiki.multitheftauto.com/wiki/OnClientElementStreamIn for reason why --- Stream out event -function streamOut() - if (source ~= localPlayer and isElement(source) and getElementType(source) == "player") then - if streamedPlayers[source] ~= nil then - streamedPlayers[source] = nil - setSoundVolume(source, 0) - triggerServerEvent("voice:removePlayerBroadcast", resourceRoot, localPlayer, source) - end - end -end -addEventHandler("onClientElementStreamOut", root, streamOut) - --- Local player death event -function onWasted() - if streamedPlayers[localPlayer] ~= nil then - streamedPlayers[localPlayer] = nil - setSoundVolume(localPlayer, 0) - triggerServerEvent("voice:removePlayerBroadcast", resourceRoot, localPlayer, localPlayer) - end -end -addEventHandler("onClientPlayerWasted", localPlayer, onWasted) +addEventHandler("onClientElementStreamIn", root, function() + if source == localPlayer then return end + if not (isElement(source) and getElementType(source) == "player") then return end -function resourceStop() - triggerServerEvent("voice:removePlayerBroadcasts", resourceRoot, localPlayer, streamedPlayers) - streamedPlayers = {} -end -addEventHandler("onClientResourceStop", resourceRoot, resourceStop, false) + if isPedDead(source) then return end -function voiceStart(source) - if (isElement(source) and getElementType(source) == "player") then - if streamedPlayers[source] ~= nil then - streamedPlayers[source] = true - end + if streamedPlayers[source] == nil then + setSoundVolume(source, 0) + streamedPlayers[source] = false + triggerServerEvent("voice_local:addToPlayerBroadcast", localPlayer, source) end -end -addEvent("voice_cl:onClientPlayerVoiceStart", true) -addEventHandler("voice_cl:onClientPlayerVoiceStart", resourceRoot, voiceStart) +end) +addEventHandler("onClientElementStreamOut", root, function() + if source == localPlayer then return end + if not (isElement(source) and getElementType(source) == "player") then return end -function voiceStop(source) - if (isElement(source) and getElementType(source) == "player") then - if streamedPlayers[source] ~= nil then - streamedPlayers[source] = false - end + if streamedPlayers[source] ~= nil then + setSoundVolume(source, 0) + streamedPlayers[source] = nil + triggerServerEvent("voice_local:removeFromPlayerBroadcast", localPlayer, source) end -end -addEvent("voice_cl:onClientPlayerVoiceStop", true) -addEventHandler("voice_cl:onClientPlayerVoiceStop", resourceRoot, voiceStop) +end) -function table.empty(a) - if type(a) ~= "table" then - return false +-- Update player talking status (for displaying) +addEventHandler("voice_local:onClientPlayerVoiceStart", root, function(player) + if not (isElement(player) and getElementType(player) == "player") then return end + + if player == localPlayer then + localPlayerTalking = true + elseif streamedPlayers[player] ~= nil then + streamedPlayers[player] = true + end +end) +addEventHandler("voice_local:onClientPlayerVoiceStop", root, function(player) + if not (isElement(player) and getElementType(player) == "player") then return end + + if player == localPlayer then + localPlayerTalking = false + elseif streamedPlayers[player] ~= nil then + streamedPlayers[player] = false end +end) - return next(a) == nil -end \ No newline at end of file +-- Load the settings received from the server +addEventHandler("voice_local:updateSettings", localPlayer, function(settingsFromServer) + settings = settingsFromServer + + if initialWaiting then + addEventHandler("onClientPreRender", root, handlePreRender, false) + initialWaiting = false + end +end, false) diff --git a/[gameplay]/voice_local/meta.xml b/[gameplay]/voice_local/meta.xml index 0b584185b..0a9a0b43c 100644 --- a/[gameplay]/voice_local/meta.xml +++ b/[gameplay]/voice_local/meta.xml @@ -1,8 +1,17 @@ - + + + + + + + + + +