Files

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