Files
mmo/systems/effect_system.gd
Marek Le 2d4002bd3f refactor
2026-05-09 23:37:26 +02:00

139 lines
4.6 KiB
GDScript

extends Node
var _active: Dictionary = {}
var _passive_auras: Dictionary = {}
func _ready() -> void:
EventBus.entity_died.connect(_on_died)
EventBus.entity_deregistered.connect(_on_dereg)
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 _heavy_accum: float = 0.0
func _physics_process(delta: float) -> void:
if not multiplayer.is_server() and multiplayer.multiplayer_peer != null:
return
_tick_durations(delta)
_heavy_accum += delta
if _heavy_accum < 0.20:
return
_heavy_accum = 0.0
_propagate_auras()
_recompute_buffs()
func apply(target: Node, effect: Effect, source: Node = null) -> void:
if not is_instance_valid(target):
return
if not target in _active:
_active[target] = []
for entry in _active[target]:
if entry.effect.effect_name == effect.effect_name and entry.source == source:
entry.remaining = effect.duration
entry.tick_timer = effect.tick_interval
EventBus.effect_applied.emit(target, effect, source)
return
_active[target].append({
"effect": effect,
"source": source,
"remaining": effect.duration,
"tick_timer": effect.tick_interval
})
EventBus.effect_applied.emit(target, effect, source)
func remove_by_source(target: Node, source: Node) -> void:
if not target in _active:
return
var keep: Array = []
for entry in _active[target]:
if entry.source == source:
EventBus.effect_expired.emit(target, entry.effect)
else:
keep.append(entry)
_active[target] = keep
func apply_passive_aura(player: Node, ability: Ability) -> void:
if ability == null or ability.passive_stat == &"":
return
_passive_auras[player] = ability
for target in get_tree().get_nodes_in_group("player"):
remove_by_source(target, player)
func _propagate_auras() -> void:
for source in _passive_auras.keys():
if not is_instance_valid(source):
_passive_auras.erase(source)
continue
var ability: Ability = _passive_auras[source]
var effect := Effect.new()
effect.effect_name = StringName("aura_%s_%s" % [ability.passive_stat, source.name])
effect.type = Effect.Type.AURA
effect.stat = ability.passive_stat
effect.value = ability.passive_value
effect.is_multiplier = true
effect.duration = 0.5
effect.aura_radius = ability.passive_radius
var src_pos: Vector3 = (source as Node3D).global_position
for target in get_tree().get_nodes_in_group("player"):
if not Stats.has(target):
continue
var d: float = (target as Node3D).global_position.distance_to(src_pos)
if d <= ability.passive_radius:
apply(target, effect, source)
func _tick_durations(delta: float) -> void:
for target in _active.keys():
if not is_instance_valid(target):
_active.erase(target)
continue
var keep: Array = []
for entry in _active[target]:
if entry.effect.duration > 0.0:
entry.remaining -= delta
if entry.remaining <= 0.0:
EventBus.effect_expired.emit(target, entry.effect)
continue
if entry.effect.tick_interval > 0.0:
entry.tick_timer -= delta
if entry.tick_timer <= 0.0:
entry.tick_timer = entry.effect.tick_interval
if entry.effect.type == Effect.Type.DOT:
EventBus.damage_requested.emit(entry.source, target, entry.effect.value, entry.effect.element)
elif entry.effect.type == Effect.Type.HOT:
EventBus.heal_requested.emit(entry.source, target, entry.effect.value)
keep.append(entry)
_active[target] = keep
func _recompute_buffs() -> void:
for target in _active.keys():
if not is_instance_valid(target) or not Stats.has(target):
continue
var bonus: Dictionary = {}
var mult: Dictionary = {}
for entry in _active[target]:
var e: Effect = entry.effect
if e.stat == &"":
continue
if e.is_multiplier:
mult[e.stat] = mult.get(e.stat, 0.0) + e.value
else:
bonus[e.stat] = bonus.get(e.stat, 0.0) + e.value
for stat in [&"buff_damage", &"buff_heal", &"buff_shield"]:
var base: float = 1.0
var v: float = base + mult.get(stat, 0.0) + bonus.get(stat, 0.0)
Stats.set_stat(target, stat, v)
EventBus.buff_changed.emit(target, stat, v)
func _on_died(entity: Node) -> void:
if entity in _active:
for entry in _active[entity]:
EventBus.effect_expired.emit(entity, entry.effect)
_active.erase(entity)
_passive_auras.erase(entity)
func _on_dereg(entity: Node) -> void:
_active.erase(entity)
_passive_auras.erase(entity)