update
This commit is contained in:
@@ -34,8 +34,8 @@ func _try_auto_attack(player: Node) -> void:
|
||||
if dist > aa_range:
|
||||
return
|
||||
EventBus.damage_requested.emit(player, targeting.current_target, dmg)
|
||||
var base: BaseStats = Stats.get_base(player)
|
||||
var aa_cd: float = base.aa_cooldown if base is PlayerStats else 0.5
|
||||
var player_base: BaseStats = Stats.get_base(player)
|
||||
var aa_cd: float = player_base.aa_cooldown if player_base is PlayerStats else 0.5
|
||||
cooldown_system.set_aa_cooldown(player, aa_cd)
|
||||
|
||||
func _on_ability_use_requested(player: Node, ability_index: int) -> void:
|
||||
@@ -56,8 +56,8 @@ func _on_ability_use_requested(player: Node, ability_index: int) -> void:
|
||||
var success: bool = _execute_ability(player, ability)
|
||||
if not success:
|
||||
return
|
||||
var base: BaseStats = Stats.get_base(player)
|
||||
var gcd_time: float = base.gcd_time if base is PlayerStats else 0.5
|
||||
var player_base: BaseStats = Stats.get_base(player)
|
||||
var gcd_time: float = player_base.gcd_time if player_base is PlayerStats else 0.5
|
||||
var gcd: float = gcd_time if ability.uses_gcd else 0.0
|
||||
cooldown_system.set_cooldown(player, ability_index, ability.cooldown, gcd)
|
||||
|
||||
@@ -100,6 +100,8 @@ func _execute_single(player: Node, targeting: Node, ability: Ability, dmg: float
|
||||
if not is_instance_valid(targeting.current_target):
|
||||
return false
|
||||
EventBus.damage_requested.emit(player, targeting.current_target, dmg)
|
||||
if ability.element != 0:
|
||||
EventBus.element_damage_dealt.emit(player, targeting.current_target, dmg, ability.element)
|
||||
EventBus.attack_executed.emit(player, player.global_position, -player.global_transform.basis.z, dmg)
|
||||
return true
|
||||
|
||||
@@ -120,6 +122,8 @@ func _execute_aoe(player: Node, ability: Ability, dmg: float) -> bool:
|
||||
var dist: float = player.global_position.distance_to(enemy.global_position)
|
||||
if dist <= ability.ability_range:
|
||||
EventBus.damage_requested.emit(player, enemy, dmg)
|
||||
if ability.element != 0:
|
||||
EventBus.element_damage_dealt.emit(player, enemy, dmg, ability.element)
|
||||
hit = true
|
||||
if hit:
|
||||
EventBus.attack_executed.emit(player, player.global_position, -player.global_transform.basis.z, dmg)
|
||||
@@ -158,12 +162,16 @@ func _execute_ult(player: Node, targeting: Node, ability: Ability, dmg: float) -
|
||||
return false
|
||||
var target: Node3D = targeting.current_target
|
||||
EventBus.damage_requested.emit(player, target, dmg * 5.0)
|
||||
var aoe_range: float = ability.aoe_radius if ability.aoe_radius > 0 else ability.ability_range
|
||||
if ability.element != 0:
|
||||
EventBus.element_damage_dealt.emit(player, target, dmg * 5.0, ability.element)
|
||||
var splash_range: float = ability.aoe_radius if ability.aoe_radius > 0 else ability.ability_range
|
||||
var enemies := get_tree().get_nodes_in_group("enemies")
|
||||
for enemy in enemies:
|
||||
if enemy != target and is_instance_valid(enemy):
|
||||
var enemy_dist: float = target.global_position.distance_to(enemy.global_position)
|
||||
if enemy_dist <= aoe_range:
|
||||
if enemy_dist <= splash_range:
|
||||
EventBus.damage_requested.emit(player, enemy, dmg * 2.0)
|
||||
if ability.element != 0:
|
||||
EventBus.element_damage_dealt.emit(player, enemy, dmg * 2.0, ability.element)
|
||||
EventBus.attack_executed.emit(player, player.global_position, -player.global_transform.basis.z, dmg * 5.0)
|
||||
return true
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
extends Node
|
||||
|
||||
func _ready() -> void:
|
||||
EventBus.role_changed.connect(_on_role_changed)
|
||||
|
||||
func _on_role_changed(player: Node, _role_type: int) -> void:
|
||||
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
|
||||
var damage_mult := 1.0
|
||||
var heal_mult := 1.0
|
||||
var shield_mult := 1.0
|
||||
for ability in ability_set.abilities:
|
||||
if ability and ability.type == Ability.Type.PASSIVE:
|
||||
var bonus: float = ability.damage / 100.0
|
||||
match ability.passive_stat:
|
||||
"damage":
|
||||
damage_mult = 1.0 + bonus
|
||||
"heal":
|
||||
heal_mult = 1.0 + bonus
|
||||
"shield":
|
||||
shield_mult = 1.0 + bonus
|
||||
Stats.set_stat(player, "buff_damage", damage_mult)
|
||||
Stats.set_stat(player, "buff_heal", heal_mult)
|
||||
Stats.set_stat(player, "buff_shield", shield_mult)
|
||||
var base: BaseStats = Stats.get_base(player)
|
||||
if base:
|
||||
var new_max: float = base.max_shield * shield_mult
|
||||
Stats.set_stat(player, "max_shield", new_max)
|
||||
var shield: float = Stats.get_stat(player, "shield")
|
||||
shield = min(shield, new_max)
|
||||
Stats.set_stat(player, "shield", shield)
|
||||
EventBus.shield_changed.emit(player, shield, new_max)
|
||||
EventBus.buff_changed.emit(player, "damage", damage_mult)
|
||||
@@ -1 +0,0 @@
|
||||
uid://da2jm0awq2lnh
|
||||
14
systems/effect.gd
Normal file
14
systems/effect.gd
Normal file
@@ -0,0 +1,14 @@
|
||||
extends Resource
|
||||
class_name Effect
|
||||
|
||||
enum Type { BUFF, DEBUFF, AURA }
|
||||
|
||||
@export var effect_name: String = ""
|
||||
@export var type: Type = Type.BUFF
|
||||
@export var stat: String = ""
|
||||
@export var value: float = 0.0
|
||||
@export var duration: float = -1.0
|
||||
@export var is_multiplier: bool = true
|
||||
@export var aura_radius: float = 0.0
|
||||
@export var tick_interval: float = 0.0
|
||||
@export var element: int = 0
|
||||
1
systems/effect.gd.uid
Normal file
1
systems/effect.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://djbni7iy5pw2m
|
||||
190
systems/effect_system.gd
Normal file
190
systems/effect_system.gd
Normal 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)
|
||||
1
systems/effect_system.gd.uid
Normal file
1
systems/effect_system.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://drdlh6tq0dfwo
|
||||
53
systems/element_system.gd
Normal file
53
systems/element_system.gd
Normal file
@@ -0,0 +1,53 @@
|
||||
extends Node
|
||||
|
||||
enum Element { NONE, FIRE }
|
||||
|
||||
var applied_elements: Dictionary = {}
|
||||
|
||||
func _ready() -> void:
|
||||
EventBus.element_damage_dealt.connect(_on_element_damage_dealt)
|
||||
EventBus.entity_died.connect(_on_entity_died)
|
||||
EventBus.effect_expired.connect(_on_effect_expired)
|
||||
|
||||
func _on_element_damage_dealt(attacker: Node, target: Node, _amount: float, element: int) -> void:
|
||||
if element == Element.NONE:
|
||||
return
|
||||
if not target.is_in_group("enemies") and not target.is_in_group("portals"):
|
||||
return
|
||||
var current: int = applied_elements.get(target, Element.NONE)
|
||||
if current != Element.NONE and current != element:
|
||||
_trigger_reaction(attacker, target, current, element)
|
||||
return
|
||||
_apply_element(attacker, target, element)
|
||||
|
||||
func _apply_element(source: Node, target: Node, element: int) -> void:
|
||||
applied_elements[target] = element
|
||||
EventBus.element_applied.emit(target, element)
|
||||
match element:
|
||||
Element.FIRE:
|
||||
_apply_fire(source, target)
|
||||
|
||||
func _apply_fire(source: Node, target: Node) -> void:
|
||||
var fire_dot := Effect.new()
|
||||
fire_dot.effect_name = "Burning"
|
||||
fire_dot.type = Effect.Type.DEBUFF
|
||||
fire_dot.stat = "damage"
|
||||
fire_dot.value = 3.0
|
||||
fire_dot.duration = 6.0
|
||||
fire_dot.is_multiplier = false
|
||||
fire_dot.tick_interval = 2.0
|
||||
fire_dot.element = Element.FIRE
|
||||
EventBus.effect_requested.emit(target, fire_dot, source)
|
||||
|
||||
func _trigger_reaction(_attacker: Node, target: Node, _elem_a: int, _elem_b: int) -> void:
|
||||
applied_elements.erase(target)
|
||||
EventBus.element_reaction.emit(target, _elem_a, _elem_b, "")
|
||||
|
||||
func _on_entity_died(entity: Node) -> void:
|
||||
applied_elements.erase(entity)
|
||||
|
||||
func _on_effect_expired(target: Node, effect: Effect) -> void:
|
||||
if effect.element != Element.NONE:
|
||||
var current: int = applied_elements.get(target, Element.NONE)
|
||||
if current == effect.element:
|
||||
applied_elements.erase(target)
|
||||
1
systems/element_system.gd.uid
Normal file
1
systems/element_system.gd.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bqebxfvticxto
|
||||
Reference in New Issue
Block a user