package observers import ( "math/rand" "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, Moves 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 map[int]*Score // one score per level Id int MaxLevels int } func (observer *GameObserver) GetListenerCallback(comp ecs.ID) listener.Callback { return listener.NewCallback( func(world *ecs.World, event ecs.EntityEvent) { observer.RemoveEntity(event.Entity, comp) }, event.EntityRemoved, comp, ) } func NewGameObserver( world *ecs.World, startlevel, width, height, cellsize int) *GameObserver { observer := &GameObserver{ CurrentLevel: startlevel, StopTimer: &components.Timer{}, Width: width, Height: height, Cellsize: cellsize, World: world, Id: rand.Intn(1000), } playerID := ecs.ComponentID[components.Player](world) obstacleID := ecs.ComponentID[components.Obstacle](world) animationID := ecs.ComponentID[components.Animation](world) playerListener := observer.GetListenerCallback(playerID) obstacleListener := observer.GetListenerCallback(obstacleID) animationListener := observer.GetListenerCallback(animationID) listen := listener.NewDispatch( &playerListener, &obstacleListener, &animationListener, ) 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[animationID] = make(map[ecs.Entity]int) resmanger := generic.NewResource[GameObserver](world) resmanger.Add(observer) return observer } func GetGameObserver(world *ecs.World) *GameObserver { resmanger := generic.NewResource[GameObserver](world) observer := resmanger.Get() 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) GetAnimations() []ecs.Entity { animationID := ecs.ComponentID[components.Animation](observer.World) return observer.GetEntities(animationID) } func (observer *GameObserver) RemoveEntities() { playerID := ecs.ComponentID[components.Player](observer.World) obstacleID := ecs.ComponentID[components.Obstacle](observer.World) animationID := ecs.ComponentID[components.Animation](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[animationID] = make(map[ecs.Entity]int) } func (observer *GameObserver) SetupLevelScore(min []int) { observer.LevelScore = make(map[int]*Score, len(min)) for level, minmoves := range min { observer.LevelScore[level] = &Score{Min: minmoves} } } func (observer *GameObserver) SetupMaxLevels(count int) { observer.MaxLevels = count } // set current level stats and reset counters func (observer *GameObserver) AddScore() { level := observer.CurrentLevel moves := observer.Moves slog.Debug("AddScore", "moves", observer.Moves) if observer.Lost { observer.LevelScore[level].Score = 0 slog.Debug("lost") } else { observer.LevelScore[level].Score = ((observer.LevelScore[level].Min * 100) / moves) / 30 slog.Debug("won", "score", observer.LevelScore[level].Score, "Min", observer.LevelScore[level].Min, "Min-x-100", observer.LevelScore[level].Min*100, "Min-x-100-moves", (observer.LevelScore[level].Min*100)/moves, ) } observer.Moves = 0 } func (observer *GameObserver) AddMove() { observer.Moves++ } func (observer *GameObserver) GetScore() int { sum := 0 for _, score := range observer.LevelScore { sum += score.Score } return sum } func (observer *GameObserver) GetLevelScore() int { return observer.LevelScore[observer.CurrentLevel].Score }