This commit is contained in:
Marek
2026-03-29 16:51:06 +02:00
parent aa2c182534
commit ea0d2b51c0
12 changed files with 176 additions and 45 deletions

View File

@@ -7,55 +7,68 @@ enum Type { SINGLE, AOE, UTILITY, ULT, PASSIVE }
@export var type: Type = Type.SINGLE
@export var damage: float = 0.0
@export var ability_range: float = 3.0
@export var cooldown: float = 0.0
@export var uses_gcd: bool = true
@export var icon: String = ""
func execute(player: Node, targeting: Node) -> void:
func execute(player: Node, targeting: Node) -> bool:
var dmg: float = _get_modified_damage(player, damage)
match type:
Type.SINGLE:
_execute_single(player, targeting, dmg)
return _execute_single(player, targeting, dmg)
Type.AOE:
_execute_aoe(player, dmg)
return _execute_aoe(player, dmg)
Type.UTILITY:
_execute_utility(player)
return _execute_utility(player)
Type.ULT:
_execute_ult(player, targeting, dmg)
return _execute_ult(player, targeting, dmg)
return false
func _get_modified_damage(player: Node, base: float) -> float:
var combat: Node = player.get_node("Combat")
return combat.apply_passive(base)
func _execute_single(player: Node, targeting: Node, dmg: float) -> void:
func _in_range(player: Node, targeting: Node) -> bool:
if ability_range <= 0:
return true
var target: Node3D = targeting.current_target
if not target or not is_instance_valid(target):
return
return false
var dist: float = player.global_position.distance_to(target.global_position)
if dist > ability_range:
return
return dist <= ability_range
func _execute_single(player: Node, targeting: Node, dmg: float) -> bool:
if not _in_range(player, targeting):
return false
var target: Node3D = targeting.current_target
EventBus.damage_requested.emit(player, target, dmg)
EventBus.attack_executed.emit(player, player.global_position, -player.global_transform.basis.z, dmg)
return true
func _execute_aoe(player: Node, dmg: float) -> void:
func _execute_aoe(player: Node, dmg: float) -> bool:
var hit := false
var enemies := player.get_tree().get_nodes_in_group("enemies")
for enemy in enemies:
var dist: float = player.global_position.distance_to(enemy.global_position)
if dist <= ability_range:
EventBus.damage_requested.emit(player, enemy, dmg)
EventBus.attack_executed.emit(player, player.global_position, -player.global_transform.basis.z, dmg)
hit = true
if hit:
EventBus.attack_executed.emit(player, player.global_position, -player.global_transform.basis.z, dmg)
return hit
func _execute_utility(player: Node) -> void:
func _execute_utility(player: Node) -> bool:
var shield: Node = player.get_node_or_null("Shield")
if shield:
shield.current_shield = shield.max_shield
EventBus.shield_changed.emit(player, shield.current_shield, shield.max_shield)
return true
return false
func _execute_ult(player: Node, targeting: Node, dmg: float) -> void:
func _execute_ult(player: Node, targeting: Node, dmg: float) -> bool:
if not _in_range(player, targeting):
return false
var target: Node3D = targeting.current_target
if not target or not is_instance_valid(target):
return
var dist: float = player.global_position.distance_to(target.global_position)
if dist > ability_range:
return
EventBus.damage_requested.emit(player, target, dmg * 4.0)
var enemies := player.get_tree().get_nodes_in_group("enemies")
for enemy in enemies:
@@ -64,3 +77,4 @@ func _execute_ult(player: Node, targeting: Node, dmg: float) -> void:
if enemy_dist <= ability_range:
EventBus.damage_requested.emit(player, enemy, dmg * 2.0)
EventBus.attack_executed.emit(player, player.global_position, -player.global_transform.basis.z, dmg * 4.0)
return true

View File

@@ -13,6 +13,7 @@ func _ready() -> void:
spawn_position = global_position
add_to_group("enemies")
EventBus.entity_died.connect(_on_entity_died)
EventBus.damage_dealt.connect(_on_damage_dealt)
func _on_entity_died(entity: Node) -> void:
if entity == self:
@@ -26,10 +27,29 @@ func _physics_process(delta: float) -> void:
velocity.y -= gravity * delta
move_and_slide()
func _on_damage_dealt(attacker: Node, damage_target: Node, _amount: float) -> void:
if damage_target == self and attacker.name == "Player":
_engage(attacker)
func _engage(new_target: Node3D) -> void:
if state == State.CHASE or state == State.ATTACK:
return
target = new_target
state = State.CHASE
_alert_nearby()
func _alert_nearby() -> void:
var enemies := get_tree().get_nodes_in_group("enemies")
for enemy in enemies:
if enemy != self and is_instance_valid(enemy):
if enemy.state == enemy.State.IDLE:
var dist: float = global_position.distance_to(enemy.global_position)
if dist <= 3.0:
enemy._engage(target)
func _on_detection_area_body_entered(body: Node3D) -> void:
if body is CharacterBody3D and body.name == "Player":
target = body
state = State.CHASE
_engage(body)
EventBus.enemy_engaged.emit(self, body)
func _on_detection_area_body_exited(body: Node3D) -> void:

