From 0a1c7cd8352b831776a886de9443e9dba6aff6d4 Mon Sep 17 00:00:00 2001 From: Mario Date: Wed, 23 Sep 2026 20:58:33 +0200 Subject: [PATCH] Ausdauer System implementiert --- .../game/control/PlayerInputControl.java | 77 ++++++++++++++++++- .../java/de/blight/game/scene/WorldScene.java | 18 +++++ 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/blight-game/src/main/java/de/blight/game/control/PlayerInputControl.java b/blight-game/src/main/java/de/blight/game/control/PlayerInputControl.java index 9cf16b4..6775a16 100644 --- a/blight-game/src/main/java/de/blight/game/control/PlayerInputControl.java +++ b/blight-game/src/main/java/de/blight/game/control/PlayerInputControl.java @@ -56,6 +56,14 @@ public class PlayerInputControl { private static final float LUNGE_FRAC_END = 0.55f; /** AnimComposer-Layer-Name für die prozedurale Ziehen/Wegstecken-Animation (Oberarm-Overlay). */ private static final String DRAW_LAYER = "__draw_weapon__"; + + // ── Ausdauer ────────────────────────────────────────────────────────────── + private static final float STAMINA_REGEN_PER_SEC = 5f; + private static final float STAMINA_SPRINT_PER_SEC = 10f; + private static final int STAMINA_ATTACK_COST = 10; + private static final int STAMINA_ATTACK5_COST = 20; + private static final int STAMINA_HEAVY_COST = 30; + private static final int STAMINA_BLOCK_HIT_COST = 30; /** Dauer der Ziehen/Wegstecken-Animation in Sekunden. */ private static final float DRAW_DURATION = 0.85f; // Ziel-Quaternions für die Ziehen-Animation: aus Heavy-Attack-Wind-Up bei t=0.933s gelesen @@ -147,6 +155,8 @@ public class PlayerInputControl { private static final float ATTACK5_SOUND_MIN_PROGRESS = 0.35f; private boolean attackSoundFired = false; private int currentComboStep = 0; + private float staminaAccum = 0f; // Fließkomma-Übertrag für kontinuierliche Ausdauer-Änderungen + private boolean staminaExhausted = false; // Sprint gesperrt bis 25% Ausdauer wiederhergestellt private final Vector3f lastGripPos = new Vector3f(); private final Vector3f scratchGripVel = new Vector3f(); // Draw/Sheath Animation System @@ -793,10 +803,17 @@ public class PlayerInputControl { // Schon ab WAITING Gehgeschwindigkeit erzwingen, damit beim Blockstart kein Sprint-Frame entsteht boolean inBlockStance = isBlocking() || blockPhase == BlockPhase.WAITING; + // Ausdauer: Regen + Sprint-Verbrauch + boolean isSprinting = sprint && !weaponDrawn && moving && !staminaExhausted; + tickStamina(tpf, isSprinting); + if (moving) { scratchMoveDir.normalizeLocal(); + boolean canSprint = sprint && !weaponDrawn && !staminaExhausted + && (mainCharacter == null || mainCharacter.getMaxStamina() <= 0 + || mainCharacter.getCurrentStamina() > 0); float speed = (inDeepWater || walk || inBlockStance) ? MOVE_SPEED * WALK_MULT - : (sprint && !weaponDrawn) ? MOVE_SPEED * SPRINT_MULT + : canSprint ? MOVE_SPEED * SPRINT_MULT : MOVE_SPEED; physicsChar.setWalkDirection(scratchWalk.set(scratchMoveDir).multLocal(speed)); @@ -1041,6 +1058,8 @@ public class PlayerInputControl { if (isMeleeType(drawnWeaponType)) { int step = comboStep; boolean isComboFollow = comboWindowActive; // war Kombo-Fenster offen? + int staminaCost = (step == 4) ? STAMINA_ATTACK5_COST : STAMINA_ATTACK_COST; + if (!spendStamina(staminaCost)) return; AnimationAction atk = getMeleeComboAction(drawnWeaponType, step); lastAttackCritical = (step == 4); currentAttackId = ++attackIdCounter; @@ -1197,6 +1216,61 @@ public class PlayerInputControl { || blockPhase == BlockPhase.EXITING; } + /** Wird aufgerufen wenn ein blockierter Treffer landet. Kostet 30 Ausdauer; unter 0 → Block bricht. */ + public void triggerBlockImpact() { + if (blockPhase != BlockPhase.HOLDING || drawnWeaponType == null) return; + if (mainCharacter != null && mainCharacter.getMaxStamina() > 0) { + if (mainCharacter.getCurrentStamina() < STAMINA_BLOCK_HIT_COST) { + mainCharacter.setCurrentStamina(0); + startBlockExit(); + return; + } + mainCharacter.setCurrentStamina(mainCharacter.getCurrentStamina() - STAMINA_BLOCK_HIT_COST); + } + AnimationAction impact = getMeleeBlockImpactAction(drawnWeaponType); + float dur = resolveClipLength(impact, 0.8f); + hitWindowActive = false; + attackSoundFired = true; + combatAnimActive = true; + combatAnimRemaining = Math.max(0.1f, dur - COMBAT_BLEND); + combatAnimDuration = dur; + combatAnimCallback = null; + combatAnimSkipComboWindow = true; + nextTransitionLength = COMBAT_BLEND; + playAction(impact); + currentAnim = impact; + } + + // ── Ausdauer-Hilfsmethoden ──────────────────────────────────────────────── + + private void tickStamina(float tpf, boolean isSprinting) { + if (mainCharacter == null || mainCharacter.getMaxStamina() <= 0) return; + float delta = STAMINA_REGEN_PER_SEC * tpf; + if (isSprinting) delta -= STAMINA_SPRINT_PER_SEC * tpf; + staminaAccum += delta; + int intPart = (int) staminaAccum; + if (intPart != 0) { + staminaAccum -= intPart; + int cur = mainCharacter.getCurrentStamina() + intPart; + mainCharacter.setCurrentStamina(Math.max(0, Math.min(mainCharacter.getMaxStamina(), cur))); + } + int stamina = mainCharacter.getCurrentStamina(); + int max = mainCharacter.getMaxStamina(); + if (!staminaExhausted && stamina == 0) { + staminaExhausted = true; + } else if (staminaExhausted && stamina >= max / 4) { + staminaExhausted = false; + } + } + + /** Zieht {@code cost} Ausdauer ab. Gibt false zurück (Aktion verweigert) wenn nicht genug vorhanden. */ + private boolean spendStamina(int cost) { + if (mainCharacter == null || mainCharacter.getMaxStamina() <= 0) return true; + if (mainCharacter.getCurrentStamina() < cost) return false; + mainCharacter.setCurrentStamina(mainCharacter.getCurrentStamina() - cost); + return true; + } + private boolean hasShieldEquipped() { return mainCharacter != null && mainCharacter.getEquippedShield() != null; } @@ -1394,6 +1468,7 @@ public class PlayerInputControl { if (drawnWeaponType == null || !isMeleeType(drawnWeaponType)) { return; } + if (!spendStamina(STAMINA_HEAVY_COST)) return; comboStep = 0; comboWindowActive = false; AnimationAction action = getMeleeHeavyAttackAction(drawnWeaponType); diff --git a/blight-game/src/main/java/de/blight/game/scene/WorldScene.java b/blight-game/src/main/java/de/blight/game/scene/WorldScene.java index 9ee8837..df1a904 100644 --- a/blight-game/src/main/java/de/blight/game/scene/WorldScene.java +++ b/blight-game/src/main/java/de/blight/game/scene/WorldScene.java @@ -365,6 +365,24 @@ public class WorldScene extends BaseAppState { MainCharacter mc = findMainCharacter(); if (mc != null) { + if (mc.getMaxHp() <= 0) { + mc.setMaxHp(100); + mc.setCurrentHp(100); + } else if (mc.getCurrentHp() <= 0) { + mc.setCurrentHp(mc.getMaxHp()); + } + if (mc.getMaxMana() <= 0) { + mc.setMaxMana(100); + mc.setCurrentMana(100); + } else if (mc.getCurrentMana() <= 0) { + mc.setCurrentMana(mc.getMaxMana()); + } + if (mc.getMaxStamina() <= 0) { + mc.setMaxStamina(100); + mc.setCurrentStamina(100); + } else if (mc.getCurrentStamina() <= 0) { + mc.setCurrentStamina(mc.getMaxStamina()); + } playerInput.setMainCharacter(mc); var wac = new de.blight.game.control.WeaponAttachmentControl(assetManager, playerInput, mc); character.addControl(wac);