refactor
This commit is contained in:
110
systems/aggro_system.gd
Normal file
110
systems/aggro_system.gd
Normal file
@@ -0,0 +1,110 @@
|
||||
extends Node
|
||||
|
||||
const COMBAT_TIMEOUT: float = 5.0
|
||||
const TANK_MULT: float = 2.0
|
||||
const HEAL_MULT: float = 0.5
|
||||
const SPREAD: float = 0.5
|
||||
const DECAY_PER_SEC: float = 1.0
|
||||
|
||||
var aggro: Dictionary = {}
|
||||
var combat_timers: Dictionary = {}
|
||||
|
||||
func _ready() -> void:
|
||||
EventBus.damage_dealt.connect(_on_damage_dealt)
|
||||
EventBus.heal_requested.connect(_on_heal_requested)
|
||||
EventBus.entity_died.connect(_on_died)
|
||||
EventBus.entity_deregistered.connect(_on_dereg)
|
||||
EventBus.enemy_detected.connect(_on_enemy_detected)
|
||||
if multiplayer.multiplayer_peer != null and not multiplayer.is_server() and not (multiplayer.multiplayer_peer is OfflineMultiplayerPeer):
|
||||
set_physics_process(false)
|
||||
else:
|
||||
set_physics_process(true)
|
||||
|
||||
var _accum: float = 0.0
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if not multiplayer.is_server() and multiplayer.multiplayer_peer != null:
|
||||
return
|
||||
_accum += delta
|
||||
if _accum < 0.20:
|
||||
return
|
||||
var dt: float = _accum
|
||||
_accum = 0.0
|
||||
for enemy in aggro.keys():
|
||||
if not is_instance_valid(enemy):
|
||||
continue
|
||||
var t: float = combat_timers.get(enemy, 0.0)
|
||||
if t > 0.0:
|
||||
combat_timers[enemy] = max(0.0, t - dt)
|
||||
else:
|
||||
var table: Dictionary = aggro[enemy]
|
||||
var to_remove: Array = []
|
||||
for player in table.keys():
|
||||
table[player] = max(0.0, table[player] - DECAY_PER_SEC * dt)
|
||||
if table[player] <= 0.0:
|
||||
to_remove.append(player)
|
||||
for p in to_remove:
|
||||
table.erase(p)
|
||||
if not aggro[enemy].is_empty():
|
||||
EventBus.enemy_engaged.emit(enemy, _top_target(enemy))
|
||||
|
||||
func _on_damage_dealt(attacker: Node, target: Node, amount: float) -> void:
|
||||
if not is_instance_valid(target) or not is_instance_valid(attacker):
|
||||
return
|
||||
if target.is_in_group("enemies"):
|
||||
var role: int = int(Stats.get_stat(attacker, "role", GameState.ROLE_DAMAGE))
|
||||
var mult: float = TANK_MULT if role == GameState.ROLE_TANK else 1.0
|
||||
_add(target, attacker, amount * mult)
|
||||
_spread(target, attacker, amount * SPREAD)
|
||||
combat_timers[target] = COMBAT_TIMEOUT
|
||||
|
||||
func _on_heal_requested(healer: Node, _target: Node, amount: float) -> void:
|
||||
if not is_instance_valid(healer):
|
||||
return
|
||||
for enemy in aggro.keys():
|
||||
if not is_instance_valid(enemy):
|
||||
continue
|
||||
if healer in aggro[enemy]:
|
||||
_add(enemy, healer, amount * HEAL_MULT)
|
||||
|
||||
func _on_enemy_detected(enemy: Node, player: Node) -> void:
|
||||
_add(enemy, player, 1.0)
|
||||
combat_timers[enemy] = COMBAT_TIMEOUT
|
||||
|
||||
func _on_died(entity: Node) -> void:
|
||||
aggro.erase(entity)
|
||||
combat_timers.erase(entity)
|
||||
for enemy in aggro.keys():
|
||||
if entity in aggro[enemy]:
|
||||
aggro[enemy].erase(entity)
|
||||
|
||||
func _on_dereg(entity: Node) -> void:
|
||||
_on_died(entity)
|
||||
|
||||
func _add(enemy: Node, player: Node, amount: float) -> void:
|
||||
if not enemy in aggro:
|
||||
aggro[enemy] = {}
|
||||
aggro[enemy][player] = aggro[enemy].get(player, 0.0) + amount
|
||||
|
||||
func _spread(enemy: Node, player: Node, amount: float) -> void:
|
||||
for other in aggro.keys():
|
||||
if other == enemy or not is_instance_valid(other):
|
||||
continue
|
||||
if (other as Node3D).global_position.distance_to((enemy as Node3D).global_position) <= 10.0:
|
||||
_add(other, player, amount)
|
||||
|
||||
func _top_target(enemy: Node) -> Node:
|
||||
if not enemy in aggro:
|
||||
return null
|
||||
var best: Node = null
|
||||
var best_v: float = -1.0
|
||||
for p in aggro[enemy].keys():
|
||||
if not is_instance_valid(p):
|
||||
continue
|
||||
if aggro[enemy][p] > best_v:
|
||||
best_v = aggro[enemy][p]
|
||||
best = p
|
||||
return best
|
||||
|
||||
func target_for(enemy: Node) -> Node:
|
||||
return _top_target(enemy)
|
||||
Reference in New Issue
Block a user