Brought in screen manager, minor refactor.

This commit is contained in:
2024-11-11 09:54:30 -05:00
parent 9130155999
commit 6f794b7bb2
21 changed files with 373 additions and 170 deletions

15
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,15 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "survive",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "main.go"
}
]
}

View File

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 682 B

After

Width:  |  Height:  |  Size: 682 B

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

76
assets/imagebank.go Normal file
View File

@@ -0,0 +1,76 @@
package assets
import (
"bytes"
"image"
"log"
_ "embed"
"github.com/hajimehoshi/ebiten/v2"
)
type AssetName string
const (
FlyEyeNormal AssetName = "FlyEyeNormal"
FlyEyeDamaged AssetName = "FlyEyeDamaged"
FlyEyeDying AssetName = "FlyEyeDying"
FlyEyeShadow AssetName = "FlyEyeShadow"
HeroNormal AssetName = "HeroNormal"
HeroDying AssetName = "HeroDying"
TileSet AssetName = "TileSet"
Altar AssetName = "Altar"
Weapon AssetName = "Weapon"
)
var (
ImageBank map[AssetName]*ebiten.Image
flyeyeImage *ebiten.Image
flyeyeImage2 *ebiten.Image
flyeyeImage3 *ebiten.Image
shadow *ebiten.Image
//go:embed fly-eye.png
flyeye_img []byte
//go:embed fly-eye2.png
flyeye_img2 []byte
//go:embed fly-eye3.png
flyeye_img3 []byte
//go:embed shadow.png
shadow_img []byte
//go:embed hero.png
hero_img []byte
//go:embed herodeath.png
herodeath_img []byte
//go:embed grasstile.png
tileset_img []byte
//go:embed altar.png
altar_img []byte
//go:embed weapon.png
weapon_img []byte
)
func LoadImages() {
ImageBank = make(map[AssetName]*ebiten.Image)
ImageBank[FlyEyeNormal] = LoadImagesFatal(flyeye_img)
ImageBank[FlyEyeDamaged] = LoadImagesFatal(flyeye_img2)
ImageBank[FlyEyeDying] = LoadImagesFatal(flyeye_img3)
ImageBank[FlyEyeShadow] = LoadImagesFatal(shadow_img)
ImageBank[HeroNormal] = LoadImagesFatal(hero_img)
ImageBank[HeroDying] = LoadImagesFatal(herodeath_img)
ImageBank[TileSet] = LoadImagesFatal(tileset_img)
ImageBank[Altar] = LoadImagesFatal(altar_img)
ImageBank[Weapon] = LoadImagesFatal(weapon_img)
}
func LoadImagesFatal(b []byte) *ebiten.Image {
img, _, err := image.Decode(bytes.NewReader(b))
if err != nil {
log.Fatal(err)
}
return ebiten.NewImageFromImage(img)
}

View File

Before

Width:  |  Height:  |  Size: 102 B

After

Width:  |  Height:  |  Size: 102 B

View File

Before

Width:  |  Height:  |  Size: 741 B

After

Width:  |  Height:  |  Size: 741 B

View File

@@ -1,8 +1,10 @@
package main package elements
import "mover/gamedata"
type Explosion struct { type Explosion struct {
Radius float64 Radius float64
Origin Coordinates Origin gamedata.Coordinates
cycle int cycle int
Active bool Active bool
} }
@@ -27,7 +29,7 @@ func (e *Explosion) Update() {
} }
func (e *Explosion) SetOrigin(origin Coordinates) { func (e *Explosion) SetOrigin(origin gamedata.Coordinates) {
e.Origin = origin e.Origin = origin
} }

View File

