Files
wnsrc/gamemodes/helix/plugins/radio/sv_plugin.lua
lifestorm 94063e4369 Upload
2024-08-04 22:55:00 +03:00

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