Brought in screen manager, minor refactor.
15
.vscode/launch.json
vendored
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 9.7 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 682 B After Width: | Height: | Size: 682 B |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
76
assets/imagebank.go
Normal 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)
|
||||
}
|
||||
|
Before Width: | Height: | Size: 102 B After Width: | Height: | Size: 102 B |
|
Before Width: | Height: | Size: 741 B After Width: | Height: | Size: 741 B |
@@ -1,8 +1,10 @@
|
||||
package main
|
||||
package elements
|
||||
|
||||
import "mover/gamedata"
|
||||
|
||||
type Explosion struct {
|
||||
Radius float64
|
||||
Origin Coordinates
|
||||
Origin gamedata.Coordinates
|
||||
cycle int
|
||||
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
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package main
|
||||
package elements
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"log"
|
||||
"mover/assets"
|
||||
"mover/gamedata"
|
||||
|
||||
_ "embed"
|
||||
"image/color"
|
||||
@@ -12,15 +12,9 @@ import (
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
heroImage *ebiten.Image
|
||||
heroDeath *ebiten.Image
|
||||
|
||||
//go:embed hero.png
|
||||
hero_img []byte
|
||||
|
||||
//go:embed herodeath.png
|
||||
herodeath_img []byte
|
||||
const (
|
||||
MOVER_WIDTH = 48
|
||||
MOVER_HEIGHT = 48
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -34,28 +28,14 @@ const (
|
||||
|
||||
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 {
|
||||
Sprite *ebiten.Image
|
||||
Maks *ebiten.Image
|
||||
MaksDest *ebiten.Image
|
||||
Angle float64
|
||||
Pos Coordinates
|
||||
Origin Coordinates
|
||||
Lastpos Coordinates
|
||||
Pos gamedata.Coordinates
|
||||
Origin gamedata.Coordinates
|
||||
Lastpos gamedata.Coordinates
|
||||
Action HeroAction
|
||||
cycles int
|
||||
Upgrade bool
|
||||
@@ -93,7 +73,7 @@ func (m *Hero) SetAngle(a float64) {
|
||||
m.Angle = a
|
||||
}
|
||||
|
||||
func (m *Hero) SetOrigin(coords Coordinates) {
|
||||
func (m *Hero) SetOrigin(coords gamedata.Coordinates) {
|
||||
m.Origin = coords
|
||||
m.Pos = coords
|
||||
}
|
||||
@@ -117,16 +97,16 @@ func (m *Hero) Draw() {
|
||||
|
||||
switch m.Action {
|
||||
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:
|
||||
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 {
|
||||
m.cycles = 0
|
||||
m.Action++
|
||||
}
|
||||
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:
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package main
|
||||
package elements
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image"
|
||||
"log"
|
||||
"mover/assets"
|
||||
"mover/gamedata"
|
||||
|
||||
_ "embed"
|
||||
"image/color"
|
||||
@@ -12,22 +12,6 @@ import (
|
||||
"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 (
|
||||
MoverActionDefault = iota
|
||||
MoverActionDamaged
|
||||
@@ -39,39 +23,13 @@ const (
|
||||
|
||||
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 {
|
||||
Sprite *ebiten.Image
|
||||
Maks *ebiten.Image
|
||||
MaksDest *ebiten.Image
|
||||
Angle float64
|
||||
Pos Coordinates
|
||||
Origin Coordinates
|
||||
Pos gamedata.Coordinates
|
||||
Origin gamedata.Coordinates
|
||||
Action MoverAction
|
||||
cycles int
|
||||
rotating bool
|
||||
@@ -106,7 +64,7 @@ func (m *Mover) SetAngle(a float64) {
|
||||
m.Angle = a
|
||||
}
|
||||
|
||||
func (m *Mover) SetOrigin(coords Coordinates) {
|
||||
func (m *Mover) SetOrigin(coords gamedata.Coordinates) {
|
||||
m.Origin = coords
|
||||
m.Pos = coords
|
||||
}
|
||||
@@ -126,18 +84,18 @@ func (m *Mover) Draw() {
|
||||
case MoverActionDefault:
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Translate(14, 40)
|
||||
m.Sprite.DrawImage(shadow, op)
|
||||
m.Sprite.DrawImage(flyeyeImage.SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil)
|
||||
m.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeShadow], op)
|
||||
m.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeNormal].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil)
|
||||
case MoverActionDamaged:
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Translate(14, 40)
|
||||
m.Sprite.DrawImage(shadow, op)
|
||||
m.Sprite.DrawImage(flyeyeImage2.SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil)
|
||||
m.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeShadow], op)
|
||||
m.Sprite.DrawImage(assets.ImageBank[assets.FlyEyeDamaged].SubImage(image.Rect(x0, y0, x1, y1)).(*ebiten.Image), nil)
|
||||
case MoverActionDying:
|
||||
|
||||
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.GeoM.Reset()
|
||||
op.Blend = ebiten.BlendSourceAtop
|
||||
@@ -145,14 +103,14 @@ func (m *Mover) Draw() {
|
||||
m.Sprite.DrawImage(m.MaksDest, nil)
|
||||
|
||||
} 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 {
|
||||
m.cycles = 0
|
||||
m.SetHit()
|
||||
}
|
||||
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 {
|
||||
m.SetHit()
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
package main
|
||||
package elements
|
||||
|
||||
import "math"
|
||||
import (
|
||||
"math"
|
||||
"mover/gamedata"
|
||||
)
|
||||
|
||||
type Projectile struct {
|
||||
Pos Coordinates
|
||||
Pos gamedata.Coordinates
|
||||
Velocity float64
|
||||
a float64
|
||||
}
|
||||
|
||||
func NewProjectile(origin Coordinates, angle, velocity float64) *Projectile {
|
||||
func NewProjectile(origin gamedata.Coordinates, angle, velocity float64) *Projectile {
|
||||
return &Projectile{
|
||||
Velocity: velocity,
|
||||
a: angle,
|
||||
@@ -1,4 +1,4 @@
|
||||
package main
|
||||
package gamedata
|
||||
|
||||
type Coordinates struct {
|
||||
X float64
|
||||
16
gamedata/gameinfo.go
Normal 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
@@ -1,8 +1,11 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"mover/assets"
|
||||
"mover/gamedata"
|
||||
"mover/screenmanager"
|
||||
"mover/screens"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
@@ -13,17 +16,23 @@ const (
|
||||
)
|
||||
|
||||
func main() {
|
||||
ver := "survive v0.10"
|
||||
|
||||
fmt.Println(ver)
|
||||
|
||||
moverGame := &Game{}
|
||||
//moverGame := &Game{}
|
||||
moverGame := screenmanager.NewManager()
|
||||
moverGame.SetDimensions(gamedata.Area{Width: screenWidth, Height: screenHeight})
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func loadScreens(m *screenmanager.Manager) {
|
||||
assets.LoadImages()
|
||||
m.AddScene(&screens.Game{})
|
||||
m.ResetScenes()
|
||||
}
|
||||
|
||||
136
screenmanager/manager.go
Normal 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]
|
||||
}
|
||||
@@ -1,14 +1,16 @@
|
||||
package main
|
||||
package screens
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"image"
|
||||
"image/color"
|
||||
"log"
|
||||
"math"
|
||||
"math/rand/v2"
|
||||
"mover/assets"
|
||||
"mover/elements"
|
||||
"mover/fonts"
|
||||
"mover/gamedata"
|
||||
|
||||
_ "embed"
|
||||
|
||||
@@ -23,19 +25,6 @@ const (
|
||||
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 {
|
||||
background *ebiten.Image
|
||||
collisionMask *ebiten.Image
|
||||
@@ -43,20 +32,21 @@ type Game struct {
|
||||
heroCollisionMask *ebiten.Image
|
||||
heroCollisionCpy *ebiten.Image
|
||||
|
||||
Pos Coordinates
|
||||
dimensions gamedata.Area
|
||||
Pos gamedata.Coordinates
|
||||
Paused bool
|
||||
initialized bool
|
||||
gameover bool
|
||||
reset bool
|
||||
runtime float64
|
||||
hero *Hero
|
||||
projectiles map[int]*Projectile
|
||||
explosion *Explosion
|
||||
hero *elements.Hero
|
||||
projectiles map[int]*elements.Projectile
|
||||
explosion *elements.Explosion
|
||||
|
||||
score int
|
||||
counter int
|
||||
timer int
|
||||
targets []*Mover
|
||||
targets []*elements.Mover
|
||||
|
||||
gamepadIDsBuf []ebiten.GamepadID
|
||||
gamepadIDs map[ebiten.GamepadID]struct{}
|
||||
@@ -64,43 +54,23 @@ type Game struct {
|
||||
//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() {
|
||||
|
||||
origin := Coordinates{X: 640 / 2, Y: 480 / 2}
|
||||
origin := gamedata.Coordinates{X: 640 / 2, Y: 480 / 2}
|
||||
|
||||
g.ConstructBackground()
|
||||
g.hero = NewHero()
|
||||
g.hero = elements.NewHero()
|
||||
g.hero.SetOrigin(origin)
|
||||
g.hero.ToggleRotate()
|
||||
|
||||
g.gameover = false
|
||||
|
||||
g.collisionMask = ebiten.NewImage(screenWidth, screenHeight)
|
||||
g.projectileMask = ebiten.NewImage(screenWidth, screenHeight)
|
||||
g.collisionMask = ebiten.NewImage(g.dimensions.Width, g.dimensions.Height)
|
||||
g.projectileMask = ebiten.NewImage(g.dimensions.Width, g.dimensions.Height)
|
||||
g.heroCollisionMask = 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.score = 0
|
||||
g.reset = false
|
||||
@@ -116,7 +86,7 @@ func (g *Game) Initialize() {
|
||||
g.timer = 0
|
||||
g.runtime = 0.
|
||||
|
||||
g.projectiles = make(map[int]*Projectile)
|
||||
g.projectiles = make(map[int]*elements.Projectile)
|
||||
g.initialized = true
|
||||
g.reset = false
|
||||
|
||||
@@ -184,7 +154,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
||||
op.GeoM.Translate(0, -16)
|
||||
op.GeoM.Rotate(g.hero.Angle)
|
||||
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
|
||||
/*
|
||||
@@ -226,7 +196,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
||||
}
|
||||
|
||||
func (g *Game) Layout(width, height int) (int, int) {
|
||||
return screenWidth, screenHeight
|
||||
return g.dimensions.Width, g.dimensions.Height
|
||||
}
|
||||
|
||||
func (g *Game) CleanupTargets() {
|
||||
@@ -234,7 +204,7 @@ func (g *Game) CleanupTargets() {
|
||||
i := 0
|
||||
for _, target := range g.targets {
|
||||
//moving valid targets to the front of the slice
|
||||
if target.Action < MoverActionDead {
|
||||
if target.Action < elements.MoverActionDead {
|
||||
g.targets[i] = target
|
||||
i++
|
||||
}
|
||||
@@ -282,7 +252,7 @@ func (g *Game) StepGame() {
|
||||
func (g *Game) SpawnEnemies() {
|
||||
f := 40000 / (g.counter + 1)
|
||||
if g.counter%f == 0 {
|
||||
g.targets = append(g.targets, NewMover())
|
||||
g.targets = append(g.targets, elements.NewMover())
|
||||
|
||||
x0 := rand.Float64() * 640
|
||||
y0 := rand.Float64() * 480
|
||||
@@ -290,15 +260,15 @@ func (g *Game) SpawnEnemies() {
|
||||
|
||||
switch quadrant {
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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:
|
||||
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))
|
||||
}
|
||||
|
||||
@@ -319,7 +289,8 @@ func (g *Game) HandlePulseWaveUpdate() {
|
||||
dy := target.Pos.Y - g.hero.Pos.Y
|
||||
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.Touched = true
|
||||
//target.SetHit()
|
||||
@@ -344,7 +315,9 @@ func (g *Game) UpdateProjectiles() {
|
||||
//compute projectile collisions
|
||||
for _, target := range g.targets {
|
||||
//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")
|
||||
|
||||
//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)
|
||||
|
||||
//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)
|
||||
for i := 0; i < len(pixels); i = i + 4 {
|
||||
if pixels[i+3] != 0 {
|
||||
@@ -367,7 +340,7 @@ func (g *Game) UpdateProjectiles() {
|
||||
delete(g.projectiles, k)
|
||||
//target.ToggleColor()
|
||||
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
|
||||
break
|
||||
}
|
||||
@@ -380,7 +353,7 @@ func (g *Game) UpdateProjectiles() {
|
||||
func (g *Game) UpdateTargets() {
|
||||
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
|
||||
dy := g.hero.Pos.Y - target.Pos.Y
|
||||
angle := math.Atan2(dy, dx)
|
||||
@@ -392,7 +365,9 @@ func (g *Game) UpdateTargets() {
|
||||
}
|
||||
|
||||
//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.DrawImage(g.hero.Sprite, nil)
|
||||
|
||||
@@ -428,10 +403,10 @@ func (g *Game) ResetTargetTouches() {
|
||||
func (g *Game) AppendProjectiles() {
|
||||
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 {
|
||||
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() {
|
||||
g.background = ebiten.NewImage(screenWidth, screenHeight)
|
||||
g.background = ebiten.NewImage(g.dimensions.Width, g.dimensions.Height)
|
||||
BLOCK_SIZE := 32
|
||||
|
||||
for i := 0; i < 640/16; i++ {
|
||||
@@ -505,7 +480,7 @@ func (g *Game) ConstructBackground() {
|
||||
//translate for grid element we're painting
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
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.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
@@ -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)
|
||||
}
|
||||