ID:139115
 
combo
var
list/combo_list = list(/combo/test_combo,/combo/test_combo_2) //This is a list of all of the combos possible
list/current_combo = list() //This is your current combo string
list/needed_combo = list() //This is the combo string you need. Define these in each combo.
needed_len
max_len = 4 //Set this to the length of the longest combo made. If their count is over this, it resets.
player/parent_mob //The owner of the combo
timeout = 10 //Ticks until the combo resets
time = 0
executing = FALSE
reset = FALSE
proc
add_key(k)
if(k=="A"||k=="S"||k=="D"||k=="W")
time=0
if(current_combo.len)
current_combo+=k
if(current_combo.len > max_len)
current_combo = list()
reset()
return FALSE
check_combos()
else
current_combo+=k
start()
if(!executing) return TRUE
return FALSE
check_combos()
var/list/l=list()
var/combo/C
var/combo/c
for(var/v=1 to combo_list.len)
c = combo_list[v]
C = new c(parent_mob)
for(var/i=1 to current_combo.len)
if(current_combo[i]==C.needed_combo[i])
else l+="F"
if(!l.Find("F") && current_combo.len==C.needed_combo.len)
C.execute()
reset()
C = null
break


Well guys, when I use this bit of code, my combos get wacky. It seems to be the check_combos() proc.

The combos I have set up have their needed_combo var defined like this:
    test_combo
needed_combo = list("W","A","S","D")


And the add_key() proc adds the key they hit, either "W","A","S",or "D", to the current_combo list. In check_combos(), it should see if these lists are identical(I tried a simple if(current_combo==C.needed_combo), but it appears that wont work with lists.), and if they are, execute the combo.

I am having 3 main issues:
1. The second test combo("W","W","A","A") will not execute, yet the first("W","A","S","D") will.
2. The attacking can be a bit laggy, which I am assuming is because of the check_combos() proc taking too much time to search through. I need some help streamlining this.
3. When the combo does execute, it sets executing to TRUE, then back to FALSE when it is over. I run a check for this, so it should only display "You got hit"(Or whatever) if you are not executing. However, it still displays the message along with the message the combo gives upon execution. I assume this is also because of the bad setup in check_combos().

I guess I just need a way to compare these lists a lot faster. Any suggestions?
I would advise against using lists at all here and use strings instead. Also, I don't think add_key(), check_combos(), or the list of possible combos should be part of the datum; they should be part of a separate datum belonging to each mob, or possibly just part of the mob itself.

One thing you might run into in a combo system is that if you have a sequence that isn't supported, you need to clear out the leading keys until you have something that could potentially be a combo. That might be something you're running into here.

A good combo checker should look like this when adding a key:

- Add the key to the current string. If a timer is waiting, clear it.
- Is there any combo that can start with this string?
NO:
- Taking off one letter at a time from the end, look for a valid combo or single-key move.
- Execute (or queue up) the move and take it off the beginning of the string.
- Start up the timeout timer (maybe 0.5s, perhaps less) and return to step 2 when it fires.
YES:
- Is there only one combo that can start with the current string, and does it match exactly?
YES:
- Execute (or queue up) the combo move and clear the string.
- Start up the timeout and return to step 2 when it fires.
NO:
- Start up the timeout and if it fires before new keys are added, execute and clear the combo.

So my code for a combo system would look more like this:

combo_manager
var/mob/M // owner
var/current = ""
var/list/available // list of moves
var/timeout = 3 // 3 ticks before move executes
var/timer_id = 0

proc/AddKey(k)
++timer_id // kill any timers waiting
current += k
while(current) // this is needed for the no-match case
var/found, c, count=0
if(current in available) found = current
for(c in available)
if(findtextEx(c, current) == 1)
++count
// exact match, no other combo could start with this
if(found && count == 1)
M.QueueMove(found)
current = ""
return
// combos could start with this
if(count)
var/id = timer_id
spawn(timeout) Timeout(id)
return
// no matches found -- look for matches in past moves
var/c = current
while(c)
if(length(c) == 1 || (c in available))
M.QueueMove(c)
current = copytext(current, length(c)+1) // strip off this combo
break // let the while(current) loop start us over and look for more combos
c = copytext(c, 1, length(c)) // strip off last key and try again

proc/Timeout(id)
if(timer_id != id) return // this timer has been canceled
if(!M) return // doesn't matter if mob is gone
// this means the user timed out so clear the queue!
while(current)
// look for any combos at the beginning, starting as long as possible
var/c = current
while(c)
if(length(c) == 1 || (c in available))
M.QueueMove(c)
current = copytext(current, length(c)+1) // strip off this combo
break // let the while(current) loop start us over and look for more combos
c = copytext(c, 1, length(c)) // strip off last key and try again


This system is setup to look for the longest combo possible and run it. If the current input isn't the beginning of a combo, it will look for shorter and shorter sequences at the beginning that might represent a valid combo or single move, until it finds one; then it rechecks to see if another combo might be waiting to run. If your current input is a valid combo and nothing else starts with that, it runs immediately. The only other option is that your input could represent the beginning of another combo (even if it is a complete combo itself), in which case it starts up a timer that waits to see if you're done pressing keys. If the timer fires, it assumes everything you've queued up is intended as a move so it goes through all the logic above but with the goal of totally clearing out the queue.

Lummox JR
In response to Lummox JR
Thanks a lot, I understand how this should work a lot better now. I also like your idea of searching for the longest combo first. I'll try a few things and come back here if anything else arises.
Thanks again!