Schleichen hinzugefügt, Bugs bei den Sounds behoben

This commit is contained in:
2026-09-24 16:37:55 +02:00
parent f457b897bd
commit 94e0cac10d
12 changed files with 104 additions and 17 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -26,7 +26,11 @@
"Sword And Shield Strafe Right", "Sword And Shield Strafe Right",
"Sword And Shield Heavy Attack", "Sword And Shield Heavy Attack",
"Picking Up", "Picking Up",
"Roll" "Roll",
"Crouched To Standing",
"Crouching Idle",
"Sneaking Forward",
"Standing To Crouched"
], ],
"actionMap": { "actionMap": {
"DEFAULT": "Idle", "DEFAULT": "Idle",
@@ -55,7 +59,11 @@
"SWORD_BLOCK_STRAFE_RIGHT": "Sword And Shield Strafe Right", "SWORD_BLOCK_STRAFE_RIGHT": "Sword And Shield Strafe Right",
"SWORD_HEAVY_ATTACK": "Sword And Shield Heavy Attack", "SWORD_HEAVY_ATTACK": "Sword And Shield Heavy Attack",
"PICK_UP": "Picking Up", "PICK_UP": "Picking Up",
"ROLL": "Roll" "ROLL": "Roll",
"DUCK": "Standing To Crouched",
"CROUCH_IDLE": "Crouching Idle",
"SNEAK": "Sneaking Forward",
"CROUCH_TO_STAND": "Crouched To Standing"
}, },
"previewModelPath": "Characters/Models/hero.j3o", "previewModelPath": "Characters/Models/hero.j3o",
"animOffsets": { "animOffsets": {
@@ -116,6 +124,7 @@
"Sword And Shield Strafe Left", "Sword And Shield Strafe Left",
"Sword And Shield Strafe Right", "Sword And Shield Strafe Right",
"Sword And Shield Heavy Attack", "Sword And Shield Heavy Attack",
"Roll" "Roll",
"Sneaking Forward"
] ]
} }

View File

