Fireballs and shadows.

This commit is contained in:
2024-11-16 19:03:07 -05:00
parent 1d65d0046e
commit e049e8c3d0
8 changed files with 287 additions and 46 deletions

BIN
assets/hot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -26,6 +26,7 @@ const (
WormDamaged ImgAssetName = "WormDamaged" WormDamaged ImgAssetName = "WormDamaged"
Worm ImgAssetName = "WormDefault" Worm ImgAssetName = "WormDefault"
Cloud ImgAssetName = "Cloud" Cloud ImgAssetName = "Cloud"
Fireball ImgAssetName = "Fireball"
) )
var ( var (
@@ -57,6 +58,8 @@ var (
wormdefault_img []byte wormdefault_img []byte
//go:embed cloud.png //go:embed cloud.png
cloud_img []byte cloud_img []byte
//go:embed hot.png
fireball_img []byte
) )
func LoadImages() { func LoadImages() {
@@ -75,6 +78,7 @@ func LoadImages() {
ImageBank[WormDamaged] = LoadImagesFatal(worm_img) ImageBank[WormDamaged] = LoadImagesFatal(worm_img)
ImageBank[Worm] = LoadImagesFatal(wormdefault_img) ImageBank[Worm] = LoadImagesFatal(wormdefault_img)
ImageBank[Cloud] = LoadImagesFatal(cloud_img) ImageBank[Cloud] = LoadImagesFatal(cloud_img)
ImageBank[Fireball] = LoadImagesFatal(fireball_img)
} }

View File

@@ -24,4 +24,5 @@ type Enemies interface {
SetExplosionInitiated() SetExplosionInitiated()
Health() int Health() int
MaxHealth() int MaxHealth() int
GetAngle() float64
} }

118
elements/fireball.go Normal file
View File

@@ -0,0 +1,118 @@
package elements
import (
"image"
"math"
"mover/assets"
"mover/gamedata"
"github.com/hajimehoshi/ebiten/v2"
)
type FireBall struct {
Sprite *ebiten.Image
position gamedata.Coordinates
angle float64
velocity float64
cycle int
}
func NewFireBall(angle, velocity float64) *FireBall {
fb := &FireBall{
Sprite: ebiten.NewImage(50, 50),
cycle: 0,
angle: angle,
velocity: velocity,
}
return fb
}
func (fb *FireBall) Update() error {
fb.position.X += fb.velocity * math.Cos(fb.angle)
fb.position.Y += fb.velocity * math.Sin(fb.angle)
fb.cycle++
return nil
}
func (fb *FireBall) Draw() {
fb.Sprite.Clear()
//fb.Sprite.Fill(color.RGBA{R: 0xff, G: 0x00, B: 0x00, A: 0xff})
idx := fb.cycle / 8 % 5
x0 := idx * 50
x1 := x0 + 50
y0 := 0
y1 := 50
fb.Sprite.DrawImage(assets.ImageBank[assets.Fireball].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil)
}
func (fb *FireBall) GetPosition() gamedata.Coordinates {
return fb.position
}
func (fb *FireBall) SetPosition(pos gamedata.Coordinates) {
fb.position = pos
}
func (fb *FireBall) SetTarget(gamedata.Coordinates) {
}
func (fb *FireBall) GetAngle() float64 {
return fb.angle
}
func (fb *FireBall) GetVelocity() float64 {
return fb.velocity
}
func (fb *FireBall) GetSprite() *ebiten.Image {
return fb.Sprite
}
func (fb *FireBall) ClearTouched() {
}
func (fb *FireBall) ExplosionInitiated() bool {
return false
}
func (fb *FireBall) GetEnemyState() gamedata.EnemyState {
return gamedata.EnemyStateDefault
}
func (fb *FireBall) SetHit() {
}
func (fb *FireBall) SetToggle() {
}
func (fb *FireBall) IsToggled() bool {
return false
}
func (fb *FireBall) SetTouched() {
}
func (fb *FireBall) IsTouched() bool {
return false
}
func (fb *FireBall) SetExplosionInitiated() {
}
func (fb *FireBall) Health() int {
return 0
}
func (fb *FireBall) MaxHealth() int {
return 1
}

View File

@@ -171,3 +171,7 @@ func (f *FlyEye) Health() int {
func (f *FlyEye) MaxHealth() int { func (f *FlyEye) MaxHealth() int {
return 1 return 1
} }
func (f *FlyEye) GetAngle() float64 {
return 0
}

View File

@@ -3,6 +3,7 @@ package elements
import ( import (
"image" "image"
"image/color" "image/color"
"math/rand/v2"
"mover/assets" "mover/assets"
"mover/gamedata" "mover/gamedata"
@@ -14,20 +15,23 @@ const (
) )
type FlyGoblin struct { type FlyGoblin struct {
Sprite *ebiten.Image Sprite *ebiten.Image
Maks *ebiten.Image Maks *ebiten.Image
MaksDest *ebiten.Image MaksDest *ebiten.Image
position gamedata.Coordinates position gamedata.Coordinates
target gamedata.Coordinates target gamedata.Coordinates
state gamedata.EnemyState state gamedata.EnemyState
cycle int cycle int
health int health int
damageduration int damageduration int
right bool right bool
touched bool touched bool
toggle bool toggle bool
sploding bool sploding bool
damage bool damage bool
called bool
deathcallback func()
fireballcallback func()
} }
func NewFlyGoblin() *FlyGoblin { func NewFlyGoblin() *FlyGoblin {
@@ -37,6 +41,7 @@ func NewFlyGoblin() *FlyGoblin {
MaksDest: ebiten.NewImage(96, 96), MaksDest: ebiten.NewImage(96, 96),
health: FG_MAXHEALTH, health: FG_MAXHEALTH,
damageduration: 0, damageduration: 0,
called: false,
} }
fg.Maks.Fill(color.White) fg.Maks.Fill(color.White)
return fg return fg
@@ -61,6 +66,22 @@ func (f *FlyGoblin) Update() error {
f.position.X += dx / 48 f.position.X += dx / 48
f.position.Y += dy / 48 f.position.Y += dy / 48
//10% chance to summon fireball
fb := rand.Float64() >= 0.9
if (f.cycle/8)%4 == 0 && fb {
if f.fireballcallback != nil {
f.fireballcallback()
}
}
}
if f.state == gamedata.EnemyStateDead && !f.called {
f.called = true
if f.deathcallback != nil {
f.deathcallback()
}
} }
f.cycle++ f.cycle++
@@ -107,6 +128,7 @@ func (f *FlyGoblin) Draw() {
f.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeDying].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), op) f.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeDying].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), op)
if idx == 3 { if idx == 3 {
f.state = gamedata.EnemyStateDead f.state = gamedata.EnemyStateDead
} }
} }
@@ -182,3 +204,15 @@ func (f *FlyGoblin) Health() int {
func (f *FlyGoblin) MaxHealth() int { func (f *FlyGoblin) MaxHealth() int {
return FG_MAXHEALTH return FG_MAXHEALTH
} }
func (f *FlyGoblin) SetDeathEvent(somefunc func()) {
f.deathcallback = somefunc
}
func (f *FlyGoblin) SetFireballCallback(somefunc func()) {
f.fireballcallback = somefunc
}
func (f *FlyGoblin) GetAngle() float64 {
return 0
}

View File

@@ -26,12 +26,14 @@ type Canvas struct {
initialized bool initialized bool
goblinspawned bool goblinspawned bool
goblindead bool
lastInputs gamedata.GameInputs lastInputs gamedata.GameInputs
runtime float64 runtime float64
counter int counter int
score int score int
hero *elements.Hero hero *elements.Hero
charge *elements.Explosion charge *elements.Explosion
goblin *elements.FlyGoblin
enemies []elements.Enemies enemies []elements.Enemies
projectiles []*elements.Projectile projectiles []*elements.Projectile
gameover bool gameover bool
@@ -49,6 +51,7 @@ func NewCanvas(a gamedata.Area) *Canvas {
initialized: false, initialized: false,
gameover: false, gameover: false,
goblinspawned: false, goblinspawned: false,
goblindead: false,
score: 0, score: 0,
runtime: 0., runtime: 0.,
counter: 0, counter: 0,
@@ -97,10 +100,35 @@ func (c *Canvas) Draw(drawimg *ebiten.Image) {
c.Sprite.DrawImage(assets.ImageBank[assets.Weapon], op) c.Sprite.DrawImage(assets.ImageBank[assets.Weapon], op)
} }
for _, es := range c.enemies {
if es.GetEnemyState() < gamedata.EnemyStateExploding {
dx := float64(assets.ImageBank[assets.FlyEyeShadow].Bounds().Dx()) / 2
dy := float64(assets.ImageBank[assets.FlyEyeShadow].Bounds().Dy()) / 2
sx := float64(es.GetSprite().Bounds().Dx()) / 48
sy := float64(es.GetSprite().Bounds().Dy()) / 48
op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(-dx, -dy)
op.GeoM.Scale(sx, sy)
op.GeoM.Translate(es.GetPosition().X, es.GetPosition().Y+float64(es.GetSprite().Bounds().Dx())/2)
c.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeShadow], op)
}
}
for _, e := range c.enemies { for _, e := range c.enemies {
e.Draw() e.Draw()
xshift := float64(e.GetSprite().Bounds().Dx() / 2)
yshift := float64(e.GetSprite().Bounds().Dy() / 2)
op := &ebiten.DrawImageOptions{} op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(e.GetPosition().X-float64(e.GetSprite().Bounds().Dx())/2, e.GetPosition().Y-float64(e.GetSprite().Bounds().Dy())/2) op.GeoM.Translate(-xshift, -yshift)
op.GeoM.Rotate(e.GetAngle())
op.GeoM.Translate(e.GetPosition().X, e.GetPosition().Y)
//op := &ebiten.DrawImageOptions{}
//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) c.Sprite.DrawImage(e.GetSprite(), op)
//do we need a health bar for this enemy? //do we need a health bar for this enemy?
@@ -152,6 +180,7 @@ func (c *Canvas) Initialize() {
c.counter = 0 c.counter = 0
c.runtime = 0. c.runtime = 0.
c.goblinspawned = false c.goblinspawned = false
c.goblindead = false
//temporary //temporary
c.hero.Action = elements.HeroActionDefault c.hero.Action = elements.HeroActionDefault
@@ -354,43 +383,70 @@ func (c *Canvas) UpdateEnemies() {
} }
if !c.gameover { if !c.gameover {
if !c.goblinspawned { if !c.goblinspawned || c.goblindead {
//spawn new enemies c.SpawnFlyEyes()
f := 40000 / (c.counter + 1)
if c.counter%f == 0 {
newenemy := elements.NewFlyEye()
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})
}
newenemy.SetTarget(c.hero.Pos)
c.enemies = append(c.enemies, newenemy)
}
} }
if !c.goblinspawned && c.counter > 1200 { if !c.goblinspawned { //&& c.counter > 1200 && !c.goblindead {
newfg := elements.NewFlyGoblin() c.SpawnGoblin()
c.enemies = append(c.enemies, newfg)
c.goblinspawned = true
} }
} }
} }
func (c *Canvas) SpawnFlyEyes() {
//spawn new enemies
f := 40000 / (c.counter + 1)
if c.counter%f == 0 {
newenemy := elements.NewFlyEye()
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})
}
newenemy.SetTarget(c.hero.Pos)
c.enemies = append(c.enemies, newenemy)
}
}
func (c *Canvas) SpawnGoblin() {
newfg := elements.NewFlyGoblin()
newfg.SetDeathEvent(c.GoblinDeathEvent)
newfg.SetFireballCallback(c.GoblinFireballEvent)
x0 := rand.Float64() * 640
y0 := rand.Float64() * 480
quadrant := rand.IntN(3)
switch quadrant {
case 0:
newfg.SetPosition(gamedata.Coordinates{X: x0, Y: -96})
case 1:
newfg.SetPosition(gamedata.Coordinates{X: x0, Y: 480 + 48})
case 2:
newfg.SetPosition(gamedata.Coordinates{X: -96, Y: y0})
case 3:
newfg.SetPosition(gamedata.Coordinates{X: 640 + x0, Y: y0})
}
c.goblin = newfg
c.enemies = append(c.enemies, newfg)
c.goblinspawned = true
}
func (c *Canvas) HasCollided(mask *ebiten.Image, size int) bool { func (c *Canvas) HasCollided(mask *ebiten.Image, size int) bool {
var result bool = false var result bool = false
var pixels []byte = make([]byte, size) var pixels []byte = make([]byte, size)
@@ -430,3 +486,27 @@ func (c *Canvas) CleanupTargets() {
} }
c.enemies = c.enemies[:i] c.enemies = c.enemies[:i]
} }
func (c *Canvas) GoblinDeathEvent() {
c.goblindead = true
c.goblinspawned = false
c.score += 10
}
func (c *Canvas) GoblinFireballEvent() {
if !c.gameover {
velocity := 8.
dx := c.hero.Pos.X - c.goblin.GetPosition().X
dy := c.hero.Pos.Y - c.goblin.GetPosition().Y
angle := math.Atan2(dy, dx)
//add some randomness to the angle
arand := rand.Float64() * math.Pi / 3
newfb := elements.NewFireBall(angle+arand, velocity)
newfb.SetPosition(c.goblin.GetPosition())
c.enemies = append(c.enemies, newfb)
}
}

View File

@@ -26,7 +26,7 @@ func NewManager() Manager {
return Manager{ return Manager{
Info: gamedata.GameInfo{ Info: gamedata.GameInfo{
Name: "survive", Name: "survive",
Version: "0.26", Version: "0.30",
Dimensions: gamedata.Area{ Dimensions: gamedata.Area{
Width: defaultWidth, Width: defaultWidth,
Height: defaultHeight, Height: defaultHeight,