Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- have shortcuts in the list so you can do List["luigi fap"] = "woow2 it's hard aww aww"
- premade particles or effects of any sort should be possible to add in the List
- neater way of modifying data (data:SetVolume())
- ]]
- chatsounds = {} local c = chatsounds
- chatsounds.AutoAddPath = "chatsounds/autoadd/"
- chatsounds.PitchRange = 5
- chatsounds.BasePitch = 100
- chatsounds.BaseVolume = 90
- chatsounds.ExclamationMultiplier = 2
- -- adobe audition adds 0.020 milliseconds on both ends, and all custom chatsounds were exported with adobe audition
- chatsounds.SubMP3Duration = 0.010
- chatsounds.PuncStart = "%f[%a%%](" -- Pattern for looking for words
- chatsounds.PuncEnd = ")%f[%s%.%,%!%\"%'%?%~%*%-%=%+%(%)%:%;%#%&%^%@%%]"
- chatsounds.ExistsCache = {}
- chatsounds.Enabled = CreateClientConVar("cl_chatsounds_enable", 1, true, true)
- chatsounds.CSoundPatches = {}
- chatsounds.Timers = {}
- chatsounds.Modifiers = {
- {
- modifier = "%",
- type = "number",
- fetch = function(number)
- return math.Clamp(number, 0, 255)
- end,
- pre = function(chtsnd)
- chtsnd:SetPitch(chtsnd:GetModVar("%"))
- --print(chtsnd:GetRealDuration(), chtsnd:GetModVar("%"), chtsnd:GetRealDuration(), chtsnd:GetPitch())
- chtsnd:SetDuration(chtsnd:GetRealDuration() / (chtsnd:GetPitch() / 255))
- end,
- },
- {
- modifier = "-",
- type = "number",
- pre = function(chtsnd)
- chtsnd:SetDuration(math.max(chtsnd:GetDuration() - chtsnd:GetModVar("-"), 0))
- end,
- },
- {
- modifier = "+",
- type = "number",
- pre = function(chtsnd)
- chtsnd:SetDuration(math.max(chtsnd:GetDuration() + chtsnd:GetModVar("+"), 0))
- end,
- },
- {
- modifier = "*",
- type = "number",
- pre = function(chtsnd)
- for key, value in pairs(chtsnd:GetScript()) do
- if chtsnd:GetIndex() > key and value.mod and value.mod.self.modifier == "*" then return end
- end
- if chtsnd:GetDuration() * chtsnd:GetModVar("*") > 25 then return end
- local count = math.Clamp(chtsnd:GetModVar("*")-1, 1, 50)
- for i = 1, count do
- local newchtsnd = table.Copy(chtsnd)
- newchtsnd:SetScript(chtsnd:GetScript())
- chtsnd:GetScript()[chtsnd:GetIndex() + i] = newchtsnd
- end
- end,
- },
- {
- modifier = "-+",
- type = "args",
- fetch = function(min, max)
- return {min = min, max = max}
- end,
- pre = function(chtsnd)
- chtsnd:SetMode(CHTSND_MODE_CSOUNDPATCH)
- chtsnd:SetThinkFunction(function(chtsnd, frame)
- chtsnd:GetCSoundPatch():ChangePitch(math.Clamp(Lerp((-frame+1), chtsnd:GetModVar("-+").min, chtsnd:GetModVar("-+").max), 0, 255))
- end)
- end,
- },
- {
- modifier = "?",
- type = "none",
- pre = function(chtsnd)
- chtsnd:SetMode(CHTSND_MODE_CSOUNDPATCH)
- chtsnd:SetThinkFunction(function(chtsnd, frame)
- print((math.abs(chtsnd:GetPlayer():EyeAngles().p-89)/178)*255)
- chtsnd:GetCSoundPatch():ChangePitch((math.abs(chtsnd:GetPlayer():EyeAngles().p-89)/178)*255, 0, 255)
- end)
- end,
- },
- {
- modifier = "&",
- type = "number",
- post = function(chtsnd)
- if chtsnd:GetPlayer():GetPos():Distance(LocalPlayer():GetPos()) < 1500 then
- LocalPlayer():SetDSP(chtsnd:GetModVar("&"))
- timer.Create("Chatsounds DSP Mod", chtsnd:GetDuration(), 1, function()
- LocalPlayer():SetDSP(0)
- end)
- end
- end,
- },
- {
- modifier = "#",
- type = "number",
- pre = function(chtsnd)
- chtsnd:SetSoundPath(c.GetSound(chtsnd:GetKey())[math.Clamp(chtsnd:GetModVar("#"), 1, #c.GetSound(chtsnd:GetKey()))].snd)
- end,
- },
- {
- modifier = "--",
- type = "number",
- pre = function(chtsnd)
- chtsnd:SetMode(CHTSND_MODE_CSOUNDPATCH)
- chtsnd:SetDuration(chtsnd:GetDuration() * (chtsnd:GetModVar("--") / 100))
- end,
- post = function(chtsnd)
- timer.Simple(chtsnd:GetDuration(), function()
- chtsnd:NilCSoundPatch()
- end)
- end,
- },
- }
- CHTSND_MODE_WORLDSOUND = 0
- CHTSND_MODE_EMITSOUND = 1
- CHTSND_MODE_CSOUNDPATCH = 2
- CHTSND_MODE_BASS = 3
- CHTSND_MOD_FETCH = 0
- CHTSND_MOD_PRE = 1
- CHTSND_MOD_POST = 2
- local CHTSND = {pitch = 100}
- CHTSND.__index = CHTSND
- function chatsounds.NewChatSound(override)
- return setmetatable(table.Copy(override) or {
- ply = LocalPlayer(),
- mode = CHTSND_MODE_EMITSOUND,
- pitch = 100,
- snd = "bitch.mp3"
- }, CHTSND)
- end
- function CHTSND:GetCSoundPatch() return self.csoundpatch end
- function CHTSND:HasMod() return self.mod ~= nil end
- function CHTSND:GetRealDuration() return self:GetListSound().length end
- function CHTSND:GetListSound() return self.sound end
- function CHTSND:NilCSoundPatch()
- local csound = self:GetCSoundPatch()
- if csound then
- csound:Stop()
- self.csoundpatch = nil
- end
- end
- function CHTSND:GetModVar(mod)
- if self:HasMod() then
- for key, value in pairs(self.mod) do
- if value.self.modifier == mod then
- return value.fetch_var
- end
- end
- end
- end
- function CHTSND:CallModFunction(enum)
- if self:HasMod() then
- for key, value in pairs(self.mod) do
- if enum == CHTSND_MOD_FETCH and value.self.fetch then
- return value.self.fetch(self)
- elseif enum == CHTSND_MOD_PRE and value.self.pre then
- return value.self.pre(self)
- elseif enum == CHTSND_MOD_POST and value.self.post then
- return value.self.post(self)
- end
- end
- end
- end
- AccessorFunc(CHTSND, "Think", "ThinkFunction")
- AccessorFunc(CHTSND, "duration", "Duration")
- AccessorFunc(CHTSND, "mode", "Mode")
- AccessorFunc(CHTSND, "pitch", "Pitch")
- --[[ function CHTSND:SetPitch(v)
- debug.Trace()
- pitch = v
- end ]]
- AccessorFunc(CHTSND, "volume", "Volume")
- AccessorFunc(CHTSND, "seed", "Seed")
- AccessorFunc(CHTSND, "snd", "SoundPath")
- AccessorFunc(CHTSND, "ply", "Player")
- AccessorFunc(CHTSND, "origin", "Origin")
- AccessorFunc(CHTSND, "index", "Index")
- AccessorFunc(CHTSND, "script", "Script")
- AccessorFunc(CHTSND, "key", "Key")
- AccessorFunc(CHTSND, "time", "Time")
- chatsounds.chtsnd = CHTSND
- function chatsounds.AddModifier(symbol, type, callbacks)
- callbacks.modifier = symbol
- callbacks.type = type
- table.insert(c.Modifiers, callbacks)
- end
- function chatsounds.InitializeSounds()
- c.List = {}
- c.Keys = {}
- include"chatsounds/sounds.lua"
- for _, file_name in ipairs(file.Find("sound/" .. c.AutoAddPath .. "*", true)) do
- if file_name:find("[^%.]+%.[^%.]") then
- c.List[file_name:match("([^%.]+)%."):lower()] = {{snd = c.AutoAddPath .. file_name}}
- end
- end
- for sound in pairs(c.List) do
- table.insert(c.Keys, sound)
- end
- table.sort(c.Keys, function(a, b) return #a > #b end)
- c.List = table.LowerKeyNames(c.List)
- c.Keys = table.LowerKeyNames(c.Keys)
- end
- chatsounds.InitializeSounds()
- -- utils
- function chatsounds.GetSound(key)
- return key and c.List[key:lower():Trim()]
- end
- function chatsounds.CRCRandom(seed, min, max)
- max = max +1
- return util.CRC(seed)%(max-min) + min
- end
- function chatsounds.GetRandomSound(sounds, seed)
- return seed and sounds[c.CRCRandom(seed, 1, #sounds)] or table.Random(sounds)
- end
- function chatsounds.GetRandomSoundFromKey(key, seed)
- local sounds = c.GetSound(key)
- if not sounds then print("chatsounds could not find key", key, c.List[key]) return end
- return seed and sounds and sounds[c.CRCRandom(seed, 1, #sounds)] or table.Random(sounds)
- end
- function chatsounds.GeneratePitch(sound, seed)
- return sound and sound.pitch or (c.BasePitch + c.CRCRandom(seed, -c.PitchRange, c.PitchRange))
- end
- function chatsounds.IsMP3(path)
- return path:find(".mp3", 0, true) ~= nil or false
- end
- function chatsounds.GetSoundDuration(sound)
- if GetSoundDuration and c.IsMP3(sound) then
- return (GetSoundDuration("sound/"..sound) or 0.1) - c.SubMP3Duration
- else
- local duration = SoundDuration(sound)
- return duration ~= 0 and duration or 1
- end
- end
- function chatsounds.SoundExists(path)
- if c.ExistsCache[path] then return false end
- if not file.Exists("sound/" .. path, true) then
- c.ExistsCache[path] = true
- return false
- end
- return true
- end
- function chatsounds.ClearExistsCache()
- c.ExistsCache = {}
- end
- function chatsounds.StopAllSounds()
- for id in pairs(c.Timers) do
- timer.Destroy("csp_" .. id)
- c.Timers[id] = nil
- end
- RunConsoleCommand("stopsounds")
- end
- function chatsounds.GetScriptFromText(text)
- if not text then return end
- local result = {}
- local original_text = text
- text = text:lower() .. " "
- for _, key in ipairs(c.Keys) do
- local fstart, fend, match
- local iteration = 0
- repeat fstart, fend, match = text:find(--[[ c.PuncStart .. ]]key:lower() --[[ .. c.PuncEnd ]], nil, true)
- iteration = iteration + 1
- if fstart then
- local offset = 0
- local mod
- for index, data in SortedPairs(c.Modifiers, true) do
- --if mod then continue end
- --print(text:sub(fend+offset, fend+2+offset))
- if text:sub(fend+offset, fend+2+offset):find(data.modifier, nil, true) then
- --print(text)
- local var =
- data.type == "number" and
- tonumber(text:match("([0-9%.]+)", fend+1) or nil)
- or
- data.type == "args" and
- tostring(text:match("([0-9%.0-9%.]+)", fend+1) or nil)
- --[[ or
- not data.type and data.fetch and data.fetch(text) ]]
- if var then
- offset = #tostring(var) + 1
- print(data.type, type(var), data.modifier, var, offset)
- mod = mod or {}
- mod[index] = {
- fetch_var = data.type and data.fetch and (data.type == "args" and data.fetch(unpack(string.Explode(".", var))) or data.fetch(var)) or var,
- self = data,
- }
- PrintTable(mod)
- elseif data.type == "none" then
- offset = 1
- mod = mod or {}
- mod[index] = {
- fetch_var = "nil",
- self = data,
- }
- end
- end
- end
- table.insert(result, {
- pos = fstart,
- key = key,
- mod = mod
- })
- text = text:sub(1, fstart-1) .. ("_"):rep(key:len() + offset) .. text:sub(fend + offset + 1)
- end
- if iteration > 500 then print("chatsounds infinite loop") break end
- until fstart == nil
- end
- -- Sort the results table by position of the word.
- table.sort(result, function(a, b) return a.pos < b.pos end)
- return result
- end
- function chatsounds.Say(ply, text, seed)
- local sound = c.GetSound(text)
- if sound then
- c.PlaySound(c.NewChatSound{
- mode = CHTSND_MODE_EMITSOUND,
- ply = ply,
- snd = c.GetRandomSound(sound, seed).snd,
- pitch = c.GeneratePitch(sound, seed),
- })
- return end
- --print("pre timer start")
- for key, text in pairs(string.Explode("|", text)) do
- local script = c.GetScriptFromText(text)
- if script then
- local volume = ({text:gsub("[!]", "")})[2] --<- ugh?
- volume = volume ~= 0 and volume * c.ExclamationMultiplier or 1
- local time = 0
- for index, data in ipairs(script) do
- local sound = c.GetRandomSoundFromKey(data.key, seed .. index)
- if sound then
- local chtsnd = c.NewChatSound(sound)
- if not chtsnd:GetDuration() then
- chtsnd:SetDuration(c.GetSoundDuration(chtsnd:GetSoundPath()))
- if not sound.length then sound.length = chtsnd:GetDuration() end
- end
- chtsnd.sound = sound
- chtsnd.script = script
- chtsnd.key = data.key
- chtsnd.mod = data.mod
- --print("nooo", chtsnd.mod)
- chtsnd:SetDuration(chtsnd:GetDuration() / (c.BasePitch / 100))
- chtsnd:SetPitch(c.GeneratePitch(sound, seed .. index))
- chtsnd:SetPlayer(ply)
- chtsnd:SetVolume(volume)
- chtsnd:SetMode(CHTSND_MODE_EMITSOUND)
- chtsnd:SetIndex(index)
- chtsnd:SetSeed(seed)
- chtsnd:CallModFunction(CHTSND_MOD_PRE)
- time = (chtsnd:GetTime() or time) + chtsnd:GetDuration()
- local id = chtsnd:GetKey() .. time
- --print(chtsnd:GetKey(), time, chtsnd:GetDuration())
- timer.Create("csp_" .. id , time - chtsnd:GetDuration(), 1, c.PlaySound, chtsnd, id)
- c.Timers[id] = true
- end
- if index > 100 then print("chatsounds tried to play over 100 sounds ", ply) break end
- end
- end
- end
- --print("pre timer end")
- end
- function chatsounds.PlaySound(chtsnd, id)
- --print("during timer", chtsnd:GetKey(), chtsnd:GetPitch(), chtsnd:GetDuration(), chtsnd:GetMode())
- --if chtsnd:GetPlayer():Nick() == "CapsAdmin" then PrintTable(chtsnd) end
- if chtsnd:GetPitch() <= 0 or not c.SoundExists(chtsnd:GetSoundPath()) then return end
- local volume = (chtsnd:GetVolume() or 1)
- local sound_level = math.Clamp(c.BaseVolume + volume, c.BaseVolume, 160)
- local pitch = math.min(chtsnd:GetPitch(), 255)
- if chtsnd:GetMode() == CHTSND_MODE_WORLDSOUND then
- for i = 1, volume do
- WorldSound(chtsnd:GetSoundPath(), chtsnd:GetOrigin() or chtsnd:GetPlayer():GetShootPos(), sound_level, pitch)
- end
- elseif chtsnd:GetMode() == CHTSND_MODE_CSOUNDPATCH then
- local csp = c.CSoundPatches[chtsnd.ply:UniqueID()]
- if csp then
- csp:Stop()
- c.CSoundPatches[chtsnd.ply:UniqueID()] = nil
- end
- chtsnd.csoundpatch = CreateSound(chtsnd.ply, chtsnd.snd)
- chtsnd.csoundpatch:SetSoundLevel(sound_level)
- chtsnd.csoundpatch:PlayEx(1, pitch)
- c.CSoundPatches[chtsnd.ply:UniqueID()] = chtsnd.csoundpatch
- if chtsnd:GetThinkFunction() then
- local unique = "chatsounds_" .. tostring(chtsnd)
- local endtime = RealTime() + chtsnd:GetDuration()
- hook.Add("Think", unique, function()
- if endtime and endtime > RealTime() then
- chtsnd.Think(chtsnd, (endtime - RealTime()) / chtsnd:GetDuration())
- else
- hook.Remove("Think", unique)
- chtsnd:NilCSoundPatch()
- end
- end)
- end
- elseif chtsnd:GetMode() == CHTSND_MODE_EMITSOUND then
- for i = 1, volume do
- chtsnd:GetPlayer():EmitSound(chtsnd:GetSoundPath(), sound_level, pitch)
- end
- end
- chtsnd:CallModFunction(CHTSND_MOD_POST)
- if id then c.Timers[id] = nil end
- end
- function chatsounds.ReceiveUsermessage(data)
- if not c.Enabled:GetBool() then return end
- local ply = data:ReadEntity()
- if not IsValid(ply) then return end
- local seed = ply:Nick() .. (data:ReadChar() + 127)
- local text = data:ReadString()
- c.Say(ply, text, seed)
- end
- usermessage.Hook("chatsounds", chatsounds.ReceiveUsermessage)
- function chatsounds.ReceiveRandomUsermessage(data)
- if not c.Enabled:GetBool() then return end
- local ply = data:ReadEntity()
- if not IsValid(ply) then return end
- local seed = ply:Nick() .. (data:ReadChar() + 127)
- local sounds = table.ClearKeys(table.Copy(c.List))
- local sound = c.GetRandomSound(sounds[c.CRCRandom(seed, 1, #sounds)], seed)
- sound.pitch = c.GeneratePitch(sound, seed)
- sound.ply = ply
- sound.mode = "EmitSound"
- c.PlaySound(sound)
- end
- usermessage.Hook("chatsounds_random", chatsounds.ReceiveRandomUsermessage)
- local function FindValue(tbl, str)
- for k,v in pairs(tbl) do
- if v.snd and v.snd:find(str) then return true end
- end
- end
- function chatsounds.Find(_,_, args)
- --[[ if not args[1] then
- print("Usage: chatsounds_find <pattern>\n\tPattern can be either a normal string or a regex pattern, escape char is %. Normal strings may need escaping.")
- return;
- end
- for key in pairs(c.List) do
- if string.find(key, args[1]) then
- MsgN(key)
- end
- end ]]
- if not args[1] then
- print("Usage: chatsounds_find <(string)pattern><(boolean)path search>\n\tPattern can be either a normal string or a regex pattern, escape char is %. Normal strings may need escaping.")
- return;
- end
- for key, data in pairs(c.List) do
- if key:find(args[1]) or args[2] and FindValue(data, args[1]) then
- print([["]] .. key .. [["]] .. " = {")
- for key, value in pairs(data) do
- if value.snd then
- MsgN("\t[" .. key .. "] = " .. tostring(value.snd))
- end
- if key == #data then
- print("}\n")
- end
- end
- end
- end
- end
- concommand.Add("chatsounds_find", chatsounds.Find)
- local function FindValue(tbl, str)
- for k,v in pairs(tbl) do
- if v.snd and v.snd:find(str, nil, true) then return true end
- end
- end
- function chatsounds.Dump(_,_,args)
- for key, data in pairs(c.List) do
- if args[1] == "mp3" and FindValue(data, ".wav") then continue end
- if args[1] == "wav" and FindValue(data, ".mp3") then continue end
- print([["]] .. key .. [["]] .. " = {")
- for key, value in pairs(data) do
- if value.snd then
- MsgN("\t[" .. key .. "] = " .. tostring(value.snd))
- end
- if key == #data then
- print("}\n")
- end
- end
- end
- end
- concommand.Add("chatsounds_dump", chatsounds.Dump)
- if file.Exists("lua/includes/modules/gm_duration.dll", true) then
- -- this is at the bottom in case it throws an error halting the whole script
- require("duration")
- end
- --Say("CHATSOUNDS RUNSTRING'd")
Advertisement
Add Comment
Please, Sign In to add comment