package game import "sync" // SafespotState tracks a player's active safespot (cover) in a room. type SafespotState struct { Active bool ObjectDefID string ObjectIndex int RoomID int HideCountdown int } // SafespotManager owns the per-player safespot state map and its mutex. // Callers receive value copies via Get/Peek so they cannot mutate state // outside the lock — this avoids the lock-copy-pointer-unlock anti-pattern. type SafespotManager struct { mu sync.Mutex states map[string]*SafespotState } func NewSafespotManager() *SafespotManager { return &SafespotManager{states: make(map[string]*SafespotState)} } // Get returns a value copy of the player's safespot state. func (m *SafespotManager) Get(name string) (SafespotState, bool) { m.mu.Lock() defer m.mu.Unlock() ss, ok := m.states[name] if !ok { return SafespotState{}, false } return *ss, true } // Has reports whether any safespot state exists for the player. func (m *SafespotManager) Has(name string) bool { m.mu.Lock() defer m.mu.Unlock() _, ok := m.states[name] return ok } // IsActive reports whether the player has an active, fully-hidden safespot // (hide countdown complete). func (m *SafespotManager) IsActive(name string) bool { m.mu.Lock() defer m.mu.Unlock() ss, ok := m.states[name] return ok && ss.Active && ss.HideCountdown <= 0 } // Set stores a safespot state for the player. func (m *SafespotManager) Set(name string, ss SafespotState) { m.mu.Lock() defer m.mu.Unlock() m.states[name] = &ss } // Delete removes the player's safespot state. Returns true if a state existed. func (m *SafespotManager) Delete(name string) bool { m.mu.Lock() defer m.mu.Unlock() _, ok := m.states[name] delete(m.states, name) return ok } // DecrementHideCountdown decrements the hide countdown for a player who is // transitioning from hiding to hidden. Returns (newCountdown, ok). A return of // ok=true with newCountdown > 0 means the player is still counting down; // newCountdown == 0 means the safespot just activated. func (m *SafespotManager) DecrementHideCountdown(name string) (int, bool) { m.mu.Lock() defer m.mu.Unlock() ss, ok := m.states[name] if !ok || !ss.Active { return 0, false } if ss.HideCountdown > 0 { ss.HideCountdown-- } return ss.HideCountdown, true }