feat: Zornfesseln implementiert (Krieger Level 28)

- Zieht Gegner in Reichweite 12m heran (Pull-Effekt 0.6s)
- Nach dem Pull: automatischer Schlag 20-30 + 80% Stärke
- enemy.gd: apply_pull() + Pull überschreibt KI-Bewegung
- Kostet 35 Wut, Cooldown 14s

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Andre 2026-03-19 21:37:19 +01:00
parent c81ba9760b
commit 63d0f3e64d
2 changed files with 78 additions and 0 deletions

View file

@ -54,6 +54,11 @@ var is_dead: bool = false
var _slow_timer: float = 0.0 var _slow_timer: float = 0.0
var _slow_factor: float = 1.0 # 1.0 = normal, 0.5 = 50% langsamer var _slow_factor: float = 1.0 # 1.0 = normal, 0.5 = 50% langsamer
# Pull (Zornfesseln)
var _pull_target: Node3D = null
var _pull_timer: float = 0.0
const PULL_SPEED: float = 18.0
# Patrol # Patrol
@export var patrol_radius: float = 8.0 @export var patrol_radius: float = 8.0
@export var patrol_speed: float = 1.5 # Laufgeschwindigkeit beim Patrouillieren @export var patrol_speed: float = 1.5 # Laufgeschwindigkeit beim Patrouillieren
@ -229,6 +234,23 @@ func _physics_process(delta):
if _slow_timer <= 0.0: if _slow_timer <= 0.0:
_slow_factor = 1.0 _slow_factor = 1.0
# Pull-Effekt (Zornfesseln)
if _pull_timer > 0.0 and _pull_target != null and is_instance_valid(_pull_target):
_pull_timer -= delta
var dir = (_pull_target.global_position - global_position)
dir.y = 0
if dir.length() > 1.0:
dir = dir.normalized()
velocity.x = dir.x * PULL_SPEED
velocity.z = dir.z * PULL_SPEED
else:
_pull_timer = 0.0 # Nah genug — Pull beendet
# Pull aktiv → normale KI überspringen
if _pull_timer > 0.0:
move_and_slide()
return
# Kein Ziel → Spieler suchen # Kein Ziel → Spieler suchen
if target == null or not is_instance_valid(target): if target == null or not is_instance_valid(target):
target = null target = null
@ -416,6 +438,11 @@ func _perform_attack():
_play_anim("autoattack") _play_anim("autoattack")
print(name + " greift an: " + str(damage) + " Schaden") print(name + " greift an: " + str(damage) + " Schaden")
func apply_pull(source: Node3D, duration: float):
_pull_target = source
_pull_timer = duration
state = State.CHASING # Aggro auslösen
func apply_slow(factor: float, duration: float): func apply_slow(factor: float, duration: float):
_slow_factor = clamp(factor, 0.1, 1.0) _slow_factor = clamp(factor, 0.1, 1.0)
_slow_timer = duration _slow_timer = duration

View file

