package sprites import ( "image" "git.vezzani.net/ben/games/common/ux/v1" "github.com/hajimehoshi/ebiten/v2" ) var ( updateCount int ) func Update() { updateCount++ } type animation struct { RowNumber uint8 TickCount uint8 Scale float32 } type subImager interface { image.Image SubImage(r image.Rectangle) image.Image } type Sprite struct { width, height int imgData subImager animations map[string]animation baseAnim *animation currentAnim *animation animationStartedAt int animationStopAt int animateOnce bool } func (s *Sprite) baseAnimation() animation { if s.baseAnim != nil { return *s.baseAnim } for i := range s.animations { if s.animations[i].RowNumber == 0 { a := s.animations[i] s.baseAnim = &a return a } } return animation{} } func (s *Sprite) getAnimation() animation { if s.currentAnim != nil { return *s.currentAnim } return s.baseAnimation() } func (s *Sprite) Image(ops imageOptions) image.Image { anim := s.getAnimation() xOffset := updateCount % int(anim.TickCount) * s.width yOffset := int(anim.RowNumber) * s.height r := image.Rect(xOffset, yOffset, s.width, s.height).Intersect(s.imgData.Bounds()) return s.imgData.SubImage(r) } func (s *Sprite) Draw(screen *ebiten.Image, options ...ImageOption) { if s.getAnimation().RowNumber == 0 { } ops := imageOptions{ scaleX: ux.Scale, scaleY: ux.Scale, } for _, o := range options { o(&ops) } geom := ebiten.GeoM{} geom.Translate(ops.x, ops.y) geom.Scale(ops.scaleX, ops.scaleY) geom.Rotate(ops.rotateTheta) screen.DrawImage( ebiten.NewImageFromImage( s.Image(ops), ), &ebiten.DrawImageOptions{ GeoM: geom, }, ) } func (s *Sprite) StartAnimation(id string, once bool) { if a, ok := s.animations[id]; ok { s.currentAnim = &a } if once { s.animateOnce = true s.animationStopAt = updateCount + int(s.currentAnim.TickCount) } s.animationStartedAt = updateCount } func (s *Sprite) StopAnimation() { s.currentAnim = nil s.animationStartedAt = 0 s.animationStopAt = 0 s.animateOnce = false } type imageOptions struct { x, y float64 scaleX, scaleY float64 rotateTheta float64 } type ImageOption func(options *imageOptions) func ToScale(x, y float64) ImageOption { return func(o *imageOptions) { o.scaleX *= x o.scaleY *= y } } func AtPosition(x, y float64) ImageOption { return func(o *imageOptions) { o.x, o.y = x, y } } func Rotate(theta float64) ImageOption { return func(o *imageOptions) { o.rotateTheta = theta } }