package zombie import ( "bytes" "fmt" "github.com/hajimehoshi/ebiten/v2" "gitlab.com/kbr4/9heroja/collision" "gitlab.com/kbr4/9heroja/configuration" "gitlab.com/kbr4/9heroja/resources" "image" "log" "time" ) const WalkSpeedMs = 10 var ( zombieImage *ebiten.Image walkTicker *time.Ticker animationTicker *time.Ticker ) type Zombie struct { step int direction configuration.Direction IsWalking bool X float64 Y float64 OffsetX float64 OffsetY float64 WhereToGoX float64 WhereToGoY float64 IsDead bool } type spritePosition struct { x int y int } func (z *Zombie) DrawZombie(screen *ebiten.Image) { // set movement positions to be hashmap of sprite positions for each direction movementPositions := map[configuration.Direction][]spritePosition{} movementPositions[configuration.North] = []spritePosition{ {0, 0}, {32, 0}, {64, 0}, {96, 0}, } movementPositions[configuration.NorthEast] = movementPositions[configuration.North] movementPositions[configuration.East] = movementPositions[configuration.North] movementPositions[configuration.SouthEast] = movementPositions[configuration.North] movementPositions[configuration.South] = movementPositions[configuration.North] movementPositions[configuration.SouthWest] = movementPositions[configuration.North] movementPositions[configuration.West] = movementPositions[configuration.North] movementPositions[configuration.NorthWest] = movementPositions[configuration.North] p := movementPositions[z.direction][z.step%4] op := &ebiten.DrawImageOptions{} op.GeoM.Reset() op.GeoM.Translate(z.X+z.OffsetX, z.Y+z.OffsetY) op.GeoM.Scale(1, 1) // Apply red tint if the zombie is dead if z.IsDead { op.ColorScale.Scale(1, 0, 0, 1) // Scale the red channel up, green and blue down. } else { screen.DrawImage(zombieImage.SubImage(image.Rect(p.x+1, p.y, p.x+resources.ZombieTileSize, p.y+resources.HeroTileSize)).(*ebiten.Image), op) } } func init() { img, _, err := image.Decode(bytes.NewReader(resources.Zombie_png)) if err != nil { log.Fatal(err) } zombieImage = ebiten.NewImageFromImage(img) walkTicker = time.NewTicker(WalkSpeedMs * time.Millisecond) animationTicker = time.NewTicker(WalkSpeedMs * 5 * time.Millisecond) // Adjust the speed of animation frames } func NewZombie() *Zombie { zombie := &Zombie{ step: 0, IsWalking: false, IsDead: false, } go func() { for { select { case <-walkTicker.C: if zombie.IsWalking { // Move the zombie towards its target position if zombie.X < zombie.WhereToGoX { zombie.X += 1 } else if zombie.X > zombie.WhereToGoX { zombie.X -= 1 } if zombie.Y < zombie.WhereToGoY { zombie.Y += 1 } else if zombie.Y > zombie.WhereToGoY { zombie.Y -= 1 } } } } }() go func() { for { select { case <-animationTicker.C: if zombie.IsWalking { zombie.step++ if zombie.step > 3 { zombie.step = 0 } } } } }() return zombie } func (z *Zombie) Walk() { if !z.IsWalking { walkTicker.Reset(WalkSpeedMs * time.Millisecond) z.IsWalking = true } } func (z *Zombie) ChangeDirection(d configuration.Direction) { if d != configuration.PreviouslyHeld { z.direction = d } } func (z *Zombie) Stop() { z.step = 2 z.IsWalking = false } func (z *Zombie) CollisionShape() collision.Polygon { if !z.IsDead { return collision.Polygon{ Points: []collision.Point{ {X: z.X + z.OffsetX, Y: z.Y + z.OffsetY}, {X: z.X + z.OffsetX + 32, Y: z.Y + z.OffsetY}, {X: z.X + z.OffsetX + 32, Y: z.Y + z.OffsetY + 32}, {X: z.X + z.OffsetX, Y: z.Y + z.OffsetY + 32}, }, } } else { return collision.Polygon{ Points: []collision.Point{}, } } } func (z *Zombie) CollisionObjectType() collision.ObjectType { return collision.Zombie } func (z *Zombie) HandleCollisionEvent(other collision.Collidable) { coll := other.CollisionObjectType() switch coll { case collision.Hero: z.Stop() z.IsDead = true case collision.Bullet: fmt.Println("Zombie hit by bullet") z.Stop() z.IsDead = true case collision.Ending: fmt.Println("Zombie hit the ending") z.Stop() z.IsDead = true } }