View File

@@ -13,3 +13,4 @@ signal health_changed(entity, current, max_val)
signal shield_changed(entity, current, max_val)
signal respawn_tick(timer)
signal enemy_engaged(enemy, target)
signal cooldown_tick(cooldowns, max_cooldowns, gcd_timer)

View File

@@ -1,28 +1,56 @@
extends Node
const GCD_TIME := 1.0
@onready var player: CharacterBody3D = get_parent()
@onready var targeting: Node = get_parent().get_node("Targeting")
@onready var player_class: Node = get_parent().get_node("PlayerClass")
var abilities: Array = []
var cooldowns: Array[float] = [0.0, 0.0, 0.0, 0.0, 0.0]
var max_cooldowns: Array[float] = [0.0, 0.0, 0.0, 0.0, 0.0]
var gcd_timer := 0.0
func _ready() -> void:
_load_abilities()
EventBus.class_changed.connect(_on_class_changed)
func _process(delta: float) -> void:
if gcd_timer > 0:
gcd_timer -= delta
for i in range(cooldowns.size()):
if cooldowns[i] > 0:
cooldowns[i] -= delta
EventBus.cooldown_tick.emit(cooldowns, max_cooldowns, gcd_timer)
func _load_abilities() -> void:
var ability_set: AbilitySet = player_class.get_ability_set()
if ability_set:
abilities = ability_set.abilities
else:
abilities = []
cooldowns = [0.0, 0.0, 0.0, 0.0, 0.0]
max_cooldowns = [0.0, 0.0, 0.0, 0.0, 0.0]
gcd_timer = 0.0
func _unhandled_input(event: InputEvent) -> void:
for i in range(min(abilities.size(), 5)):
if event.is_action_pressed("ability_%s" % (i + 1)) and abilities[i]:
if abilities[i].type == Ability.Type.PASSIVE:
return
abilities[i].execute(player, targeting)
if cooldowns[i] > 0:
return
if abilities[i].uses_gcd and gcd_timer > 0:
return
var success: bool = abilities[i].execute(player, targeting)
if not success:
return
var ability_cd: float = abilities[i].cooldown
var gcd_cd: float = GCD_TIME if abilities[i].uses_gcd else 0.0
cooldowns[i] = ability_cd
max_cooldowns[i] = max(ability_cd, gcd_cd)
if abilities[i].uses_gcd:
gcd_timer = GCD_TIME
return
func apply_passive(base_damage: float) -> float:

View File

@@ -1,11 +1,20 @@
extends CanvasLayer
const GCD_TIME := 1.0
@onready var health_bar: ProgressBar = $HealthBar
@onready var shield_bar: ProgressBar = $ShieldBar
@onready var respawn_label: Label = $RespawnTimer
@onready var class_icon: Label = $AbilityBar/ClassIcon/Label
@onready var ability_panels: Array = [
$AbilityBar/Ability1,
$AbilityBar/Ability2,
$AbilityBar/Ability3,
$AbilityBar/Ability4,
$AbilityBar/Ability5,
]
var player_node: Node = null
var ability_labels: Array[String] = ["1", "2", "3", "4", "P"]
func _ready() -> void:
respawn_label.visible = false
@@ -15,6 +24,7 @@ func _ready() -> void:
EventBus.player_respawned.connect(_on_player_respawned)
EventBus.class_changed.connect(_on_class_changed)
EventBus.respawn_tick.connect(_on_respawn_tick)
EventBus.cooldown_tick.connect(_on_cooldown_tick)
func _on_health_changed(entity: Node, current: float, max_val: float) -> void:
if entity.name == "Player":
@@ -41,3 +51,22 @@ func _on_class_changed(_player: Node, class_type: int) -> void:
0: class_icon.text = "T"
1: class_icon.text = "D"
2: class_icon.text = "H"
func _on_cooldown_tick(cooldowns: Array, max_cooldowns: Array, gcd_timer: float) -> void:
for i in range(min(ability_panels.size(), cooldowns.size())):
var panel: Panel = ability_panels[i]
var label: Label = panel.get_node("Label")
var overlay: ColorRect = panel.get_node("CooldownOverlay")
var cd: float = cooldowns[i]
var gcd: float = gcd_timer if i != 2 and i != 4 else 0.0
var active_cd: float = max(cd, gcd)
var max_cd: float = max_cooldowns[i] if max_cooldowns[i] > 0 else GCD_TIME
if active_cd > 0:
var ratio: float = clamp(active_cd / max_cd, 0.0, 1.0)
overlay.visible = true
overlay.anchor_bottom = ratio
label.text = str(ceil(active_cd))
else:
overlay.visible = false
label.text = ability_labels[i]