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