@@ -1,9 +1,9 @@
package main package elements
import ( import (
"bytes"
"image" "image"
"log" "mover/assets"
"mover/gamedata"
_ "embed" _ "embed"
"image/color" "image/color"
@@ -12,15 +12,9 @@ import (
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
) )
var ( const (
heroImage *ebiten.Image MOVER_WIDTH = 48
heroDeath *ebiten.Image MOVER_HEIGHT = 48
//go:embed hero.png
hero_img []byte
//go:embed herodeath.png
herodeath_img []byte
) )
const ( const (
@@ -34,28 +28,14 @@ const (
type HeroAction uint type HeroAction uint
func init() {
img, _, err := image.Decode(bytes.NewReader(hero_img))
if err != nil {
log.Fatal(err)
}
heroImage = ebiten.NewImageFromImage(img)
img, _, err = image.Decode(bytes.NewReader(herodeath_img))
if err != nil {
log.Fatal(err)
}
heroDeath = ebiten.NewImageFromImage(img)
}
type Hero struct { type Hero struct {
Sprite *ebiten.Image Sprite *ebiten.Image
Maks *ebiten.Image Maks *ebiten.Image
MaksDest *ebiten.Image MaksDest *ebiten.Image
Angle float64 Angle float64
Pos Coordinates Pos gamedata.Coordinates
Origin Coordinates Origin gamedata.Coordinates
Lastpos Coordinates Lastpos gamedata.Coordinates
Action HeroAction Action HeroAction
cycles int cycles int
Upgrade bool Upgrade bool
@@ -93,7 +73,7 @@ func (m *Hero) SetAngle(a float64) {
m.Angle = a m.Angle = a
} }
func (m *Hero) SetOrigin(coords Coordinates) { func (m *Hero) SetOrigin(coords gamedata.Coordinates) {
m.Origin = coords m.Origin = coords
m.Pos = coords m.Pos = coords
} }
@@ -117,16 +97,16 @@ func (m *Hero) Draw() {
switch m.Action { switch m.Action {
case HeroActionDefault: case HeroActionDefault:
m.Sprite.DrawImage(heroImage.SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), op) m.Sprite.DrawImage(assets.ImageBank[assets.HeroNormal].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), op)
case HeroActionDying: case HeroActionDying:
m.Sprite.DrawImage(heroDeath.SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), op) m.Sprite.DrawImage(assets.ImageBank[assets.HeroDying].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), op)
if m.dyingcount >= 31 { if m.dyingcount >= 31 {
m.cycles = 0 m.cycles = 0
m.Action++ m.Action++
} }
case HeroActionExploding: case HeroActionExploding:
m.Sprite.DrawImage(heroDeath.SubImage(image.Rect(48*3, 0, 48*4, 48)).(*ebiten.Image), op) m.Sprite.DrawImage(assets.ImageBank[assets.HeroDying].SubImage(image.Rect(48*3, 0, 48*4, 48)).(*ebiten.Image), op)
default: default:
} }
} }

View File

