Files
mmo/systems/ability_system.gd
Marek Lenczewski f1d34ebf1d update
2026-04-04 00:00:15 +02:00

128 lines
5.0 KiB
GDScript

extends Node
func _ready() -> void:
EventBus.ability_use.connect(_on_ability_use)
func _on_ability_use(_player: Node, ability_index: int) -> void:
if not PlayerData.alive:
return
var ability_set: AbilitySet = PlayerData.ability_set
if not ability_set or ability_index >= ability_set.abilities.size():
return
var ability: Ability = ability_set.abilities[ability_index]
if not ability or ability.type == Ability.Type.PASSIVE:
return
if PlayerData.cooldowns[ability_index] > 0:
return
if ability.uses_gcd and PlayerData.gcd > 0:
return
var success: bool = _execute_ability(ability)
if not success:
return
var gcd: float = PlayerData.gcd_time if ability.uses_gcd else 0.0
PlayerData.cooldowns[ability_index] = ability.cooldown
PlayerData.max_cooldowns[ability_index] = max(ability.cooldown, gcd)
if gcd > 0:
PlayerData.gcd = gcd
func _execute_ability(ability: Ability) -> bool:
var stat: String = "heal" if ability.is_heal else "damage"
var dmg: float = _apply_passive(ability.damage, stat)
var player: Node = get_tree().get_first_node_in_group("player")
match ability.type:
Ability.Type.SINGLE:
return _execute_single(player, ability, dmg)
Ability.Type.AOE:
return _execute_aoe(player, ability, dmg)
Ability.Type.UTILITY:
return _execute_utility(ability)
Ability.Type.ULT:
return _execute_ult(player, ability, dmg)
return false
func _apply_passive(base: float, stat: String) -> float:
var mult: float = 1.0
match stat:
"damage": mult = PlayerData.buff_damage
"heal": mult = PlayerData.buff_heal
return base * mult
func _in_range(ability: Ability) -> bool:
if ability.ability_range <= 0 or ability.is_heal:
return true
if not is_instance_valid(PlayerData.target):
return false
var player: Node = get_tree().get_first_node_in_group("player")
var dist: float = player.global_position.distance_to(PlayerData.target.global_position)
return dist <= ability.ability_range
func _execute_single(player: Node, ability: Ability, dmg: float) -> bool:
if ability.is_heal:
EventBus.heal_requested.emit(player, player, dmg)
EventBus.attack_executed.emit(player, player.global_position, -player.global_transform.basis.z, dmg)
return true
if not _in_range(ability):
return false
if not is_instance_valid(PlayerData.target):
return false
EventBus.damage_requested.emit(player, PlayerData.target, dmg)
if ability.element != 0:
EventBus.element_damage_dealt.emit(player, PlayerData.target, dmg, ability.element)
EventBus.attack_executed.emit(player, player.global_position, -player.global_transform.basis.z, dmg)
return true
func _execute_aoe(player: Node, ability: Ability, dmg: float) -> bool:
if ability.is_heal:
EventBus.heal_requested.emit(player, player, dmg)
EventBus.attack_executed.emit(player, player.global_position, -player.global_transform.basis.z, dmg)
return true
var hit := false
for enemy in get_tree().get_nodes_in_group("enemies"):
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)
return hit
func _execute_utility(ability: Ability) -> bool:
if PlayerData.max_shield <= 0:
return false
var shield: float = PlayerData.shield
if ability.damage > 0:
shield = PlayerData.max_shield * (ability.damage / 100.0)
else:
if shield >= PlayerData.max_shield:
return false
shield = PlayerData.max_shield
PlayerData.set_shield(shield)
return true
func _execute_ult(player: Node, ability: Ability, dmg: float) -> bool:
if ability.is_heal:
EventBus.heal_requested.emit(player, player, dmg)
var aoe_range: float = ability.aoe_radius if ability.aoe_radius > 0 else ability.ability_range
EventBus.attack_executed.emit(player, player.global_position, -player.global_transform.basis.z, dmg)
return true
if not _in_range(ability):
return false
if not is_instance_valid(PlayerData.target):
return false
var target: Node3D = PlayerData.target
EventBus.damage_requested.emit(player, target, dmg * 5.0)
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
for enemy in get_tree().get_nodes_in_group("enemies"):
if enemy != target and is_instance_valid(enemy):
var enemy_dist: float = target.global_position.distance_to(enemy.global_position)
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