update
This commit is contained in:
17
plan.md
17
plan.md
@@ -64,15 +64,15 @@
|
|||||||
- player.gd — Kern, verbindet Komponenten
|
- player.gd — Kern, verbindet Komponenten
|
||||||
- camera.gd — LMB freies Umsehen, RMB Kamera + Laufrichtung anpassen
|
- camera.gd — LMB freies Umsehen, RMB Kamera + Laufrichtung anpassen
|
||||||
- movement.gd — Bewegung (WASD relativ zur Kamera), Springen, Schwerkraft
|
- movement.gd — Bewegung (WASD relativ zur Kamera), Springen, Schwerkraft
|
||||||
- combat.gd — Führt Abilities aus, Fähigkeiten 1-5
|
- combat.gd — Führt Abilities aus, verwaltet Cooldowns (GCD 1s), Fähigkeiten 1-5
|
||||||
- 1 Single: Anvisiertes Ziel angreifen (10 Schaden, Distanzprüfung)
|
- 1 Single: 10 Schaden, Distanzprüfung, 0s CD, GCD
|
||||||
- 2 AOE: Alle Gegner im Bereich (5 Schaden, halber Single-Schaden)
|
- 2 AOE: 5 Schaden im Bereich, 3s CD, GCD
|
||||||
- 3 Utility: Schild-Regeneration sofort zurücksetzen
|
- 3 Utility: Schild sofort auf 100%, 5s CD, kein GCD
|
||||||
- 4 Ult: 4x Schaden an anvisiertem Ziel + 2x AOE-Schaden um das Ziel herum
|
- 4 Ult: 4x Single + 2x AOE um Ziel, 30s CD, GCD
|
||||||
- 5 Passive: 50% mehr Schaden (permanent aktiv)
|
- 5 Passive: 50% mehr Schaden (permanent aktiv, kein CD)
|
||||||
- targeting.gd — Klick/TAB anvisieren, Kampfmodus bei Gegner-Angriff, Auto-Targeting auf nächsten Gegner
|
- targeting.gd — Klick/TAB anvisieren, Kampfmodus bei Gegner-Angriff, Auto-Targeting auf nächsten Gegner
|
||||||
- event_bus.gd — Autoload-Singleton, globale Signals
|
- event_bus.gd — Autoload-Singleton, globale Signals
|
||||||
- enemy.gd — Gegner-Kern, State Machine (Idle, Verfolgen, Angreifen, Zurückkehren)
|
- enemy.gd — Gegner-Kern, State Machine (Idle, Verfolgen, Angreifen, Zurückkehren), Aggro bei Schaden, alarmiert Gegner in 3m
|
||||||
- enemy_movement.gd — Navigation zum Ziel/Spawnpunkt
|
- enemy_movement.gd — Navigation zum Ziel/Spawnpunkt
|
||||||
- enemy_combat.gd — Angriff über Event (damage_requested)
|
- enemy_combat.gd — Angriff über Event (damage_requested)
|
||||||
- health.gd — Leben, 1/s Regeneration, Tod bei 0 (wiederverwendbar)
|
- health.gd — Leben, 1/s Regeneration, Tod bei 0 (wiederverwendbar)
|
||||||
@@ -83,7 +83,7 @@
|
|||||||
- enemy_healthbar.gd — Liest Health/Shield vom Gegner, aktualisiert Balken über dem Gegner, gelber Rand bei Anvisierung
|
- enemy_healthbar.gd — Liest Health/Shield vom Gegner, aktualisiert Balken über dem Gegner, gelber Rand bei Anvisierung
|
||||||
|
|
||||||
## Abilities (Resources)
|
## Abilities (Resources)
|
||||||
- ability.gd (Resource) — name, type, damage, range, execute()
|
- ability.gd (Resource) — name, type, damage, range, cooldown, uses_gcd, execute()
|
||||||
- ability_set.gd (Resource) — Set von 5 Abilities pro Klasse
|
- ability_set.gd (Resource) — Set von 5 Abilities pro Klasse
|
||||||
- ability_modifier.gd (Resource) — Verändert Ability (Element, Beruf, Prestige)
|
- ability_modifier.gd (Resource) — Verändert Ability (Element, Beruf, Prestige)
|
||||||
- Typen: Single, AOE, Utility, Ult, Passive
|
- Typen: Single, AOE, Utility, Ult, Passive
|
||||||
@@ -105,3 +105,4 @@
|
|||||||
- shield_changed(entity, current, max) — Schild hat sich verändert
|
- shield_changed(entity, current, max) — Schild hat sich verändert
|
||||||
- respawn_tick(timer) — Respawn-Countdown Update
|
- respawn_tick(timer) — Respawn-Countdown Update
|
||||||
- enemy_engaged(enemy, target) — Gegner hat Spieler anvisiert
|
- enemy_engaged(enemy, target) — Gegner hat Spieler anvisiert
|
||||||
|
- cooldown_tick(cooldowns, max_cooldowns, gcd_timer) — Cooldown-Update für HUD
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[gd_resource type="Resource" script_class="Ability" load_steps=2 format=3]
|
[gd_resource type="Resource" script_class="Ability" format=3 uid="uid://bpx3l13iuynfv"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://scripts/abilities/ability.gd" id="1"]
|
[ext_resource type="Script" uid="uid://c03xbbf3yhfl3" path="res://scripts/abilities/ability.gd" id="1"]
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
script = ExtResource("1")
|
script = ExtResource("1")
|
||||||
@@ -8,4 +8,5 @@ ability_name = "AOE"
|
|||||||
type = 1
|
type = 1
|
||||||
damage = 5.0
|
damage = 5.0
|
||||||
ability_range = 5.0
|
ability_range = 5.0
|
||||||
|
cooldown = 3.0
|
||||||
icon = "2"
|
icon = "2"
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[gd_resource type="Resource" script_class="Ability" load_steps=2 format=3]
|
[gd_resource type="Resource" script_class="Ability" format=3 uid="uid://dadpl32yujwhe"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://scripts/abilities/ability.gd" id="1"]
|
[ext_resource type="Script" uid="uid://c03xbbf3yhfl3" path="res://scripts/abilities/ability.gd" id="1"]
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
script = ExtResource("1")
|
script = ExtResource("1")
|
||||||
@@ -8,4 +8,5 @@ ability_name = "Damage Boost"
|
|||||||
type = 4
|
type = 4
|
||||||
damage = 50.0
|
damage = 50.0
|
||||||
ability_range = 0.0
|
ability_range = 0.0
|
||||||
icon = "5"
|
uses_gcd = false
|
||||||
|
icon = "P"
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
[gd_resource type="Resource" script_class="Ability" load_steps=2 format=3]
|
[gd_resource type="Resource" script_class="Ability" format=3 uid="uid://dwvc8b3cmce8l"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://scripts/abilities/ability.gd" id="1"]
|
[ext_resource type="Script" uid="uid://c03xbbf3yhfl3" path="res://scripts/abilities/ability.gd" id="1"]
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
script = ExtResource("1")
|
script = ExtResource("1")
|
||||||
ability_name = "Single"
|
ability_name = "Single"
|
||||||
type = 0
|
|
||||||
damage = 10.0
|
damage = 10.0
|
||||||
ability_range = 3.0
|
ability_range = 20.0
|
||||||
icon = "1"
|
icon = "1"
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
[gd_resource type="Resource" script_class="Ability" load_steps=2 format=3]
|
[gd_resource type="Resource" script_class="Ability" format=3 uid="uid://s32wvlww2ls2"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://scripts/abilities/ability.gd" id="1"]
|
[ext_resource type="Script" uid="uid://c03xbbf3yhfl3" path="res://scripts/abilities/ability.gd" id="1"]
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
script = ExtResource("1")
|
script = ExtResource("1")
|
||||||
ability_name = "Burst"
|
ability_name = "Burst"
|
||||||
type = 3
|
type = 3
|
||||||
damage = 10.0
|
damage = 10.0
|
||||||
ability_range = 5.0
|
ability_range = 20.0
|
||||||
|
cooldown = 30.0
|
||||||
icon = "4"
|
icon = "4"
|
||||||
|
|||||||
@@ -1,11 +1,12 @@
|
|||||||
[gd_resource type="Resource" script_class="Ability" load_steps=2 format=3]
|
[gd_resource type="Resource" script_class="Ability" format=3 uid="uid://du0hyuuj26ea0"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://scripts/abilities/ability.gd" id="1"]
|
[ext_resource type="Script" uid="uid://c03xbbf3yhfl3" path="res://scripts/abilities/ability.gd" id="1"]
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
script = ExtResource("1")
|
script = ExtResource("1")
|
||||||
ability_name = "Shield Reset"
|
ability_name = "Shield Reset"
|
||||||
type = 2
|
type = 2
|
||||||
damage = 0.0
|
|
||||||
ability_range = 0.0
|
ability_range = 0.0
|
||||||
|
cooldown = 5.0
|
||||||
|
uses_gcd = false
|
||||||
icon = "3"
|
icon = "3"
|
||||||
|
|||||||
@@ -109,6 +109,13 @@ vertical_alignment = 1
|
|||||||
custom_minimum_size = Vector2(45, 45)
|
custom_minimum_size = Vector2(45, 45)
|
||||||
theme_override_styles/panel = SubResource("StyleBoxFlat_ability_active")
|
theme_override_styles/panel = SubResource("StyleBoxFlat_ability_active")
|
||||||
|
|
||||||
|
[node name="CooldownOverlay" type="ColorRect" parent="AbilityBar/Ability1"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
color = Color(0, 0, 0, 0.6)
|
||||||
|
visible = false
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="AbilityBar/Ability1"]
|
[node name="Label" type="Label" parent="AbilityBar/Ability1"]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
@@ -124,6 +131,13 @@ vertical_alignment = 1
|
|||||||
custom_minimum_size = Vector2(45, 45)
|
custom_minimum_size = Vector2(45, 45)
|
||||||
theme_override_styles/panel = SubResource("StyleBoxFlat_ability_active")
|
theme_override_styles/panel = SubResource("StyleBoxFlat_ability_active")
|
||||||
|
|
||||||
|
[node name="CooldownOverlay" type="ColorRect" parent="AbilityBar/Ability2"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
color = Color(0, 0, 0, 0.6)
|
||||||
|
visible = false
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="AbilityBar/Ability2"]
|
[node name="Label" type="Label" parent="AbilityBar/Ability2"]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
@@ -139,6 +153,13 @@ vertical_alignment = 1
|
|||||||
custom_minimum_size = Vector2(45, 45)
|
custom_minimum_size = Vector2(45, 45)
|
||||||
theme_override_styles/panel = SubResource("StyleBoxFlat_ability_active")
|
theme_override_styles/panel = SubResource("StyleBoxFlat_ability_active")
|
||||||
|
|
||||||
|
[node name="CooldownOverlay" type="ColorRect" parent="AbilityBar/Ability3"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
color = Color(0, 0, 0, 0.6)
|
||||||
|
visible = false
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="AbilityBar/Ability3"]
|
[node name="Label" type="Label" parent="AbilityBar/Ability3"]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
@@ -154,6 +175,13 @@ vertical_alignment = 1
|
|||||||
custom_minimum_size = Vector2(45, 45)
|
custom_minimum_size = Vector2(45, 45)
|
||||||
theme_override_styles/panel = SubResource("StyleBoxFlat_ability_active")
|
theme_override_styles/panel = SubResource("StyleBoxFlat_ability_active")
|
||||||
|
|
||||||
|
[node name="CooldownOverlay" type="ColorRect" parent="AbilityBar/Ability4"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
color = Color(0, 0, 0, 0.6)
|
||||||
|
visible = false
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="AbilityBar/Ability4"]
|
[node name="Label" type="Label" parent="AbilityBar/Ability4"]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
@@ -169,6 +197,13 @@ vertical_alignment = 1
|
|||||||
custom_minimum_size = Vector2(45, 45)
|
custom_minimum_size = Vector2(45, 45)
|
||||||
theme_override_styles/panel = SubResource("StyleBoxFlat_ability_round")
|
theme_override_styles/panel = SubResource("StyleBoxFlat_ability_round")
|
||||||
|
|
||||||
|
[node name="CooldownOverlay" type="ColorRect" parent="AbilityBar/Ability5"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
color = Color(0, 0, 0, 0.6)
|
||||||
|
visible = false
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="AbilityBar/Ability5"]
|
[node name="Label" type="Label" parent="AbilityBar/Ability5"]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
|
|||||||
@@ -7,55 +7,68 @@ enum Type { SINGLE, AOE, UTILITY, ULT, PASSIVE }
|
|||||||
@export var type: Type = Type.SINGLE
|
@export var type: Type = Type.SINGLE
|
||||||
@export var damage: float = 0.0
|
@export var damage: float = 0.0
|
||||||
@export var ability_range: float = 3.0
|
@export var ability_range: float = 3.0
|
||||||
|
@export var cooldown: float = 0.0
|
||||||
|
@export var uses_gcd: bool = true
|
||||||
@export var icon: String = ""
|
@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)
|
var dmg: float = _get_modified_damage(player, damage)
|
||||||
match type:
|
match type:
|
||||||
Type.SINGLE:
|
Type.SINGLE:
|
||||||
_execute_single(player, targeting, dmg)
|
return _execute_single(player, targeting, dmg)
|
||||||
Type.AOE:
|
Type.AOE:
|
||||||
_execute_aoe(player, dmg)
|
return _execute_aoe(player, dmg)
|
||||||
Type.UTILITY:
|
Type.UTILITY:
|
||||||
_execute_utility(player)
|
return _execute_utility(player)
|
||||||
Type.ULT:
|
Type.ULT:
|
||||||
_execute_ult(player, targeting, dmg)
|
return _execute_ult(player, targeting, dmg)
|
||||||
|
return false
|
||||||
|
|
||||||
func _get_modified_damage(player: Node, base: float) -> float:
|
func _get_modified_damage(player: Node, base: float) -> float:
|
||||||
var combat: Node = player.get_node("Combat")
|
var combat: Node = player.get_node("Combat")
|
||||||
return combat.apply_passive(base)
|
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
|
var target: Node3D = targeting.current_target
|
||||||
if not target or not is_instance_valid(target):
|
if not target or not is_instance_valid(target):
|
||||||
return
|
return false
|
||||||
var dist: float = player.global_position.distance_to(target.global_position)
|
var dist: float = player.global_position.distance_to(target.global_position)
|
||||||
if dist > ability_range:
|
return dist <= ability_range
|
||||||
return
|
|
||||||
|
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.damage_requested.emit(player, target, dmg)
|
||||||
EventBus.attack_executed.emit(player, player.global_position, -player.global_transform.basis.z, 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")
|
var enemies := player.get_tree().get_nodes_in_group("enemies")
|
||||||
for enemy in enemies:
|
for enemy in enemies:
|
||||||
var dist: float = player.global_position.distance_to(enemy.global_position)
|
var dist: float = player.global_position.distance_to(enemy.global_position)
|
||||||
if dist <= ability_range:
|
if dist <= ability_range:
|
||||||
EventBus.damage_requested.emit(player, enemy, dmg)
|
EventBus.damage_requested.emit(player, enemy, dmg)
|
||||||
|
hit = true
|
||||||
|
if hit:
|
||||||
EventBus.attack_executed.emit(player, player.global_position, -player.global_transform.basis.z, dmg)
|
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")
|
var shield: Node = player.get_node_or_null("Shield")
|
||||||
if shield:
|
if shield:
|
||||||
shield.current_shield = shield.max_shield
|
shield.current_shield = shield.max_shield
|
||||||
EventBus.shield_changed.emit(player, 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
|
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)
|
EventBus.damage_requested.emit(player, target, dmg * 4.0)
|
||||||
var enemies := player.get_tree().get_nodes_in_group("enemies")
|
var enemies := player.get_tree().get_nodes_in_group("enemies")
|
||||||
for enemy in enemies:
|
for enemy in enemies:
|
||||||
@@ -64,3 +77,4 @@ func _execute_ult(player: Node, targeting: Node, dmg: float) -> void:
|
|||||||
if enemy_dist <= ability_range:
|
if enemy_dist <= ability_range:
|
||||||
EventBus.damage_requested.emit(player, enemy, dmg * 2.0)
|
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)
|
EventBus.attack_executed.emit(player, player.global_position, -player.global_transform.basis.z, dmg * 4.0)
|
||||||
|
return true
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ func _ready() -> void:
|
|||||||
spawn_position = global_position
|
spawn_position = global_position
|
||||||
add_to_group("enemies")
|
add_to_group("enemies")
|
||||||
EventBus.entity_died.connect(_on_entity_died)
|
EventBus.entity_died.connect(_on_entity_died)
|
||||||
|
EventBus.damage_dealt.connect(_on_damage_dealt)
|
||||||
|
|
||||||
func _on_entity_died(entity: Node) -> void:
|
func _on_entity_died(entity: Node) -> void:
|
||||||
if entity == self:
|
if entity == self:
|
||||||
@@ -26,10 +27,29 @@ func _physics_process(delta: float) -> void:
|
|||||||
velocity.y -= gravity * delta
|
velocity.y -= gravity * delta
|
||||||
move_and_slide()
|
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:
|
func _on_detection_area_body_entered(body: Node3D) -> void:
|
||||||
if body is CharacterBody3D and body.name == "Player":
|
if body is CharacterBody3D and body.name == "Player":
|
||||||
target = body
|
_engage(body)
|
||||||
state = State.CHASE
|
|
||||||
EventBus.enemy_engaged.emit(self, body)
|
EventBus.enemy_engaged.emit(self, body)
|
||||||
|
|
||||||
func _on_detection_area_body_exited(body: Node3D) -> void:
|
func _on_detection_area_body_exited(body: Node3D) -> void:
|
||||||
|
|||||||
@@ -13,3 +13,4 @@ signal health_changed(entity, current, max_val)
|
|||||||
signal shield_changed(entity, current, max_val)
|
signal shield_changed(entity, current, max_val)
|
||||||
signal respawn_tick(timer)
|
signal respawn_tick(timer)
|
||||||
signal enemy_engaged(enemy, target)
|
signal enemy_engaged(enemy, target)
|
||||||
|
signal cooldown_tick(cooldowns, max_cooldowns, gcd_timer)
|
||||||
|
|||||||
@@ -1,28 +1,56 @@
|
|||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
|
const GCD_TIME := 1.0
|
||||||
|
|
||||||
@onready var player: CharacterBody3D = get_parent()
|
@onready var player: CharacterBody3D = get_parent()
|
||||||
@onready var targeting: Node = get_parent().get_node("Targeting")
|
@onready var targeting: Node = get_parent().get_node("Targeting")
|
||||||
@onready var player_class: Node = get_parent().get_node("PlayerClass")
|
@onready var player_class: Node = get_parent().get_node("PlayerClass")
|
||||||
|
|
||||||
var abilities: Array = []
|
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:
|
func _ready() -> void:
|
||||||
_load_abilities()
|
_load_abilities()
|
||||||
EventBus.class_changed.connect(_on_class_changed)
|
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:
|
func _load_abilities() -> void:
|
||||||
var ability_set: AbilitySet = player_class.get_ability_set()
|
var ability_set: AbilitySet = player_class.get_ability_set()
|
||||||
if ability_set:
|
if ability_set:
|
||||||
abilities = ability_set.abilities
|
abilities = ability_set.abilities
|
||||||
else:
|
else:
|
||||||
abilities = []
|
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:
|
func _unhandled_input(event: InputEvent) -> void:
|
||||||
for i in range(min(abilities.size(), 5)):
|
for i in range(min(abilities.size(), 5)):
|
||||||
if event.is_action_pressed("ability_%s" % (i + 1)) and abilities[i]:
|
if event.is_action_pressed("ability_%s" % (i + 1)) and abilities[i]:
|
||||||
if abilities[i].type == Ability.Type.PASSIVE:
|
if abilities[i].type == Ability.Type.PASSIVE:
|
||||||
return
|
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
|
return
|
||||||
|
|
||||||
func apply_passive(base_damage: float) -> float:
|
func apply_passive(base_damage: float) -> float:
|
||||||
|
|||||||
@@ -1,11 +1,20 @@
|
|||||||
extends CanvasLayer
|
extends CanvasLayer
|
||||||
|
|
||||||
|
const GCD_TIME := 1.0
|
||||||
|
|
||||||
@onready var health_bar: ProgressBar = $HealthBar
|
@onready var health_bar: ProgressBar = $HealthBar
|
||||||
@onready var shield_bar: ProgressBar = $ShieldBar
|
@onready var shield_bar: ProgressBar = $ShieldBar
|
||||||
@onready var respawn_label: Label = $RespawnTimer
|
@onready var respawn_label: Label = $RespawnTimer
|
||||||
@onready var class_icon: Label = $AbilityBar/ClassIcon/Label
|
@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:
|
func _ready() -> void:
|
||||||
respawn_label.visible = false
|
respawn_label.visible = false
|
||||||
@@ -15,6 +24,7 @@ func _ready() -> void:
|
|||||||
EventBus.player_respawned.connect(_on_player_respawned)
|
EventBus.player_respawned.connect(_on_player_respawned)
|
||||||
EventBus.class_changed.connect(_on_class_changed)
|
EventBus.class_changed.connect(_on_class_changed)
|
||||||
EventBus.respawn_tick.connect(_on_respawn_tick)
|
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:
|
func _on_health_changed(entity: Node, current: float, max_val: float) -> void:
|
||||||
if entity.name == "Player":
|
if entity.name == "Player":
|
||||||
@@ -41,3 +51,22 @@ func _on_class_changed(_player: Node, class_type: int) -> void:
|
|||||||
0: class_icon.text = "T"
|
0: class_icon.text = "T"
|
||||||
1: class_icon.text = "D"
|
1: class_icon.text = "D"
|
||||||
2: class_icon.text = "H"
|
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]
|
||||||
|
|||||||
Reference in New Issue
Block a user