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)
 | |
| 	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)
 | |
| 
 | |
| 	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) 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) {
 | |
| 	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
 | |
| }
 |