mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-09-25 04:36:12 -04:00
256 lines
5.3 KiB
Go
256 lines
5.3 KiB
Go
|
package d2asset
|
||
|
|
||
|
import (
|
||
|
"errors"
|
||
|
"image/color"
|
||
|
|
||
|
"github.com/OpenDiablo2/D2Shared/d2data/d2dc6"
|
||
|
"github.com/OpenDiablo2/D2Shared/d2helper"
|
||
|
"github.com/OpenDiablo2/OpenDiablo2/d2corehelper"
|
||
|
|
||
|
"github.com/hajimehoshi/ebiten"
|
||
|
)
|
||
|
|
||
|
type playMode int
|
||
|
|
||
|
const (
|
||
|
playModePause playMode = iota
|
||
|
playModeForward
|
||
|
playModeBackward
|
||
|
)
|
||
|
|
||
|
type animationFrame struct {
|
||
|
width int
|
||
|
height int
|
||
|
offsetX int
|
||
|
offsetY int
|
||
|
|
||
|
image *ebiten.Image
|
||
|
}
|
||
|
|
||
|
type animationDirection struct {
|
||
|
frames []*animationFrame
|
||
|
}
|
||
|
|
||
|
type Animation struct {
|
||
|
directions []*animationDirection
|
||
|
frameIndex int
|
||
|
directionIndex int
|
||
|
lastFrameTime float64
|
||
|
|
||
|
compositeMode ebiten.CompositeMode
|
||
|
colorMod color.Color
|
||
|
|
||
|
playMode playMode
|
||
|
playLength float64
|
||
|
playLoop bool
|
||
|
}
|
||
|
|
||
|
func createAnimationFromDC6(dc6 *d2dc6.DC6File) (*Animation, error) {
|
||
|
animation := &Animation{
|
||
|
playLength: 1.0,
|
||
|
playLoop: true,
|
||
|
}
|
||
|
|
||
|
for frameIndex, frame := range dc6.Frames {
|
||
|
image, err := ebiten.NewImage(int(frame.Width), int(frame.Height), ebiten.FilterNearest)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
if err := image.ReplacePixels(frame.ColorData()); err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
directionIndex := frameIndex / int(dc6.FramesPerDirection)
|
||
|
if directionIndex >= len(animation.directions) {
|
||
|
animation.directions = append(animation.directions, new(animationDirection))
|
||
|
}
|
||
|
|
||
|
direction := animation.directions[directionIndex]
|
||
|
direction.frames = append(direction.frames, &animationFrame{
|
||
|
width: int(frame.Width),
|
||
|
height: int(frame.Height),
|
||
|
offsetX: int(frame.OffsetX),
|
||
|
offsetY: int(frame.OffsetY),
|
||
|
image: image,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
return animation, nil
|
||
|
}
|
||
|
|
||
|
func (a *Animation) clone() *Animation {
|
||
|
animation := *a
|
||
|
return &animation
|
||
|
}
|
||
|
|
||
|
func (a *Animation) Advance(elapsed float64) error {
|
||
|
if a.playMode == playModePause {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
frameCount := a.GetFrameCount()
|
||
|
frameLength := a.playLength / float64(frameCount)
|
||
|
a.lastFrameTime += elapsed
|
||
|
framesAdvanced := int(a.lastFrameTime / frameLength)
|
||
|
a.lastFrameTime -= float64(framesAdvanced) * frameLength
|
||
|
|
||
|
for i := 0; i < framesAdvanced; i++ {
|
||
|
switch a.playMode {
|
||
|
case playModeForward:
|
||
|
a.frameIndex++
|
||
|
if a.frameIndex >= frameCount {
|
||
|
if a.playLoop {
|
||
|
a.frameIndex = 0
|
||
|
} else {
|
||
|
a.frameIndex = frameCount - 1
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
case playModeBackward:
|
||
|
a.frameIndex--
|
||
|
if a.frameIndex < 0 {
|
||
|
if a.playLoop {
|
||
|
a.frameIndex = frameCount - 1
|
||
|
} else {
|
||
|
a.frameIndex = 0
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (a *Animation) Render(target *ebiten.Image, offsetX, offsetY int) error {
|
||
|
direction := a.directions[a.directionIndex]
|
||
|
frame := direction.frames[a.frameIndex]
|
||
|
|
||
|
opts := &ebiten.DrawImageOptions{}
|
||
|
opts.GeoM.Translate(float64(frame.offsetX+offsetX), float64(frame.offsetY+offsetY))
|
||
|
opts.CompositeMode = a.compositeMode
|
||
|
if a.colorMod != nil {
|
||
|
opts.ColorM = d2corehelper.ColorToColorM(a.colorMod)
|
||
|
}
|
||
|
|
||
|
return target.DrawImage(frame.image, opts)
|
||
|
}
|
||
|
|
||
|
func (a *Animation) GetFrameSize(frameIndex int) (int, int, error) {
|
||
|
direction := a.directions[a.directionIndex]
|
||
|
if frameIndex >= len(direction.frames) {
|
||
|
return 0, 0, errors.New("invalid frame index")
|
||
|
}
|
||
|
|
||
|
frame := direction.frames[frameIndex]
|
||
|
return frame.width, frame.height, nil
|
||
|
}
|
||
|
|
||
|
func (a *Animation) GetCurrentFrameSize() (int, int) {
|
||
|
width, height, _ := a.GetFrameSize(a.frameIndex)
|
||
|
return width, height
|
||
|
}
|
||
|
|
||
|
func (a *Animation) GetFrameBounds() (int, int) {
|
||
|
maxWidth, maxHeight := 0, 0
|
||
|
|
||
|
direction := a.directions[a.directionIndex]
|
||
|
for _, frame := range direction.frames {
|
||
|
maxWidth = d2helper.MaxInt(maxWidth, frame.width)
|
||
|
maxHeight = d2helper.MaxInt(maxHeight, frame.height)
|
||
|
}
|
||
|
|
||
|
return maxWidth, maxHeight
|
||
|
}
|
||
|
|
||
|
func (a *Animation) GetCurrentFrame() int {
|
||
|
return a.frameIndex
|
||
|
}
|
||
|
|
||
|
func (a *Animation) GetFrameCount() int {
|
||
|
direction := a.directions[a.directionIndex]
|
||
|
return len(direction.frames)
|
||
|
}
|
||
|
|
||
|
func (a *Animation) IsOnFirstFrame() bool {
|
||
|
return a.frameIndex == 0
|
||
|
}
|
||
|
|
||
|
func (a *Animation) IsOnLastFrame() bool {
|
||
|
return a.frameIndex == a.GetFrameCount()-1
|
||
|
}
|
||
|
|
||
|
func (a *Animation) GetDirectionCount() int {
|
||
|
return len(a.directions)
|
||
|
}
|
||
|
|
||
|
func (a *Animation) SetDirection(directionIndex int) error {
|
||
|
if directionIndex >= len(a.directions) {
|
||
|
return errors.New("invalid direction index")
|
||
|
}
|
||
|
|
||
|
a.directionIndex = directionIndex
|
||
|
a.frameIndex = 0
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (a *Animation) GetDirection() int {
|
||
|
return a.directionIndex
|
||
|
}
|
||
|
|
||
|
func (a *Animation) SetCurrentFrame(frameIndex int) error {
|
||
|
if frameIndex >= a.GetFrameCount() {
|
||
|
return errors.New("invalid frame index")
|
||
|
}
|
||
|
|
||
|
a.frameIndex = frameIndex
|
||
|
a.lastFrameTime = 0
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (a *Animation) Rewind() {
|
||
|
a.SetCurrentFrame(0)
|
||
|
}
|
||
|
|
||
|
func (a *Animation) PlayForward() {
|
||
|
a.playMode = playModeForward
|
||
|
a.lastFrameTime = 0
|
||
|
}
|
||
|
|
||
|
func (a *Animation) PlayBackward() {
|
||
|
a.playMode = playModeBackward
|
||
|
a.lastFrameTime = 0
|
||
|
}
|
||
|
|
||
|
func (a *Animation) Pause() {
|
||
|
a.playMode = playModePause
|
||
|
a.lastFrameTime = 0
|
||
|
}
|
||
|
|
||
|
func (a *Animation) SetPlayLoop(loop bool) {
|
||
|
a.playLoop = true
|
||
|
}
|
||
|
|
||
|
func (a *Animation) SetPlayLength(playLength float64) {
|
||
|
a.playLength = playLength
|
||
|
a.lastFrameTime = 0
|
||
|
}
|
||
|
|
||
|
func (a *Animation) SetPlayLengthMs(playLengthMs int) {
|
||
|
a.SetPlayLength(float64(playLengthMs) / 1000.0)
|
||
|
}
|
||
|
|
||
|
func (a *Animation) SetColorMod(color color.Color) {
|
||
|
a.colorMod = color
|
||
|
}
|
||
|
|
||
|
func (a *Animation) SetBlend(blend bool) {
|
||
|
if blend {
|
||
|
a.compositeMode = ebiten.CompositeModeLighter
|
||
|
} else {
|
||
|
a.compositeMode = ebiten.CompositeModeSourceOver
|
||
|
}
|
||
|
}
|