123 lines
4.2 KiB
GDScript
123 lines
4.2 KiB
GDScript
## StatusManager — 状态效果管理(Autoload: StatusManager)
|
||
## 权威来源:combat_mechanics_depth.md §3.2.5、implementation_plan.md §2.4
|
||
extends Node
|
||
|
||
const STATUS_BATCH_LIMIT: int = 500
|
||
|
||
var _active_statuses: Array = [] # Array[StatusInstance]
|
||
var _physics_frame: int = 0
|
||
|
||
func _physics_process(delta: float) -> void:
|
||
_physics_frame += 1
|
||
var use_batch: bool = _active_statuses.size() > STATUS_BATCH_LIMIT
|
||
var batch_parity: int = _physics_frame % 2
|
||
|
||
var i: int = _active_statuses.size() - 1
|
||
while i >= 0:
|
||
var inst: StatusInstance = _active_statuses[i]
|
||
if use_batch and (inst.entity_id % 2) != batch_parity:
|
||
i -= 1
|
||
continue
|
||
|
||
inst.remaining_duration -= delta
|
||
inst.tick_accumulator += delta
|
||
|
||
if inst.type_def and not inst.type_def.is_combo_tracker and inst.type_def.dot_damage_per_tick > 0.0:
|
||
var interval: float = max(0.001, inst.type_def.tick_interval)
|
||
while inst.tick_accumulator >= interval:
|
||
inst.tick_accumulator -= interval
|
||
_apply_dot_tick(inst)
|
||
|
||
if inst.remaining_duration <= 0.0:
|
||
_active_statuses[i] = _active_statuses[_active_statuses.size() - 1]
|
||
_active_statuses.pop_back()
|
||
i -= 1
|
||
|
||
## 施加状态;duration < 0 时使用 StatusTypeDef 默认 duration
|
||
func apply(entity_id: int, status_type_id: int, stacks: int = 1, duration: float = -1.0, owner_id: int = -1) -> void:
|
||
var typedef: StatusTypeDef = StatusRegistry.get_type(status_type_id)
|
||
if typedef == null:
|
||
push_warning("StatusManager.apply: 未知 status_id=%d" % status_type_id)
|
||
return
|
||
|
||
var dur: float = typedef.duration if duration < 0.0 else duration
|
||
var existing_idx: int = _find(entity_id, status_type_id)
|
||
|
||
if existing_idx >= 0:
|
||
var inst: StatusInstance = _active_statuses[existing_idx]
|
||
match typedef.stack_mode:
|
||
StatusTypeDef.StackMode.REFRESH:
|
||
inst.remaining_duration = dur
|
||
inst.stacks = max(inst.stacks, stacks)
|
||
StatusTypeDef.StackMode.INTENSITY:
|
||
var cap: int = typedef.max_stacks if typedef.max_stacks > 0 else 9999
|
||
inst.stacks = min(inst.stacks + stacks, cap)
|
||
inst.remaining_duration = dur
|
||
StatusTypeDef.StackMode.INDEPENDENT:
|
||
pass # 独立实例:追加新条目
|
||
if typedef.stack_mode != StatusTypeDef.StackMode.INDEPENDENT:
|
||
EventBus.emit(EventID.STATUS_APPLIED, {
|
||
"target_id": entity_id, "status_type": status_type_id, "stacks": inst.stacks
|
||
})
|
||
return
|
||
|
||
var inst_new := StatusInstance.new()
|
||
inst_new.setup(entity_id, typedef, stacks, dur, owner_id)
|
||
_active_statuses.append(inst_new)
|
||
EventBus.emit(EventID.STATUS_APPLIED, {
|
||
"target_id": entity_id, "status_type": status_type_id, "stacks": inst_new.stacks
|
||
})
|
||
|
||
func get_stacks(entity_id: int, status_type_id: int) -> int:
|
||
var idx: int = _find(entity_id, status_type_id)
|
||
if idx < 0:
|
||
return 0
|
||
return _active_statuses[idx].stacks
|
||
|
||
func has_status(entity_id: int, status_type_id: int) -> bool:
|
||
return _find(entity_id, status_type_id) >= 0
|
||
|
||
func remove_all_for_entity(entity_id: int) -> void:
|
||
var i: int = _active_statuses.size() - 1
|
||
while i >= 0:
|
||
if _active_statuses[i].entity_id == entity_id:
|
||
_active_statuses[i] = _active_statuses[_active_statuses.size() - 1]
|
||
_active_statuses.pop_back()
|
||
i -= 1
|
||
|
||
func remove_source(owner_id: int) -> void:
|
||
var i: int = _active_statuses.size() - 1
|
||
while i >= 0:
|
||
if _active_statuses[i].owner_id == owner_id:
|
||
_active_statuses[i] = _active_statuses[_active_statuses.size() - 1]
|
||
_active_statuses.pop_back()
|
||
i -= 1
|
||
|
||
func get_active_count() -> int:
|
||
return _active_statuses.size()
|
||
|
||
func reset() -> void:
|
||
_active_statuses.clear()
|
||
_physics_frame = 0
|
||
|
||
func _find(entity_id: int, status_type_id: int) -> int:
|
||
for i in _active_statuses.size():
|
||
var inst: StatusInstance = _active_statuses[i]
|
||
if inst.entity_id == entity_id and inst.status_type_id == status_type_id:
|
||
return i
|
||
return -1
|
||
|
||
func _apply_dot_tick(inst: StatusInstance) -> void:
|
||
if not EnemyManager.has_entity(inst.entity_id):
|
||
return
|
||
var dmg: float = inst.type_def.dot_damage_per_tick * float(inst.stacks)
|
||
if dmg <= 0.0:
|
||
return
|
||
EnemyManager.apply_damage(inst.entity_id, dmg, inst.owner_id, false)
|
||
|
||
func _swap_and_pop(idx: int) -> void:
|
||
var last: int = _active_statuses.size() - 1
|
||
if idx != last:
|
||
_active_statuses[idx] = _active_statuses[last]
|
||
_active_statuses.pop_back()
|