# Player.gd # Steuert den Spielercharakter: Bewegung, Kamera, HP, Angriff, Zielauswahl extends CharacterBody3D const SPEED = 5.0 const JUMP_VELOCITY = 4.5 const GRAVITY = 9.8 var max_hp = 100 var current_hp = 100 var target = null # Aktuell markierter Gegner var equipped_weapon = null # Ausgerüstete Waffe (null = unbewaffnet, Schaden = 1) # Global Cooldown System (GCD) var global_cooldown = 0.0 const GLOBAL_COOLDOWN_TIME = 1.5 # GCD in Sekunden var autoattack_active = false # Ob Autoattack nach GCD weiterlaufen soll # Skills System - individuelle Cooldowns (zusätzlich zum GCD) var heavy_strike_cooldown = 0.0 const HEAVY_STRIKE_DAMAGE_MIN = 10 const HEAVY_STRIKE_DAMAGE_MAX = 15 const HEAVY_STRIKE_COOLDOWN = 3.0 const HEAVY_STRIKE_RANGE = 2.0 @onready var camera_pivot = $CameraPivot @onready var camera = $CameraPivot/Camera3D @onready var hud = $HUD func _ready(): hud.update_health(current_hp, max_hp) hud.set_active_slot(0) # Icons für Skills setzen hud.set_slot_icon(0, "res://icons/autoattack_icon.svg") # Slot 1: Autoattack hud.set_slot_icon(1, "res://icons/heavy_strike_icon.svg") # Slot 2: Heavy Strike # HUD-Klicks verbinden hud.slot_clicked.connect(_on_slot_clicked) # Handler für HUD-Slot-Klicks func _on_slot_clicked(slot_index: int): match slot_index: 0: # Autoattack manuell starten if target != null and global_cooldown <= 0: start_autoattack() perform_autoattack() 1: # Heavy Strike use_heavy_strike() # Schaden am Spieler abziehen und HP-Leiste aktualisieren func take_damage(amount): current_hp = clamp(current_hp - amount, 0, max_hp) hud.update_health(current_hp, max_hp) if current_hp <= 0: die() # HP heilen und HP-Leiste aktualisieren func heal(amount): current_hp = clamp(current_hp + amount, 0, max_hp) hud.update_health(current_hp, max_hp) func die(): print("Spieler gestorben!") # Schaden basierend auf ausgerüsteter Waffe (unbewaffnet = 1) func get_attack_damage() -> int: if equipped_weapon == null: return 1 return randi_range(equipped_weapon.min_damage, equipped_weapon.max_damage) # Reichweite basierend auf ausgerüsteter Waffe (unbewaffnet = 1.5) func get_attack_range() -> float: if equipped_weapon == null: return 1.5 return equipped_weapon.range # Angriffsgeschwindigkeit basierend auf ausgerüsteter Waffe (unbewaffnet = 1.5s) func get_attack_cooldown() -> float: if equipped_weapon == null: return 1.5 return equipped_weapon.attack_speed # Ziel markieren — start_attack=true startet sofort die Autoattack func set_target(new_target, start_attack: bool = false): if target != null and is_instance_valid(target): target.hide_health() target = new_target target.show_health() print("Ziel markiert: ", target.name) if start_attack: start_autoattack() # Autoattack aktivieren func start_autoattack(): autoattack_active = true print("Autoattack aktiviert") # Autoattack deaktivieren func stop_autoattack(): autoattack_active = false print("Autoattack deaktiviert") # Führt einen Autoattack aus (wird vom GCD-System aufgerufen) func perform_autoattack(): if target == null or not is_instance_valid(target): target = null autoattack_active = false return var distance = global_position.distance_to(target.global_position) if distance <= get_attack_range(): var dmg = get_attack_damage() target.take_damage(dmg) print("Autoattack: ", dmg, " Schaden") trigger_global_cooldown() else: print("Ziel zu weit entfernt für Autoattack") # Global Cooldown auslösen func trigger_global_cooldown(): global_cooldown = GLOBAL_COOLDOWN_TIME # Heavy Strike: Starker Angriff mit Cooldown func use_heavy_strike(): if target == null or not is_instance_valid(target): print("Kein Ziel für Heavy Strike!") return # GCD Check if global_cooldown > 0: print("Global Cooldown aktiv: ", "%.1f" % global_cooldown, "s") return # Skill-eigener Cooldown Check if heavy_strike_cooldown > 0: print("Heavy Strike noch im Cooldown: ", "%.1f" % heavy_strike_cooldown, "s") return var distance = global_position.distance_to(target.global_position) if distance > HEAVY_STRIKE_RANGE: print("Ziel zu weit entfernt für Heavy Strike!") return var damage = randi_range(HEAVY_STRIKE_DAMAGE_MIN, HEAVY_STRIKE_DAMAGE_MAX) target.take_damage(damage) heavy_strike_cooldown = HEAVY_STRIKE_COOLDOWN trigger_global_cooldown() start_autoattack() # Autoattack nach Skill automatisch aktivieren print("Heavy Strike! ", damage, " Schaden") # Raycast von der Kamera auf Mausposition — trifft Gegner mit take_damage() func _try_select_target(start_attack: bool = false): var space_state = get_world_3d().direct_space_state var viewport = get_viewport() var mouse_pos = viewport.get_mouse_position() var ray_origin = camera.project_ray_origin(mouse_pos) var ray_end = ray_origin + camera.project_ray_normal(mouse_pos) * 100.0 var query = PhysicsRayQueryParameters3D.create(ray_origin, ray_end) query.exclude = [self] var result = space_state.intersect_ray(query) if result and result.collider.has_method("take_damage"): set_target(result.collider, start_attack) func _physics_process(delta): # Global Cooldown herunterzählen var gcd_was_active = global_cooldown > 0 if global_cooldown > 0: global_cooldown -= delta # Wenn GCD gerade abgelaufen ist und Autoattack aktiv, führe Autoattack aus if gcd_was_active and global_cooldown <= 0 and autoattack_active: perform_autoattack() # Skill-Cooldowns herunterzählen if heavy_strike_cooldown > 0: heavy_strike_cooldown -= delta # HUD Cooldowns aktualisieren hud.set_slot_cooldown(0, global_cooldown) # Slot 1: GCD für Autoattack hud.set_slot_cooldown(1, max(global_cooldown, heavy_strike_cooldown)) # Slot 2: max(GCD, Skill-CD) # Schwerkraft if not is_on_floor(): velocity.y -= GRAVITY * delta # Springen if Input.is_action_just_pressed("ui_accept") and is_on_floor(): velocity.y = JUMP_VELOCITY # Linksklick: nur markieren if Input.is_action_just_pressed("select_target"): _try_select_target(false) # Rechtsklick: markieren + angreifen if Input.is_action_just_pressed("ui_right_mouse"): _try_select_target(true) # Aktionsleiste 1-9 if Input.is_action_just_pressed("action_1"): hud.set_active_slot(0) if target != null and global_cooldown <= 0: start_autoattack() perform_autoattack() if Input.is_action_just_pressed("action_2"): hud.set_active_slot(1) use_heavy_strike() if Input.is_action_just_pressed("action_3"): hud.set_active_slot(2) if Input.is_action_just_pressed("action_4"): hud.set_active_slot(3) if Input.is_action_just_pressed("action_5"): hud.set_active_slot(4) if Input.is_action_just_pressed("action_6"): hud.set_active_slot(5) if Input.is_action_just_pressed("action_7"): hud.set_active_slot(6) if Input.is_action_just_pressed("action_8"): hud.set_active_slot(7) if Input.is_action_just_pressed("action_9"): hud.set_active_slot(8) # TEST: T drücken = 10 Schaden if Input.is_action_just_pressed("test_damage"): take_damage(10) # Eingabe var input_dir = Vector2.ZERO if Input.is_action_pressed("move_forward"): input_dir.y -= 1 if Input.is_action_pressed("move_back"): input_dir.y += 1 if Input.is_action_pressed("move_left"): input_dir.x -= 1 if Input.is_action_pressed("move_right"): input_dir.x += 1 # Bewegung relativ zur Kamera var world_yaw = rotation.y + camera_pivot.rotation.y var forward = Vector3(-sin(world_yaw), 0, -cos(world_yaw)).normalized() var right = Vector3(cos(world_yaw), 0, -sin(world_yaw)).normalized() var direction = (forward * -input_dir.y + right * input_dir.x) velocity.x = direction.x * SPEED velocity.z = direction.z * SPEED # RMB gehalten: Spieler schaut in Kamerarichtung if Input.is_mouse_button_pressed(MOUSE_BUTTON_RIGHT): rotation.y = world_yaw camera_pivot.rotation.y = 0 move_and_slide()