This commit is contained in:
Marek Le
2026-04-02 23:22:51 +02:00
parent 73af6abeb7
commit 3488856b91
22 changed files with 534 additions and 78 deletions

190
systems/effect_system.gd Normal file
View File

@@ -0,0 +1,190 @@
extends Node
var active_effects: Dictionary = {}
func _ready() -> void:
EventBus.role_changed.connect(_on_role_changed)
EventBus.entity_died.connect(_on_entity_died)
EventBus.effect_requested.connect(_on_effect_requested)
const AURA_REFRESH := 0.5
func _process(delta: float) -> void:
for entity in active_effects.keys():
if not is_instance_valid(entity):
active_effects.erase(entity)
continue
var entries: Array = active_effects[entity]
var i: int = entries.size() - 1
while i >= 0:
var entry: Dictionary = entries[i]
var effect: Effect = entry["effect"]
if effect.duration > 0:
entry["remaining"] -= delta
if entry["remaining"] <= 0:
var is_aura_buff: bool = entry.get("is_aura_buff", false)
entries.remove_at(i)
if not is_aura_buff:
EventBus.effect_expired.emit(entity, effect)
_recalc_stat_buffs(entity)
i -= 1
continue
if effect.tick_interval > 0:
entry["tick_timer"] -= delta
if entry["tick_timer"] <= 0:
entry["tick_timer"] += effect.tick_interval
_apply_tick(entity, entry)
if effect.type == Effect.Type.AURA and effect.aura_radius > 0 and effect.duration < 0:
_propagate_aura(entity, entry, effect)
i -= 1
func _propagate_aura(source_entity: Node, _entry: Dictionary, aura: Effect) -> void:
if not source_entity is Node3D:
return
var players := get_tree().get_nodes_in_group("player")
for player in players:
if not is_instance_valid(player) or not Stats.is_alive(player):
continue
var dist: float = source_entity.global_position.distance_to(player.global_position)
if dist > aura.aura_radius:
continue
if _has_aura_buff(player, aura.effect_name, source_entity):
_refresh_aura_buff(player, aura.effect_name, source_entity)
else:
var buff := Effect.new()
buff.effect_name = aura.effect_name
buff.type = Effect.Type.BUFF
buff.stat = aura.stat
buff.value = aura.value
buff.duration = AURA_REFRESH
buff.is_multiplier = aura.is_multiplier
_apply_aura_buff(player, buff, source_entity)
func _has_aura_buff(target: Node, aura_name: String, source: Node) -> bool:
if not active_effects.has(target):
return false
for entry in active_effects[target]:
if entry["effect"].effect_name == aura_name and entry.get("aura_source") == source:
return true
return false
func _refresh_aura_buff(target: Node, aura_name: String, source: Node) -> void:
if not active_effects.has(target):
return
for entry in active_effects[target]:
if entry["effect"].effect_name == aura_name and entry.get("aura_source") == source:
entry["remaining"] = AURA_REFRESH
return
func _apply_aura_buff(target: Node, effect: Effect, source: Node) -> void:
if not active_effects.has(target):
active_effects[target] = []
var entry := {
"effect": effect,
"source": source,
"remaining": effect.duration,
"tick_timer": effect.tick_interval,
"aura_source": source,
"is_aura_buff": true,
}
active_effects[target].append(entry)
if effect.is_multiplier:
_recalc_stat_buffs(target)
func apply_effect(target: Node, effect: Effect, source: Node) -> void:
if not active_effects.has(target):
active_effects[target] = []
var replaced := false
var entries: Array = active_effects[target]
for i in range(entries.size()):
if entries[i]["effect"].effect_name == effect.effect_name:
entries[i]["effect"] = effect
entries[i]["source"] = source
entries[i]["remaining"] = effect.duration
entries[i]["tick_timer"] = effect.tick_interval
replaced = true
break
if not replaced:
entries.append({
"effect": effect,
"source": source,
"remaining": effect.duration,
"tick_timer": effect.tick_interval,
})
EventBus.effect_applied.emit(target, effect)
if effect.is_multiplier:
_recalc_stat_buffs(target)
func clear_effects(entity: Node) -> void:
active_effects.erase(entity)
if is_instance_valid(entity):
_recalc_stat_buffs(entity)
func _on_effect_requested(target: Node, effect: Effect, source: Node) -> void:
apply_effect(target, effect, source)
func _on_entity_died(entity: Node) -> void:
clear_effects(entity)
func _on_role_changed(player: Node, _role_type: int) -> void:
_remove_permanent_effects(player)
var role: Node = player.get_node_or_null("Role")
if not role:
return
var ability_set: AbilitySet = role.get_ability_set()
if not ability_set:
return
for ability in ability_set.abilities:
if ability and ability.type == Ability.Type.PASSIVE:
var effect := Effect.new()
effect.effect_name = ability.ability_name
effect.type = Effect.Type.AURA
effect.stat = ability.passive_stat
effect.value = ability.damage / 100.0
effect.duration = -1.0
effect.is_multiplier = true
effect.aura_radius = ability.ability_range
apply_effect(player, effect, player)
func _remove_permanent_effects(entity: Node) -> void:
if not active_effects.has(entity):
return
var entries: Array = active_effects[entity]
var i: int = entries.size() - 1
while i >= 0:
if entries[i]["effect"].duration < 0:
EventBus.effect_expired.emit(entity, entries[i]["effect"])
entries.remove_at(i)
i -= 1
_recalc_stat_buffs(entity)
func _recalc_stat_buffs(entity: Node) -> void:
var mults := { "damage": 1.0, "heal": 1.0, "shield": 1.0 }
if active_effects.has(entity):
for entry in active_effects[entity]:
var effect: Effect = entry["effect"]
if effect.is_multiplier and effect.stat in mults:
mults[effect.stat] += effect.value
for stat in mults:
Stats.set_stat(entity, "buff_" + stat, mults[stat])
EventBus.buff_changed.emit(entity, stat, mults[stat])
var base: BaseStats = Stats.get_base(entity)
if base:
var shield_mult: float = mults["shield"]
var new_max: float = base.max_shield * shield_mult
Stats.set_stat(entity, "max_shield", new_max)
var shield: float = Stats.get_stat(entity, "shield")
shield = min(shield, new_max)
Stats.set_stat(entity, "shield", shield)
EventBus.shield_changed.emit(entity, shield, new_max)
func _apply_tick(entity: Node, entry: Dictionary) -> void:
var effect: Effect = entry["effect"]
var source: Node = entry["source"]
if not is_instance_valid(source):
source = entity
if not effect.is_multiplier:
if effect.type == Effect.Type.DEBUFF:
EventBus.damage_requested.emit(source, entity, effect.value)
elif effect.type == Effect.Type.BUFF:
EventBus.heal_requested.emit(source, entity, effect.value)