91 lines
2.5 KiB
Go
91 lines
2.5 KiB
Go
package emotion
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"sync"
|
|
)
|
|
|
|
// ErrUnknownPersona is returned by Manager when an operation targets a persona
|
|
// id that was never registered.
|
|
var ErrUnknownPersona = errors.New("emotion: unknown persona")
|
|
|
|
// entry binds a persona's machine and affection together so whatever happens to
|
|
// one persona never leaks into another.
|
|
type entry struct {
|
|
machine *Machine
|
|
affection *Affection
|
|
}
|
|
|
|
// Manager owns per-persona emotion and affection. Each persona id maps to its
|
|
// own Machine and Affection, which is what makes emotion follow the persona
|
|
// rather than a behavior mode. All methods are safe for concurrent use.
|
|
type Manager struct {
|
|
mu sync.RWMutex
|
|
items map[string]*entry
|
|
}
|
|
|
|
// NewManager returns an empty Manager.
|
|
func NewManager() *Manager {
|
|
return &Manager{items: make(map[string]*entry)}
|
|
}
|
|
|
|
// Set registers (or replaces) the machine and affection for id.
|
|
func (m *Manager) Set(id string, machine *Machine, affection *Affection) {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
m.items[id] = &entry{machine: machine, affection: affection}
|
|
}
|
|
|
|
// Get returns the machine and affection for id, and whether it is registered.
|
|
func (m *Manager) Get(id string) (*Machine, *Affection, bool) {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
e, ok := m.items[id]
|
|
if !ok {
|
|
return nil, nil, false
|
|
}
|
|
return e.machine, e.affection, true
|
|
}
|
|
|
|
// Remove deletes the entry for id, reporting whether an entry was present.
|
|
func (m *Manager) Remove(id string) bool {
|
|
m.mu.Lock()
|
|
defer m.mu.Unlock()
|
|
if _, ok := m.items[id]; !ok {
|
|
return false
|
|
}
|
|
delete(m.items, id)
|
|
return true
|
|
}
|
|
|
|
// Transition moves the persona identified by id into the given emotion state.
|
|
func (m *Manager) Transition(id string, to EmotionState) error {
|
|
machine, _, ok := m.Get(id)
|
|
if !ok {
|
|
return fmt.Errorf("emotion: transition for %q: %w", id, ErrUnknownPersona)
|
|
}
|
|
if err := machine.Transition(to); err != nil {
|
|
return fmt.Errorf("emotion: transition persona %q to %q: %w", id, to, err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// AdjustAffection applies a signed delta to the persona's affection. It returns
|
|
// the before and after values and an error for unknown personas.
|
|
func (m *Manager) AdjustAffection(id string, delta int) (before, after int, err error) {
|
|
_, affection, ok := m.Get(id)
|
|
if !ok {
|
|
return 0, 0, fmt.Errorf("emotion: affection for %q: %w", id, ErrUnknownPersona)
|
|
}
|
|
before, after = affection.Adjust(delta)
|
|
return before, after, nil
|
|
}
|
|
|
|
// Count reports how many personas are currently tracked.
|
|
func (m *Manager) Count() int {
|
|
m.mu.RLock()
|
|
defer m.mu.RUnlock()
|
|
return len(m.items)
|
|
}
|