1
- local fMinDistance = 0
2
- local fMaxDistance = 25
3
- local mathexp = math.exp
1
+ -- Remote events:
2
+ addEvent (" voice_local:onClientPlayerVoiceStart" , true )
3
+ addEvent (" voice_local:onClientPlayerVoiceStop" , true )
4
+ addEvent (" voice_local:updateSettings" , true )
5
+
6
+ -- Only starts handling player voices after receiving the settings from the server
7
+ local initialWaiting = true
8
+
4
9
local streamedPlayers = {}
5
- local fDistDiff = fMinDistance - fMaxDistance
10
+ local localPlayerTalking = false
6
11
7
12
local sx , sy = guiGetScreenSize ()
8
- local nx , ny = sx / 1920 , sy / 1080
9
13
10
- local width = 108 * nx
11
- local height = 180 * ny
12
- local xOffset = 75 * nx
13
- local yOffset = 50 * ny
14
- local halfWidth = width / 2
15
- local halfHeight = height / 2
14
+ local devSX , devSY = sx / 1920 , sy / 1080
15
+ local iconWidth = 108 * devSX
16
+ local iconHalfWidth = iconWidth / 2
17
+ local iconHeight = 180 * devSY
18
+ local iconHalfHeight = iconHeight / 2
19
+ local iconTexture = dxCreateTexture (" icon.png" , " dxt5" , true , " clamp" )
20
+
21
+ local function drawTalkingIcon (player , camDistToPlayer )
22
+ local boneX , boneY , boneZ = getPedBonePosition (player , 8 )
23
+ local screenX , screenY = getScreenFromWorldPosition (boneX , boneY , boneZ + 0.4 )
24
+ if screenX and screenY then
25
+ local factor = 1 / camDistToPlayer
26
+ dxDrawImage (
27
+ screenX - iconHalfWidth * factor ,
28
+ screenY - iconHalfHeight * factor ,
29
+ iconWidth * factor ,
30
+ iconHeight * factor ,
31
+ iconTexture , 0 , 0 , 0 , - 1 , false
32
+ )
33
+ end
34
+ end
35
+
36
+ local function handlePreRender ()
37
+ local debugY = 50
38
+ local maxDistance = settings .maxVoiceDistance .value
39
+ local cameraX , cameraY , cameraZ = getCameraMatrix ()
40
+ local localPlayerX , localPlayerY , localPlayerZ = getElementPosition (localPlayer )
41
+ for player , talking in pairs (streamedPlayers ) do
42
+ local otherPlayerX , otherPlayerY , otherPlayerZ = getElementPosition (player )
43
+ local realDistanceToPlayer = getDistanceBetweenPoints3D (localPlayerX , localPlayerY , localPlayerZ , otherPlayerX , otherPlayerY , otherPlayerZ )
44
+ local playerVolume
45
+ if (realDistanceToPlayer >= maxDistance ) then
46
+ playerVolume = 0.0
47
+ else
48
+ playerVolume = (1.0 - (realDistanceToPlayer / maxDistance )^ 2 )
49
+ end
16
50
17
- local icon = dxCreateTexture (" icon.png" , " dxt5" , true , " clamp" )
51
+ -- Voice voume is usually unfortunately very low, resulting in players
52
+ -- barely hearing others if we set the player voice volume to 1.0
53
+ -- So we need to increase it to like 6.0 to make it audible
54
+ playerVolume = playerVolume * settings .voiceSoundBoost .value
18
55
19
- function preRender ()
20
- local x , y , z , lx , ly , lz = getCameraMatrix ()
56
+ setSoundVolume (player , playerVolume )
21
57
22
- for player , talking in pairs (streamedPlayers ) do
23
- if player and isElement (player ) and getElementType (player ) == " player" then
24
- local x1 , y1 , z1 = getElementPosition (player )
25
- local fDistance = getDistanceBetweenPoints3D (x , y , z , x1 , y1 , z1 )
26
-
27
- local fVolume
28
- if (fDistance <= fMinDistance ) then
29
- fVolume = 100
30
- elseif (fDistance >= fMaxDistance ) then
31
- fVolume = 0.0
32
- else
33
- fVolume = mathexp (- (fDistance - fMinDistance ) * (5.0 / fDistDiff )) * 100
34
- end
35
-
36
- if isLineOfSightClear (x , y , z , x1 , y1 , z1 , false , false , false , false , true , true , true , localPlayer ) then
37
- setSoundVolume (player , fVolume )
38
- setSoundEffectEnabled (player , " compressor" , false )
39
- else
40
- setSoundVolume (player , fVolume * 2 )
41
- setSoundEffectEnabled (player , " compressor" , true )
42
- end
43
-
44
- if talking and isLineOfSightClear (x , y , z , x1 , y1 , z1 , false , false , false , false , true , true , true , localPlayer ) then
45
- local boneX , boneY , boneZ = getPedBonePosition (player , 8 )
46
- local screenX , screenY = getScreenFromWorldPosition (boneX , boneY , boneZ + 0.5 )
47
- if screenX and screenY and fDistance < fMaxDistance then
48
- fDistance = 1 / fDistance
49
- dxDrawImage (screenX - halfWidth * fDistance , screenY - halfHeight * fDistance , width * fDistance , height * fDistance , icon , 0 , 0 , 0 , - 1 , false )
50
- end
51
- end
58
+ if DEBUG_MODE then
59
+ dxDrawRectangle (20 , debugY - 5 , 300 , 25 , tocolor (0 , 0 , 0 , 200 ))
60
+ dxDrawText ((" %s | Distance: %.2f | Voice Volume: %.2f" ):format (getPlayerName (player ), realDistanceToPlayer , playerVolume ), 30 , debugY )
61
+ debugY = debugY + 15
52
62
end
53
- end
54
- end
55
- addEventHandler (" onClientPreRender" , root , preRender )
56
63
57
- function onStart ()
58
- local x , y , z = getElementPosition (localPlayer )
59
- for _ , player in ipairs (getElementsWithinRange (x , y , z , 250 , " player" )) do
60
- if streamedPlayers [player ] == nil then
61
- streamedPlayers [player ] = false
64
+ if talking and (settings .showTalkingIcon .value == true )
65
+ and realDistanceToPlayer < maxDistance
66
+ and isLineOfSightClear (cameraX , cameraY , cameraZ , otherPlayerX , otherPlayerY , otherPlayerZ , false , false , false , false , true , true , true , localPlayer ) then
67
+ drawTalkingIcon (player , getDistanceBetweenPoints3D (cameraX , cameraY , cameraZ , otherPlayerX , otherPlayerY , otherPlayerZ ))
62
68
end
63
69
end
64
- triggerServerEvent (" voice:setPlayerBroadcast" , resourceRoot , localPlayer , streamedPlayers )
70
+ if localPlayerTalking and (settings .showTalkingIcon .value == true ) then
71
+ drawTalkingIcon (localPlayer , getDistanceBetweenPoints3D (cameraX , cameraY , cameraZ , localPlayerX , localPlayerY , localPlayerZ ))
72
+ end
65
73
end
66
- addEventHandler (" onClientResourceStart" , resourceRoot , onStart , false )
67
74
68
- function playerJoin ()
69
- if getElementType ( source ) == " player" then
70
- if streamedPlayers [source ] == nil then
71
- streamedPlayers [ source ] = false
72
- triggerServerEvent ( " voice:addToPlayerBroadcast " , resourceRoot , localPlayer , source )
75
+ addEventHandler ( " onClientResourceStart " , resourceRoot , function ()
76
+ for _ , player in pairs ( getElementsByType ( " player" , root , true )) do
77
+ if player ~= localPlayer and streamedPlayers [player ] == nil then
78
+ setSoundVolume ( player , 0 )
79
+ streamedPlayers [ player ] = false
73
80
end
74
81
end
75
- end
76
- addEventHandler ( " onClientPlayerJoin " , root , playerJoin )
82
+ triggerServerEvent ( " voice_local:setPlayerBroadcast " , localPlayer , streamedPlayers )
83
+ end , false )
77
84
78
- function playerQuit ()
85
+ -- Handle remote/other player quit
86
+ addEventHandler (" onClientPlayerQuit" , root , function ()
79
87
if streamedPlayers [source ] ~= nil then
80
88
streamedPlayers [source ] = nil
81
- setSoundVolume (source , 0 )
82
- triggerServerEvent (" voice:removePlayerBroadcast" , resourceRoot , localPlayer , source )
89
+ triggerServerEvent (" voice_local:removeFromPlayerBroadcast" , localPlayer , source )
83
90
end
84
- end
85
- addEventHandler (" onClientPlayerQuit" , root , playerQuit )
91
+ end )
86
92
87
93
-- Code considers this event's problem @ "Note" box: https://wiki.multitheftauto.com/wiki/OnClientElementStreamIn
88
94
-- It should be modified if said behavior ever changes
89
- function streamIn ()
90
- if (isElement (source ) and getElementType (source ) == " player" and isPedDead (source ) == false ) then
91
- if streamedPlayers [source ] == nil then
92
- streamedPlayers [source ] = false
93
- triggerServerEvent (" voice:addToPlayerBroadcast" , resourceRoot , localPlayer , source )
94
- end
95
- end
96
- end
97
- addEventHandler (" onClientElementStreamIn" , root , streamIn )
98
-
99
- -- To ensure table integrity, stream out & local player death into 2 separate, safer events
100
- -- See the "Note" box at https://wiki.multitheftauto.com/wiki/OnClientElementStreamIn for reason why
101
- -- Stream out event
102
- function streamOut ()
103
- if (source ~= localPlayer and isElement (source ) and getElementType (source ) == " player" ) then
104
- if streamedPlayers [source ] ~= nil then
105
- streamedPlayers [source ] = nil
106
- setSoundVolume (source , 0 )
107
- triggerServerEvent (" voice:removePlayerBroadcast" , resourceRoot , localPlayer , source )
108
- end
109
- end
110
- end
111
- addEventHandler (" onClientElementStreamOut" , root , streamOut )
112
-
113
- -- Local player death event
114
- function onWasted ()
115
- if streamedPlayers [localPlayer ] ~= nil then
116
- streamedPlayers [localPlayer ] = nil
117
- setSoundVolume (localPlayer , 0 )
118
- triggerServerEvent (" voice:removePlayerBroadcast" , resourceRoot , localPlayer , localPlayer )
119
- end
120
- end
121
- addEventHandler (" onClientPlayerWasted" , localPlayer , onWasted )
95
+ addEventHandler (" onClientElementStreamIn" , root , function ()
96
+ if source == localPlayer then return end
97
+ if not (isElement (source ) and getElementType (source ) == " player" ) then return end
122
98
123
- function resourceStop ()
124
- triggerServerEvent (" voice:removePlayerBroadcasts" , resourceRoot , localPlayer , streamedPlayers )
125
- streamedPlayers = {}
126
- end
127
- addEventHandler (" onClientResourceStop" , resourceRoot , resourceStop , false )
99
+ if isPedDead (source ) then return end
128
100
129
- function voiceStart (source )
130
- if (isElement (source ) and getElementType (source ) == " player" ) then
131
- if streamedPlayers [source ] ~= nil then
132
- streamedPlayers [source ] = true
133
- end
101
+ if streamedPlayers [source ] == nil then
102
+ setSoundVolume (source , 0 )
103
+ streamedPlayers [source ] = false
104
+ triggerServerEvent (" voice_local:addToPlayerBroadcast" , localPlayer , source )
134
105
end
135
- end
136
- addEvent (" voice_cl:onClientPlayerVoiceStart" , true )
137
- addEventHandler (" voice_cl:onClientPlayerVoiceStart" , resourceRoot , voiceStart )
106
+ end )
107
+ addEventHandler (" onClientElementStreamOut" , root , function ()
108
+ if source == localPlayer then return end
109
+ if not (isElement (source ) and getElementType (source ) == " player" ) then return end
138
110
139
- function voiceStop (source )
140
- if (isElement (source ) and getElementType (source ) == " player" ) then
141
- if streamedPlayers [source ] ~= nil then
142
- streamedPlayers [source ] = false
143
- end
111
+ if streamedPlayers [source ] ~= nil then
112
+ setSoundVolume (source , 0 )
113
+ streamedPlayers [source ] = nil
114
+ triggerServerEvent (" voice_local:removeFromPlayerBroadcast" , localPlayer , source )
144
115
end
145
- end
146
- addEvent (" voice_cl:onClientPlayerVoiceStop" , true )
147
- addEventHandler (" voice_cl:onClientPlayerVoiceStop" , resourceRoot , voiceStop )
116
+ end )
148
117
149
- function table .empty (a )
150
- if type (a ) ~= " table" then
151
- return false
118
+ -- Update player talking status (for displaying)
119
+ addEventHandler (" voice_local:onClientPlayerVoiceStart" , root , function (player )
120
+ if not (isElement (player ) and getElementType (player ) == " player" ) then return end
121
+
122
+ if player == localPlayer then
123
+ localPlayerTalking = true
124
+ elseif streamedPlayers [player ] ~= nil then
125
+ streamedPlayers [player ] = true
126
+ end
127
+ end )
128
+ addEventHandler (" voice_local:onClientPlayerVoiceStop" , root , function (player )
129
+ if not (isElement (player ) and getElementType (player ) == " player" ) then return end
130
+
131
+ if player == localPlayer then
132
+ localPlayerTalking = false
133
+ elseif streamedPlayers [player ] ~= nil then
134
+ streamedPlayers [player ] = false
152
135
end
136
+ end )
153
137
154
- return next (a ) == nil
155
- end
138
+ -- Load the settings received from the server
139
+ addEventHandler (" voice_local:updateSettings" , localPlayer , function (settingsFromServer )
140
+ settings = settingsFromServer
141
+
142
+ if initialWaiting then
143
+ addEventHandler (" onClientPreRender" , root , handlePreRender , false )
144
+ initialWaiting = false
145
+ end
146
+ end , false )
0 commit comments