初次提交

This commit is contained in:
2026-07-20 10:56:52 +08:00
commit 7bcc0026e0
462 changed files with 50191 additions and 0 deletions
+103
View File
@@ -0,0 +1,103 @@
## MinionManager — 玩家召唤物管理(Autoload: MinionManager
## 权威来源:architecture_design.md §5.12 + ADR-R4-N4(独立管理器,方案 B)
## 数量上限小(MAX_MINIONS=20ADR-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