diff --git a/assets/imagebank.go b/assets/imagebank.go index a13b5b9..fd675a5 100644 --- a/assets/imagebank.go +++ b/assets/imagebank.go @@ -29,6 +29,7 @@ const ( Fireball ImgAssetName = "Fireball" Splash ImgAssetName = "Splash" LaserBeam ImgAssetName = "LaserBeam" + ItemLaser ImgAssetName = "ItemLaser" ) var ( @@ -66,6 +67,8 @@ var ( splash_img []byte //go:embed laserbeam.png laserbeam_img []byte + //go:embed item-laser.png + itemlaser_img []byte ) func LoadImages() { @@ -87,6 +90,7 @@ func LoadImages() { ImageBank[Fireball] = LoadImagesFatal(fireball_img) ImageBank[Splash] = LoadImagesFatal(splash_img) ImageBank[LaserBeam] = LoadImagesFatal(laserbeam_img) + ImageBank[ItemLaser] = LoadImagesFatal(itemlaser_img) } diff --git a/assets/item-laser.png b/assets/item-laser.png new file mode 100644 index 0000000..43c5a1d Binary files /dev/null and b/assets/item-laser.png differ diff --git a/elements/weapondrop.go b/elements/weapondrop.go new file mode 100644 index 0000000..c86d5bd --- /dev/null +++ b/elements/weapondrop.go @@ -0,0 +1,66 @@ +package elements + +import ( + "math" + "mover/assets" + "mover/gamedata" + + "github.com/hajimehoshi/ebiten/v2" +) + +type WeaponDrop struct { + Sprite *ebiten.Image + position gamedata.Coordinates + weapontype gamedata.WeaponType + cycle int + collected bool +} + +func NewWeaponDrop(wt gamedata.WeaponType) *WeaponDrop { + wp := &WeaponDrop{ + Sprite: ebiten.NewImage(32, 32), + weapontype: wt, + cycle: 0, + collected: false, + } + return wp +} + +func (wp *WeaponDrop) SetPosition(pos gamedata.Coordinates) { + wp.position = pos +} + +func (wp *WeaponDrop) GetPosition() gamedata.Coordinates { + return wp.position +} + +func (wp *WeaponDrop) GetWeaponType() gamedata.WeaponType { + return wp.weapontype +} + +func (wp *WeaponDrop) Update() error { + wp.cycle++ + return nil +} + +func (wp *WeaponDrop) Draw() { + wp.Sprite.Clear() + + dy := 2 * math.Sin(float64(wp.cycle)/(math.Pi*2)) + op := &ebiten.DrawImageOptions{} + op.GeoM.Translate(0, dy) + + switch wp.weapontype { + case gamedata.WeaponTypeLaser: + wp.Sprite.DrawImage(assets.ImageBank[assets.ItemLaser], op) + } + +} + +func (wp *WeaponDrop) SetCollected(c bool) { + wp.collected = c +} + +func (wp *WeaponDrop) IsCollected() bool { + return wp.collected +} diff --git a/gameelement/canvas.go b/gameelement/canvas.go index ee9c852..1ec7c12 100644 --- a/gameelement/canvas.go +++ b/gameelement/canvas.go @@ -35,6 +35,7 @@ type Canvas struct { counter int score int splashes []*elements.Splash + wpdrops []*elements.WeaponDrop hero *elements.Hero charge *elements.Explosion goblin *elements.FlyGoblin @@ -82,6 +83,7 @@ func (c *Canvas) Update() error { c.Initialize() } else { c.UpdateHero() + c.UpdateWeaponDrops() c.UpdateWeapons() c.UpdateProjectiles() c.UpdateCharge() @@ -90,6 +92,7 @@ func (c *Canvas) Update() error { c.CleanupTargets() c.UpdateSplashes() c.CleanSplashes() + c.CleanupDrops() c.counter++ } @@ -135,6 +138,14 @@ func (c *Canvas) Draw(drawimg *ebiten.Image) { } } + //draw weapon drops + for _, drop := range c.wpdrops { + drop.Draw() + op := &ebiten.DrawImageOptions{} + op.GeoM.Translate(drop.GetPosition().X-float64(drop.Sprite.Bounds().Dx())/2, drop.GetPosition().Y-float64(drop.Sprite.Bounds().Dy())/2) + c.Sprite.DrawImage(drop.Sprite, op) + } + //draw enemies for _, e := range c.enemies { e.Draw() @@ -212,6 +223,7 @@ func (c *Canvas) Initialize() { c.InitializeHero() c.CleanSplashes() + c.ResetWeaponDrops() c.enemies = c.enemies[:0] c.gameover = false c.initialized = true @@ -536,6 +548,16 @@ func (c *Canvas) CleanupTargets() { // remove dead targets by iterating over all targets i := 0 for _, e := range c.enemies { + + //compute odds for dropping an item on dead enemies + if e.GetEnemyState() == elements.MoverActionDead { + if rand.Float64() > 0.98 { + drop := elements.NewWeaponDrop(gamedata.WeaponTypeLaser) + drop.SetPosition(e.GetPosition()) + c.wpdrops = append(c.wpdrops, drop) + } + } + //moving valid targets to the front of the slice if e.GetEnemyState() < elements.MoverActionDead && !(e.GetPosition().X < -640*2 || e.GetPosition().X > 640*2 || @@ -847,7 +869,7 @@ func (c *Canvas) LaserAttempt4() { } //fmt.Printf("%f \n", a) - if d <= 50 && e.GetEnemyState() <= gamedata.EnemyStateHit { + if d <= float64(e.GetSprite().Bounds().Dx()) && e.GetEnemyState() <= gamedata.EnemyStateHit { if IsPixelColliding(c.laserMask, e.GetSprite(), image.Pt(0, 0), @@ -890,6 +912,22 @@ func (c *Canvas) CleanSplashes() { c.splashes = c.splashes[:i] } +func (c *Canvas) CleanupDrops() { + i := 0 + for _, drop := range c.wpdrops { + if !drop.IsCollected() { + c.wpdrops[i] = drop + i++ + } + } + + for j := i; j < len(c.wpdrops); j++ { + c.wpdrops[j] = nil + } + + c.wpdrops = c.wpdrops[:i] +} + func (c *Canvas) UpdateWeapons() { if !c.gameover { //check for weapon inputs @@ -909,3 +947,41 @@ func (c *Canvas) UpdateWeapons() { } } + +func (c *Canvas) UpdateWeaponDrops() { + //do we have any drops? let's calculate the chances + // + + for _, drop := range c.wpdrops { + drop.Update() + + //has the hero collided with any? add to holster + //boundary box collision check + if c.hero.Pos.X >= drop.GetPosition().X-float64(drop.Sprite.Bounds().Dx())/2 && + c.hero.Pos.X <= drop.GetPosition().X+float64(drop.Sprite.Bounds().Dx())/2 && + c.hero.Pos.Y >= drop.GetPosition().Y-float64(drop.Sprite.Bounds().Dy())/2 && + c.hero.Pos.Y <= drop.GetPosition().Y+float64(drop.Sprite.Bounds().Dy())/2 { + //fmt.Println("hero trying to pick up weapon maybe") + + if IsPixelColliding( + c.hero.Sprite, + drop.Sprite, + image.Pt(int(c.hero.Pos.X), int(c.hero.Pos.Y)), + image.Pt(int(drop.GetPosition().X), int(drop.GetPosition().Y)), + ) { + fmt.Println("weapon acquired") + drop.SetCollected(true) + c.holster.AddWeapon(weapons.NewLaser()) + } + + } + } + +} + +func (c *Canvas) ResetWeaponDrops() { + for i := range c.wpdrops { + c.wpdrops[i] = nil + } + c.wpdrops = c.wpdrops[:0] +} diff --git a/screenmanager/manager.go b/screenmanager/manager.go index 2cc87ee..48f018c 100644 --- a/screenmanager/manager.go +++ b/screenmanager/manager.go @@ -26,7 +26,7 @@ func NewManager() Manager { return Manager{ Info: gamedata.GameInfo{ Name: "survive", - Version: "0.30", + Version: "0.32", Dimensions: gamedata.Area{ Width: defaultWidth, Height: defaultHeight, diff --git a/weapons/holster.go b/weapons/holster.go index 120965b..a722481 100644 --- a/weapons/holster.go +++ b/weapons/holster.go @@ -13,7 +13,7 @@ func NewHolster() *Holster { activewp: gamedata.WeaponTypeGun, } holster.AddWeapon(NewGun()) - holster.AddWeapon(NewLaser()) + //holster.AddWeapon(NewLaser()) return holster } @@ -46,8 +46,9 @@ func (h *Holster) CycleWeapon() { } //keep searching until we find the next weapon that exists + var nextwp gamedata.WeaponType = h.activewp for ok := false; !ok; { - nextwp := (h.activewp + 1) % gamedata.WeaponTypeMax + nextwp = (nextwp + 1) % gamedata.WeaponTypeMax _, ok = h.guns[nextwp] if ok { h.activewp = nextwp