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" "math" "time" ) const WalkSpeedMs = 50 var ( zombieImage *ebiten.Image walkTicker *time.Ticker ) type Zombie struct { step int direction configuration.Direction IsWalking bool X float64 Y float64 OffsetX float64 OffsetY float64 IsDead bool TargetX float64 TargetY float64 } 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. } 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) // go routine that runs on every tick and increases step } func NewZombie() *Zombie { zombie := &Zombie{ step: 0, IsWalking: false, IsDead: false, } go func() { for { select { case <-walkTicker.C: if zombie.IsWalking { zombie.step++ if zombie.step > 3 { zombie.step = 0 } zombie.Move() } } } }() 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 } } func (z *Zombie) Move() { fmt.Printf("Zombie target: %f, %f\n", z.TargetX, z.TargetY) directionX := (z.TargetX - z.X) / math.Abs(z.TargetX+z.X) directionY := (z.TargetY - z.Y) / math.Abs(z.TargetY+z.Y) z.X += directionX z.Y += directionY }