diff --git a/assets/cloud.png b/assets/cloud.png new file mode 100644 index 0000000..c8283c4 Binary files /dev/null and b/assets/cloud.png differ diff --git a/assets/imagebank.go b/assets/imagebank.go index 6c7291c..5e7b7db 100644 --- a/assets/imagebank.go +++ b/assets/imagebank.go @@ -25,6 +25,7 @@ const ( Weapon ImgAssetName = "Weapon" WormDamaged ImgAssetName = "WormDamaged" Worm ImgAssetName = "WormDefault" + Cloud ImgAssetName = "Cloud" ) var ( @@ -54,6 +55,8 @@ var ( worm_img []byte //go:embed wormdefault.png wormdefault_img []byte + //go:embed cloud.png + cloud_img []byte ) func LoadImages() { @@ -71,6 +74,7 @@ func LoadImages() { ImageBank[Weapon] = LoadImagesFatal(weapon_img) ImageBank[WormDamaged] = LoadImagesFatal(worm_img) ImageBank[Worm] = LoadImagesFatal(wormdefault_img) + ImageBank[Cloud] = LoadImagesFatal(cloud_img) } diff --git a/elements/cloud.go b/elements/cloud.go new file mode 100644 index 0000000..f690605 --- /dev/null +++ b/elements/cloud.go @@ -0,0 +1,62 @@ +package elements + +import ( + "math" + "mover/assets" + "mover/gamedata" + + "github.com/hajimehoshi/ebiten/v2" +) + +type Cloud struct { + Sprite *ebiten.Image + position gamedata.Coordinates + angle float64 + velocity float64 + Alpha float64 +} + +func NewCloud(a gamedata.Area, angle, velocity float64) *Cloud { + c := &Cloud{ + Sprite: ebiten.NewImage(a.Width, a.Height), + angle: angle, //rand.Float64() * (math.Pi * 2), + velocity: velocity, + } + return c +} + +func (c *Cloud) Update() error { + + c.position.X += c.velocity * math.Cos(c.angle) + c.position.Y += c.velocity * math.Sin(c.angle) + return nil +} + +func (c *Cloud) Draw() { + c.Sprite.Clear() + //c.Sprite.Fill(color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}) + //c.Sprite.Fill(color.White) + + cloudsprite := assets.ImageBank[assets.Cloud] + + spritex := cloudsprite.Bounds().Dx() + spritey := cloudsprite.Bounds().Dy() + + cloudx := c.Sprite.Bounds().Dx() + cloudy := c.Sprite.Bounds().Dy() + + scalex := float64(cloudx) / float64(spritex) + scaley := float64(cloudy) / float64(spritey) + + op := &ebiten.DrawImageOptions{} + op.GeoM.Scale(scalex, scaley) + c.Sprite.DrawImage(assets.ImageBank[assets.Cloud], op) +} + +func (c *Cloud) SetPosition(p gamedata.Coordinates) { + c.position = p +} + +func (c *Cloud) GetPosition() gamedata.Coordinates { + return c.position +} diff --git a/elements/enemies.go b/elements/enemies.go index fe79954..f4b553d 100644 --- a/elements/enemies.go +++ b/elements/enemies.go @@ -23,4 +23,5 @@ type Enemies interface { ExplosionInitiated() bool SetExplosionInitiated() Health() int + MaxHealth() int } diff --git a/elements/flyeye.go b/elements/flyeye.go index e963a67..717f3ca 100644 --- a/elements/flyeye.go +++ b/elements/flyeye.go @@ -163,5 +163,11 @@ func (f *FlyEye) SetExplosionInitiated() { } func (f *FlyEye) Health() int { + //health bars reserved for special enemies, flyeye is a one + //hitter so returning zero ensure no health bar is rendered return 0 } + +func (f *FlyEye) MaxHealth() int { + return 1 +} diff --git a/elements/flygoblin.go b/elements/flygoblin.go index 894898b..5e51cfb 100644 --- a/elements/flygoblin.go +++ b/elements/flygoblin.go @@ -9,6 +9,10 @@ import ( "github.com/hajimehoshi/ebiten/v2" ) +const ( + FG_MAXHEALTH = 100 +) + type FlyGoblin struct { Sprite *ebiten.Image Maks *ebiten.Image @@ -18,10 +22,8 @@ type FlyGoblin struct { state gamedata.EnemyState cycle int health int - dyingcount int damageduration int right bool - hit bool touched bool toggle bool sploding bool @@ -33,7 +35,7 @@ func NewFlyGoblin() *FlyGoblin { Sprite: ebiten.NewImage(96, 96), Maks: ebiten.NewImage(96, 96), MaksDest: ebiten.NewImage(96, 96), - health: 100, + health: FG_MAXHEALTH, damageduration: 0, } fg.Maks.Fill(color.White) @@ -176,3 +178,7 @@ func (f *FlyGoblin) SetExplosionInitiated() { func (f *FlyGoblin) Health() int { return f.health } + +func (f *FlyGoblin) MaxHealth() int { + return FG_MAXHEALTH +} diff --git a/gameelement/canvas.go b/gameelement/canvas.go index febe3f6..921aa8b 100644 --- a/gameelement/canvas.go +++ b/gameelement/canvas.go @@ -4,6 +4,7 @@ import ( "fmt" "image/color" "math" + "math/rand/v2" "mover/assets" "mover/elements" "mover/fonts" @@ -50,6 +51,7 @@ func NewCanvas(a gamedata.Area) *Canvas { goblinspawned: false, score: 0, runtime: 0., + counter: 0, } c.eventmap = make(map[gamedata.GameEvent]func()) return c @@ -70,9 +72,9 @@ func (c *Canvas) Update() error { c.UpdateCharge() c.UpdateEnemies() c.CleanupTargets() - + c.counter++ } - c.counter++ + return nil } @@ -105,7 +107,7 @@ func (c *Canvas) Draw(drawimg *ebiten.Image) { 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), float32(y0), float32(e.MaxHealth())*2+4, 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) } } @@ -351,7 +353,8 @@ func (c *Canvas) UpdateEnemies() { e.Update() } if !c.gameover { - /* + + if !c.goblinspawned { //spawn new enemies f := 40000 / (c.counter + 1) @@ -376,13 +379,15 @@ func (c *Canvas) UpdateEnemies() { newenemy.SetTarget(c.hero.Pos) c.enemies = append(c.enemies, newenemy) - }*/ + } + } - if !c.goblinspawned { + if !c.goblinspawned && c.counter > 1200 { newfg := elements.NewFlyGoblin() c.enemies = append(c.enemies, newfg) c.goblinspawned = true } + } } diff --git a/gameelement/cloudlayer.go b/gameelement/cloudlayer.go new file mode 100644 index 0000000..2fde433 --- /dev/null +++ b/gameelement/cloudlayer.go @@ -0,0 +1,109 @@ +package gameelement + +import ( + "math" + "math/rand/v2" + "mover/elements" + "mover/gamedata" + + "github.com/hajimehoshi/ebiten/v2" +) + +type CloudLayer struct { + Sprite *ebiten.Image + clouds []*elements.Cloud + dimensions gamedata.Area + cycle int +} + +func NewCloudLayer(a gamedata.Area) *CloudLayer { + c := &CloudLayer{ + Sprite: ebiten.NewImage(a.Width, a.Height), + cycle: 0, + dimensions: a, + } + return c +} + +func (c *CloudLayer) SetInputs(gamedata.GameInputs) { +} + +func (c *CloudLayer) Update() error { + c.cycle++ + + for _, cloud := range c.clouds { + cloud.Update() + + cpos := cloud.GetPosition() + if cpos.X > float64(c.dimensions.Width)+float64(cloud.Sprite.Bounds().Dx()) { + dx := -float64(cloud.Sprite.Bounds().Dx()) + cloud.SetPosition(gamedata.Coordinates{X: dx, Y: cloud.GetPosition().Y}) + } + if cpos.X < -float64(cloud.Sprite.Bounds().Dx()) { + dx := float64(c.dimensions.Width + cloud.Sprite.Bounds().Dx()) + cloud.SetPosition(gamedata.Coordinates{X: dx, Y: cloud.GetPosition().Y}) + } + if cpos.Y > float64(c.dimensions.Height)+float64(cloud.Sprite.Bounds().Dy()) { + dy := -float64(cloud.Sprite.Bounds().Dy()) + cloud.SetPosition(gamedata.Coordinates{X: cloud.GetPosition().X, Y: dy}) + } + if cpos.Y < -float64(cloud.Sprite.Bounds().Dy()) { + dy := float64(c.dimensions.Height + cloud.Sprite.Bounds().Dy()) + cloud.SetPosition(gamedata.Coordinates{X: cloud.GetPosition().X, Y: dy}) + } + } + + return nil +} + +func (c *CloudLayer) Draw(drawimg *ebiten.Image) { + c.Sprite.Clear() + + for _, cloud := range c.clouds { + cloud.Draw() + op := &ebiten.DrawImageOptions{} + op.GeoM.Translate(cloud.GetPosition().X, cloud.GetPosition().Y) + op.ColorScale.ScaleAlpha(float32(cloud.Alpha)) + c.Sprite.DrawImage(cloud.Sprite, op) + } + + drawimg.DrawImage(c.Sprite, nil) +} + +func (c *CloudLayer) Initialize() { + + //cull previous cloud layer + for i := 0; i < len(c.clouds); i++ { + c.clouds[i] = nil + } + c.clouds = c.clouds[:0] + + numclouds := rand.IntN(20) + angle := rand.Float64() * math.Pi * 2 + + for i := 0; i < numclouds; i++ { + + a := gamedata.Area{ + Height: rand.IntN(c.dimensions.Width/2) + 1, + Width: rand.IntN(c.dimensions.Height/2) + 1, + } + + velocity := rand.Float64() * 3 + //velocity := 0. + + newcloud := elements.NewCloud(a, angle, velocity) + + newcloud.Alpha = rand.Float64() / 2 + + newcloud.SetPosition(gamedata.Coordinates{ + X: rand.Float64() * float64(c.dimensions.Width), + Y: rand.Float64() * float64(c.dimensions.Height), + }) + + c.clouds = append(c.clouds, newcloud) + } +} + +func (c *CloudLayer) RegisterEvents(e gamedata.GameEvent, f func()) { + +} diff --git a/screens/primary.go b/screens/primary.go index 9c6e771..8175caf 100644 --- a/screens/primary.go +++ b/screens/primary.go @@ -32,19 +32,28 @@ func NewPrimary() *Primary { musicInitialized: false, } + gamearea := gamedata.Area{Width: 640, Height: 480} + + //initialize our layer map p.gameevents = make(map[gamedata.GameEvent]bool) - p.elements = append(p.elements, gameelement.NewBackground(gamedata.Area{Width: 640, Height: 480})) + //create background layer + p.elements = append(p.elements, gameelement.NewBackground(gamearea)) - canvas := gameelement.NewCanvas(gamedata.Area{Width: 640, Height: 480}) + //create canvas (game) layer + canvas := gameelement.NewCanvas(gamearea) canvas.RegisterEvents(gamedata.GameEventPlayerDeath, p.EventHandlerPlayerDeath) canvas.RegisterEvents(gamedata.GameEventCharge, p.EventHandlerCharge) canvas.RegisterEvents(gamedata.GameEventNewShot, p.EventHandlerNewShot) canvas.RegisterEvents(gamedata.GameEventTargetHit, p.EventHandlerTargetHit) canvas.RegisterEvents(gamedata.GameEventExplosion, p.EventHandlerExplosion) - p.elements = append(p.elements, canvas) + //create foreground cloud layer + clouds := gameelement.NewCloudLayer(gamearea) + clouds.Initialize() + p.elements = append(p.elements, clouds) + return p }