Combinations [code review]

A bit of background: I’m designing a system to handle combinations. A combination is simply a sequence of keys that must be pressed (or held) in a specific order. Think of combinations in various fighting games. The combinations are defined in XML and loaded at runtime. As of now, I’m happy to say the system works perfectly.



I’m looking for comments on my combination handling code. The code I’ve included is the method I use to determine if a combination has been executed. I’m interested in simple critique: is it a good method? Where can I improve? Are there alternative/better ways to do this? Any glaring optimizations I’ve overlooked? Thanks in advance!



Some things to note about the code: the setState(int) method resets the statetick; states[] is the array of input states in chronological order that must be satisfied for the combination to be completed; curState is the current input state being satisfied; invalidate() ends the combination and resets the state to START_COMBINATION. Also note that an input state is valid if the desired keys, and only the desired keys are pressed.



Here’s an example combination entry in XML, just if you’re curious:


<!-- Whirlwind: quick to execute, damages all nearby enemies -->
<combination name="whirlwind.combo"
                     attack="slash.attacks.WhirlwindAttack"
                     delay="60" >
   <button id="Slash" />
   <button id="Slash-Chop" />
   <button id="Slash" t="5" /> (t: minimum duration of the input state)
   <button id="Thrust" />
</combination>



So, here's the relevant code:


public void update() {
    //Ignore everything while we're in the cooldown period between attacks
    if (delayTick > 0) {
        delayTick--;
        return;
    }
   
    statetick++;
    switch (comboState) {
    case START_COMBINATION:
        if (states[0].isValid())
            setState(WAIT_DURATION);
       
        break;
    case NEW_STATE:
        curState++;
       
        //We've satisfied the entire series of states, ATTAAAACK!!!
        if (curState >= states.length - 1) {
            attack();
            break;
        }
       
        //Entered a new state; start waiting for the minimum duration
        setState(WAIT_DURATION);
        break;
       
    case WAIT_DURATION:
        //Player did not hold input state for minimum duration; invalidate
        if (statetick < states[curState].minDuration && !states[curState].isValid())
            invalidate();
        //Player held input state for too long; invalidate
        if (statetick > states[curState].minDuration + TIMEOUT_TOLERANCE
            && states[curState].isValid())
                invalidate();
        //Current state no longer valid, transition to next state
        if (!states[curState].isValid())
            setState(TRANSITION);
        break;
       
    case TRANSITION:
        //Next state is valid, make current
        if (states[curState+1].isValid()) {
            setState(NEW_STATE);
            break;
        }
        //Player used controls other than the next input state; invalidate
        if (Bindings.get().getCurrentInputState() != 0) //TODO: the 0 is kinda ugly
            invalidate();
        //Player took too long going to the next state; invalidate
        if (statetick > TIMEOUT_TOLERANCE)
            invalidate();
        break;
       
    default:
        assert false; //should never happen!
    }
}



I appologize for the rather excessive length, but I'd really appreciate it if the community comments/offers suggestions. Thank you! :)

- Eric

I’ve never done something like that before, but the way you want handle it looks pretty good. It doesn’t seem to be too resources expensive.