@ -97,6 +97,10 @@ var is_defending: bool = false
var _defend_timer: float = 0.0 var _defend_timer: float = 0.0
var _defend_mode: String = "" # "schildwall" oder "trotz" var _defend_mode: String = "" # "schildwall" oder "trotz"
# Zornfesseln — verzögerter Schlag nach Pull
var _zornfesseln_target = null
var _zornfesseln_strike_timer: float = 0.0
# Blutrausch — Blutungs-DOT # Blutrausch — Blutungs-DOT
var _bleed_target = null var _bleed_target = null
var _bleed_timer: float = 0.0 # verbleibende Dauer var _bleed_timer: float = 0.0 # verbleibende Dauer
@ -155,6 +159,10 @@ const BLUTRAUSCH_DOT_DAMAGE: int = 4 # Schaden pro Tick
const WIRBELWIND_COOLDOWN: float = 6.0 const WIRBELWIND_COOLDOWN: float = 6.0
const WIRBELWIND_RADIUS: float = 3.5 const WIRBELWIND_RADIUS: float = 3.5
const WIRBELWIND_RAGE: int = 40 const WIRBELWIND_RAGE: int = 40
const ZORNFESSELN_COOLDOWN: float = 14.0
const ZORNFESSELN_RANGE: float = 12.0 # Max Zielreichweite
const ZORNFESSELN_RAGE: int = 35
const ZORNFESSELN_PULL_DURATION: float = 0.6 # Sekunden bis Gegner da ist
# Cast System # Cast System
var is_casting: bool = false var is_casting: bool = false
@ -296,6 +304,15 @@ func _init_class_skills():
"cooldown": TEKTONISCHER_SCHLAG_COOLDOWN, "cooldown": TEKTONISCHER_SCHLAG_COOLDOWN,
"cast_time": 0.0, "cast_time": 0.0,
}) })
if level >= 28:
available_skills.append({
"id": "zornfesseln",
"name": "Zornfesseln",
"description": "Reißt einen Gegner heran und schlägt ihn mit voller Wucht.",
"icon": "res://icons/zornfesseln_icon.svg",
"cooldown": ZORNFESSELN_COOLDOWN,
"cast_time": 0.0,
})
if level >= 20: if level >= 20:
available_skills.append({ available_skills.append({
"id": "wirbelwind", "id": "wirbelwind",
@ -680,6 +697,10 @@ func execute_skill(skill_id: String):
if current_resource < WIRBELWIND_RAGE: if current_resource < WIRBELWIND_RAGE:
print("Zu wenig Wut!") print("Zu wenig Wut!")
return return
if skill_id == "zornfesseln":
if current_resource < ZORNFESSELN_RAGE:
print("Zu wenig Wut!")
return
# Cast-Zeit? # Cast-Zeit?
if skill["cast_time"] > 0: if skill["cast_time"] > 0:
@ -720,6 +741,8 @@ func _apply_skill(skill_id: String):
_do_blutrausch() _do_blutrausch()
"wirbelwind": "wirbelwind":
_do_wirbelwind() _do_wirbelwind()
"zornfesseln":
_do_zornfesseln()
# ─── Autoattack ─────────────────────────────────────────────── # ─── Autoattack ───────────────────────────────────────────────
@ -812,6 +835,24 @@ func _do_wirbelwind():
trigger_global_cooldown() trigger_global_cooldown()
print("Wirbelwind! %d Gegner getroffen" % hit_count) print("Wirbelwind! %d Gegner getroffen" % hit_count)
# ─── Zornfesseln ──────────────────────────────────────────────
func _do_zornfesseln():
if target == null or not is_instance_valid(target):
return
var dist = global_position.distance_to(target.global_position)
if dist > ZORNFESSELN_RANGE:
print("Ziel zu weit für Zornfesseln!")
return
if not _spend_rage(ZORNFESSELN_RAGE):
return
target.apply_pull(self, ZORNFESSELN_PULL_DURATION)
_zornfesseln_target = target
_zornfesseln_strike_timer = ZORNFESSELN_PULL_DURATION
skill_cooldowns["zornfesseln"] = ZORNFESSELN_COOLDOWN
trigger_global_cooldown()
print("Zornfesseln! Gegner wird herangezogen...")
# ─── Durchbeißen / Schildwall / Trotz ──────────────────────── # ─── Durchbeißen / Schildwall / Trotz ────────────────────────
# ─── Blutrausch ─────────────────────────────────────────────── # ─── Blutrausch ───────────────────────────────────────────────
@ -1168,6 +1209,16 @@ func _physics_process(delta):
# ── Wut-Verfall ─────────────────────────────────────────── # ── Wut-Verfall ───────────────────────────────────────────
_update_rage_decay(delta) _update_rage_decay(delta)
# ── Zornfesseln Schlag ────────────────────────────────────
if _zornfesseln_strike_timer > 0.0:
_zornfesseln_strike_timer -= delta
if _zornfesseln_strike_timer <= 0.0:
if _zornfesseln_target != null and is_instance_valid(_zornfesseln_target):
var damage = randi_range(20, 30) + int(strength * 0.8)
_zornfesseln_target.take_damage(damage)
print("Zornfesseln Schlag! %d Schaden" % damage)
_zornfesseln_target = null
# ── Blutungs-DOT ────────────────────────────────────────── # ── Blutungs-DOT ──────────────────────────────────────────
if _bleed_timer > 0.0: if _bleed_timer > 0.0:
_bleed_timer -= delta _bleed_timer -= delta