v0.1.0 initial commit

This commit is contained in:
2026-07-15 01:28:56 -04:00
commit 874abee663
35 changed files with 3486 additions and 0 deletions
+65
View File
@@ -0,0 +1,65 @@
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
}