188 lines
5.2 KiB
Go
188 lines
5.2 KiB
Go
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
|
|
}
|