Files
Terrarum/src/net/torvald/spriteanimation/SpriteAnimation.kt
Song Minjae 60c81baffd more SANE way of drawing elements (actor zoom broken)
Former-commit-id: edca68d5d25c4e45be209da46449ad9e0b2c35cf
Former-commit-id: 38858d235b553e41bea7d506b402fe0a92a1aab0
2016-10-31 14:10:37 +09:00

259 lines
6.7 KiB
Kotlin

/* Original code author: Sean Laurvick
* This code is based on the original author's code written in Lua.
*/
package net.torvald.spriteanimation
import net.torvald.terrarum.StateInGame
import net.torvald.terrarum.Terrarum
import com.jme3.math.FastMath
import org.newdawn.slick.Graphics
import org.newdawn.slick.Image
import org.newdawn.slick.SlickException
import org.newdawn.slick.SpriteSheet
class SpriteAnimation @Throws(SlickException::class)
constructor() {
private var spriteImage: SpriteSheet? = null
var height: Int = 0
private set
var width: Int = 0
private set
private var currentFrame = 1
private var currentRow = 1
private var nFrames: Int = 0
private var nRows: Int = 0
private var delay = 200
private var delta = 0
private val looping = true
private var animationRunning = true
private var flipHorizontal = false
private var flipVertical = false
private var visible = false
private val offsetX = 0
private val offsetY = 0
private var prevScale = 1f
private var currentImage: Image? = null
/**
* Sets spritesheet.
* MUST be called AFTER setDimension.
* @param imagePath path to the sprite sheet image.
* *
* @throws SlickException
*/
@Throws(SlickException::class)
fun setSpriteImage(imagePath: String) {
spriteImage = SpriteSheet(imagePath, this.width, this.height)
}
/**
* Sets animation delay. Will default to 200 if not called.
* @param delay in milliseconds
*/
fun setDelay(delay: Int) {
this.delay = delay
}
/**
* Sets sprite dimension. This is necessary.
* @param w
* *
* @param h
*/
fun setDimension(w: Int, h: Int) {
width = w
height = h
}
/**
* Sets sheet rows and animation frames. Will default to
* 1, 1 (still image of top left from the sheet) if not called.
* @param rows
* *
* @param frames
*/
fun setRowsAndFrames(rows: Int, frames: Int) {
nRows = rows
nFrames = frames
}
fun setAsVisible() {
visible = true
}
fun setAsInvisible() {
visible = false
}
fun update(delta: Int) {
if (animationRunning) {
//skip this if animation is stopped
this.delta += delta
//check if it's time to advance the frame
if (this.delta >= this.delay) {
//if set to not loop, keep the frame at the last frame
if (this.currentFrame == this.nFrames && !this.looping) {
this.currentFrame = this.nFrames - 1
}
//advance one frame, then reset delta counter
this.currentFrame = this.currentFrame % this.nFrames + 1
this.delta = 0
}
}
}
/**
* Render to specific coordinates. Will assume bottom-center point as image position.
* Will round to integer.
* @param g
* *
* @param posX bottom-center point
* *
* @param posY bottom-center point
* *
* @param scale
*/
@JvmOverloads fun render(g: Graphics, posX: Float, posY: Float, scale: Float = 1f) {
// Null checking
if (currentImage == null) {
currentImage = getScaledSprite(scale)
}
if (visible) {
// re-scale image if scale has been changed
if (prevScale != scale) {
currentImage = getScaledSprite(scale)
prevScale = scale
}
val flippedImage = currentImage!!.getFlippedCopy(flipHorizontal, flipVertical)
flippedImage.startUse()
flippedImage.drawEmbedded(
Math.round(posX).toFloat(),
FastMath.floor(posY).toFloat(),
FastMath.floor(width * scale).toFloat(),
FastMath.floor(height * scale).toFloat()
)
flippedImage.endUse()
}
}
fun switchSprite(newRow: Int) {
currentRow = newRow
//if beyond the frame index then reset
if (currentFrame > nFrames) {
reset()
}
}
fun switchSprite(newRow: Int, newMax: Int) {
if (newMax > 0) {
nFrames = newMax
}
currentRow = newRow
//if beyond the frame index then reset
if (currentFrame > nFrames) {
reset()
}
}
fun switchSpriteDelay(newDelay: Int) {
if (newDelay > 0) {
delay = newDelay
}
}
fun switchSprite(newRow: Int, newMax: Int, newDelay: Int) {
if (newMax > 0) {
nFrames = newMax
}
if (newDelay > 0) {
delay = newDelay
}
currentRow = newRow
//if beyond the frame index then reset
if (currentFrame > nFrames) {
reset()
}
}
fun reset() {
currentFrame = 1
}
fun start() {
//starts the animation
animationRunning = true
}
fun start(selectFrame: Int) {
//starts the animation
animationRunning = true
//optional: seleft the frame no which to start the animation
currentFrame = selectFrame
}
fun stop() {
animationRunning = false
}
fun stop(selectFrame: Int) {
animationRunning = false
currentFrame = selectFrame
}
fun flip(horizontal: Boolean, vertical: Boolean) {
flipHorizontal = horizontal
flipVertical = vertical
}
fun flippedHorizontal(): Boolean {
return flipHorizontal
}
fun flippedVertical(): Boolean {
return flipVertical
}
private fun getScaledSprite(scale: Float): Image {
val selectedImage = spriteImage!!.getSprite(currentFrame - 1, currentRow - 1)
//Image selectedImage = sprites[currentRow - 1][currentFrame - 1];
// resample
/*float nearestResampleScale = (scale > 1) ? Math.round(scale) : 1;
float linearResampleScale = scale / nearestResampleScale;
// scale 1.8 -> resample in 2(nearest), then resample in 0.9(linear)
// scale by nearestResampleScale (2, 3, ...)
selectedImage.setFilter(Image.FILTER_NEAREST);
Image selImgNearestScaled = selectedImage.getScaledCopy(nearestResampleScale);
// scale by linearResampleScale (.x)
Image selImgLinearScaled;
if (scale % 1 > 0) {
selImgNearestScaled.setFilter(Image.FILTER_LINEAR);
selImgLinearScaled = selImgNearestScaled.getScaledCopy(linearResampleScale);
return selImgLinearScaled;
}
else {
return selImgNearestScaled;
}*/
selectedImage.filter = Image.FILTER_NEAREST
return selectedImage.getScaledCopy(scale)
}
}