View difference between Paste ID: hPHgy2iN and weMqyL0N
SHOW: | | - or go back to the newest paste.
1-
local print = epoe.Print
1+
2-
local me = me
2+
local sounddict = {
3-
-- config / gmod specific
3+
    ["hi"] = "",
4
    ["hi there"] = "",
5-
local SOUNDS = {
5+
    ["eat me"] = "",
6-
	hi = "ambient/levels/prison/radio_random11.wav",
6+
    ["eat"] = "",
7-
	oh = "ambient/energy/spark3.wav",
7+
    ["an eye for an eye"] = "",
8-
	boop = "buttons/button16.wav",
8+
    ["an eye for an apple or banana"] = "",
9-
	kick = "weapons/crossbow/hitbod1.wav",
9+
    ["orange"] = "",
10-
	beep = "npc/attack_helicopter/aheli_crash_alert2.wav",
10+
    ["cactus"] = "",
11-
	snare = "physics/flesh/flesh_strider_impact_bullet3.wav",
11+
    ["cactus please"] = "",
12-
	wood = "physics/wood/wood_box_impact_bullet4.wav"
12+
    ["no sir"] = "",
13
}
14
15-
local MODIFIERS = {
15+
local testphraselist = {
16-
	["&"] = {
16+
    "hello",
17-
		args = {
17+
    "say hi",
18-
			function(dsp) return math.Clamp(tonumber(dsp) or 0, 0, 128) end
18+
    "hi there",
19-
		},
19+
    "oh hi there",
20-
		
20+
    "an eye for an eye for an eye for an apple or banana",
21-
		start = function(self, dsp)
21+
    "an eye for an apple or banana",
22-
			LocalPlayer():SetDSP(dsp)
22+
    "cactus orange no sir",
23-
		end,	
23+
    "eat me hi",
24-
		
24+
25-
		stop = function(self, dsp) 
25+
26-
			LocalPlayer():SetDSP(0)
26+
local soundtree = {}
27-
		end,
27+
local soundtree_filekey = newproxy and newproxy() or {}
28-
	},
28+
for sound_patt, sound_file in pairs(sounddict) do
29-
	["=="] = {
29+
    local currnode = soundtree
30-
		args = {
30+
    for part in string.gmatch(sound_patt, "([a-z_]+)") do
31-
			function(time) return tonumber(time) or 1 end
31+
        -- TODO: preprocess part to ignore case and such
32-
		},
32+
        currnode[part] = currnode[part] or {}
33-
		
33+
        currnode = currnode[part]
34-
		init = function(self, time)
34+
    end
35-
			self.duration = time
35+
    currnode[soundtree_filekey] = string.gsub(sound_patt, " ", "_")..".wav"
36-
		end,	
36+
37-
	},
37+
38-
	["--"] = {
38+
function stuff(soundtree, phrase)
39-
		args = {
39+
    -- TODO: preprocess phrase to ignore case and such
40-
			function(stop_percent) return tonumber(stop_percent) or 100 end
40+
    
41-
		},
41+
    local foundlist = {}
42-
		
42+
    
43-
		init = function(self, stop_percent)
43+
    local stack_i = 0
44-
			self.duration = self.duration * (stop_percent / 100)
44+
    local stack_eposlist = { [0] = 1 }
45-
		end,	
45+
    local stack_nodelist = { [0] = soundtree }
46-
	},
46+
    local complete, atend = false, false
47-
	["="] = {
47+
    local backtrack = false
48-
		args = {
48+
    repeat
49-
			function(time) return tonumber(time) or 0 end
49+
        local spos = stack_eposlist[stack_i]
50-
		},
50+
        local node = stack_nodelist[stack_i]
51-
		
51+
        stack_i = stack_i+1
52-
		init = function(self, time)
52+
        -- get next word
53-
			self.duration = time
53+
        local word_s, word_e, mods = string.find(phrase, "([a-z_]+)(%p%d)+", spos, true)
54-
		end,
54+
        if word_s then
55-
	},
55+
            -- we have a next word
56-
	["%"] = {
56+
            local word = string.sub(phrase, word_s, word_e)
57-
		args = {
57+
            local subnode = node[word] -- (you could do pattern matching here)
58-
			function(pitch) return math.Clamp(tonumber(pitch) or 100, 0, 255) end,
58+
            if subnode then
59-
			function(time) return tonumber(time) or 0 end,
59+
                -- we have a pattern that matches this next word
60-
		},
60+
                -- (could be first, middle or last word in pattern!)
61-
		
61+
                stack_eposlist[stack_i] = word_e+1
62-
		init = function(self, pitch, time)			
62+
                stack_nodelist[stack_i] = subnode
63-
			self.duration = self.duration / (pitch / 100)							
63+
                -- continue
64-
			self.duration = self.duration + time
64+
            else
65-
		end,
65+
                -- there is no continuing pattern that matches this word
66-
		
66+
                if stack_i == 1 then
67-
		think = function(self, pitch, time)			
67+
                    -- this is the first word or next unmatched word and no pattern starts with it
68-
			if self.csp then
68+
                    -- so jump to next word
69-
				self.csp:ChangePitch(pitch, time)
69+
                    stack_i = 0
70-
			end
70+
                    stack_eposlist[0] = word_e+1
71-
		end,
71+
                else
72-
	},
72+
                    -- we were matching a pattern, see what comes of it
73-
	["^"] = {
73+
                    -- (this word did not continue any pattern, so it's not considered)
74-
		args = {
74+
                    backtrack = true
75-
			function(volume) return math.Clamp(tonumber(volume) or 100, 0, 100) end,
75+
                end
76-
			function(time) return tonumber(time) or 0 end,
76+
            end
77-
		},
77+
        else
78-
		
78+
            -- there is no next word, check for patterns
79-
		think = function(self, volume, time)
79+
            -- (last few words may be rechecked if there is no sound for their nodes)
80-
			if self.csp then
80+
            atend = true
81-
				if self.source_entity == LocalPlayer() then
81+
            backtrack = true
82-
					volume = volume / 2 
82+
        end
83-
				end
83+
        if backtrack then
84-
													
84+
            -- find the longest pattern on the stack that has a sound associated
85-
				self.csp:ChangeVolume(volume / 100, time)
85+
            for stack_j = stack_i-1, 1, -1 do
86-
			end
86+
                -- work backwards through each word to see if it has a sound
87-
		end,
87+
                local node = stack_nodelist[stack_j]
88-
	}
88+
                local soundfile = node[soundtree_filekey]
89
                if soundfile then
90
                    -- this word has a sound
91-
local function PLAY_SOUND(self)
91+
                    table.insert(foundlist, soundfile)
92-
	local csp = CreateSound(me, self.path) 
92+
                    -- TODO: handle mods here
93-
	csp:PlayEx(1, 0)
93+
                    
94-
	self.source_entity = me
94+
                    if stack_j == stack_i and atend then
95-
	self.csp = csp
95+
                        -- we consumed the last word
96
                        complete = true
97
                    else
98-
local function STOP_SOUND(self)
98+
                        -- start searching for new patterns
99-
	if self.csp then
99+
                        stack_i = 0
100-
		self.csp:Stop()
100+
                        -- resume from word after longest pattern with a sound
101-
	end
101+
                        stack_eposlist[0] = stack_eposlist[stack_j+1]
102
                    end
103
                    -- stop backtracking
104-
local function GET_DURATION(self)
104+
                    break
105-
	return SoundDuration(self.path)	or 0
105+
                end
106
            end
107
            backtrack, atend = false, false
108-
--
108+
        end
109-
local cache = {}
109+
    until complete
110
    
111-
local function parse(str)
111+
    return foundlist
112-
	if cache[str] then return cache[str] end
112+
113-
	
113+
114-
	local out = {}
114+
for testphrase_i = 1, #testphraselist do
115-
	
115+
    local testphrase = testphraselist[testphrase_i]
116-
	-- lowercase it so we don't have to deal with case sensitivity
116+
    local out = stuff(soundtree, testphrase)
117-
	str = str:lower()
117+
    for sound_i = 1, #out do
118-
	
118+
        print(out[sound_i])
119-
	-- add a last space so it matches the end as well
119+
    end
120-
	str = str .. " "
120+
end