package render import ( "time" "symph/game" "symph/parameter" "symph/types" ) // FadeGrid projects the engine's consumed-note ring onto strip coordinates for // one frame: burn-out progress per (y, x, chord-distance). Entries outside the // window or past ItemFadeDuration are dropped. A consumed cell at distance 0 // scrolls off on the next beat; the fade is shorter than the tightest beat, so // it always resolves before the strip shifts type FadeGrid struct { value [parameter.GamePlayIndexYMax][parameter.GamePlayIndexXMax][game.TileLookaheadMaxWindow]types.Value t [parameter.GamePlayIndexYMax][parameter.GamePlayIndexXMax][game.TileLookaheadMaxWindow]float64 } func (f *FadeGrid) Refresh(s *game.GameState, now time.Time) { *f = FadeGrid{} for _, c := range s.Consumed() { if c.Value == types.ValueNone { continue } age := now.Sub(c.At) if age < 0 || age >= parameter.ItemFadeDuration { continue } d := c.Z - s.CurrentPlayIndexZ if d < 0 || d >= game.TileLookaheadMaxWindow { continue } f.value[c.Y][c.X][d] = c.Value f.t[c.Y][c.X][d] = float64(age) / float64(parameter.ItemFadeDuration) } } // At reports the item burning out at a strip cell and its progress in [0,1) func (f *FadeGrid) At(y, x, d int) (types.Value, float64, bool) { if d < 0 || d >= game.TileLookaheadMaxWindow { return types.ValueNone, 0, false } v := f.value[y][x][d] return v, f.t[y][x][d], v != types.ValueNone }