155 lines
3.8 KiB
GDScript
155 lines
3.8 KiB
GDScript
class_name Unit
|
|
extends CharacterBody3D
|
|
|
|
# Konstante(n)
|
|
const ANIM_BLOCK_HIT := "Block_Hit"
|
|
const STAMINA_TICK_SECONDS := 0.1
|
|
# Exportierte Eigenschaften (balancing)
|
|
@export var maximum_health: int = 10
|
|
@export var maximum_stamina: int = 50
|
|
@export var stamina_regeneration_rate: int = 2
|
|
@export var speed: float = 4.0
|
|
@export var acceleration: float = 4.0
|
|
@export var jump_speed: float = 8.0
|
|
@export var jump_cost: int = 20
|
|
@export var attack_cost: int = 10
|
|
@export var damage: int = 1
|
|
@export var model: Node3D
|
|
@export var reward_gold: int = 3
|
|
|
|
# Animation
|
|
@onready var anim_tree: AnimationTree = $AnimationTree
|
|
@onready var anim_state: AnimationNodeStateMachinePlayback = anim_tree.get("parameters/playback")
|
|
|
|
# Animation-Sets
|
|
@export var hit_animations: PackedStringArray = ["Hit_A", "Hit_B"]
|
|
@export var death_animations: PackedStringArray = ["Death_A", "Death_B"]
|
|
# Zustände
|
|
enum States {
|
|
idle,
|
|
walking,
|
|
running,
|
|
chasing,
|
|
jumping,
|
|
attacking,
|
|
blocking,
|
|
dead,
|
|
aiming
|
|
}
|
|
# Signale
|
|
signal name_changed(unit_name: String)
|
|
signal health_changed(current_health: int, maximum_health: int)
|
|
signal stamina_changed(current_stamina: int, maximum_stamina: int)
|
|
signal state_changed(old_state: States, new_state: States)
|
|
signal unit_died(unit: Unit)
|
|
|
|
# Zufall für Animationsauswahl
|
|
var rng := RandomNumberGenerator.new()
|
|
|
|
# Interne Backing-Felder
|
|
var _state: States = States.idle
|
|
var _unit_name: String = ""
|
|
var _health: int = maximum_health
|
|
var _stamina: int = maximum_stamina
|
|
var gravity: float = ProjectSettings.get_setting("physics/3d/default_gravity")
|
|
|
|
# Eigenschaften mit validen Getter/Setter (ohne Rekursion)
|
|
var state: States:
|
|
get:
|
|
return _state
|
|
set(value):
|
|
if value == _state:
|
|
return
|
|
var old: States = _state
|
|
_state = value
|
|
state_changed.emit(old, _state)
|
|
|
|
@export var unit_name: String:
|
|
get:
|
|
return _unit_name
|
|
set(value):
|
|
_unit_name = value
|
|
name_changed.emit(_unit_name, str(_state))
|
|
|
|
var health: int:
|
|
get:
|
|
return _health
|
|
set(value):
|
|
if is_dead():
|
|
push_warning("Fehler in Health-Handling. Unit bereits tot!")
|
|
return
|
|
_health = clampi(value, 0, maximum_health)
|
|
health_changed.emit(_health, maximum_health)
|
|
if hit_animations.size() > 0 and _health > 0:
|
|
anim_state.travel(random_string(hit_animations))
|
|
if _health <= 0:
|
|
die()
|
|
|
|
var stamina: int:
|
|
get:
|
|
return _stamina
|
|
set(value):
|
|
_stamina = clampi(value, 0, maximum_stamina)
|
|
stamina_changed.emit(_stamina, maximum_stamina)
|
|
|
|
# Stamina Regeneration
|
|
var stamina_timer: float = 0.0
|
|
|
|
func _process(delta: float) -> void:
|
|
stamina_timer += delta
|
|
if stamina_timer >= STAMINA_TICK_SECONDS:
|
|
stamina += stamina_regeneration_rate
|
|
stamina_timer = 0.0
|
|
|
|
|
|
func is_dead() -> bool:
|
|
return _state == States.dead
|
|
|
|
|
|
func take_damage(damage_amount: int) -> void:
|
|
if is_dead():
|
|
print(_unit_name + " kann keinen Schaden nehmen -> bereits tot!")
|
|
return
|
|
if _state == States.blocking:
|
|
anim_state.travel(ANIM_BLOCK_HIT)
|
|
return
|
|
health = _health - damage_amount
|
|
|
|
|
|
func can_spend_stamina(stamina_needed: int) -> bool:
|
|
return stamina_needed <= _stamina
|
|
|
|
|
|
func spend_stamina(stamina_needed: int) -> void:
|
|
stamina = _stamina - stamina_needed
|
|
|
|
|
|
func die() -> void:
|
|
if not is_dead():
|
|
state = States.dead
|
|
unit_died.emit(self)
|
|
if death_animations.size() > 0:
|
|
anim_state.travel(random_string(death_animations))
|
|
# Erhöhe das Gold des Spielers beim Tod dieses Targets
|
|
if reward_gold > 0 and GameManager:
|
|
GameManager.increase_player_gold(reward_gold)
|
|
else:
|
|
print(_unit_name + " kann nicht sterben -> bereits tot!")
|
|
|
|
|
|
func remove_unit() -> void:
|
|
if self is Player:
|
|
var player := self as Player
|
|
player.player_game_over()
|
|
else:
|
|
queue_free()
|
|
|
|
# Hilfsfunktion: zufälliger String aus PackedStringArray
|
|
func random_string(arr: PackedStringArray) -> String:
|
|
if arr.is_empty():
|
|
return ""
|
|
if rng.seed == 0:
|
|
rng.randomize()
|
|
var idx := rng.randi_range(0, arr.size() - 1)
|
|
return arr[idx]
|