@@ -16,6 +16,9 @@ public enum AnimationAction {
RUNNING_JUMP, RUNNING_JUMP,
ROLL, ROLL,
DUCK, DUCK,
CROUCH_IDLE,
CROUCH_TO_STAND,
SNEAK,
PICK_UP, PICK_UP,
LIE_DOWN, LIE_DOWN,
LIE_UP, LIE_UP,
@@ -80,7 +83,10 @@ public enum AnimationAction {
case JUMP -> TextRegistry.resolve(null, key, "Jump"); case JUMP -> TextRegistry.resolve(null, key, "Jump");
case RUNNING_JUMP -> TextRegistry.resolve(null, key, "Running Jump"); case RUNNING_JUMP -> TextRegistry.resolve(null, key, "Running Jump");
case ROLL -> TextRegistry.resolve(null, key, "Rolle"); case ROLL -> TextRegistry.resolve(null, key, "Rolle");
case DUCK -> TextRegistry.resolve(null, key, "Duck"); case DUCK -> TextRegistry.resolve(null, key, "Hinducken");
case CROUCH_IDLE -> TextRegistry.resolve(null, key, "Ducken (Idle)");
case CROUCH_TO_STAND -> TextRegistry.resolve(null, key, "Aufstehen (Ducken)");
case SNEAK -> TextRegistry.resolve(null, key, "Schleichen");
case PICK_UP -> TextRegistry.resolve(null, key, "Pick up"); case PICK_UP -> TextRegistry.resolve(null, key, "Pick up");
case LIE_DOWN -> TextRegistry.resolve(null, key, "Hinlegen"); case LIE_DOWN -> TextRegistry.resolve(null, key, "Hinlegen");
case LIE_UP -> TextRegistry.resolve(null, key, "Aufstehen (Bett)"); case LIE_UP -> TextRegistry.resolve(null, key, "Aufstehen (Bett)");

View File

@@ -23,6 +23,7 @@ public class KeyBindings {
public int drawRangedWeapon = KeyInput.KEY_2; public int drawRangedWeapon = KeyInput.KEY_2;
public int sheathWeapon = KeyInput.KEY_X; public int sheathWeapon = KeyInput.KEY_X;
public int roll = KeyInput.KEY_LCONTROL; public int roll = KeyInput.KEY_LCONTROL;
public int sneak = KeyInput.KEY_X;
/** Metadaten für die Config-UI: Feldname im Objekt + Anzeigename. */ /** Metadaten für die Config-UI: Feldname im Objekt + Anzeigename. */
public static final String[][] ENTRIES = { public static final String[][] ENTRIES = {
@@ -44,6 +45,7 @@ public class KeyBindings {
{"drawRangedWeapon", "Fernkampfwaffe ziehen"}, {"drawRangedWeapon", "Fernkampfwaffe ziehen"},
{"sheathWeapon", "Waffe wegstecken"}, {"sheathWeapon", "Waffe wegstecken"},
{"roll", "Rolle"}, {"roll", "Rolle"},
{"sneak", "Schleichen"},
}; };
public int get(String fieldName) { public int get(String fieldName) {

View File

@@ -103,11 +103,12 @@ public class PlayerInputControl {
private static final String ACT_COMBAT_ATK = "CombatAttack"; private static final String ACT_COMBAT_ATK = "CombatAttack";
private static final String ACT_COMBAT_ALT = "CombatAltAction"; private static final String ACT_COMBAT_ALT = "CombatAltAction";
private static final String ACT_ROLL = "Roll"; private static final String ACT_ROLL = "Roll";
private static final String ACT_SNEAK = "Sneak";
private static final String[] ACTION_NAMES = { private static final String[] ACTION_NAMES = {
"Forward", "Backward", "Left", "Right", "Jump", "Sprint", "Walk", "Forward", "Backward", "Left", "Right", "Jump", "Sprint", "Walk",
ACT_DRAW_MELEE, ACT_DRAW_RANGED, ACT_SHEATH, ACT_COMBAT_ATK, ACT_COMBAT_ALT, ACT_DRAW_MELEE, ACT_DRAW_RANGED, ACT_SHEATH, ACT_COMBAT_ATK, ACT_COMBAT_ALT,
ACT_ROLL ACT_ROLL, ACT_SNEAK
}; };
private final InputManager inputManager; private final InputManager inputManager;
@@ -273,6 +274,12 @@ public class PlayerInputControl {
/** Autopilot: wenn gesetzt, geht der Charakter automatisch in diese (normalisierte) Richtung. */ /** Autopilot: wenn gesetzt, geht der Charakter automatisch in diese (normalisierte) Richtung. */
private Vector3f autopilotDir = null; private Vector3f autopilotDir = null;
// ── Schleichen / Ducken ───────────────────────────────────────────────────
private enum SneakPhase { NONE, DOWN, ACTIVE, UP }
private SneakPhase sneakPhase = SneakPhase.NONE;
private float sneakTransitionRemaining = 0f;
private boolean sneakKeyHeld = false;
// ── Dodge Roll ──────────────────────────────────────────────────────────── // ── Dodge Roll ────────────────────────────────────────────────────────────
private boolean rollActive = false; private boolean rollActive = false;
private float rollRemaining = 0f; private float rollRemaining = 0f;
@@ -334,6 +341,7 @@ public class PlayerInputControl {
} }
case ACT_COMBAT_ALT -> handleCombatAlt(isPressed); case ACT_COMBAT_ALT -> handleCombatAlt(isPressed);
case ACT_ROLL -> { if (isPressed) triggerRollFromInput(); } case ACT_ROLL -> { if (isPressed) triggerRollFromInput(); }
case ACT_SNEAK -> { if (!weaponDrawn) handleSneakKey(isPressed); }
} }
}; };
@@ -712,6 +720,7 @@ public class PlayerInputControl {
inputManager.addMapping(ACT_COMBAT_ATK, new MouseButtonTrigger(MouseInput.BUTTON_LEFT)); inputManager.addMapping(ACT_COMBAT_ATK, new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
inputManager.addMapping(ACT_COMBAT_ALT, new MouseButtonTrigger(MouseInput.BUTTON_RIGHT)); inputManager.addMapping(ACT_COMBAT_ALT, new MouseButtonTrigger(MouseInput.BUTTON_RIGHT));
inputManager.addMapping(ACT_ROLL, new KeyTrigger(kb.roll)); inputManager.addMapping(ACT_ROLL, new KeyTrigger(kb.roll));
inputManager.addMapping(ACT_SNEAK, new KeyTrigger(kb.sneak));
inputManager.addListener(actionListener, ACTION_NAMES); inputManager.addListener(actionListener, ACTION_NAMES);
} }
@@ -844,6 +853,25 @@ public class PlayerInputControl {
} }
} }
// ── Sneak-Übergangs-Phasen: Timer ticken, Bewegung läuft weiter (walk speed) ──
if (sneakPhase == SneakPhase.DOWN) {
sneakTransitionRemaining -= tpf;
if (sneakTransitionRemaining <= 0f) {
if (sneakKeyHeld) {
sneakPhase = SneakPhase.ACTIVE;
currentAnim = null;
} else {
startSneakUp();
}
}
} else if (sneakPhase == SneakPhase.UP) {
sneakTransitionRemaining -= tpf;
if (sneakTransitionRemaining <= 0f) {
sneakPhase = SneakPhase.NONE;
currentAnim = null;
}
}
// Ruhezustand (liegen / sitzen) – blockiert alle Bewegungseingaben // Ruhezustand (liegen / sitzen) – blockiert alle Bewegungseingaben
if (lockedInPlace) { if (lockedInPlace) {
physicsChar.setWalkDirection(Vector3f.ZERO); physicsChar.setWalkDirection(Vector3f.ZERO);
@@ -886,16 +914,19 @@ public class PlayerInputControl {
// Schon ab WAITING Gehgeschwindigkeit erzwingen, damit beim Blockstart kein Sprint-Frame entsteht // Schon ab WAITING Gehgeschwindigkeit erzwingen, damit beim Blockstart kein Sprint-Frame entsteht
boolean inBlockStance = isBlocking() || blockPhase == BlockPhase.WAITING; boolean inBlockStance = isBlocking() || blockPhase == BlockPhase.WAITING;
boolean sneaking = sneakPhase != SneakPhase.NONE;
// Ausdauer: Regen + Sprint-Verbrauch // Ausdauer: Regen + Sprint-Verbrauch
boolean isSprinting = sprint && !weaponDrawn && moving && !staminaExhausted; boolean isSprinting = sprint && !weaponDrawn && moving && !staminaExhausted && !sneaking;
tickStamina(tpf, isSprinting); tickStamina(tpf, isSprinting);
if (moving) { if (moving) {
scratchMoveDir.normalizeLocal(); scratchMoveDir.normalizeLocal();
boolean canSprint = sprint && !weaponDrawn && !staminaExhausted boolean canSprint = !sneaking && sprint && !weaponDrawn && !staminaExhausted
&& (mainCharacter == null || mainCharacter.getMaxStamina() <= 0 && (mainCharacter == null || mainCharacter.getMaxStamina() <= 0
|| mainCharacter.getCurrentStamina() > 0); || mainCharacter.getCurrentStamina() > 0);
float speed = (inDeepWater || walk || inBlockStance) ? MOVE_SPEED * WALK_MULT float speed = sneaking ? MOVE_SPEED * WALK_MULT
: (inDeepWater || walk || inBlockStance) ? MOVE_SPEED * WALK_MULT
: canSprint ? MOVE_SPEED * SPRINT_MULT : canSprint ? MOVE_SPEED * SPRINT_MULT
: MOVE_SPEED; : MOVE_SPEED;
physicsChar.setWalkDirection(scratchWalk.set(scratchMoveDir).multLocal(speed)); physicsChar.setWalkDirection(scratchWalk.set(scratchMoveDir).multLocal(speed));
@@ -1038,10 +1069,14 @@ public class PlayerInputControl {
if (groundGraceFrames > 0) groundGraceFrames--; if (groundGraceFrames > 0) groundGraceFrames--;
// Block-Phase verwaltet ihre eigenen Animationen. // Block-Phase verwaltet ihre eigenen Animationen.
if (!combatAnimActive && !isBlocking()) { // Während Sneak-Übergang (DOWN/UP) läuft die Transitions-Animation durch – nicht überschreiben.
if (!combatAnimActive && !isBlocking()
&& sneakPhase != SneakPhase.DOWN && sneakPhase != SneakPhase.UP) {
AnimationAction target; AnimationAction target;
if (jumpFrames > 0 || (!physicsChar.onGround() && groundGraceFrames <= 0)) { if (jumpFrames > 0 || (!physicsChar.onGround() && groundGraceFrames <= 0)) {
target = moving ? AnimationAction.RUNNING_JUMP : AnimationAction.JUMP; target = moving ? AnimationAction.RUNNING_JUMP : AnimationAction.JUMP;
} else if (sneaking) {
target = moving ? AnimationAction.SNEAK : AnimationAction.CROUCH_IDLE;
} else if (moving) { } else if (moving) {
target = (inDeepWater || walk) ? AnimationAction.WALK target = (inDeepWater || walk) ? AnimationAction.WALK
: sprint ? AnimationAction.SPRINT : sprint ? AnimationAction.SPRINT
@@ -1365,6 +1400,31 @@ public class PlayerInputControl {
return true; return true;
} }
private void handleSneakKey(boolean pressed) {
sneakKeyHeld = pressed;
if (pressed && sneakPhase == SneakPhase.NONE
&& !rollActive && !pickupActive && !blockingAnimActive
&& !turnActive && !lockedInPlace && !combatAnimActive) {
sneakPhase = SneakPhase.DOWN;
boolean moving = forward || backward || left || right;
sneakTransitionRemaining = resolveClipLength(AnimationAction.DUCK, 0.5f) * (moving ? 0.5f : 1f);
nextTransitionLength = 0.1f;
playAction(AnimationAction.DUCK);
currentAnim = AnimationAction.DUCK;
} else if (!pressed && sneakPhase == SneakPhase.ACTIVE) {
startSneakUp();
}
}
private void startSneakUp() {
sneakPhase = SneakPhase.UP;
boolean moving = forward || backward || left || right;
sneakTransitionRemaining = resolveClipLength(AnimationAction.CROUCH_TO_STAND, 0.5f) * (moving ? 0.5f : 1f);
nextTransitionLength = 0.1f;
playAction(AnimationAction.CROUCH_TO_STAND);
currentAnim = AnimationAction.CROUCH_TO_STAND;
}
private void triggerRollFromInput() { private void triggerRollFromInput() {
if (rollActive || pickupActive || blockingAnimActive || turnActive if (rollActive || pickupActive || blockingAnimActive || turnActive
|| lockedInPlace || combatAnimActive || isBlocking()) return; || lockedInPlace || combatAnimActive || isBlocking()) return;
@@ -1772,22 +1832,32 @@ public class PlayerInputControl {
} }
private String currentAnimToGait(AnimationAction anim) { private String currentAnimToGait(AnimationAction anim) {
if (anim == AnimationAction.WALK) return "walking"; if (anim == null) return null;
if (anim == AnimationAction.RUN) return "running"; return switch (anim) {
if (anim == AnimationAction.SPRINT) return "sprinting"; case WALK,
return null; SWORD_WALK, AXE_WALK, TWO_HAND_WALK,
BOW_WALK, CROSSBOW_WALK, CRYSTAL_WALK,
SWORD_BLOCK_MOVE, SWORD_BLOCK_MOVE_BACKWARD,
SWORD_BLOCK_STRAFE_LEFT, SWORD_BLOCK_STRAFE_RIGHT -> "walking";
case RUN,
SWORD_RUN, AXE_RUN, TWO_HAND_RUN,
BOW_RUN, CROSSBOW_RUN, CRYSTAL_RUN -> "running";
case SPRINT -> "sprinting";
case SNEAK -> "walking";
default -> null;
};
} }
private void triggerStep(String gait) { private void triggerStep(String gait) {
float vol = (sneakPhase == SneakPhase.ACTIVE) ? 0.25f : 1f;
if (currentWaterDepth > 0f) { if (currentWaterDepth > 0f) {
// Im Wasser: Wassergeräusche aus footsteps/water/walking bzw. /running
float[] weights = new float[de.blight.game.audio.SurfaceType.values().length]; float[] weights = new float[de.blight.game.audio.SurfaceType.values().length];
weights[de.blight.game.audio.SurfaceType.WATER.ordinal()] = 1f; weights[de.blight.game.audio.SurfaceType.WATER.ordinal()] = 1f;
footstepSystem.play(weights, gait); footstepSystem.play(weights, gait, vol);
} else { } else {
Vector3f pos = physicsChar.getPhysicsLocation(); Vector3f pos = physicsChar.getPhysicsLocation();
float[] weights = surfaceQuery.apply(pos.x, pos.z); float[] weights = surfaceQuery.apply(pos.x, pos.z);
footstepSystem.play(weights, gait); footstepSystem.play(weights, gait, vol);
} }
} }