Python1320

Untitled

Dec 12th, 2010
91
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 16.36 KB | None | 0 0
  1. --[[
  2.     have shortcuts in the list so you can do List["luigi fap"] = "woow2 it's hard aww aww"
  3.     premade particles or effects of any sort should be possible to add in the List
  4.     neater way of modifying data (data:SetVolume())
  5.  
  6. ]]
  7. chatsounds = {} local c = chatsounds
  8.  
  9. chatsounds.AutoAddPath = "chatsounds/autoadd/"
  10. chatsounds.PitchRange = 5
  11. chatsounds.BasePitch = 100
  12. chatsounds.BaseVolume = 90
  13. chatsounds.ExclamationMultiplier = 2
  14. -- adobe audition adds 0.020 milliseconds on both ends, and all custom chatsounds were exported with adobe audition
  15. chatsounds.SubMP3Duration = 0.010
  16.  
  17. chatsounds.PuncStart = "%f[%a%%](" -- Pattern for looking for words
  18. chatsounds.PuncEnd = ")%f[%s%.%,%!%\"%'%?%~%*%-%=%+%(%)%:%;%#%&%^%@%%]"
  19. chatsounds.ExistsCache = {}
  20.  
  21. chatsounds.Enabled = CreateClientConVar("cl_chatsounds_enable", 1, true, true)
  22.  
  23. chatsounds.CSoundPatches = {}
  24. chatsounds.Timers = {}
  25.  
  26. chatsounds.Modifiers = {
  27.     {
  28.         modifier = "%",
  29.         type = "number",
  30.  
  31.         fetch = function(number)
  32.             return math.Clamp(number, 0, 255)
  33.         end,
  34.  
  35.         pre = function(chtsnd)
  36.             chtsnd:SetPitch(chtsnd:GetModVar("%"))
  37.             --print(chtsnd:GetRealDuration(), chtsnd:GetModVar("%"), chtsnd:GetRealDuration(), chtsnd:GetPitch())
  38.             chtsnd:SetDuration(chtsnd:GetRealDuration() / (chtsnd:GetPitch() / 255))
  39.         end,
  40.     },
  41.     {
  42.         modifier = "-",
  43.         type = "number",
  44.  
  45.         pre = function(chtsnd)
  46.             chtsnd:SetDuration(math.max(chtsnd:GetDuration() - chtsnd:GetModVar("-"), 0))
  47.         end,
  48.     },
  49.     {
  50.         modifier = "+",
  51.         type = "number",
  52.  
  53.         pre = function(chtsnd)
  54.             chtsnd:SetDuration(math.max(chtsnd:GetDuration() + chtsnd:GetModVar("+"), 0))
  55.         end,
  56.     },
  57.     {
  58.         modifier = "*",
  59.         type = "number",
  60.  
  61.         pre = function(chtsnd)
  62.  
  63.             for key, value in pairs(chtsnd:GetScript()) do
  64.                 if chtsnd:GetIndex() > key and value.mod and value.mod.self.modifier == "*" then return end
  65.             end
  66.  
  67.  
  68.             if chtsnd:GetDuration() * chtsnd:GetModVar("*") > 25 then return end
  69.  
  70.             local count = math.Clamp(chtsnd:GetModVar("*")-1, 1, 50)
  71.  
  72.             for i = 1, count do
  73.                 local newchtsnd = table.Copy(chtsnd)
  74.                 newchtsnd:SetScript(chtsnd:GetScript())
  75.                 chtsnd:GetScript()[chtsnd:GetIndex() + i] = newchtsnd
  76.             end
  77.         end,
  78.     },
  79.     {
  80.         modifier = "-+",
  81.         type = "args",
  82.  
  83.         fetch = function(min, max)
  84.             return {min = min, max = max}
  85.         end,
  86.  
  87.         pre = function(chtsnd)
  88.             chtsnd:SetMode(CHTSND_MODE_CSOUNDPATCH)
  89.             chtsnd:SetThinkFunction(function(chtsnd, frame)
  90.                 chtsnd:GetCSoundPatch():ChangePitch(math.Clamp(Lerp((-frame+1), chtsnd:GetModVar("-+").min, chtsnd:GetModVar("-+").max), 0, 255))
  91.             end)
  92.         end,
  93.     },
  94.     {
  95.         modifier = "?",
  96.         type = "none",
  97.  
  98.         pre = function(chtsnd)
  99.             chtsnd:SetMode(CHTSND_MODE_CSOUNDPATCH)
  100.             chtsnd:SetThinkFunction(function(chtsnd, frame)
  101.                 print((math.abs(chtsnd:GetPlayer():EyeAngles().p-89)/178)*255)
  102.                 chtsnd:GetCSoundPatch():ChangePitch((math.abs(chtsnd:GetPlayer():EyeAngles().p-89)/178)*255, 0, 255)
  103.             end)
  104.         end,
  105.     },
  106.     {
  107.         modifier = "&",
  108.         type = "number",
  109.  
  110.         post = function(chtsnd)
  111.             if chtsnd:GetPlayer():GetPos():Distance(LocalPlayer():GetPos()) < 1500 then
  112.                 LocalPlayer():SetDSP(chtsnd:GetModVar("&"))
  113.                 timer.Create("Chatsounds DSP Mod", chtsnd:GetDuration(), 1, function()
  114.                     LocalPlayer():SetDSP(0)
  115.                 end)
  116.             end
  117.         end,
  118.     },
  119.     {
  120.         modifier = "#",
  121.         type = "number",
  122.  
  123.         pre = function(chtsnd)
  124.             chtsnd:SetSoundPath(c.GetSound(chtsnd:GetKey())[math.Clamp(chtsnd:GetModVar("#"), 1, #c.GetSound(chtsnd:GetKey()))].snd)
  125.         end,
  126.     },
  127.     {
  128.         modifier = "--",
  129.         type = "number",
  130.  
  131.         pre = function(chtsnd)
  132.             chtsnd:SetMode(CHTSND_MODE_CSOUNDPATCH)
  133.             chtsnd:SetDuration(chtsnd:GetDuration() * (chtsnd:GetModVar("--") / 100))
  134.         end,
  135.        
  136.         post = function(chtsnd)
  137.             timer.Simple(chtsnd:GetDuration(), function()
  138.                 chtsnd:NilCSoundPatch()
  139.             end)
  140.         end,
  141.     },
  142. }
  143.  
  144. CHTSND_MODE_WORLDSOUND = 0
  145. CHTSND_MODE_EMITSOUND = 1
  146. CHTSND_MODE_CSOUNDPATCH = 2
  147. CHTSND_MODE_BASS = 3
  148.  
  149. CHTSND_MOD_FETCH = 0
  150. CHTSND_MOD_PRE = 1
  151. CHTSND_MOD_POST = 2
  152.  
  153. local CHTSND = {pitch = 100}
  154. CHTSND.__index = CHTSND
  155.  
  156. function chatsounds.NewChatSound(override)
  157.     return setmetatable(table.Copy(override) or {
  158.         ply = LocalPlayer(),
  159.         mode = CHTSND_MODE_EMITSOUND,
  160.         pitch = 100,
  161.         snd = "bitch.mp3"
  162.     }, CHTSND)
  163. end
  164.  
  165. function CHTSND:GetCSoundPatch() return self.csoundpatch end
  166. function CHTSND:HasMod() return self.mod ~= nil end
  167. function CHTSND:GetRealDuration() return self:GetListSound().length end
  168. function CHTSND:GetListSound() return self.sound end
  169. function CHTSND:NilCSoundPatch()
  170.     local csound = self:GetCSoundPatch()
  171.     if csound then
  172.         csound:Stop()
  173.         self.csoundpatch = nil
  174.     end
  175. end
  176. function CHTSND:GetModVar(mod)
  177.     if self:HasMod() then
  178.         for key, value in pairs(self.mod) do
  179.             if value.self.modifier == mod then
  180.                 return value.fetch_var
  181.             end
  182.         end
  183.     end
  184. end
  185.  
  186. function CHTSND:CallModFunction(enum)
  187.     if self:HasMod() then
  188.         for key, value in pairs(self.mod) do
  189.             if enum == CHTSND_MOD_FETCH and value.self.fetch then
  190.                 return value.self.fetch(self)
  191.             elseif enum == CHTSND_MOD_PRE and value.self.pre then
  192.                 return value.self.pre(self)
  193.             elseif enum == CHTSND_MOD_POST and value.self.post then
  194.                 return value.self.post(self)
  195.             end
  196.         end
  197.     end
  198. end
  199.  
  200. AccessorFunc(CHTSND, "Think", "ThinkFunction")
  201. AccessorFunc(CHTSND, "duration", "Duration")
  202. AccessorFunc(CHTSND, "mode", "Mode")
  203. AccessorFunc(CHTSND, "pitch", "Pitch")
  204. --[[ function CHTSND:SetPitch(v)
  205.     debug.Trace()
  206.     pitch = v
  207. end ]]
  208. AccessorFunc(CHTSND, "volume", "Volume")
  209. AccessorFunc(CHTSND, "seed", "Seed")
  210. AccessorFunc(CHTSND, "snd", "SoundPath")
  211. AccessorFunc(CHTSND, "ply", "Player")
  212. AccessorFunc(CHTSND, "origin", "Origin")
  213. AccessorFunc(CHTSND, "index", "Index")
  214. AccessorFunc(CHTSND, "script", "Script")
  215. AccessorFunc(CHTSND, "key", "Key")
  216. AccessorFunc(CHTSND, "time", "Time")
  217.  
  218. chatsounds.chtsnd = CHTSND
  219.  
  220.  
  221. function chatsounds.AddModifier(symbol, type, callbacks)
  222.     callbacks.modifier = symbol
  223.     callbacks.type = type
  224.  
  225.     table.insert(c.Modifiers, callbacks)
  226. end
  227.  
  228. function chatsounds.InitializeSounds()
  229.     c.List = {}
  230.     c.Keys = {}
  231.  
  232.     include"chatsounds/sounds.lua"
  233.  
  234.     for _, file_name in ipairs(file.Find("sound/" .. c.AutoAddPath .. "*", true)) do
  235.         if file_name:find("[^%.]+%.[^%.]") then
  236.             c.List[file_name:match("([^%.]+)%."):lower()] = {{snd = c.AutoAddPath .. file_name}}
  237.         end
  238.     end
  239.  
  240.     for sound in pairs(c.List) do
  241.         table.insert(c.Keys, sound)
  242.     end
  243.  
  244.     table.sort(c.Keys, function(a, b) return #a > #b end)
  245.    
  246.     c.List = table.LowerKeyNames(c.List)
  247.     c.Keys = table.LowerKeyNames(c.Keys)
  248. end
  249.  
  250. chatsounds.InitializeSounds()
  251.  
  252. -- utils
  253. function chatsounds.GetSound(key)
  254.     return key and c.List[key:lower():Trim()]
  255. end
  256.  
  257. function chatsounds.CRCRandom(seed, min, max)
  258.     max = max +1
  259.     return util.CRC(seed)%(max-min) + min
  260. end
  261.  
  262. function chatsounds.GetRandomSound(sounds, seed)
  263.     return seed and sounds[c.CRCRandom(seed, 1, #sounds)] or table.Random(sounds)
  264. end
  265.  
  266. function chatsounds.GetRandomSoundFromKey(key, seed)
  267.     local sounds = c.GetSound(key)
  268.     if not sounds then print("chatsounds could not find key", key, c.List[key]) return end
  269.     return seed and sounds and sounds[c.CRCRandom(seed, 1, #sounds)] or table.Random(sounds)
  270. end
  271.  
  272. function chatsounds.GeneratePitch(sound, seed)
  273.     return sound and sound.pitch or (c.BasePitch + c.CRCRandom(seed, -c.PitchRange, c.PitchRange))
  274. end
  275.  
  276. function chatsounds.IsMP3(path)
  277.     return path:find(".mp3", 0, true) ~= nil or false
  278. end
  279.  
  280. function chatsounds.GetSoundDuration(sound)
  281.     if GetSoundDuration and c.IsMP3(sound) then
  282.         return (GetSoundDuration("sound/"..sound) or 0.1) - c.SubMP3Duration
  283.     else
  284.         local duration = SoundDuration(sound)
  285.         return duration ~= 0 and duration or 1
  286.     end
  287. end
  288.  
  289. function chatsounds.SoundExists(path)
  290.     if c.ExistsCache[path] then return false end
  291.  
  292.     if not file.Exists("sound/" .. path, true) then
  293.         c.ExistsCache[path] = true
  294.         return false
  295.     end
  296.     return true
  297. end
  298.  
  299. function chatsounds.ClearExistsCache()
  300.     c.ExistsCache = {}
  301. end
  302.  
  303. function chatsounds.StopAllSounds()
  304.     for id in pairs(c.Timers) do
  305.         timer.Destroy("csp_" .. id)
  306.         c.Timers[id] = nil
  307.     end
  308.     RunConsoleCommand("stopsounds")
  309. end
  310.  
  311. function chatsounds.GetScriptFromText(text)
  312.     if not text then return end
  313.  
  314.     local result = {}
  315.     local original_text = text
  316.     text = text:lower() .. " "
  317.     for _, key in ipairs(c.Keys) do
  318.         local fstart, fend, match
  319.         local iteration = 0
  320.         repeat fstart, fend, match = text:find(--[[ c.PuncStart ..  ]]key:lower() --[[ .. c.PuncEnd ]], nil, true)
  321.         iteration = iteration + 1
  322.             if fstart then
  323.  
  324.                 local offset = 0
  325.                 local mod
  326.  
  327.                 for index, data in SortedPairs(c.Modifiers, true) do
  328.                     --if mod then continue end
  329.                     --print(text:sub(fend+offset, fend+2+offset))
  330.                     if text:sub(fend+offset, fend+2+offset):find(data.modifier, nil, true) then
  331.                         --print(text)
  332.                         local var =
  333.                             data.type == "number" and
  334.                                 tonumber(text:match("([0-9%.]+)", fend+1) or nil)
  335.                         or
  336.                             data.type == "args" and
  337.                                 tostring(text:match("([0-9%.0-9%.]+)", fend+1) or nil)
  338.                         --[[ or
  339.                             not data.type and data.fetch and data.fetch(text) ]]
  340.  
  341.                         if var then
  342.                             offset = #tostring(var) + 1
  343.                             print(data.type, type(var), data.modifier, var, offset)
  344.                             mod = mod or {}
  345.                             mod[index] = {
  346.                                 fetch_var = data.type and data.fetch and (data.type == "args" and data.fetch(unpack(string.Explode(".", var))) or data.fetch(var)) or var,
  347.                                 self = data,
  348.                             }
  349.                             PrintTable(mod)
  350.                         elseif data.type == "none" then
  351.                             offset = 1
  352.                             mod = mod or {}
  353.                             mod[index] = {
  354.                                 fetch_var = "nil",
  355.                                 self = data,
  356.                             }
  357.                         end
  358.                     end
  359.                 end
  360.  
  361.                 table.insert(result, {
  362.                     pos = fstart,
  363.                     key = key,
  364.                     mod = mod
  365.                 })
  366.  
  367.                 text = text:sub(1, fstart-1) .. ("_"):rep(key:len() + offset) .. text:sub(fend + offset + 1)
  368.             end
  369.             if iteration > 500 then print("chatsounds infinite loop") break end
  370.         until fstart == nil
  371.     end
  372.     -- Sort the results table by position of the word.
  373.     table.sort(result, function(a, b) return a.pos < b.pos end)
  374.    
  375.     return result
  376. end
  377.  
  378. function chatsounds.Say(ply, text, seed)
  379.     local sound = c.GetSound(text)
  380.  
  381.     if sound then
  382.         c.PlaySound(c.NewChatSound{
  383.             mode = CHTSND_MODE_EMITSOUND,
  384.             ply = ply,
  385.             snd = c.GetRandomSound(sound, seed).snd,
  386.             pitch = c.GeneratePitch(sound, seed),
  387.         })
  388.     return end
  389.    
  390.     --print("pre timer start")
  391.    
  392.     for key, text in pairs(string.Explode("|", text)) do
  393.         local script = c.GetScriptFromText(text)
  394.  
  395.         if script then
  396.             local volume = ({text:gsub("[!]", "")})[2] --<- ugh?
  397.             volume = volume ~= 0 and volume * c.ExclamationMultiplier or 1
  398.  
  399.             local time = 0
  400.  
  401.             for index, data in ipairs(script) do
  402.                 local sound = c.GetRandomSoundFromKey(data.key, seed .. index)
  403.  
  404.                 if sound then
  405.                     local chtsnd = c.NewChatSound(sound)
  406.                     if not chtsnd:GetDuration() then
  407.                         chtsnd:SetDuration(c.GetSoundDuration(chtsnd:GetSoundPath()))
  408.                         if not sound.length then sound.length = chtsnd:GetDuration() end
  409.                     end
  410.  
  411.                     chtsnd.sound = sound
  412.                     chtsnd.script = script
  413.                     chtsnd.key = data.key
  414.                     chtsnd.mod = data.mod
  415.                     --print("nooo", chtsnd.mod)
  416.                    
  417.                     chtsnd:SetDuration(chtsnd:GetDuration() / (c.BasePitch / 100))
  418.                     chtsnd:SetPitch(c.GeneratePitch(sound, seed .. index))
  419.                     chtsnd:SetPlayer(ply)
  420.                     chtsnd:SetVolume(volume)
  421.                     chtsnd:SetMode(CHTSND_MODE_EMITSOUND)
  422.                     chtsnd:SetIndex(index)
  423.                     chtsnd:SetSeed(seed)
  424.  
  425.                     chtsnd:CallModFunction(CHTSND_MOD_PRE)
  426.  
  427.                     time = (chtsnd:GetTime() or time) + chtsnd:GetDuration()
  428.  
  429.                     local id = chtsnd:GetKey() .. time
  430.                    
  431.                     --print(chtsnd:GetKey(), time, chtsnd:GetDuration())
  432.                    
  433.                     timer.Create("csp_" .. id , time - chtsnd:GetDuration(), 1, c.PlaySound, chtsnd, id)
  434.                     c.Timers[id] = true
  435.                 end
  436.  
  437.                 if index > 100 then print("chatsounds tried to play over 100 sounds ", ply) break end
  438.             end
  439.         end
  440.     end
  441.     --print("pre timer end")
  442. end
  443.  
  444. function chatsounds.PlaySound(chtsnd, id)
  445.     --print("during timer", chtsnd:GetKey(), chtsnd:GetPitch(), chtsnd:GetDuration(), chtsnd:GetMode())
  446.    
  447.     --if chtsnd:GetPlayer():Nick() == "CapsAdmin" then PrintTable(chtsnd) end
  448.     if chtsnd:GetPitch() <= 0 or not c.SoundExists(chtsnd:GetSoundPath()) then return end
  449.  
  450.     local volume = (chtsnd:GetVolume() or 1)
  451.     local sound_level = math.Clamp(c.BaseVolume + volume, c.BaseVolume, 160)
  452.     local pitch = math.min(chtsnd:GetPitch(), 255)
  453.  
  454.     if chtsnd:GetMode() == CHTSND_MODE_WORLDSOUND then
  455.         for i = 1, volume do
  456.             WorldSound(chtsnd:GetSoundPath(), chtsnd:GetOrigin() or chtsnd:GetPlayer():GetShootPos(), sound_level, pitch)
  457.         end
  458.     elseif chtsnd:GetMode() == CHTSND_MODE_CSOUNDPATCH then
  459.        
  460.         local csp = c.CSoundPatches[chtsnd.ply:UniqueID()]
  461.        
  462.         if csp then
  463.             csp:Stop()
  464.             c.CSoundPatches[chtsnd.ply:UniqueID()] = nil
  465.         end
  466.        
  467.         chtsnd.csoundpatch = CreateSound(chtsnd.ply, chtsnd.snd)
  468.         chtsnd.csoundpatch:SetSoundLevel(sound_level)
  469.         chtsnd.csoundpatch:PlayEx(1, pitch)
  470.  
  471.         c.CSoundPatches[chtsnd.ply:UniqueID()] = chtsnd.csoundpatch
  472.        
  473.         if chtsnd:GetThinkFunction() then
  474.             local unique = "chatsounds_" .. tostring(chtsnd)
  475.             local endtime = RealTime() + chtsnd:GetDuration()
  476.             hook.Add("Think", unique, function()
  477.                 if endtime and endtime > RealTime() then
  478.                     chtsnd.Think(chtsnd, (endtime - RealTime()) / chtsnd:GetDuration())
  479.                 else
  480.                     hook.Remove("Think", unique)
  481.                    
  482.                     chtsnd:NilCSoundPatch()
  483.                 end
  484.             end)
  485.         end
  486.  
  487.     elseif chtsnd:GetMode() == CHTSND_MODE_EMITSOUND then
  488.         for i = 1, volume do
  489.             chtsnd:GetPlayer():EmitSound(chtsnd:GetSoundPath(), sound_level, pitch)
  490.         end
  491.     end
  492.    
  493.     chtsnd:CallModFunction(CHTSND_MOD_POST)
  494.        
  495.     if id then c.Timers[id] = nil end
  496. end
  497.  
  498. function chatsounds.ReceiveUsermessage(data)
  499.     if not c.Enabled:GetBool() then return end
  500.  
  501.     local ply = data:ReadEntity()
  502.     if not IsValid(ply) then return end
  503.  
  504.     local seed = ply:Nick() .. (data:ReadChar() + 127)
  505.     local text = data:ReadString()
  506.  
  507.     c.Say(ply, text, seed)
  508. end
  509. usermessage.Hook("chatsounds", chatsounds.ReceiveUsermessage)
  510.  
  511. function chatsounds.ReceiveRandomUsermessage(data)
  512.     if not c.Enabled:GetBool() then return end
  513.  
  514.     local ply = data:ReadEntity()
  515.     if not IsValid(ply) then return end
  516.  
  517.     local seed = ply:Nick() .. (data:ReadChar() + 127)
  518.  
  519.     local sounds = table.ClearKeys(table.Copy(c.List))
  520.     local sound = c.GetRandomSound(sounds[c.CRCRandom(seed, 1, #sounds)], seed)
  521.     sound.pitch = c.GeneratePitch(sound, seed)
  522.     sound.ply = ply
  523.     sound.mode = "EmitSound"
  524.            
  525.     c.PlaySound(sound)
  526. end
  527. usermessage.Hook("chatsounds_random", chatsounds.ReceiveRandomUsermessage)
  528.  
  529. local function FindValue(tbl, str)
  530.     for k,v in pairs(tbl) do
  531.         if v.snd and v.snd:find(str) then return true end
  532.     end
  533. end
  534.  
  535. function chatsounds.Find(_,_, args)
  536. --[[    if not args[1] then
  537.         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.")
  538.         return;
  539.     end
  540.     for key in pairs(c.List) do
  541.         if string.find(key, args[1]) then
  542.             MsgN(key)
  543.         end
  544.     end ]]
  545.     if not args[1] then
  546.         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.")
  547.         return;
  548.     end
  549.     for key, data in pairs(c.List) do
  550.         if key:find(args[1]) or args[2] and FindValue(data, args[1]) then
  551.             print([["]] .. key .. [["]] ..  " = {")
  552.             for key, value in pairs(data) do
  553.                 if value.snd then
  554.                     MsgN("\t[" .. key .. "] = " .. tostring(value.snd))
  555.                 end
  556.                
  557.                 if key == #data then
  558.                     print("}\n")
  559.                 end
  560.             end
  561.         end
  562.     end
  563. end
  564. concommand.Add("chatsounds_find", chatsounds.Find)
  565.  
  566. local function FindValue(tbl, str)
  567.     for k,v in pairs(tbl) do
  568.         if v.snd and v.snd:find(str, nil, true) then return true end
  569.     end
  570. end
  571.  
  572. function chatsounds.Dump(_,_,args)
  573.     for key, data in pairs(c.List) do
  574.         if args[1] == "mp3" and FindValue(data, ".wav") then continue end
  575.         if args[1] == "wav" and FindValue(data, ".mp3") then continue end
  576.         print([["]] .. key .. [["]] ..  " = {")
  577.         for key, value in pairs(data) do
  578.             if value.snd then
  579.                 MsgN("\t[" .. key .. "] = " .. tostring(value.snd))
  580.             end
  581.            
  582.             if key == #data then
  583.                 print("}\n")
  584.             end
  585.         end
  586.     end
  587. end
  588. concommand.Add("chatsounds_dump", chatsounds.Dump)
  589.  
  590. if file.Exists("lua/includes/modules/gm_duration.dll", true) then
  591.     -- this is at the bottom in case it throws an error halting the whole script
  592.     require("duration")
  593. end
  594.  
  595. --Say("CHATSOUNDS RUNSTRING'd")
Advertisement
Add Comment
Please, Sign In to add comment