2024-02-06 15:26:20 +01:00
|
|
|
package game
|
|
|
|
|
|
|
|
|
|
import (
|
2024-02-06 19:02:25 +01:00
|
|
|
"fmt"
|
2024-02-06 15:26:20 +01:00
|
|
|
"image"
|
2024-02-19 19:05:48 +01:00
|
|
|
"log/slog"
|
2024-02-26 12:56:12 +01:00
|
|
|
"openquell/config"
|
2024-02-11 13:00:56 +01:00
|
|
|
"openquell/observers"
|
2024-02-06 15:26:20 +01:00
|
|
|
|
|
|
|
|
"github.com/hajimehoshi/ebiten/v2"
|
|
|
|
|
"github.com/mlange-42/arche/ecs"
|
2024-04-16 19:10:32 +02:00
|
|
|
input "github.com/quasilyte/ebitengine-input"
|
2024-02-06 15:26:20 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type Game struct {
|
2024-02-23 18:47:15 +01:00
|
|
|
World *ecs.World
|
|
|
|
|
Bounds image.Rectangle
|
|
|
|
|
ScreenWidth, ScreenHeight, Cellsize int
|
|
|
|
|
Scenes map[SceneName]Scene
|
|
|
|
|
CurrentScene SceneName
|
|
|
|
|
Observer *observers.GameObserver
|
2024-03-21 13:25:06 +01:00
|
|
|
Levels []*Level // fed in PlayScene.GenerateLevels()
|
2024-02-26 12:56:12 +01:00
|
|
|
Config *config.Config
|
2024-04-16 19:10:32 +02:00
|
|
|
InputSystem input.System
|
|
|
|
|
Input *input.Handler
|
2024-02-06 15:26:20 +01:00
|
|
|
}
|
|
|
|
|
|
2024-02-26 12:56:12 +01:00
|
|
|
func NewGame(width, height, cellsize int, cfg *config.Config, startscene SceneName) *Game {
|
2024-02-06 15:26:20 +01:00
|
|
|
world := ecs.NewWorld()
|
|
|
|
|
|
|
|
|
|
game := &Game{
|
|
|
|
|
Bounds: image.Rectangle{},
|
|
|
|
|
World: &world,
|
|
|
|
|
ScreenWidth: width,
|
|
|
|
|
ScreenHeight: height,
|
2024-02-14 19:35:36 +01:00
|
|
|
Scenes: map[SceneName]Scene{},
|
2024-02-23 18:47:15 +01:00
|
|
|
Cellsize: cellsize,
|
2024-02-26 12:56:12 +01:00
|
|
|
Config: cfg,
|
2024-02-06 15:26:20 +01:00
|
|
|
}
|
|
|
|
|
|
2024-02-28 19:58:05 +01:00
|
|
|
game.Observer = observers.NewGameObserver(
|
|
|
|
|
&world, cfg.Startlevel, width, height, cellsize)
|
|
|
|
|
|
2024-04-16 19:10:32 +02:00
|
|
|
game.InputSystem.Init(input.SystemConfig{
|
|
|
|
|
DevicesEnabled: input.AnyDevice,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
game.Input = game.InputSystem.NewHandler(0, game.Config.Keymap)
|
|
|
|
|
|
2024-02-14 19:35:36 +01:00
|
|
|
game.Scenes[Welcome] = NewWelcomeScene(game)
|
2024-02-18 18:19:34 +01:00
|
|
|
game.Scenes[Menu] = NewMenuScene(game)
|
2024-02-18 12:32:18 +01:00
|
|
|
game.Scenes[About] = NewAboutScene(game)
|
2024-02-18 18:19:34 +01:00
|
|
|
game.Scenes[Popup] = NewPopupScene(game)
|
2024-03-21 13:25:06 +01:00
|
|
|
game.Scenes[Play] = NewPlayScene(game, cfg.Startlevel)
|
2024-02-18 18:19:34 +01:00
|
|
|
game.Scenes[Select] = NewSelectScene(game)
|
|
|
|
|
|
2024-02-16 18:23:19 +01:00
|
|
|
game.CurrentScene = startscene
|
2024-02-06 19:02:25 +01:00
|
|
|
|
|
|
|
|
fmt.Println(game.World.Stats().String())
|
2024-02-06 15:26:20 +01:00
|
|
|
|
|
|
|
|
return game
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-14 19:35:36 +01:00
|
|
|
func (game *Game) GetCurrentScene() Scene {
|
|
|
|
|
return game.Scenes[game.CurrentScene]
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 15:26:20 +01:00
|
|
|
func (game *Game) Update() error {
|
2024-04-16 19:10:32 +02:00
|
|
|
// update ebitengine-input
|
|
|
|
|
game.InputSystem.Update()
|
|
|
|
|
|
2024-02-18 18:19:34 +01:00
|
|
|
// handle level ends
|
2024-02-28 19:58:05 +01:00
|
|
|
timer := game.Observer.StopTimer
|
2024-02-13 18:15:52 +01:00
|
|
|
|
|
|
|
|
if timer.IsReady() {
|
2024-02-19 19:05:48 +01:00
|
|
|
// a level is either lost or won, we display a small popup
|
|
|
|
|
// asking the user how to continue from here
|
2024-02-13 18:15:52 +01:00
|
|
|
timer.Reset()
|
2024-02-19 19:05:48 +01:00
|
|
|
|
2024-02-28 19:58:05 +01:00
|
|
|
slog.Debug("timer ready", "lost", game.Observer.Lost, "retry", game.Observer.Retry)
|
|
|
|
|
game.Observer.AddScore()
|
2024-02-19 19:05:48 +01:00
|
|
|
|
2024-02-28 19:58:05 +01:00
|
|
|
game.Scenes[Nextlevel] = NewNextlevelScene(game)
|
2024-02-19 19:05:48 +01:00
|
|
|
game.CurrentScene = Nextlevel
|
2024-02-13 18:15:52 +01:00
|
|
|
}
|
|
|
|
|
|
2024-02-14 19:35:36 +01:00
|
|
|
scene := game.GetCurrentScene()
|
|
|
|
|
scene.Update()
|
|
|
|
|
|
2024-02-18 18:19:34 +01:00
|
|
|
if scene.Clearscreen() {
|
|
|
|
|
ebiten.SetScreenClearedEveryFrame(true)
|
|
|
|
|
} else {
|
|
|
|
|
ebiten.SetScreenClearedEveryFrame(false)
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-14 19:35:36 +01:00
|
|
|
next := scene.GetNext()
|
2024-03-18 19:34:57 +01:00
|
|
|
|
2024-02-14 19:35:36 +01:00
|
|
|
if next != game.CurrentScene {
|
2024-02-19 19:05:48 +01:00
|
|
|
if next == Play && game.CurrentScene == Nextlevel {
|
|
|
|
|
// switched from nextlevel (lost or won) popup to play (either retry or next level)
|
2024-02-28 19:58:05 +01:00
|
|
|
if !game.Observer.Retry {
|
|
|
|
|
game.Observer.CurrentLevel++
|
2024-02-19 19:05:48 +01:00
|
|
|
}
|
2024-02-28 19:58:05 +01:00
|
|
|
game.Observer.Retry = false
|
2024-02-19 19:05:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if next == Play {
|
|
|
|
|
// fresh setup of actual level every time we enter the play scene
|
2024-02-28 19:58:05 +01:00
|
|
|
game.Scenes[Play].SetLevel(game.Observer.CurrentLevel)
|
2024-02-19 19:05:48 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// make sure we stay on the selected scene
|
2024-02-18 12:32:18 +01:00
|
|
|
scene.ResetNext()
|
2024-02-19 19:05:48 +01:00
|
|
|
|
|
|
|
|
// finally switch
|
2024-02-14 19:35:36 +01:00
|
|
|
game.CurrentScene = next
|
2024-02-19 19:05:48 +01:00
|
|
|
|
2024-02-28 19:58:05 +01:00
|
|
|
// FIXME: add some reset function to game.Observer for these kinds of things
|
|
|
|
|
game.Observer.Lost = false
|
2024-02-06 15:26:20 +01:00
|
|
|
}
|
|
|
|
|
|
2024-02-13 18:15:52 +01:00
|
|
|
timer.Update()
|
|
|
|
|
|
2024-02-06 15:26:20 +01:00
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (game *Game) Draw(screen *ebiten.Image) {
|
2024-02-14 19:35:36 +01:00
|
|
|
game.GetCurrentScene().Draw(screen)
|
2024-02-06 15:26:20 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (g *Game) Layout(newWidth, newHeight int) (int, int) {
|
|
|
|
|
return g.ScreenWidth, g.ScreenHeight
|
|
|
|
|
}
|