sourceafFantomMappy::AnimBlock.fan


** Represents a 'AnimBlock' as used by Mappy. 
@Js
class AnimBlock {
    
    ** The function that defines the animation frame sequence.
    ** Defaults to 'AnimFunc.none'
    |AnimBlock| animFunc    := AnimFunc().none
    
    ** The 'delay'is the number of times `AnimBlock.updateAnimation` needs to be called before the 
    ** 'currentFrameIndex' changes. In effect this controls the speed of the animation.
    Int delay {
        set { 
            if (it < 0) throw ArgErr("delay '$it' should be positive")
            &delay = it 
        }
    }
    
    ** Returns the number of times `AnimBlock.updateAnimation` has to be called before the current 
    ** frame is changed to the next in sequence. 
    ** 
    ** If 'delayCountdown == 0' the current frame will change on the next call to 
    ** `AnimBlock.updateAnimation` and 'delayCountdown' will reset to 'delay'.
    Int delayCountdown {
        set {
            if (it < 0) throw ArgErr("delayCountdown '$it' should be positive")
            &delayCountdown = it 
        }
    }
    
    ** User data.
    Int? userData
    
    ** The current frame index.
    Int frameIndex {
        set { 
            if (it < 0)             throw ArgErr("frameIndex '$it' should be positive") 
            if (it > frames.size)   throw ArgErr("frameIndex '$it' can not be more than no. of frames '$frames.size'") 
            &frameIndex = it 
        }
    }

    ** Returns the current frame (image index). If this AnimBlock has no frames then 0 is returned
    Int frame {
        get {
            ((frameIndex < 0) || (frameIndex >= frames.size)) ? 0: frames[frameIndex]
        }
        private set
    }

    ** An array of frames (image indexes) that represent the animation sequence
    Int[] frames := [,]

    
    
    // ---- Public Methods ----------------------------------------------------
    
    ** Increments the frame index. 
    ** Should the index exceed the maximum ( 'frames.size' ) then the given 'clipFunc' is called.
    Void incFrameIndex(|->| clipFunc) {
        &frameIndex++
        if (&frameIndex >= frames.size)
            clipFunc()
    }

    ** Decrements the frame index. 
    ** Should the index drop below zero then the given 'clipFunc' is called.
    Void decFrameIndex(|->| clipFunc) {
        &frameIndex--
        if (&frameIndex < 0)
            clipFunc()
    }
    
    ** Counts down the delay and updates the current frame to the next in the animation sequence.
    Void updateAnimation() {
        &delayCountdown--
        if (&delayCountdown < 0) {
            &delayCountdown = delay
            
            // let the animFunc do all the leg work
            animFunc(this)
        }
    }
}