86 lines
2 KiB
Go
86 lines
2 KiB
Go
// Package state provides game state machine management with event callbacks.
|
|
package state
|
|
|
|
import (
|
|
"image"
|
|
"sync"
|
|
"time"
|
|
|
|
"git.cloonar.com/openclawd/iso-bot/pkg/plugin"
|
|
)
|
|
|
|
// Transition records a state change.
|
|
type Transition struct {
|
|
From plugin.GameState
|
|
To plugin.GameState
|
|
Timestamp time.Time
|
|
}
|
|
|
|
// Manager tracks game state and fires callbacks on transitions.
|
|
type Manager struct {
|
|
mu sync.RWMutex
|
|
current plugin.GameState
|
|
previous plugin.GameState
|
|
enterTime time.Time
|
|
history []Transition
|
|
callbacks map[plugin.GameState][]func(Transition)
|
|
detector plugin.GameDetector
|
|
}
|
|
|
|
// NewManager creates a state manager with the given detector.
|
|
func NewManager(detector plugin.GameDetector) *Manager {
|
|
return &Manager{
|
|
current: plugin.StateUnknown,
|
|
enterTime: time.Now(),
|
|
callbacks: make(map[plugin.GameState][]func(Transition)),
|
|
detector: detector,
|
|
}
|
|
}
|
|
|
|
// Update analyzes the current frame and updates state.
|
|
func (m *Manager) Update(frame image.Image) plugin.GameState {
|
|
newState := m.detector.DetectState(frame)
|
|
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
|
|
if newState != m.current {
|
|
t := Transition{
|
|
From: m.current,
|
|
To: newState,
|
|
Timestamp: time.Now(),
|
|
}
|
|
m.history = append(m.history, t)
|
|
m.previous = m.current
|
|
m.current = newState
|
|
m.enterTime = time.Now()
|
|
|
|
// Fire callbacks outside lock? For simplicity, keep in lock for now.
|
|
for _, cb := range m.callbacks[newState] {
|
|
cb(t)
|
|
}
|
|
}
|
|
|
|
return m.current
|
|
}
|
|
|
|
// Current returns the current game state.
|
|
func (m *Manager) Current() plugin.GameState {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
return m.current
|
|
}
|
|
|
|
// TimeInState returns how long we've been in the current state.
|
|
func (m *Manager) TimeInState() time.Duration {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
return time.Since(m.enterTime)
|
|
}
|
|
|
|
// OnState registers a callback for entering a specific state.
|
|
func (m *Manager) OnState(state plugin.GameState, cb func(Transition)) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
m.callbacks[state] = append(m.callbacks[state], cb)
|
|
}
|