初次提交
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
## MinionManager — 玩家召唤物管理(Autoload: MinionManager)
|
||||
## 权威来源:architecture_design.md §5.12 + ADR-R4-N4(独立管理器,方案 B)
|
||||
## 数量上限小(MAX_MINIONS=20,ADR-R4-N4/plan S5),不需 C# 热路径,用 Array[Dictionary]
|
||||
## 炮台型(Stationary)AI:定点对范围内最近敌人开火
|
||||
##
|
||||
## 注:plan S5 提到 "SoA stride=8",但 §5.12 权威实现明确用 Array[Dictionary]
|
||||
## 且 "不需要 C# 热路径",故按 §5.12 实现;SoA 化为后续可选优化。
|
||||
extends Node
|
||||
|
||||
const MAX_MINIONS: int = 20 # ADR-R4-N4:同场最大召唤物,超出 FIFO 顶替最旧
|
||||
const DEFAULT_LIFETIME: float = 30.0
|
||||
|
||||
## 活跃召唤物:{ id, owner_id, pos, lifetime_rem, range, fire_interval, fire_cd,
|
||||
## damage, bullet_speed, bullet_radius, bullet_lifetime, damage_type }
|
||||
var _minions: Array = []
|
||||
var _next_id: int = 0
|
||||
|
||||
## def: Dictionary(项目无 .tres,用字典描述召唤物面板)
|
||||
## 可选键:lifetime, range, fire_interval, damage, bullet_speed, bullet_radius,
|
||||
## bullet_lifetime, damage_type
|
||||
func spawn_minion(def: Dictionary, owner_id: int, pos: Vector2) -> int:
|
||||
if _minions.size() >= MAX_MINIONS:
|
||||
_expire(_minions[0]["id"]) # FIFO:顶掉最旧
|
||||
var lifetime: float = float(def.get("lifetime", DEFAULT_LIFETIME))
|
||||
if lifetime <= 0.0:
|
||||
lifetime = DEFAULT_LIFETIME
|
||||
var id: int = _next_id
|
||||
_next_id = (_next_id + 1) % 100000
|
||||
var entry: Dictionary = {
|
||||
"id": id,
|
||||
"owner_id": owner_id,
|
||||
"pos": pos,
|
||||
"lifetime_rem": lifetime,
|
||||
"range": float(def.get("range", 350.0)),
|
||||
"fire_interval": maxf(0.05, float(def.get("fire_interval", 0.8))),
|
||||
"fire_cd": 0.0,
|
||||
"damage": float(def.get("damage", 4.0)),
|
||||
"bullet_speed": float(def.get("bullet_speed", 360.0)),
|
||||
"bullet_radius": float(def.get("bullet_radius", 5.0)),
|
||||
"bullet_lifetime": float(def.get("bullet_lifetime", 2.0)),
|
||||
"damage_type": int(def.get("damage_type", 0)),
|
||||
}
|
||||
_minions.append(entry)
|
||||
EventBus.emit(EventID.MINION_SPAWNED, {
|
||||
"minion_id": id, "owner_id": owner_id, "current_count": _minions.size(),
|
||||
})
|
||||
return id
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
var i: int = 0
|
||||
while i < _minions.size():
|
||||
var m: Dictionary = _minions[i]
|
||||
m["lifetime_rem"] -= delta
|
||||
if m["lifetime_rem"] <= 0.0:
|
||||
_expire(m["id"])
|
||||
continue # 不自增 i:当前槽已被 remove_at 后移的元素填补
|
||||
# 炮台 AI:冷却到点且范围内有敌人则朝最近敌人开火
|
||||
m["fire_cd"] -= delta
|
||||
if m["fire_cd"] <= 0.0:
|
||||
var pos: Vector2 = m["pos"]
|
||||
var rng: float = m["range"]
|
||||
if SpatialGrid.query_circle(pos, rng).size() > 0:
|
||||
var tgt: Vector2 = EnemyManager.get_nearest_pos(pos, rng)
|
||||
var dir: Vector2 = (tgt - pos)
|
||||
dir = dir.normalized() if dir.length() > 0.001 else Vector2.RIGHT
|
||||
BulletManager.spawn_bullet(
|
||||
pos, dir * m["bullet_speed"], m["bullet_lifetime"],
|
||||
m["bullet_radius"], m["damage"], 1.0, m["damage_type"],
|
||||
m["owner_id"], 0, 0.0, {})
|
||||
m["fire_cd"] = m["fire_interval"]
|
||||
i += 1
|
||||
|
||||
func _expire(minion_id: int) -> void:
|
||||
for i in _minions.size():
|
||||
if _minions[i]["id"] == minion_id:
|
||||
var owner: int = _minions[i]["owner_id"]
|
||||
_minions.remove_at(i)
|
||||
EventBus.emit(EventID.MINION_EXPIRED, {
|
||||
"minion_id": minion_id, "owner_id": owner, "current_count": _minions.size(),
|
||||
})
|
||||
return
|
||||
|
||||
func recall_all(owner_id: int = -1) -> void:
|
||||
# owner_id<0 → 全部召回(Run 结束);否则仅该 owner
|
||||
var ids: Array = []
|
||||
for m in _minions:
|
||||
if owner_id < 0 or m["owner_id"] == owner_id:
|
||||
ids.append(m["id"])
|
||||
for mid in ids:
|
||||
_expire(mid)
|
||||
|
||||
func get_count(owner_id: int = -1) -> int:
|
||||
if owner_id < 0:
|
||||
return _minions.size()
|
||||
var c: int = 0
|
||||
for m in _minions:
|
||||
if m["owner_id"] == owner_id:
|
||||
c += 1
|
||||
return c
|
||||
|
||||
func reset() -> void:
|
||||
_minions.clear()
|
||||
_next_id = 0
|
||||
Reference in New Issue
Block a user