## 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)
