diff --git a/elements/enemies.go b/elements/enemies.go index 20325f4..fe79954 100644 --- a/elements/enemies.go +++ b/elements/enemies.go @@ -22,4 +22,5 @@ type Enemies interface { IsTouched() bool ExplosionInitiated() bool SetExplosionInitiated() + Health() int } diff --git a/elements/flyeye.go b/elements/flyeye.go index 6aa9abb..e963a67 100644 --- a/elements/flyeye.go +++ b/elements/flyeye.go @@ -161,3 +161,7 @@ func (f *FlyEye) ExplosionInitiated() bool { func (f *FlyEye) SetExplosionInitiated() { f.sploding = true } + +func (f *FlyEye) Health() int { + return 0 +} diff --git a/elements/flygoblin.go b/elements/flygoblin.go new file mode 100644 index 0000000..894898b --- /dev/null +++ b/elements/flygoblin.go @@ -0,0 +1,178 @@ +package elements + +import ( + "image" + "image/color" + "mover/assets" + "mover/gamedata" + + "github.com/hajimehoshi/ebiten/v2" +) + +type FlyGoblin struct { + Sprite *ebiten.Image + Maks *ebiten.Image + MaksDest *ebiten.Image + position gamedata.Coordinates + target gamedata.Coordinates + state gamedata.EnemyState + cycle int + health int + dyingcount int + damageduration int + right bool + hit bool + touched bool + toggle bool + sploding bool + damage bool +} + +func NewFlyGoblin() *FlyGoblin { + fg := &FlyGoblin{ + Sprite: ebiten.NewImage(96, 96), + Maks: ebiten.NewImage(96, 96), + MaksDest: ebiten.NewImage(96, 96), + health: 100, + damageduration: 0, + } + fg.Maks.Fill(color.White) + return fg +} + +func (f *FlyGoblin) Update() error { + + if f.damage { + f.damageduration++ + if f.damageduration > 30 { + f.damage = false + f.damageduration = 0 + } + } + + if f.state < gamedata.EnemyStateDying { + dx := f.target.X - f.position.X + dy := f.target.Y - f.position.Y + + f.right = dx/48 > 0 + + f.position.X += dx / 48 + f.position.Y += dy / 48 + + } + + f.cycle++ + return nil +} + +func (f *FlyGoblin) Draw() { + f.Sprite.Clear() + f.MaksDest.Clear() + + idx := (f.cycle / 8) % 4 + x0 := 96 * idx + x1 := x0 + 96 + y0 := 0 + y1 := 96 + + op := &ebiten.DrawImageOptions{} + if f.right { + op.GeoM.Scale(-1, 1) + op.GeoM.Translate(MOVER_WIDTH*2, 0) + } + + switch f.state { + case gamedata.EnemyStateDefault: + if !f.toggle { + f.Sprite.DrawImage(assets.ImageBank[assets.Worm].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), op) + } else { + f.Sprite.DrawImage(assets.ImageBank[assets.WormDamaged].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), op) + } + case gamedata.EnemyStateHit: + if (f.cycle/5)%2 == 0 && f.damage { + f.MaksDest.DrawImage(assets.ImageBank[assets.WormDamaged].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), op) + op := &ebiten.DrawImageOptions{} + op.GeoM.Reset() + op.Blend = ebiten.BlendSourceAtop + f.MaksDest.DrawImage(f.Maks, op) + f.Sprite.DrawImage(f.MaksDest, nil) + } else { + f.Sprite.DrawImage(assets.ImageBank[assets.WormDamaged].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), op) + } + case gamedata.EnemyStateExploding: + op.GeoM.Scale(2, 2) + //op.GeoM.Translate(-48, -48) + f.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeDying].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), op) + if idx == 3 { + f.state = gamedata.EnemyStateDead + } + } + +} + +func (f *FlyGoblin) GetPosition() gamedata.Coordinates { + return f.position +} + +func (f *FlyGoblin) SetPosition(pos gamedata.Coordinates) { + f.position = pos +} + +func (f *FlyGoblin) SetTarget(target gamedata.Coordinates) { + f.target = target +} + +func (f *FlyGoblin) GetSprite() *ebiten.Image { + return f.Sprite +} + +func (f *FlyGoblin) GetEnemyState() gamedata.EnemyState { + return f.state +} + +func (f *FlyGoblin) SetHit() { + f.health-- + f.damage = true + f.state = gamedata.EnemyStateHit + + if f.health <= 0 { + f.state = gamedata.EnemyStateExploding + f.cycle = 0 + } +} + +func (f *FlyGoblin) SetToggle() { + f.toggle = !f.toggle + + if !f.toggle { + f.state = gamedata.EnemyStateDefault + } +} + +func (f *FlyGoblin) IsToggled() bool { + return f.toggle +} + +func (f *FlyGoblin) SetTouched() { + f.touched = true +} + +func (f *FlyGoblin) ClearTouched() { + f.touched = false +} + +func (f *FlyGoblin) IsTouched() bool { + return f.touched +} + +func (f *FlyGoblin) ExplosionInitiated() bool { + return f.sploding +} + +func (f *FlyGoblin) SetExplosionInitiated() { + f.sploding = true +} + +func (f *FlyGoblin) Health() int { + return f.health +} diff --git a/gameelement/canvas.go b/gameelement/canvas.go index d340579..febe3f6 100644 --- a/gameelement/canvas.go +++ b/gameelement/canvas.go @@ -4,7 +4,6 @@ import ( "fmt" "image/color" "math" - "math/rand/v2" "mover/assets" "mover/elements" "mover/fonts" @@ -24,16 +23,17 @@ type Canvas struct { eventmap map[gamedata.GameEvent]func() - initialized bool - lastInputs gamedata.GameInputs - runtime float64 - counter int - score int - hero *elements.Hero - charge *elements.Explosion - enemies []elements.Enemies - projectiles []*elements.Projectile - gameover bool + initialized bool + goblinspawned bool + lastInputs gamedata.GameInputs + runtime float64 + counter int + score int + hero *elements.Hero + charge *elements.Explosion + enemies []elements.Enemies + projectiles []*elements.Projectile + gameover bool } func NewCanvas(a gamedata.Area) *Canvas { @@ -41,12 +41,13 @@ func NewCanvas(a gamedata.Area) *Canvas { Sprite: ebiten.NewImage(a.Width, a.Height), projectileMask: ebiten.NewImage(a.Width, a.Height), collisionMask: ebiten.NewImage(a.Width, a.Height), - heroCollisionMask: ebiten.NewImage(46, 46), - heroCollisionCpy: ebiten.NewImage(46, 46), + heroCollisionMask: ebiten.NewImage(48, 48), + heroCollisionCpy: ebiten.NewImage(48, 48), hero: elements.NewHero(), charge: elements.NewExplosion(), initialized: false, gameover: false, + goblinspawned: false, score: 0, runtime: 0., } @@ -97,8 +98,16 @@ func (c *Canvas) Draw(drawimg *ebiten.Image) { for _, e := range c.enemies { e.Draw() op := &ebiten.DrawImageOptions{} - op.GeoM.Translate(e.GetPosition().X-46/2, e.GetPosition().Y-46/2) + op.GeoM.Translate(e.GetPosition().X-float64(e.GetSprite().Bounds().Dx())/2, e.GetPosition().Y-float64(e.GetSprite().Bounds().Dy())/2) c.Sprite.DrawImage(e.GetSprite(), op) + + //do we need a health bar for this enemy? + if e.Health() > 0 { + x0 := e.GetPosition().X - float64(e.GetSprite().Bounds().Dx()) + y0 := e.GetPosition().Y - 2/3.*float64(e.GetSprite().Bounds().Dy()) + vector.DrawFilledRect(c.Sprite, float32(x0), float32(y0), 204, 12, color.Black, true) + vector.DrawFilledRect(c.Sprite, float32(x0+2), float32(y0+2), float32(e.Health())*2, 8, color.RGBA{R: 0xff, G: 0x00, B: 0x00, A: 0xff}, true) + } } for _, p := range c.projectiles { @@ -140,6 +149,7 @@ func (c *Canvas) Initialize() { c.score = 0 c.counter = 0 c.runtime = 0. + c.goblinspawned = false //temporary c.hero.Action = elements.HeroActionDefault @@ -168,8 +178,8 @@ func (c *Canvas) UpdateHeroPosition() { func (c *Canvas) ComputeHeroCollisions() { for _, e := range c.enemies { //compute collision with hero - if c.hero.Pos.X >= e.GetPosition().X-46/2 && c.hero.Pos.X <= e.GetPosition().X+46/2 && - c.hero.Pos.Y >= e.GetPosition().Y-46/2 && c.hero.Pos.Y <= e.GetPosition().Y+46/2 && + if c.hero.Pos.X >= e.GetPosition().X-float64(e.GetSprite().Bounds().Dx())/2 && c.hero.Pos.X <= e.GetPosition().X+float64(e.GetSprite().Bounds().Dx())/2 && + c.hero.Pos.Y >= e.GetPosition().Y-float64(e.GetSprite().Bounds().Dy())/2 && c.hero.Pos.Y <= e.GetPosition().Y+float64(e.GetSprite().Bounds().Dy())/2 && e.GetEnemyState() < gamedata.EnemyStateDying { // target.Action < elements.MoverActionDying && g.hero.Action < elements.HeroActionDying { @@ -183,7 +193,7 @@ func (c *Canvas) ComputeHeroCollisions() { op.GeoM.Translate((c.hero.Pos.X-e.GetPosition().X)-float64(e.GetSprite().Bounds().Dx())/2, (c.hero.Pos.Y-e.GetPosition().Y)-float64(e.GetSprite().Bounds().Dy())/2) c.heroCollisionMask.DrawImage(e.GetSprite(), op) - if c.HasCollided(c.heroCollisionMask, 46*46*4) { + if c.HasCollided(c.heroCollisionMask, 48*48*4) { c.hero.SetHit() c.gameover = true @@ -341,30 +351,37 @@ func (c *Canvas) UpdateEnemies() { e.Update() } if !c.gameover { - //spawn new enemies - f := 40000 / (c.counter + 1) + /* + //spawn new enemies + f := 40000 / (c.counter + 1) - if c.counter%f == 0 { - newenemy := elements.NewFlyEye() + if c.counter%f == 0 { + newenemy := elements.NewFlyEye() - x0 := rand.Float64() * 640 - y0 := rand.Float64() * 480 - quadrant := rand.IntN(3) + x0 := rand.Float64() * 640 + y0 := rand.Float64() * 480 + quadrant := rand.IntN(3) - switch quadrant { - case 0: - newenemy.SetPosition(gamedata.Coordinates{X: x0, Y: -48}) - case 1: - newenemy.SetPosition(gamedata.Coordinates{X: x0, Y: 480 + 48}) - case 2: - newenemy.SetPosition(gamedata.Coordinates{X: -48, Y: y0}) - case 3: - newenemy.SetPosition(gamedata.Coordinates{X: 640 + x0, Y: y0}) - } + switch quadrant { + case 0: + newenemy.SetPosition(gamedata.Coordinates{X: x0, Y: -48}) + case 1: + newenemy.SetPosition(gamedata.Coordinates{X: x0, Y: 480 + 48}) + case 2: + newenemy.SetPosition(gamedata.Coordinates{X: -48, Y: y0}) + case 3: + newenemy.SetPosition(gamedata.Coordinates{X: 640 + x0, Y: y0}) + } - newenemy.SetTarget(c.hero.Pos) + newenemy.SetTarget(c.hero.Pos) - c.enemies = append(c.enemies, newenemy) + c.enemies = append(c.enemies, newenemy) + }*/ + + if !c.goblinspawned { + newfg := elements.NewFlyGoblin() + c.enemies = append(c.enemies, newfg) + c.goblinspawned = true } } }