@@ -1,9 +1,9 @@
package main package elements
import ( import (
"bytes"
"image" "image"
"log" "mover/assets"
"mover/gamedata"
_ "embed" _ "embed"
"image/color" "image/color"
@@ -12,22 +12,6 @@ import (
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
) )
var (
flyeyeImage *ebiten.Image
flyeyeImage2 *ebiten.Image
flyeyeImage3 *ebiten.Image
shadow *ebiten.Image
//go:embed fly-eye.png
flyeye_img []byte
//go:embed fly-eye2.png
flyeye_img2 []byte
//go:embed fly-eye3.png
flyeye_img3 []byte
//go:embed shadow.png
shadow_img []byte
)
const ( const (
MoverActionDefault = iota MoverActionDefault = iota
MoverActionDamaged MoverActionDamaged
@@ -39,39 +23,13 @@ const (
type MoverAction uint type MoverAction uint
func init() {
img, _, err := image.Decode(bytes.NewReader(flyeye_img))
if err != nil {
log.Fatal(err)
}
flyeyeImage = ebiten.NewImageFromImage(img)
img, _, err = image.Decode(bytes.NewReader(flyeye_img2))
if err != nil {
log.Fatal(err)
}
flyeyeImage2 = ebiten.NewImageFromImage(img)
img, _, err = image.Decode(bytes.NewReader(flyeye_img3))
if err != nil {
log.Fatal(err)
}
flyeyeImage3 = ebiten.NewImageFromImage(img)
img, _, err = image.Decode(bytes.NewReader(shadow_img))
if err != nil {
log.Fatal(err)
}
shadow = ebiten.NewImageFromImage(img)
}
type Mover struct { type Mover struct {
Sprite *ebiten.Image Sprite *ebiten.Image
Maks *ebiten.Image Maks *ebiten.Image
MaksDest *ebiten.Image MaksDest *ebiten.Image
Angle float64 Angle float64
Pos Coordinates Pos gamedata.Coordinates
Origin Coordinates Origin gamedata.Coordinates
Action MoverAction Action MoverAction
cycles int cycles int
rotating bool rotating bool
@@ -106,7 +64,7 @@ func (m *Mover) SetAngle(a float64) {
m.Angle = a m.Angle = a
} }
func (m *Mover) SetOrigin(coords Coordinates) { func (m *Mover) SetOrigin(coords gamedata.Coordinates) {
m.Origin = coords m.Origin = coords
m.Pos = coords m.Pos = coords
} }
@@ -126,18 +84,18 @@ func (m *Mover) Draw() {
case MoverActionDefault: case MoverActionDefault:
op := &ebiten.DrawImageOptions{} op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(14, 40) op.GeoM.Translate(14, 40)
m.Sprite.DrawImage(shadow, op) m.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeShadow], op)
m.Sprite.DrawImage(flyeyeImage.SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil) m.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeNormal].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil)
case MoverActionDamaged: case MoverActionDamaged:
op := &ebiten.DrawImageOptions{} op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(14, 40) op.GeoM.Translate(14, 40)
m.Sprite.DrawImage(shadow, op) m.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeShadow], op)
m.Sprite.DrawImage(flyeyeImage2.SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil) m.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeDamaged].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil)
case MoverActionDying: case MoverActionDying:
if (m.cycles/5)%2 == 0 { if (m.cycles/5)%2 == 0 {
m.MaksDest.DrawImage(flyeyeImage2.SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil) m.MaksDest.DrawImage(assets.ImageBank[assets.FlyEyeDamaged].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil)
op := &ebiten.DrawImageOptions{} op := &ebiten.DrawImageOptions{}
op.GeoM.Reset() op.GeoM.Reset()
op.Blend = ebiten.BlendSourceAtop op.Blend = ebiten.BlendSourceAtop
@@ -145,14 +103,14 @@ func (m *Mover) Draw() {
m.Sprite.DrawImage(m.MaksDest, nil) m.Sprite.DrawImage(m.MaksDest, nil)
} else { } else {
m.Sprite.DrawImage(flyeyeImage2.SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil) m.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeDamaged].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil)
} }
if m.dyingcount >= 31 { if m.dyingcount >= 31 {
m.cycles = 0 m.cycles = 0
m.SetHit() m.SetHit()
} }
case MoverActionExploding: case MoverActionExploding:
m.Sprite.DrawImage(flyeyeImage3.SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil) m.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeDying].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil)
if idx == 3 { if idx == 3 {
m.SetHit() m.SetHit()
} }

View File

