66 lines
2.0 KiB
Go
66 lines
2.0 KiB
Go
package game
|
|
|
|
import (
|
|
"symph/parameter"
|
|
"time"
|
|
)
|
|
|
|
// Status is the persistent player effect state granted by items. It survives a
|
|
// level change and is cleared on restart. Timed effects hold an absolute
|
|
// deadline, charge effects a count. Adding an effect adds a field here plus a
|
|
// row in resolvers — the pause/interlude shift and the restart clear are
|
|
// already funneled through this type
|
|
type Status struct {
|
|
BoostUntil time.Time // zero or past = inactive
|
|
Shield int // charges; each absorbs one negative contact. No expiry
|
|
}
|
|
|
|
// Shielded reports whether an absorb charge is held
|
|
func (s Status) Shielded() bool { return s.Shield > 0 }
|
|
|
|
// Boosted reports whether a Boost is active at instant t. Drives the tempo
|
|
// (deltaAt, evaluated at beat boundaries) and hazard immunity (hitHazard,
|
|
// evaluated at the contact instant)
|
|
func (s Status) Boosted(t time.Time) bool {
|
|
return !s.BoostUntil.IsZero() && t.Before(s.BoostUntil)
|
|
}
|
|
|
|
// BoostRemaining reports the Boost span left at t; 0 when inactive
|
|
func (s Status) BoostRemaining(t time.Time) time.Duration {
|
|
if !s.Boosted(t) {
|
|
return 0
|
|
}
|
|
return s.BoostUntil.Sub(t)
|
|
}
|
|
|
|
// shift moves every absolute deadline forward by d, holding the remaining span
|
|
// across a pause or a level-clear interlude
|
|
// `at` is the instant the dead span opened. A deadline already lapsed
|
|
// at `at` is cleared, not shifted — dead time never revives an effect
|
|
func (s *Status) shift(at time.Time, d time.Duration) {
|
|
if s.BoostUntil.IsZero() {
|
|
return
|
|
}
|
|
if !s.Boosted(at) {
|
|
s.BoostUntil = time.Time{}
|
|
return
|
|
}
|
|
s.BoostUntil = s.BoostUntil.Add(d)
|
|
}
|
|
|
|
// grantShield arms the absorb charge. Charges do not stack: a pickup while
|
|
// shielded re-arms
|
|
func (s *Status) grantShield() { s.Shield = parameter.ShieldCharges }
|
|
|
|
// grantBoost re-arms the tempo deadline
|
|
func (s *Status) grantBoost(now time.Time) { s.BoostUntil = now.Add(parameter.BoostDuration) }
|
|
|
|
// absorb spends one charge; reports whether one was held
|
|
func (s *Status) absorb() bool {
|
|
if s.Shield <= 0 {
|
|
return false
|
|
}
|
|
s.Shield--
|
|
return true
|
|
}
|