mirror of
https://github.com/lifestorm/wnsrc.git
synced 2025-12-17 21:53:46 +03:00
294 lines
8.5 KiB
Lua
294 lines
8.5 KiB
Lua
|
|
--[[
|
||
|
|
| This file was obtained through the combined efforts
|
||
|
|
| of Madbluntz & Plymouth Antiquarian Society.
|
||
|
|
|
|
||
|
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
|
||
|
|
| Maloy, DrPepper10 @ RIP, Atle!
|
||
|
|
|
|
||
|
|
| Visit for more: https://plymouth.thetwilightzone.ru/
|
||
|
|
--]]
|
||
|
|
|
||
|
|
|
||
|
|
local ipairs = ipairs
|
||
|
|
local IsValid = IsValid
|
||
|
|
local table = table
|
||
|
|
|
||
|
|
util.AddNetworkString("ixRadioChannels")
|
||
|
|
|
||
|
|
function ix.radio:SayOnRadio(speaker, text, radioType, channelID)
|
||
|
|
if (!speaker:Alive() or speaker:IsRestricted() or !speaker:GetCharacter()) then
|
||
|
|
speaker:NotifyLocalized("notNow")
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
local entity = speaker:GetEyeTraceNoCursor().Entity
|
||
|
|
local data = {radioType = radioType, channel = channelID}
|
||
|
|
|
||
|
|
if (!channelID and IsValid(entity) and entity:GetClass() == "ix_item") then
|
||
|
|
local item = ix.item.instances[entity.ixItemID]
|
||
|
|
if (item and item.isRadio) then
|
||
|
|
if (!item:GetData("enabled")) then
|
||
|
|
speaker:NotifyLocalized("radioNotOn")
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
local range = radioType == self.types.whisper and math.pow(ix.config.Get("chatRange", 280) * 0.25, 2) or 10000
|
||
|
|
if (entity:GetPos():DistToSqr(speaker:EyePos()) < range) then
|
||
|
|
data.channel = item:GetChannel()
|
||
|
|
data.stationary = true
|
||
|
|
else
|
||
|
|
speaker:NotifyLocalized("radioTooFar")
|
||
|
|
return
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if (!data.channel) then
|
||
|
|
data.channel = speaker:GetCharacter():GetRadioChannel()
|
||
|
|
end
|
||
|
|
|
||
|
|
local result, reason = hook.Run("CanPlayerSayRadio", speaker, text, data)
|
||
|
|
if (result != nil) then
|
||
|
|
if (reason) then
|
||
|
|
speaker:Notify(reason)
|
||
|
|
end
|
||
|
|
|
||
|
|
return result
|
||
|
|
end
|
||
|
|
|
||
|
|
local channel = self:FindByID(data.channel)
|
||
|
|
if (!channel) then
|
||
|
|
speaker:Notify("radioInvalidChannel")
|
||
|
|
return
|
||
|
|
end
|
||
|
|
|
||
|
|
if (radioType == self.types.yell) then
|
||
|
|
local filtered = string.gsub(text, "[^%a]", "")
|
||
|
|
local length = string.len(filtered)
|
||
|
|
local capsLength = string.len(string.gsub(filtered, "[^%u]", ""))
|
||
|
|
|
||
|
|
if (capsLength / length > 0.9 and length >= 15) then
|
||
|
|
radioType = self.types.scream
|
||
|
|
data.radioType = radioType
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local range = ix.config.Get("chatRange", 280)
|
||
|
|
if (radioType == self.types.whisper) then
|
||
|
|
range = range * 0.5
|
||
|
|
elseif (radioType >= self.types.yell) then
|
||
|
|
range = range * 2
|
||
|
|
end
|
||
|
|
range = range * range
|
||
|
|
|
||
|
|
if (!speaker:GetActiveCombineSuit()) then
|
||
|
|
speaker:EmitSound("willardnetworks/radio/handheld/handheld" .. math.random(1, 4) .. ".wav", 75)
|
||
|
|
end
|
||
|
|
|
||
|
|
local speakerEyePos = speaker:EyePos()
|
||
|
|
local bStationaries = #channel.stationaries > 0
|
||
|
|
local listeners, listenersByKeys
|
||
|
|
if (ix.config.Get("radioLimitRange", 0) > 0) then
|
||
|
|
local channelListeners = channel.listeners
|
||
|
|
|
||
|
|
listeners, listenersByKeys = {}, {}
|
||
|
|
for i = #channelListeners, 1, -1 do
|
||
|
|
local client = channelListeners[i]
|
||
|
|
if (channel.backpackRadio or client:TestPVS(speaker) or ((client:EyePos():DistToSqr(speakerEyePos) / ((channel.rangeMultiplier or 1) ^ 2)) < (ix.config.Get("radioLimitRange") ^ 2))) then
|
||
|
|
listeners[#listeners + 1] = client
|
||
|
|
listenersByKeys[client] = true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
else
|
||
|
|
listeners = bStationaries and table.Copy(channel.listeners) or channel.listeners
|
||
|
|
listenersByKeys = channel.listenersByKeys
|
||
|
|
end
|
||
|
|
local eavesdrop = speaker:GetMoveType() != MOVETYPE_NOCLIP and {}
|
||
|
|
local yellEavesDrop = radioType >= self.types.yell and {}
|
||
|
|
|
||
|
|
for _, client in ipairs(player.GetAll()) do
|
||
|
|
if (!client:Alive()) then
|
||
|
|
continue
|
||
|
|
end
|
||
|
|
|
||
|
|
if (listenersByKeys[client]) then
|
||
|
|
continue
|
||
|
|
end
|
||
|
|
|
||
|
|
local clientEyePos = client:EyePos()
|
||
|
|
if (bStationaries) then
|
||
|
|
local bFound = false
|
||
|
|
for i = #channel.stationaries, 1, -1 do
|
||
|
|
local stationary = channel.stationaries[i]
|
||
|
|
if (!IsValid(stationary)) then
|
||
|
|
table.remove(channel.stationaries, i)
|
||
|
|
continue
|
||
|
|
end
|
||
|
|
|
||
|
|
if (stationary:GetPos():DistToSqr(clientEyePos) <= range) then
|
||
|
|
bFound = true
|
||
|
|
listeners[#listeners + 1] = client
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if (bFound) then continue end
|
||
|
|
end
|
||
|
|
|
||
|
|
if (eavesdrop and speakerEyePos:DistToSqr(clientEyePos) <= range) then
|
||
|
|
eavesdrop[#eavesdrop + 1] = client
|
||
|
|
continue
|
||
|
|
end
|
||
|
|
|
||
|
|
if (channel.backpackRadio) then
|
||
|
|
local bAdd = false
|
||
|
|
for listener, _ in pairs(listenersByKeys) do
|
||
|
|
if (listener:GetMoveType() == MOVETYPE_NOCLIP and !listener:InVehicle()) then continue end
|
||
|
|
|
||
|
|
if (listener:EyePos():DistToSqr(clientEyePos) <= range) then
|
||
|
|
bAdd = true
|
||
|
|
break
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if (bAdd) then
|
||
|
|
eavesdrop[#eavesdrop + 1] = client
|
||
|
|
continue
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if (yellEavesDrop) then
|
||
|
|
-- potential yell eavesdropper (to check once we know all listeners)
|
||
|
|
yellEavesDrop[client] = clientEyePos
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if (yellEavesDrop) then
|
||
|
|
-- copy potential eavesdroppers
|
||
|
|
local clients = yellEavesDrop
|
||
|
|
yellEavesDrop = {}
|
||
|
|
|
||
|
|
-- compile list of valid listener positions
|
||
|
|
local toCheck = {}
|
||
|
|
for _, listener in ipairs(listeners) do
|
||
|
|
-- people not actually on the channel do not count
|
||
|
|
if (!listenersByKeys[listener]) then continue end
|
||
|
|
|
||
|
|
-- admins in observer don't count
|
||
|
|
if (listener:GetMoveType() == MOVETYPE_NOCLIP and !listener:InVehicle()) then continue end
|
||
|
|
toCheck[#toCheck + 1] = listener:EyePos()
|
||
|
|
end
|
||
|
|
|
||
|
|
-- compile list of actual eavesdroppers
|
||
|
|
for client, clientEyePos in pairs(clients) do
|
||
|
|
for _, listenerPos in ipairs(toCheck) do
|
||
|
|
if (clientEyePos:DistToSqr(listenerPos) <= range / 8) then
|
||
|
|
yellEavesDrop[#yellEavesDrop + 1] = client
|
||
|
|
continue
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
ix.chat.Send(speaker, "radio", text, false, listeners, data)
|
||
|
|
|
||
|
|
if (eavesdrop and #eavesdrop > 0) then
|
||
|
|
ix.chat.Send(speaker, "radio_eavesdrop", text, false, eavesdrop, data)
|
||
|
|
end
|
||
|
|
|
||
|
|
if (yellEavesDrop and #yellEavesDrop > 0) then
|
||
|
|
ix.chat.Send(speaker, "radio_eavesdrop_yell", text, false, yellEavesDrop, data)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ix.radio:AddListenerToChannel(client, channelID)
|
||
|
|
local channel = self:FindByID(channelID)
|
||
|
|
if (channel and !channel.listenersByKeys[client] and self:CharacterHasChannel(client:GetCharacter(), channelID)) then
|
||
|
|
channel.listeners[#channel.listeners + 1] = client
|
||
|
|
channel.listenersByKeys[client] = #channel.listeners
|
||
|
|
|
||
|
|
local clientChannels = client:GetLocalVar("radioChannels", {})
|
||
|
|
clientChannels[channel.uniqueID] = true
|
||
|
|
client:SetLocalVar("radioChannels", clientChannels)
|
||
|
|
|
||
|
|
if (!self:FindByID(client:GetCharacter():GetRadioChannel())) then
|
||
|
|
client:GetCharacter():SetRadioChannel(channelID)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ix.radio:RemoveListenerFromChannel(client, channelID)
|
||
|
|
local channel = self:FindByID(channelID)
|
||
|
|
if (channel and channel.listenersByKeys[client] and !self:CharacterHasChannel(client:GetCharacter(), channel.uniqueID)) then
|
||
|
|
-- Move last listeners into client's slot, overwrite client to remove
|
||
|
|
local key = channel.listenersByKeys[client]
|
||
|
|
channel.listeners[key] = channel.listeners[#channel.listeners]
|
||
|
|
-- Update key of moved client
|
||
|
|
channel.listenersByKeys[channel.listeners[key]] = key
|
||
|
|
|
||
|
|
-- Remove last listeners
|
||
|
|
channel.listeners[#channel.listeners] = nil
|
||
|
|
-- Clear client key
|
||
|
|
channel.listenersByKeys[client] = nil
|
||
|
|
|
||
|
|
local clientChannels = client:GetLocalVar("radioChannels", {})
|
||
|
|
clientChannels[channel.uniqueID] = nil
|
||
|
|
client:SetLocalVar("radioChannels", clientChannels)
|
||
|
|
|
||
|
|
-- Reset transmit channel if client doesn't have this channel anymore
|
||
|
|
if (client:GetCharacter():GetRadioChannel() == channel.uniqueID) then
|
||
|
|
local channels = self:GetAllChannelsFromChar(client:GetCharacter())
|
||
|
|
client:GetCharacter():SetRadioChannel(channels and channels[1] or "")
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ix.radio:AddStationaryToChannel(channelID, entity)
|
||
|
|
local channel = self:FindByID(channelID)
|
||
|
|
if (channel and IsValid(entity)) then
|
||
|
|
table.insert(channel.stationaries, entity)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ix.radio:RemoveStationaryFromChannel(channelID, entity)
|
||
|
|
local channel = self:FindByID(channelID)
|
||
|
|
if (channel and IsValid(entity)) then
|
||
|
|
for k, v in ipairs(channel.stationaries) do
|
||
|
|
if (v == entity) then
|
||
|
|
table.remove(channel.stationaries, k)
|
||
|
|
return
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ix.radio:CleanStationariesFromChannel(channelID)
|
||
|
|
local channel = self:FindByID(channelID)
|
||
|
|
if (channel) then
|
||
|
|
for i = #channel.stationaries, 1, -1 do
|
||
|
|
if (!IsValid(channel.stationaries[i])) then
|
||
|
|
table.remove(channel.stationaries, i)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ix.radio:AddPlayerChannelSubscription(client, channelID)
|
||
|
|
local channel = self:FindByID(channelID)
|
||
|
|
if (channel) then
|
||
|
|
local character = client:GetCharacter()
|
||
|
|
|
||
|
|
character:SetRadioChannels(channel.uniqueID)
|
||
|
|
self:AddListenerToChannel(client, channel.uniqueID)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function ix.radio:RemovePlayerChannelSubscription(client, channelID)
|
||
|
|
local channel = self:FindByID(channelID)
|
||
|
|
if (channel) then
|
||
|
|
local character = client:GetCharacter()
|
||
|
|
character:SetRadioChannels(channel.uniqueID, true)
|
||
|
|
self:RemoveListenerFromChannel(character, channelID)
|
||
|
|
end
|
||
|
|
end
|