package observers import ( "openquell/components" "log/slog" "github.com/mlange-42/arche/ecs" "github.com/mlange-42/arche/ecs/event" "github.com/mlange-42/arche/generic" "github.com/mlange-42/arche/listener" ) type Score struct { Min, Score int } // Used for global game state. Also stores mobile entities of the // current level. The Entities map will be reset on each level // initialization in grid/grid.go type GameObserver struct { World *ecs.World CurrentLevel, Width int Height, Cellsize, Score int StopTimer *components.Timer Lost bool // set to true if player is struck or something, by default: win! Retry bool NextlevelText string Entities map[ecs.ID]map[ecs.Entity]int LevelScore []Score // one score per level } func (observer *GameObserver) GetListenerCallback(comp ecs.ID) listener.Callback { return listener.NewCallback( func(world *ecs.World, event ecs.EntityEvent) { observer.RemoveEntity(event.Entity, comp) slog.Debug("player removed") }, event.EntityRemoved, comp, ) } func NewGameObserver(world *ecs.World, startlevel, levelcount, width, height, cellsize int) *GameObserver { observer := &GameObserver{ CurrentLevel: startlevel, StopTimer: &components.Timer{}, Width: width, Height: height, Cellsize: cellsize, World: world, } playerID := ecs.ComponentID[components.Player](world) obstacleID := ecs.ComponentID[components.Obstacle](world) particleID := ecs.ComponentID[components.Particle](world) playerListener := observer.GetListenerCallback(playerID) obstacleListener := observer.GetListenerCallback(obstacleID) particleListener := observer.GetListenerCallback(particleID) listen := listener.NewDispatch( &playerListener, &obstacleListener, &particleListener, ) world.SetListener(&listen) observer.Entities = make(map[ecs.ID]map[ecs.Entity]int) observer.Entities[playerID] = make(map[ecs.Entity]int) observer.Entities[obstacleID] = make(map[ecs.Entity]int) observer.Entities[particleID] = make(map[ecs.Entity]int) observer.LevelScore = make([]Score, levelcount) resmanger := generic.NewResource[GameObserver](world) resmanger.Add(observer) return observer } func GetGameObserver(world *ecs.World) *GameObserver { observerID := ecs.ResourceID[GameObserver](world) observer := world.Resources().Get(observerID).(*GameObserver) return observer } func (observer *GameObserver) Gameover() { observer.Lost = true } func (observer *GameObserver) AddEntity(entity ecs.Entity, comp ecs.ID) { observer.Entities[comp][entity] = 1 } func (observer *GameObserver) RemoveEntity(entity ecs.Entity, comp ecs.ID) { delete(observer.Entities[comp], entity) } func (observer *GameObserver) GetEntities(comp ecs.ID) []ecs.Entity { keys := make([]ecs.Entity, 0, len(observer.Entities[comp])) for k, _ := range observer.Entities[comp] { keys = append(keys, k) } return keys } func (observer *GameObserver) GetPlayers() []ecs.Entity { playerID := ecs.ComponentID[components.Player](observer.World) return observer.GetEntities(playerID) } func (observer *GameObserver) GetObstacles() []ecs.Entity { obstacleID := ecs.ComponentID[components.Obstacle](observer.World) return observer.GetEntities(obstacleID) } func (observer *GameObserver) GetParticles() []ecs.Entity { particleID := ecs.ComponentID[components.Particle](observer.World) return observer.GetEntities(particleID) } func (observer *GameObserver) RemoveEntities() { playerID := ecs.ComponentID[components.Player](observer.World) obstacleID := ecs.ComponentID[components.Obstacle](observer.World) particleID := ecs.ComponentID[components.Particle](observer.World) observer.Entities = make(map[ecs.ID]map[ecs.Entity]int) observer.Entities[playerID] = make(map[ecs.Entity]int) observer.Entities[obstacleID] = make(map[ecs.Entity]int) observer.Entities[particleID] = make(map[ecs.Entity]int) } func (observer *GameObserver) SetupLevelScore(min []int) { for level, minmoves := range min { observer.LevelScore[level].Min = minmoves } } // to be called from scenes where player wins or looses func (observer *GameObserver) AddScore(level, moves int) { observer.LevelScore[level].Score = ((observer.LevelScore[level].Min * 100) / moves) / 30 } func (observer *GameObserver) GetScore() int { sum := 0 for _, score := range observer.LevelScore { sum += score.Score } return sum } func (observer *GameObserver) GetLevelScore(level int) int { return observer.LevelScore[level].Score }