## DpsTracker — DPS 环形缓冲区(Autoload: DpsTracker) ## 权威来源:implementation_plan.md §2.5.E(P6-N38、P6-N47) extends Node const DPS_WINDOW_SEC: float = 3.0 const _RB_SIZE: int = 256 var _rb_time: PackedFloat64Array = PackedFloat64Array() var _rb_dmg: PackedFloat32Array = PackedFloat32Array() var _rb_head: int = 0 var _rb_count: int = 0 var _accumulated_dmg: float = 0.0 func _ready() -> void: _rb_time.resize(_RB_SIZE) _rb_dmg.resize(_RB_SIZE) func record_damage(amount: float) -> void: if amount <= 0.0: return var now: float = Time.get_ticks_msec() / 1000.0 _rb_time[_rb_head] = now _rb_dmg[_rb_head] = amount _rb_head = (_rb_head + 1) % _RB_SIZE if _rb_count < _RB_SIZE: _rb_count += 1 _accumulated_dmg += amount func get_dps() -> float: _recalc_window(Time.get_ticks_msec() / 1000.0) return _accumulated_dmg / DPS_WINDOW_SEC func _recalc_window(now: float) -> void: var sum: float = 0.0 var valid: int = 0 var tail: int = (_rb_head - _rb_count + _RB_SIZE) % _RB_SIZE for k in _rb_count: var idx: int = (tail + k) % _RB_SIZE if (now - _rb_time[idx]) <= DPS_WINDOW_SEC: sum += _rb_dmg[idx] valid += 1 _accumulated_dmg = sum _rb_count = valid func reset() -> void: _rb_head = 0 _rb_count = 0 _accumulated_dmg = 0.0 _rb_time.fill(0.0) _rb_dmg.fill(0.0)