package game import ( "fmt" "image" "log/slog" "openquell/config" "openquell/observers" "github.com/hajimehoshi/ebiten/v2" "github.com/mlange-42/arche/ecs" ) type Game struct { World *ecs.World Bounds image.Rectangle ScreenWidth, ScreenHeight, Cellsize int Scenes map[SceneName]Scene CurrentScene SceneName Observer *observers.GameObserver Levels []*Level // needed to feed select_scene Config *config.Config } func NewGame(width, height, cellsize int, cfg *config.Config, startscene SceneName) *Game { world := ecs.NewWorld() game := &Game{ Bounds: image.Rectangle{}, World: &world, ScreenWidth: width, ScreenHeight: height, Scenes: map[SceneName]Scene{}, Cellsize: cellsize, Config: cfg, } // observers.NewPlayerObserver(&world) // observers.NewParticleObserver(&world) // observers.NewObstacleObserver(&world) game.Observer = observers.NewGameObserver(&world, cfg.Startlevel, width, height, cellsize) game.Scenes[Welcome] = NewWelcomeScene(game) game.Scenes[Menu] = NewMenuScene(game) game.Scenes[About] = NewAboutScene(game) game.Scenes[Popup] = NewPopupScene(game) game.Scenes[Play] = NewLevelScene(game, cfg.Startlevel) game.Scenes[Select] = NewSelectScene(game) game.CurrentScene = startscene fmt.Println(game.World.Stats().String()) return game } func (game *Game) GetCurrentScene() Scene { return game.Scenes[game.CurrentScene] } func (game *Game) Update() error { gameobserver := observers.GetGameObserver(game.World) // handle level ends timer := gameobserver.StopTimer if timer.IsReady() { // a level is either lost or won, we display a small popup // asking the user how to continue from here timer.Reset() slog.Debug("timer ready", "lost", gameobserver.Lost, "retry", gameobserver.Retry) if !gameobserver.Lost { gameobserver.Score++ // FIXME: use level.Score(), see TODO } game.Scenes[Nextlevel] = NewNextlevelScene(game, gameobserver.Lost) game.CurrentScene = Nextlevel } scene := game.GetCurrentScene() scene.Update() if scene.Clearscreen() { ebiten.SetScreenClearedEveryFrame(true) } else { ebiten.SetScreenClearedEveryFrame(false) } next := scene.GetNext() if next != game.CurrentScene { if next == Play && game.CurrentScene == Nextlevel { // switched from nextlevel (lost or won) popup to play (either retry or next level) if !gameobserver.Retry { gameobserver.CurrentLevel++ } gameobserver.Retry = false } if next == Play { // fresh setup of actual level every time we enter the play scene game.Scenes[Play].SetLevel(gameobserver.CurrentLevel) } // make sure we stay on the selected scene scene.ResetNext() // finally switch game.CurrentScene = next // FIXME: add some reset function to gameobserver for these kinds of things gameobserver.Lost = false } timer.Update() return nil } func (game *Game) Draw(screen *ebiten.Image) { game.GetCurrentScene().Draw(screen) } func (g *Game) Layout(newWidth, newHeight int) (int, int) { return g.ScreenWidth, g.ScreenHeight }