Files
spellforge/scripts/autoloads/zone_manager.gd
T
2026-07-20 10:56:52 +08:00

77 lines
3.2 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## ZoneManager — 地面效果区域管理(Autoload: ZoneManager
## 权威来源:architecture_design.md §8 ADR-R5-N1P6-N71 tick 用 while + -=
## SoA PackedFloat32Array stride=8;基于 SpatialGrid 空间索引(禁用 Area2D
## [0]=cx [1]=cy [2]=radius [3]=status_id [4]=duration [5]=tick_interval [6]=tick_accum [7]=owner_id
##
## 注:本 GDScript 外壳承载 tick 热循环为 S5 功能验收路径(P-S5-04);
## C# 子节点 ZoneManagerCs 驱动的压力路径(P-S5-ZM-0164 区×1000 敌 <1ms)留作后续优化,
## 与项目既有 C# 回退策略(R-08)一致。
extends Node
const ZONE_STRIDE: int = 8
const MAX_ZONES: int = 64
var _zone_data: PackedFloat32Array = PackedFloat32Array()
var _active_zones: int = 0
func _ready() -> void:
_zone_data.resize(MAX_ZONES * ZONE_STRIDE)
## 生成一个地面效果区域;满员时顶掉最旧(index 0 前移)
func spawn_zone(cx: float, cy: float, radius: float, status_id: int,
duration: float, tick_interval: float, owner_id: int) -> void:
if _active_zones >= MAX_ZONES:
# 顶掉最旧 Zoneindex 0),整体前移(O(N)N≤64)
for j in range(ZONE_STRIDE, _active_zones * ZONE_STRIDE):
_zone_data[j - ZONE_STRIDE] = _zone_data[j]
_active_zones -= 1
var base: int = _active_zones * ZONE_STRIDE
_zone_data[base + 0] = cx
_zone_data[base + 1] = cy
_zone_data[base + 2] = radius
_zone_data[base + 3] = float(status_id)
_zone_data[base + 4] = duration
_zone_data[base + 5] = maxf(0.01, tick_interval)
_zone_data[base + 6] = 0.0 # tick_accum
_zone_data[base + 7] = float(owner_id)
_active_zones += 1
func _physics_process(delta: float) -> void:
if _active_zones == 0:
return
var i: int = 0
while i < _active_zones:
var base: int = i * ZONE_STRIDE
_zone_data[base + 4] -= delta # duration 倒计时
_zone_data[base + 6] += delta # tick_accum 累积
# 速率限制:外层 if 守卫 + 内层 while + -= tick_intervalP6-N71,保留余量精度)
# SpatialGrid.query_circle 仅在 if 守卫内查询一次,不在 while 内重复查询
if _zone_data[base + 6] >= _zone_data[base + 5]:
var cx: float = _zone_data[base + 0]
var cy: float = _zone_data[base + 1]
var rad: float = _zone_data[base + 2]
var sid: int = int(_zone_data[base + 3])
var oid: int = int(_zone_data[base + 7])
var targets: PackedInt32Array = SpatialGrid.query_circle(Vector2(cx, cy), rad)
while _zone_data[base + 6] >= _zone_data[base + 5]:
_zone_data[base + 6] -= _zone_data[base + 5] # -= tick_interval 保留余量
for t_id in targets:
StatusManager.apply(t_id, sid, 1, -1.0, oid) # stacks=1, duration=默认, owner
if _zone_data[base + 4] <= 0.0: # duration 耗尽 → swap-and-pop 移除 + VFX 淡出
VFXManager.play("zone_expire", Vector2(_zone_data[base + 0], _zone_data[base + 1]))
if i < _active_zones - 1:
var last: int = (_active_zones - 1) * ZONE_STRIDE
for k in ZONE_STRIDE:
_zone_data[base + k] = _zone_data[last + k]
_active_zones -= 1
else:
i += 1
func get_active_count() -> int:
return _active_zones
func reset() -> void:
_active_zones = 0 # 惰性清理:SoA 数据不清零,_active_zones 控制有效范围