@@ -1,14 +1,17 @@
package main package elements
import "math" import (
"math"
"mover/gamedata"
)
type Projectile struct { type Projectile struct {
Pos Coordinates Pos gamedata.Coordinates
Velocity float64 Velocity float64
a float64 a float64
} }
func NewProjectile(origin Coordinates, angle, velocity float64) *Projectile { func NewProjectile(origin gamedata.Coordinates, angle, velocity float64) *Projectile {
return &Projectile{ return &Projectile{
Velocity: velocity, Velocity: velocity,
a: angle, a: angle,

View File

@@ -1,4 +1,4 @@
package main package gamedata
type Coordinates struct { type Coordinates struct {
X float64 X float64

16
gamedata/gameinfo.go Normal file
View File

@@ -0,0 +1,16 @@
package gamedata
type Area struct {
Width int
Height int
}
func (a *Area) Area() int {
return a.Height * a.Width
}
type GameInfo struct {
Name string
Version string
Dimensions Area
}

25
main.go
View File

@@ -1,8 +1,11 @@
package main package main
import ( import (
"fmt"
"log" "log"
"mover/assets"
"mover/gamedata"
"mover/screenmanager"
"mover/screens"
"github.com/hajimehoshi/ebiten/v2" "github.com/hajimehoshi/ebiten/v2"
) )
@@ -13,17 +16,23 @@ const (
) )
func main() { func main() {
ver := "survive v0.10" //moverGame := &Game{}
moverGame := screenmanager.NewManager()
fmt.Println(ver) moverGame.SetDimensions(gamedata.Area{Width: screenWidth, Height: screenHeight})
moverGame := &Game{}
ebiten.SetWindowSize(screenWidth*1.5, screenHeight*1.5) ebiten.SetWindowSize(screenWidth*1.5, screenHeight*1.5)
ebiten.SetWindowTitle(ver) ebiten.SetWindowTitle(moverGame.Info.Name + ": v" + moverGame.Info.Version)
if err := ebiten.RunGame(moverGame); err != nil { loadScreens(&moverGame)
if err := ebiten.RunGame(&moverGame); err != nil {
log.Fatal(err) log.Fatal(err)
} }
} }
func loadScreens(m *screenmanager.Manager) {
assets.LoadImages()
m.AddScene(&screens.Game{})
m.ResetScenes()
}

136
screenmanager/manager.go Normal file
View File

@@ -0,0 +1,136 @@
package screenmanager
import (
"mover/gamedata"
"mover/screens"
"github.com/hajimehoshi/ebiten/v2"
)
const (
defaultWidth = 1024
defaultHeight = 768
)
type Manager struct {
Info gamedata.GameInfo
currentScene screens.Screen
currentSceneId uint
nextSceneId uint
screens []screens.Screen
internalerr error
}
// can be used to create default manager instance
func NewManager() Manager {
return Manager{
Info: gamedata.GameInfo{
Name: "survive",
Version: "0.12",
Dimensions: gamedata.Area{
Width: defaultWidth,
Height: defaultHeight,
},
},
currentSceneId: 0,
nextSceneId: 1,
internalerr: nil,
}
}
// ebitengine update proxy on behalf of current scene
func (m *Manager) Update() error {
if m.currentScene == nil {
return nil
}
err := m.currentScene.Update()
if err != nil {
return err
}
return m.internalerr
}
// shutdown application
func (m *Manager) Quit() {
m.internalerr = ebiten.Termination
}
// calls current scene's draw method if the currentscene is valid
func (m *Manager) Draw(screen *ebiten.Image) {
if m.currentScene != nil {
m.currentScene.Draw(screen)
}
}
// ebitengine proxy for layout
func (m *Manager) Layout(outsideWidth, outsideHeight int) (screenWidth, screenHeight int) {
return m.Info.Dimensions.Width, m.Info.Dimensions.Height
}
// appends scene to the managed screens
func (m *Manager) AddScene(s screens.Screen) {
setDefaultHandlers(m, s)
s.SetDimensions(m.Info.Dimensions)
m.screens = append(m.screens, s)
}
// sets the default callback handlers for a given scene within manager
// Default Handling behaviours:
//
// reset: sets (scene, nextscene) to {0, 1}
// scene completion: sets (scene, nextscene) to {nextscene, nextscene+1}
// end game: shutdown groovy
//
// note: NOOP and RELOAD are purposefully not mapped; they are scene
// specific and should be mapped to by user of groovy
func setDefaultHandlers(m *Manager, s screens.Screen) {
s.SetEventHandler(screens.EventReset, func() { m.ResetScenes() })
s.SetEventHandler(screens.EventCompleted, func() { m.TransitionScene() })
s.SetEventHandler(screens.EventEndgame, func() { m.Quit() })
}
// we're going to reset the scene to the first one
func (m *Manager) ResetScenes() {
m.currentSceneId = 0
m.nextSceneId = 1
m.SetCurrentScene(0)
}
// sets the current scene, based on sceneindex n
// n > scenelist, quit
// otherwise, scene = n
func (m *Manager) SetCurrentScene(sceneId uint) {
if sceneId >= uint(len(m.screens)) {
m.Quit()
} else {
m.currentSceneId = sceneId
m.currentScene = m.screens[sceneId]
m.nextSceneId = m.currentSceneId + 1
}
}
// handle scene transition
func (m *Manager) TransitionScene() {
m.SetCurrentScene(m.nextSceneId)
}
// set new sceneId as the successor
func (m *Manager) SetNextScene(sceneId uint) {
m.nextSceneId = sceneId
}
// sets sene dimensions
func (m *Manager) SetDimensions(a gamedata.Area) {
m.Info.Dimensions = a
}
// report number of total screens
func (m *Manager) SceneCount() uint {
return uint(len(m.screens))
}
func (m *Manager) GetScene(sceneId uint) screens.Screen {
return m.screens[sceneId]
}

View File

@@ -1,14 +1,16 @@
package main package screens
import ( import (
"bytes"
"fmt" "fmt"
"image" "image"
"image/color" "image/color"
"log" "log"
"math" "math"
"math/rand/v2" "math/rand/v2"
"mover/assets"
"mover/elements"
"mover/fonts" "mover/fonts"
"mover/gamedata"
_ "embed" _ "embed"
@@ -23,19 +25,6 @@ const (
MOVER_HEIGHT = 48 MOVER_HEIGHT = 48
) )
var (
weaponImage *ebiten.Image
tilesetImage *ebiten.Image
altarImage *ebiten.Image
//go:embed grasstile.png
tileset_img []byte
//go:embed altar.png
altar_img []byte
//go:embed weapon.png
weapon_img []byte
)
type Game struct { type Game struct {
background *ebiten.Image background *ebiten.Image
collisionMask *ebiten.Image collisionMask *ebiten.Image
@@ -43,20 +32,21 @@ type Game struct {
heroCollisionMask *ebiten.Image heroCollisionMask *ebiten.Image
heroCollisionCpy *ebiten.Image heroCollisionCpy *ebiten.Image
Pos Coordinates dimensions gamedata.Area
Pos gamedata.Coordinates
Paused bool Paused bool
initialized bool initialized bool
gameover bool gameover bool
reset bool reset bool
runtime float64 runtime float64
hero *Hero hero *elements.Hero
projectiles map[int]*Projectile projectiles map[int]*elements.Projectile
explosion *Explosion explosion *elements.Explosion
score int score int
counter int counter int
timer int timer int
targets []*Mover targets []*elements.Mover
gamepadIDsBuf []ebiten.GamepadID gamepadIDsBuf []ebiten.GamepadID
gamepadIDs map[ebiten.GamepadID]struct{} gamepadIDs map[ebiten.GamepadID]struct{}
@@ -64,43 +54,23 @@ type Game struct {
//pressedButtons map[ebiten.GamepadID][]string //pressedButtons map[ebiten.GamepadID][]string
} }
func init() {
img, _, err := image.Decode(bytes.NewReader(tileset_img))
if err != nil {
log.Fatal(err)
}
tilesetImage = ebiten.NewImageFromImage(img)
img, _, err = image.Decode(bytes.NewReader(altar_img))
if err != nil {
log.Fatal(err)
}
altarImage = ebiten.NewImageFromImage(img)
img, _, err = image.Decode(bytes.NewReader(weapon_img))
if err != nil {
log.Fatal(err)
}
weaponImage = ebiten.NewImageFromImage(img)
}
func (g *Game) Initialize() { func (g *Game) Initialize() {
origin := Coordinates{X: 640 / 2, Y: 480 / 2} origin := gamedata.Coordinates{X: 640 / 2, Y: 480 / 2}
g.ConstructBackground() g.ConstructBackground()
g.hero = NewHero() g.hero = elements.NewHero()
g.hero.SetOrigin(origin) g.hero.SetOrigin(origin)
g.hero.ToggleRotate() g.hero.ToggleRotate()
g.gameover = false g.gameover = false
g.collisionMask = ebiten.NewImage(screenWidth, screenHeight) g.collisionMask = ebiten.NewImage(g.dimensions.Width, g.dimensions.Height)
g.projectileMask = ebiten.NewImage(screenWidth, screenHeight) g.projectileMask = ebiten.NewImage(g.dimensions.Width, g.dimensions.Height)
g.heroCollisionMask = ebiten.NewImage(MOVER_WIDTH, MOVER_HEIGHT) g.heroCollisionMask = ebiten.NewImage(MOVER_WIDTH, MOVER_HEIGHT)
g.heroCollisionCpy = ebiten.NewImage(MOVER_WIDTH, MOVER_HEIGHT) g.heroCollisionCpy = ebiten.NewImage(MOVER_WIDTH, MOVER_HEIGHT)
g.explosion = NewExplosion() g.explosion = elements.NewExplosion()
g.explosion.SetOrigin(origin) g.explosion.SetOrigin(origin)
g.score = 0 g.score = 0
g.reset = false g.reset = false
@@ -116,7 +86,7 @@ func (g *Game) Initialize() {
g.timer = 0 g.timer = 0
g.runtime = 0. g.runtime = 0.
g.projectiles = make(map[int]*Projectile) g.projectiles = make(map[int]*elements.Projectile)
g.initialized = true g.initialized = true
g.reset = false g.reset = false
@@ -184,7 +154,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
op.GeoM.Translate(0, -16) op.GeoM.Translate(0, -16)
op.GeoM.Rotate(g.hero.Angle) op.GeoM.Rotate(g.hero.Angle)
op.GeoM.Translate(g.hero.Pos.X, g.hero.Pos.Y) op.GeoM.Translate(g.hero.Pos.X, g.hero.Pos.Y)
screen.DrawImage(weaponImage, op) screen.DrawImage(assets.ImageBank[assets.Weapon], op)
//secondary/upgraded weapon sprite; in testing proves sort of distracting //secondary/upgraded weapon sprite; in testing proves sort of distracting
/* /*
@@ -226,7 +196,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
} }
func (g *Game) Layout(width, height int) (int, int) { func (g *Game) Layout(width, height int) (int, int) {
return screenWidth, screenHeight return g.dimensions.Width, g.dimensions.Height
} }
func (g *Game) CleanupTargets() { func (g *Game) CleanupTargets() {
@@ -234,7 +204,7 @@ func (g *Game) CleanupTargets() {
i := 0 i := 0
for _, target := range g.targets { for _, target := range g.targets {
//moving valid targets to the front of the slice //moving valid targets to the front of the slice
if target.Action < MoverActionDead { if target.Action < elements.MoverActionDead {
g.targets[i] = target g.targets[i] = target
i++ i++
} }
@@ -282,7 +252,7 @@ func (g *Game) StepGame() {
func (g *Game) SpawnEnemies() { func (g *Game) SpawnEnemies() {
f := 40000 / (g.counter + 1) f := 40000 / (g.counter + 1)
if g.counter%f == 0 { if g.counter%f == 0 {
g.targets = append(g.targets, NewMover()) g.targets = append(g.targets, elements.NewMover())
x0 := rand.Float64() * 640 x0 := rand.Float64() * 640
y0 := rand.Float64() * 480 y0 := rand.Float64() * 480
@@ -290,15 +260,15 @@ func (g *Game) SpawnEnemies() {
switch quadrant { switch quadrant {
case 0: case 0:
g.targets[len(g.targets)-1].SetOrigin(Coordinates{X: x0, Y: -MOVER_HEIGHT}) g.targets[len(g.targets)-1].SetOrigin(gamedata.Coordinates{X: x0, Y: -MOVER_HEIGHT})
case 1: case 1:
g.targets[len(g.targets)-1].SetOrigin(Coordinates{X: x0, Y: screenHeight + MOVER_HEIGHT}) g.targets[len(g.targets)-1].SetOrigin(gamedata.Coordinates{X: x0, Y: float64(g.dimensions.Height) + MOVER_HEIGHT})
case 2: case 2:
g.targets[len(g.targets)-1].SetOrigin(Coordinates{X: -MOVER_WIDTH, Y: y0}) g.targets[len(g.targets)-1].SetOrigin(gamedata.Coordinates{X: -MOVER_WIDTH, Y: y0})
case 3: case 3:
g.targets[len(g.targets)-1].SetOrigin(Coordinates{X: screenWidth + x0, Y: y0}) g.targets[len(g.targets)-1].SetOrigin(gamedata.Coordinates{X: float64(g.dimensions.Width) + x0, Y: y0})
default: default:
g.targets[len(g.targets)-1].SetOrigin(Coordinates{X: x0, Y: y0}) g.targets[len(g.targets)-1].SetOrigin(gamedata.Coordinates{X: x0, Y: y0})
fmt.Println("WTF " + string(quadrant)) fmt.Println("WTF " + string(quadrant))
} }
@@ -319,7 +289,8 @@ func (g *Game) HandlePulseWaveUpdate() {
dy := target.Pos.Y - g.hero.Pos.Y dy := target.Pos.Y - g.hero.Pos.Y
r := math.Sqrt(dx*dx + dy*dy) r := math.Sqrt(dx*dx + dy*dy)
if r >= g.explosion.Radius-5 && r <= g.explosion.Radius+5 && target.Action <= MoverActionDamaged && !target.Touched { if r >= g.explosion.Radius-5 && r <= g.explosion.Radius+5 &&
target.Action <= elements.MoverActionDamaged && !target.Touched {
target.ToggleColor() target.ToggleColor()
target.Touched = true target.Touched = true
//target.SetHit() //target.SetHit()
@@ -344,7 +315,9 @@ func (g *Game) UpdateProjectiles() {
//compute projectile collisions //compute projectile collisions
for _, target := range g.targets { for _, target := range g.targets {
//first, boundary check //first, boundary check
if p.Pos.X >= target.Pos.X-MOVER_WIDTH/2 && p.Pos.X <= target.Pos.X+MOVER_WIDTH/2 && p.Pos.Y >= target.Pos.Y-MOVER_HEIGHT/2 && p.Pos.Y <= target.Pos.Y+MOVER_HEIGHT/2 && target.Action == MoverActionDamaged { if p.Pos.X >= target.Pos.X-MOVER_WIDTH/2 && p.Pos.X <= target.Pos.X+MOVER_WIDTH/2 &&
p.Pos.Y >= target.Pos.Y-MOVER_HEIGHT/2 && p.Pos.Y <= target.Pos.Y+MOVER_HEIGHT/2 &&
target.Action == elements.MoverActionDamaged {
//fmt.Println("potential collision") //fmt.Println("potential collision")
//the following computes total collisions in the image using a projectile mask that is a duplicate of what is on screen //the following computes total collisions in the image using a projectile mask that is a duplicate of what is on screen
@@ -359,7 +332,7 @@ func (g *Game) UpdateProjectiles() {
g.collisionMask.DrawImage(target.Sprite, op) g.collisionMask.DrawImage(target.Sprite, op)
//var pixels []byte = make([]byte, MOVER_WIDTH*MOVER_HEIGHT*4) //var pixels []byte = make([]byte, MOVER_WIDTH*MOVER_HEIGHT*4)
var pixels []byte = make([]byte, screenWidth*screenHeight*4) var pixels []byte = make([]byte, g.dimensions.Width*g.dimensions.Height*4)
g.collisionMask.ReadPixels(pixels) g.collisionMask.ReadPixels(pixels)
for i := 0; i < len(pixels); i = i + 4 { for i := 0; i < len(pixels); i = i + 4 {
if pixels[i+3] != 0 { if pixels[i+3] != 0 {
@@ -367,7 +340,7 @@ func (g *Game) UpdateProjectiles() {
delete(g.projectiles, k) delete(g.projectiles, k)
//target.ToggleColor() //target.ToggleColor()
target.SetHit() target.SetHit()
//target.SetOrigin(Coordinates{X: rand.Float64() * 640, Y: rand.Float64() * 480}) //target.SetOrigin(gamedata.Coordinates{X: rand.Float64() * 640, Y: rand.Float64() * 480})
target.Hit = true target.Hit = true
break break
} }
@@ -380,7 +353,7 @@ func (g *Game) UpdateProjectiles() {
func (g *Game) UpdateTargets() { func (g *Game) UpdateTargets() {
for _, target := range g.targets { for _, target := range g.targets {
if !target.Hit && g.hero.Action < HeroActionDying { if !target.Hit && g.hero.Action < elements.HeroActionDying {
dx := g.hero.Pos.X - target.Pos.X dx := g.hero.Pos.X - target.Pos.X
dy := g.hero.Pos.Y - target.Pos.Y dy := g.hero.Pos.Y - target.Pos.Y
angle := math.Atan2(dy, dx) angle := math.Atan2(dy, dx)
@@ -392,7 +365,9 @@ func (g *Game) UpdateTargets() {
} }
//compute collision with hero //compute collision with hero
if g.hero.Pos.X >= target.Pos.X-MOVER_WIDTH/2 && g.hero.Pos.X <= target.Pos.X+MOVER_WIDTH/2 && g.hero.Pos.Y >= target.Pos.Y-MOVER_HEIGHT/2 && g.hero.Pos.Y <= target.Pos.Y+MOVER_HEIGHT/2 && target.Action < MoverActionDying && g.hero.Action < HeroActionDying { if g.hero.Pos.X >= target.Pos.X-MOVER_WIDTH/2 && g.hero.Pos.X <= target.Pos.X+MOVER_WIDTH/2 &&
g.hero.Pos.Y >= target.Pos.Y-MOVER_HEIGHT/2 && g.hero.Pos.Y <= target.Pos.Y+MOVER_HEIGHT/2 &&
target.Action < elements.MoverActionDying && g.hero.Action < elements.HeroActionDying {
g.heroCollisionMask.Clear() g.heroCollisionMask.Clear()
g.heroCollisionMask.DrawImage(g.hero.Sprite, nil) g.heroCollisionMask.DrawImage(g.hero.Sprite, nil)
@@ -428,10 +403,10 @@ func (g *Game) ResetTargetTouches() {
func (g *Game) AppendProjectiles() { func (g *Game) AppendProjectiles() {
if g.counter%14 == 0 && ebiten.IsStandardGamepadButtonPressed(0, ebiten.StandardGamepadButtonFrontBottomRight) { if g.counter%14 == 0 && ebiten.IsStandardGamepadButtonPressed(0, ebiten.StandardGamepadButtonFrontBottomRight) {
g.projectiles[g.counter] = NewProjectile(Coordinates{X: g.hero.Pos.X, Y: g.hero.Pos.Y}, g.hero.Angle, 5.) g.projectiles[g.counter] = elements.NewProjectile(gamedata.Coordinates{X: g.hero.Pos.X, Y: g.hero.Pos.Y}, g.hero.Angle, 5.)
if g.hero.Upgrade { if g.hero.Upgrade {
g.projectiles[g.counter+1] = NewProjectile(Coordinates{X: g.hero.Pos.X, Y: g.hero.Pos.Y}, g.hero.Angle+math.Pi, 5.) g.projectiles[g.counter+1] = elements.NewProjectile(gamedata.Coordinates{X: g.hero.Pos.X, Y: g.hero.Pos.Y}, g.hero.Angle+math.Pi, 5.)
} }
} }
} }
@@ -487,7 +462,7 @@ func (g *Game) UpdateHeroPosition() {
} }
func (g *Game) ConstructBackground() { func (g *Game) ConstructBackground() {
g.background = ebiten.NewImage(screenWidth, screenHeight) g.background = ebiten.NewImage(g.dimensions.Width, g.dimensions.Height)
BLOCK_SIZE := 32 BLOCK_SIZE := 32
for i := 0; i < 640/16; i++ { for i := 0; i < 640/16; i++ {
@@ -505,7 +480,7 @@ func (g *Game) ConstructBackground() {
//translate for grid element we're painting //translate for grid element we're painting
op := &ebiten.DrawImageOptions{} op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(float64(i)*16, float64(j)*16) op.GeoM.Translate(float64(i)*16, float64(j)*16)
g.background.DrawImage(tilesetImage.SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), op) g.background.DrawImage(assets.ImageBank[assets.TileSet].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), op)
} }
} }
@@ -514,5 +489,13 @@ func (g *Game) ConstructBackground() {
op := &ebiten.DrawImageOptions{} op := &ebiten.DrawImageOptions{}
op.GeoM.Translate(ax, ay) op.GeoM.Translate(ax, ay)
g.background.DrawImage(altarImage, op) g.background.DrawImage(assets.ImageBank[assets.Altar], op)
}
func (g *Game) SetDimensions(a gamedata.Area) {
g.dimensions = a
}
func (g *Game) SetEventHandler(e ScreenManagerEvent, f func()) {
} }

25
screens/scene.go Normal file
View File

@@ -0,0 +1,25 @@
package screens
import (
"mover/gamedata"
"github.com/hajimehoshi/ebiten/v2"
)
type ScreenManagerEvent int
const (
EventNoop ScreenManagerEvent = iota
EventReset // reset to initial scene
EventLoad // loading elements
EventReload // reload current scene
EventCompleted // current scene has completed
EventEndgame // shutdown all scenes
)
type Screen interface {
Update() error
Draw(screen *ebiten.Image)
SetEventHandler(e ScreenManagerEvent, f func())
SetDimensions(a gamedata.Area)
}