Weiter am Kampfsystem gearbeitet - hier Kampfpuppen und ausweichrolle
This commit is contained in:
BIN
assets/imported/animations/Falling To Roll.fbx
Normal file
BIN
assets/imported/animations/Falling To Roll.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Picking Up.fbx
Normal file
BIN
assets/imported/animations/Picking Up.fbx
Normal file
Binary file not shown.
BIN
blight-assets/src/main/resources/Characters/clips/Picking Up.j3o
Normal file
BIN
blight-assets/src/main/resources/Characters/clips/Picking Up.j3o
Normal file
Binary file not shown.
BIN
blight-assets/src/main/resources/Characters/clips/Roll.j3o
Normal file
BIN
blight-assets/src/main/resources/Characters/clips/Roll.j3o
Normal file
Binary file not shown.
@@ -24,7 +24,9 @@
|
|||||||
"Sword And Shield Block Walk Backwards",
|
"Sword And Shield Block Walk Backwards",
|
||||||
"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",
|
||||||
|
"Picking Up",
|
||||||
|
"Roll"
|
||||||
],
|
],
|
||||||
"actionMap": {
|
"actionMap": {
|
||||||
"DEFAULT": "Idle",
|
"DEFAULT": "Idle",
|
||||||
@@ -51,7 +53,9 @@
|
|||||||
"SWORD_BLOCK_MOVE_BACKWARD": "Sword And Shield Block Walk Backwards",
|
"SWORD_BLOCK_MOVE_BACKWARD": "Sword And Shield Block Walk Backwards",
|
||||||
"SWORD_BLOCK_STRAFE_LEFT": "Sword And Shield Strafe Left",
|
"SWORD_BLOCK_STRAFE_LEFT": "Sword And Shield Strafe Left",
|
||||||
"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",
|
||||||
|
"ROLL": "Roll"
|
||||||
},
|
},
|
||||||
"previewModelPath": "Characters/Models/hero.j3o",
|
"previewModelPath": "Characters/Models/hero.j3o",
|
||||||
"animOffsets": {
|
"animOffsets": {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ public enum AnimationAction {
|
|||||||
SPRINT,
|
SPRINT,
|
||||||
JUMP,
|
JUMP,
|
||||||
RUNNING_JUMP,
|
RUNNING_JUMP,
|
||||||
|
ROLL,
|
||||||
DUCK,
|
DUCK,
|
||||||
PICK_UP,
|
PICK_UP,
|
||||||
LIE_DOWN,
|
LIE_DOWN,
|
||||||
@@ -78,6 +79,7 @@ public enum AnimationAction {
|
|||||||
case SPRINT -> TextRegistry.resolve(null, key, "Sprint");
|
case SPRINT -> TextRegistry.resolve(null, key, "Sprint");
|
||||||
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 DUCK -> TextRegistry.resolve(null, key, "Duck");
|
case DUCK -> TextRegistry.resolve(null, key, "Duck");
|
||||||
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");
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ public class PlayerInputControl {
|
|||||||
private static final int STAMINA_ATTACK5_COST = 20;
|
private static final int STAMINA_ATTACK5_COST = 20;
|
||||||
private static final int STAMINA_HEAVY_COST = 30;
|
private static final int STAMINA_HEAVY_COST = 30;
|
||||||
private static final int STAMINA_BLOCK_HIT_COST = 30;
|
private static final int STAMINA_BLOCK_HIT_COST = 30;
|
||||||
|
private static final int STAMINA_ROLL_COST = 20;
|
||||||
|
private static final float ROLL_DOUBLE_TAP_WINDOW = 0.3f;
|
||||||
/** Dauer der Ziehen/Wegstecken-Animation in Sekunden. */
|
/** Dauer der Ziehen/Wegstecken-Animation in Sekunden. */
|
||||||
private static final float DRAW_DURATION = 0.85f;
|
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
|
// Ziel-Quaternions für die Ziehen-Animation: aus Heavy-Attack-Wind-Up bei t=0.933s gelesen
|
||||||
@@ -194,6 +196,8 @@ public class PlayerInputControl {
|
|||||||
|
|
||||||
private com.jme3.anim.SkinningControl skinningControl = null;
|
private com.jme3.anim.SkinningControl skinningControl = null;
|
||||||
private com.jme3.anim.Armature armature = null;
|
private com.jme3.anim.Armature armature = null;
|
||||||
|
private com.jme3.anim.Joint hipsJoint = null;
|
||||||
|
|
||||||
|
|
||||||
// ── Fußgeräusche ─────────────────────────────────────────────────────────
|
// ── Fußgeräusche ─────────────────────────────────────────────────────────
|
||||||
// Bone-Namen nach Phase-0-Bone-Log eintragen:
|
// Bone-Namen nach Phase-0-Bone-Log eintragen:
|
||||||
@@ -258,6 +262,17 @@ 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;
|
||||||
|
|
||||||
|
// ── Dodge Roll ────────────────────────────────────────────────────────────
|
||||||
|
// Double-tap-Timer pro Richtung: -1 = kein erster Tap ausstehend
|
||||||
|
private float rollTapFwd = -1f;
|
||||||
|
private float rollTapBwd = -1f;
|
||||||
|
private float rollTapLeft = -1f;
|
||||||
|
private float rollTapRight = -1f;
|
||||||
|
private int pendingRollDir = 0; // 1=vor, 2=zurück, 3=links, 4=rechts
|
||||||
|
private boolean rollActive = false;
|
||||||
|
private float rollRemaining = 0f;
|
||||||
|
private Vector3f rollDir = null;
|
||||||
|
|
||||||
// Pre-allokierte Scratch-Objekte — keine Heap-Allokation pro Frame in update()
|
// Pre-allokierte Scratch-Objekte — keine Heap-Allokation pro Frame in update()
|
||||||
private final Vector3f scratchCamDir = new Vector3f();
|
private final Vector3f scratchCamDir = new Vector3f();
|
||||||
private final Vector3f scratchCamLeft = new Vector3f();
|
private final Vector3f scratchCamLeft = new Vector3f();
|
||||||
@@ -270,10 +285,10 @@ public class PlayerInputControl {
|
|||||||
private final ActionListener actionListener = (name, isPressed, tpf) -> {
|
private final ActionListener actionListener = (name, isPressed, tpf) -> {
|
||||||
if (paused) return;
|
if (paused) return;
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "Forward" -> forward = isPressed;
|
case "Forward" -> { if (isPressed) detectRollTap(1); forward = isPressed; }
|
||||||
case "Backward" -> backward = isPressed;
|
case "Backward" -> { if (isPressed) detectRollTap(2); backward = isPressed; }
|
||||||
case "Left" -> left = isPressed;
|
case "Left" -> { if (isPressed) detectRollTap(3); left = isPressed; }
|
||||||
case "Right" -> right = isPressed;
|
case "Right" -> { if (isPressed) detectRollTap(4); right = isPressed; }
|
||||||
case "Sprint" -> sprint = isPressed;
|
case "Sprint" -> sprint = isPressed;
|
||||||
case "Walk" -> walk = isPressed;
|
case "Walk" -> walk = isPressed;
|
||||||
case "Jump" -> { if (isPressed && physicsChar != null) { physicsChar.jump(); jumpFrames = 12; } }
|
case "Jump" -> { if (isPressed && physicsChar != null) { physicsChar.jump(); jumpFrames = 12; } }
|
||||||
@@ -283,19 +298,7 @@ public class PlayerInputControl {
|
|||||||
case ACT_COMBAT_ATK -> {
|
case ACT_COMBAT_ATK -> {
|
||||||
if (isPressed) {
|
if (isPressed) {
|
||||||
if (!weaponDrawn || blockingAnimActive || drawAnimActive || isBlocking()) break;
|
if (!weaponDrawn || blockingAnimActive || drawAnimActive || isBlocking()) break;
|
||||||
if (combatAnimActive) {
|
// Immer Hold-Timer starten – auch mitten in einer Kombo
|
||||||
if (comboWindowActive) {
|
|
||||||
// Kombo-Follow während zweiter Hälfte: sofort fluid weiter
|
|
||||||
combatAnimActive = false;
|
|
||||||
combatAnimRemaining = 0f;
|
|
||||||
combatAnimSkipComboWindow = false;
|
|
||||||
heavyLungeVel = null;
|
|
||||||
heavyLungeStartRemaining = 0f;
|
|
||||||
heavyLungeEndRemaining = 0f;
|
|
||||||
handleCombatAttack();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
attackHoldPending = true;
|
attackHoldPending = true;
|
||||||
attackHoldTimer = 0f;
|
attackHoldTimer = 0f;
|
||||||
} else {
|
} else {
|
||||||
@@ -303,12 +306,20 @@ public class PlayerInputControl {
|
|||||||
attackHoldPending = false;
|
attackHoldPending = false;
|
||||||
if (attackHoldTimer > QUICK_TAP_THRESHOLD) {
|
if (attackHoldTimer > QUICK_TAP_THRESHOLD) {
|
||||||
handleHeavyAttack();
|
handleHeavyAttack();
|
||||||
} else {
|
} else if (combatAnimActive && comboWindowActive) {
|
||||||
// Schneller Klick → schwere Anim abbrechen, normale Kombo starten
|
// Schneller Klick im Kombo-Fenster → Kombo fortsetzen
|
||||||
combatAnimActive = false;
|
combatAnimActive = false;
|
||||||
combatAnimRemaining = 0f;
|
combatAnimRemaining = 0f;
|
||||||
|
combatAnimSkipComboWindow = false;
|
||||||
|
heavyLungeVel = null;
|
||||||
|
heavyLungeStartRemaining = 0f;
|
||||||
|
heavyLungeEndRemaining = 0f;
|
||||||
|
handleCombatAttack();
|
||||||
|
} else if (!combatAnimActive) {
|
||||||
|
// Schneller Klick außerhalb Kombo → normaler Angriff
|
||||||
handleCombatAttack();
|
handleCombatAttack();
|
||||||
}
|
}
|
||||||
|
// Schneller Klick während Kombo aber außerhalb Fenster → ignorieren
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -355,6 +366,7 @@ public class PlayerInputControl {
|
|||||||
if (skinningControl != null) {
|
if (skinningControl != null) {
|
||||||
armature = skinningControl.getArmature();
|
armature = skinningControl.getArmature();
|
||||||
logJointNames(armature);
|
logJointNames(armature);
|
||||||
|
hipsJoint = armature.getJoint("mixamorig:Hips");
|
||||||
jointRightArm = armature.getJoint("mixamorig:RightArm");
|
jointRightArm = armature.getJoint("mixamorig:RightArm");
|
||||||
jointRightForeArm = armature.getJoint("mixamorig:RightForeArm");
|
jointRightForeArm = armature.getJoint("mixamorig:RightForeArm");
|
||||||
jointLeftArm = armature.getJoint("mixamorig:LeftArm");
|
jointLeftArm = armature.getJoint("mixamorig:LeftArm");
|
||||||
@@ -685,7 +697,7 @@ public class PlayerInputControl {
|
|||||||
runningClip = animPlayer.getCurrentClip();
|
runningClip = animPlayer.getCurrentClip();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (visual != null && animOffsetElapsed < AnimPlayer.BLEND_DURATION) {
|
if (visual != null && animOffsetElapsed < AnimPlayer.BLEND_DURATION && !rollActive) {
|
||||||
animOffsetElapsed += tpf;
|
animOffsetElapsed += tpf;
|
||||||
float t = Math.min(1f, animOffsetElapsed / AnimPlayer.BLEND_DURATION);
|
float t = Math.min(1f, animOffsetElapsed / AnimPlayer.BLEND_DURATION);
|
||||||
animOffsetCurrent.set(animOffsetFrom);
|
animOffsetCurrent.set(animOffsetFrom);
|
||||||
@@ -704,6 +716,29 @@ public class PlayerInputControl {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Double-tap-Timer ablaufen lassen
|
||||||
|
if (rollTapFwd > 0f) { rollTapFwd -= tpf; if (rollTapFwd < 0f) rollTapFwd = -1f; }
|
||||||
|
if (rollTapBwd > 0f) { rollTapBwd -= tpf; if (rollTapBwd < 0f) rollTapBwd = -1f; }
|
||||||
|
if (rollTapLeft > 0f) { rollTapLeft -= tpf; if (rollTapLeft < 0f) rollTapLeft = -1f; }
|
||||||
|
if (rollTapRight > 0f) { rollTapRight -= tpf; if (rollTapRight < 0f) rollTapRight = -1f; }
|
||||||
|
|
||||||
|
// Doppeltippen → Rolle einleiten (nicht während Kampf, Block, Pickup usw.)
|
||||||
|
if (pendingRollDir != 0) {
|
||||||
|
if (!rollActive && !pickupActive && !blockingAnimActive && !turnActive
|
||||||
|
&& !lockedInPlace && !combatAnimActive && !isBlocking()) {
|
||||||
|
cam.getDirection(scratchCamDir).setY(0).normalizeLocal();
|
||||||
|
cam.getLeft(scratchCamLeft).setY(0).normalizeLocal();
|
||||||
|
Vector3f dir = switch (pendingRollDir) {
|
||||||
|
case 1 -> scratchCamDir.clone();
|
||||||
|
case 2 -> scratchCamDir.clone().negateLocal();
|
||||||
|
case 3 -> scratchCamLeft.clone();
|
||||||
|
default -> scratchCamLeft.clone().negateLocal();
|
||||||
|
};
|
||||||
|
startRoll(dir);
|
||||||
|
}
|
||||||
|
pendingRollDir = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Navigator: hat Vorrang vor allem außer Pause
|
// Navigator: hat Vorrang vor allem außer Pause
|
||||||
if (navigator != null && navigator.isActive()) {
|
if (navigator != null && navigator.isActive()) {
|
||||||
navigator.update(tpf);
|
navigator.update(tpf);
|
||||||
@@ -722,6 +757,20 @@ public class PlayerInputControl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (rollActive) {
|
||||||
|
rollRemaining -= tpf;
|
||||||
|
if (rollRemaining <= 0f) {
|
||||||
|
rollActive = false;
|
||||||
|
rollDir = null;
|
||||||
|
currentAnim = null;
|
||||||
|
} else {
|
||||||
|
if (physicsChar != null) {
|
||||||
|
physicsChar.setWalkDirection(Vector3f.ZERO);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Drehung auf der Stelle (kein Vorwärtsbewegen)
|
// Drehung auf der Stelle (kein Vorwärtsbewegen)
|
||||||
if (turnActive) {
|
if (turnActive) {
|
||||||
turnRemaining -= tpf;
|
turnRemaining -= tpf;
|
||||||
@@ -838,11 +887,22 @@ public class PlayerInputControl {
|
|||||||
// Hold-Timer muss jede Frame akkumulieren, unabhängig vom Anim-Status
|
// Hold-Timer muss jede Frame akkumulieren, unabhängig vom Anim-Status
|
||||||
if (attackHoldPending) {
|
if (attackHoldPending) {
|
||||||
attackHoldTimer += tpf;
|
attackHoldTimer += tpf;
|
||||||
// Erst wenn klar ist dass es ein schwerer Angriff wird → Animation starten
|
if (attackHoldTimer > QUICK_TAP_THRESHOLD) {
|
||||||
if (attackHoldTimer > QUICK_TAP_THRESHOLD && !combatAnimActive) {
|
if (combatAnimActive && !combatAnimSkipComboWindow) {
|
||||||
|
// Normale Kombo unterbrechen und schweren Angriff einleiten
|
||||||
|
combatAnimActive = false;
|
||||||
|
combatAnimRemaining = 0f;
|
||||||
|
comboWindowActive = false;
|
||||||
|
comboStep = 0;
|
||||||
|
heavyLungeVel = null;
|
||||||
|
heavyLungeStartRemaining = 0f;
|
||||||
|
heavyLungeEndRemaining = 0f;
|
||||||
|
startHeavyCharge(); // setzt combatAnimSkipComboWindow = true → verhindert Loop
|
||||||
|
} else if (!combatAnimActive) {
|
||||||
startHeavyCharge();
|
startHeavyCharge();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Draw/Sheath-Animation Timer (Oberkörper-Overlay, Beine laufen weiter)
|
// Draw/Sheath-Animation Timer (Oberkörper-Overlay, Beine laufen weiter)
|
||||||
if (drawAnimActive) {
|
if (drawAnimActive) {
|
||||||
@@ -1271,6 +1331,41 @@ public class PlayerInputControl {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void detectRollTap(int dir) {
|
||||||
|
float cur = switch (dir) {
|
||||||
|
case 1 -> rollTapFwd;
|
||||||
|
case 2 -> rollTapBwd;
|
||||||
|
case 3 -> rollTapLeft;
|
||||||
|
default -> rollTapRight;
|
||||||
|
};
|
||||||
|
if (cur > 0f) {
|
||||||
|
pendingRollDir = dir;
|
||||||
|
} else {
|
||||||
|
switch (dir) {
|
||||||
|
case 1 -> rollTapFwd = ROLL_DOUBLE_TAP_WINDOW;
|
||||||
|
case 2 -> rollTapBwd = ROLL_DOUBLE_TAP_WINDOW;
|
||||||
|
case 3 -> rollTapLeft = ROLL_DOUBLE_TAP_WINDOW;
|
||||||
|
default -> rollTapRight = ROLL_DOUBLE_TAP_WINDOW;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startRoll(Vector3f dir) {
|
||||||
|
if (!spendStamina(STAMINA_ROLL_COST)) return;
|
||||||
|
nextTransitionLength = 0.1f;
|
||||||
|
rollActive = true;
|
||||||
|
rollRemaining = resolveClipLength(AnimationAction.ROLL, 0.7f);
|
||||||
|
rollDir = dir;
|
||||||
|
forward = backward = left = right = false;
|
||||||
|
if (physicsChar != null) physicsChar.setWalkDirection(Vector3f.ZERO);
|
||||||
|
if (visual != null) {
|
||||||
|
scratchRot.lookAt(dir, Vector3f.UNIT_Y);
|
||||||
|
visual.setLocalRotation(scratchRot);
|
||||||
|
}
|
||||||
|
playAction(AnimationAction.ROLL);
|
||||||
|
currentAnim = AnimationAction.ROLL;
|
||||||
|
}
|
||||||
|
|
||||||
private boolean hasShieldEquipped() {
|
private boolean hasShieldEquipped() {
|
||||||
return mainCharacter != null && mainCharacter.getEquippedShield() != null;
|
return mainCharacter != null && mainCharacter.getEquippedShield() != null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -409,7 +409,7 @@ public class WorldScene extends BaseAppState {
|
|||||||
WorldNpcsState npcsState = new WorldNpcsState(keyBindings, physicsChar, playerInput, mc);
|
WorldNpcsState npcsState = new WorldNpcsState(keyBindings, physicsChar, playerInput, mc);
|
||||||
npcsState.setThirdPersonCamera(thirdPersonCam);
|
npcsState.setThirdPersonCamera(thirdPersonCam);
|
||||||
app.getStateManager().attach(npcsState);
|
app.getStateManager().attach(npcsState);
|
||||||
app.getStateManager().attach(new InteractionHudState());
|
app.getStateManager().attach(new InteractionHudState(keyBindings));
|
||||||
inventoryState = new InventoryState(mc, keyBindings);
|
inventoryState = new InventoryState(mc, keyBindings);
|
||||||
inventoryState.setEnabled(false);
|
inventoryState.setEnabled(false);
|
||||||
app.getStateManager().attach(inventoryState);
|
app.getStateManager().attach(inventoryState);
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import com.jme3.math.Vector3f;
|
|||||||
import com.jme3.renderer.Camera;
|
import com.jme3.renderer.Camera;
|
||||||
import com.jme3.scene.Node;
|
import com.jme3.scene.Node;
|
||||||
import com.jme3.scene.Spatial;
|
import com.jme3.scene.Spatial;
|
||||||
|
import de.blight.game.config.KeyBindings;
|
||||||
|
import de.blight.game.config.KeyNames;
|
||||||
import de.blight.lang.TextResolver;
|
import de.blight.lang.TextResolver;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@@ -33,6 +35,10 @@ public class InteractionHudState extends BaseAppState {
|
|||||||
private DialogHudState dialogHud;
|
private DialogHudState dialogHud;
|
||||||
|
|
||||||
private BitmapText labelText;
|
private BitmapText labelText;
|
||||||
|
private KeyBindings keyBindings;
|
||||||
|
|
||||||
|
public InteractionHudState() {}
|
||||||
|
public InteractionHudState(KeyBindings keyBindings) { this.keyBindings = keyBindings; }
|
||||||
|
|
||||||
// ── Lifecycle ─────────────────────────────────────────────────────────────
|
// ── Lifecycle ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -134,6 +140,9 @@ public class InteractionHudState extends BaseAppState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String text = TextResolver.get().resolveId(labelKey);
|
String text = TextResolver.get().resolveId(labelKey);
|
||||||
|
if (keyBindings != null) {
|
||||||
|
text = text.replace("{interact}", KeyNames.of(keyBindings.interact));
|
||||||
|
}
|
||||||
labelText.setText(text);
|
labelText.setText(text);
|
||||||
float textW = labelText.getLineWidth();
|
float textW = labelText.getLineWidth();
|
||||||
labelText.setLocalTranslation(screenV.x - textW * 0.5f, screenV.y, 1f);
|
labelText.setLocalTranslation(screenV.x - textW * 0.5f, screenV.y, 1f);
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import com.jme3.bullet.BulletAppState;
|
|||||||
import com.jme3.bullet.control.CharacterControl;
|
import com.jme3.bullet.control.CharacterControl;
|
||||||
import com.jme3.bullet.control.RigidBodyControl;
|
import com.jme3.bullet.control.RigidBodyControl;
|
||||||
import com.jme3.bullet.util.CollisionShapeFactory;
|
import com.jme3.bullet.util.CollisionShapeFactory;
|
||||||
import com.jme3.font.BitmapFont;
|
|
||||||
import com.jme3.font.BitmapText;
|
|
||||||
import com.jme3.input.InputManager;
|
import com.jme3.input.InputManager;
|
||||||
import com.jme3.input.MouseInput;
|
import com.jme3.input.MouseInput;
|
||||||
import com.jme3.input.controls.ActionListener;
|
import com.jme3.input.controls.ActionListener;
|
||||||
@@ -37,6 +35,7 @@ import de.blight.common.PlacedModel;
|
|||||||
import de.blight.common.PlacedModelIO;
|
import de.blight.common.PlacedModelIO;
|
||||||
import de.blight.common.model.CombatDummy;
|
import de.blight.common.model.CombatDummy;
|
||||||
import de.blight.common.model.CombatDummyIO;
|
import de.blight.common.model.CombatDummyIO;
|
||||||
|
import de.blight.game.animation.AnimationAction;
|
||||||
import de.blight.game.combat.DamageCalculator;
|
import de.blight.game.combat.DamageCalculator;
|
||||||
import de.blight.game.combat.MeleeAttackCallback;
|
import de.blight.game.combat.MeleeAttackCallback;
|
||||||
import de.blight.game.config.KeyBindings;
|
import de.blight.game.config.KeyBindings;
|
||||||
@@ -62,11 +61,12 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
|||||||
// ── Spielwelt-Konstanten ──────────────────────────────────────────────────
|
// ── Spielwelt-Konstanten ──────────────────────────────────────────────────
|
||||||
private static final float FOCUS_RANGE = 6f;
|
private static final float FOCUS_RANGE = 6f;
|
||||||
private static final float RESTORE_DELAY = 60f;
|
private static final float RESTORE_DELAY = 60f;
|
||||||
private static final float HP_BAR_SHOW_TIME = 5f;
|
//private static final float HP_BAR_SHOW_TIME = 5f;
|
||||||
private static final float STAND_UP_RANGE = 3f;
|
private static final float STAND_UP_RANGE = 3f;
|
||||||
private static final int DEFAULT_MAX_HP = 250;
|
private static final int DEFAULT_MAX_HP = 10;
|
||||||
private static final float FALL_DURATION = 0.35f;
|
|
||||||
private static final float MODEL_HALF_H = 0.75f;
|
private static final float MODEL_HALF_H = 0.75f;
|
||||||
|
private static final float KNOCK_INITIAL_VEL = 0.4f; // rad/s initial push
|
||||||
|
private static final float KNOCK_GRAVITY = 8f; // angular accel at upright (rad/s²)
|
||||||
|
|
||||||
// ── Welt-HP-Bar-Konstanten ────────────────────────────────────────────────
|
// ── Welt-HP-Bar-Konstanten ────────────────────────────────────────────────
|
||||||
private static final float WB_WIDTH = 0.8f;
|
private static final float WB_WIDTH = 0.8f;
|
||||||
@@ -103,8 +103,6 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
|||||||
// ── Feind-HUD ─────────────────────────────────────────────────────────────
|
// ── Feind-HUD ─────────────────────────────────────────────────────────────
|
||||||
private Node enemyHudNode;
|
private Node enemyHudNode;
|
||||||
private Geometry enemyHpFill;
|
private Geometry enemyHpFill;
|
||||||
private BitmapText enemyNameText;
|
|
||||||
private BitmapText enemyHpText;
|
|
||||||
/** x-Koordinate des linken Randes der Feind-HP-Bar (Screen-Pixel). */
|
/** x-Koordinate des linken Randes der Feind-HP-Bar (Screen-Pixel). */
|
||||||
private float ehbX;
|
private float ehbX;
|
||||||
/** y-Koordinate des unteren Randes der Feind-HP-Bar (Screen-Pixel). */
|
/** y-Koordinate des unteren Randes der Feind-HP-Bar (Screen-Pixel). */
|
||||||
@@ -314,25 +312,6 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
|||||||
enemyHpFill.setLocalTranslation(ehbX + EHB_BORDER, ehbY + EHB_BORDER, 2f);
|
enemyHpFill.setLocalTranslation(ehbX + EHB_BORDER, ehbY + EHB_BORDER, 2f);
|
||||||
enemyHudNode.attachChild(enemyHpFill);
|
enemyHudNode.attachChild(enemyHpFill);
|
||||||
|
|
||||||
BitmapFont font = assetManager.loadFont("Interface/Fonts/Default.fnt");
|
|
||||||
float renderedSize = font.getCharSet().getRenderedSize();
|
|
||||||
|
|
||||||
// Gegner-Name (über Bar)
|
|
||||||
enemyNameText = new BitmapText(font);
|
|
||||||
enemyNameText.setSize(renderedSize * 2f);
|
|
||||||
enemyNameText.setLocalScale(0.5f);
|
|
||||||
enemyNameText.setColor(new ColorRGBA(1f, 0.95f, 0.6f, 1f));
|
|
||||||
enemyNameText.setQueueBucket(RenderQueue.Bucket.Gui);
|
|
||||||
enemyHudNode.attachChild(enemyNameText);
|
|
||||||
|
|
||||||
// HP-Zahlen (in Bar)
|
|
||||||
enemyHpText = new BitmapText(font);
|
|
||||||
enemyHpText.setSize(renderedSize * 2f);
|
|
||||||
enemyHpText.setLocalScale(0.5f);
|
|
||||||
enemyHpText.setColor(ColorRGBA.White);
|
|
||||||
enemyHpText.setQueueBucket(RenderQueue.Bucket.Gui);
|
|
||||||
enemyHudNode.attachChild(enemyHpText);
|
|
||||||
|
|
||||||
guiNode.attachChild(enemyHudNode);
|
guiNode.attachChild(enemyHudNode);
|
||||||
enemyHudNode.setCullHint(Spatial.CullHint.Always);
|
enemyHudNode.setCullHint(Spatial.CullHint.Always);
|
||||||
}
|
}
|
||||||
@@ -375,23 +354,8 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void updateEnemyHudContent(DummyRuntime dr) {
|
private void updateEnemyHudContent(DummyRuntime dr) {
|
||||||
float sw = cam.getWidth();
|
|
||||||
float ratio = dr.maxHp > 0 ? (float) dr.currentHp / dr.maxHp : 0f;
|
float ratio = dr.maxHp > 0 ? (float) dr.currentHp / dr.maxHp : 0f;
|
||||||
applyEnemyHpRatio(ratio);
|
applyEnemyHpRatio(ratio);
|
||||||
|
|
||||||
String name = "Kampfpuppe";
|
|
||||||
enemyNameText.setText(name);
|
|
||||||
float nameW = enemyNameText.getLineWidth() * 0.5f;
|
|
||||||
enemyNameText.setLocalTranslation(
|
|
||||||
sw * 0.5f - nameW,
|
|
||||||
ehbY + EHB_H + EHB_BORDER * 2 + 6f, 3f);
|
|
||||||
|
|
||||||
String hpStr = dr.currentHp + " / " + dr.maxHp;
|
|
||||||
enemyHpText.setText(hpStr);
|
|
||||||
float hpW = enemyHpText.getLineWidth() * 0.5f;
|
|
||||||
enemyHpText.setLocalTranslation(
|
|
||||||
sw * 0.5f - hpW,
|
|
||||||
ehbY + EHB_BORDER + 1f, 3f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── MeleeAttackCallback ───────────────────────────────────────────────────
|
// ── MeleeAttackCallback ───────────────────────────────────────────────────
|
||||||
@@ -439,12 +403,17 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
|||||||
private void knockout(DummyRuntime dr) {
|
private void knockout(DummyRuntime dr) {
|
||||||
dr.fallen = true;
|
dr.fallen = true;
|
||||||
dr.fallenLerp = 0f;
|
dr.fallenLerp = 0f;
|
||||||
|
dr.knockAngle = 0f;
|
||||||
|
dr.knockAngVel = KNOCK_INITIAL_VEL;
|
||||||
dr.hpBarNode.setCullHint(Spatial.CullHint.Always);
|
dr.hpBarNode.setCullHint(Spatial.CullHint.Always);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void standUp(DummyRuntime dr) {
|
private void standUp(DummyRuntime dr) {
|
||||||
|
playerInput.requestAnimation(AnimationAction.PICK_UP, 0f, null);
|
||||||
dr.fallen = false;
|
dr.fallen = false;
|
||||||
dr.fallenLerp = 0f;
|
dr.fallenLerp = 0f;
|
||||||
|
dr.knockAngle = 0f;
|
||||||
|
dr.knockAngVel = 0f;
|
||||||
dr.currentHp = dr.maxHp;
|
dr.currentHp = dr.maxHp;
|
||||||
dr.restoreTimer = 0f;
|
dr.restoreTimer = 0f;
|
||||||
dr.dummyNode.setLocalRotation(new Quaternion());
|
dr.dummyNode.setLocalRotation(new Quaternion());
|
||||||
@@ -476,13 +445,18 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
|||||||
for (int i = 0; i < dummies.size(); i++) {
|
for (int i = 0; i < dummies.size(); i++) {
|
||||||
DummyRuntime dr = dummies.get(i);
|
DummyRuntime dr = dummies.get(i);
|
||||||
|
|
||||||
// Umfall-Animation
|
// Umfall-Animation (physikalisch: Schwerkraft-Drehmoment nimmt mit cos(Winkel) ab)
|
||||||
if (dr.fallen && dr.fallenLerp < 1f) {
|
if (dr.fallen && dr.fallenLerp < 1f) {
|
||||||
dr.fallenLerp = Math.min(1f, dr.fallenLerp + tpf / FALL_DURATION);
|
float gravAccel = KNOCK_GRAVITY * FastMath.cos(dr.knockAngle);
|
||||||
Quaternion target = new Quaternion().fromAngles(0f, 0f, -FastMath.HALF_PI);
|
dr.knockAngVel += gravAccel * tpf;
|
||||||
Quaternion cur = new Quaternion();
|
dr.knockAngle += dr.knockAngVel * tpf;
|
||||||
cur.slerp(Quaternion.IDENTITY, target, dr.fallenLerp);
|
if (dr.knockAngle >= FastMath.HALF_PI) {
|
||||||
dr.dummyNode.setLocalRotation(cur);
|
dr.knockAngle = FastMath.HALF_PI;
|
||||||
|
dr.knockAngVel = 0f;
|
||||||
|
dr.fallenLerp = 1f;
|
||||||
|
}
|
||||||
|
dr.dummyNode.setLocalRotation(
|
||||||
|
new Quaternion().fromAngles(-dr.knockAngle, 0f, 0f));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Welt-HP-Balken-Timer
|
// Welt-HP-Balken-Timer
|
||||||
@@ -588,6 +562,8 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
|||||||
float hpBarTimer = 0f;
|
float hpBarTimer = 0f;
|
||||||
boolean fallen = false;
|
boolean fallen = false;
|
||||||
float fallenLerp = 0f;
|
float fallenLerp = 0f;
|
||||||
|
float knockAngle = 0f; // aktueller Kippwinkel (0..PI/2)
|
||||||
|
float knockAngVel = 0f; // Winkelgeschwindigkeit (rad/s)
|
||||||
|
|
||||||
DummyRuntime(String id, float wx, float wy, float wz,
|
DummyRuntime(String id, float wx, float wy, float wz,
|
||||||
Node dummyNode, Node hpBarNode, Geometry hpBarFg, int maxHp,
|
Node dummyNode, Node hpBarNode, Geometry hpBarFg, int maxHp,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ dialog.speaker.player=Spieler
|
|||||||
# ── Interactables ─────────────────────────────────────────────────────────────
|
# ── Interactables ─────────────────────────────────────────────────────────────
|
||||||
interactable.bench.name=Bank
|
interactable.bench.name=Bank
|
||||||
interactable.bed.name=Bett
|
interactable.bed.name=Bett
|
||||||
dummy.stand_up=Aufstellen [F]
|
dummy.stand_up=Aufstellen [{interact}]
|
||||||
dummy.name=Kampfpuppe
|
dummy.name=Kampfpuppe
|
||||||
|
|
||||||
# ── Items ────────────────────────────────────────────────────────────────────
|
# ── Items ────────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ dialog.speaker.player=Player
|
|||||||
# ── Interactables ─────────────────────────────────────────────────────────────
|
# ── Interactables ─────────────────────────────────────────────────────────────
|
||||||
interactable.bench.name=Bench
|
interactable.bench.name=Bench
|
||||||
interactable.bed.name=Bed
|
interactable.bed.name=Bed
|
||||||
dummy.stand_up=Stand Up [F]
|
dummy.stand_up=Stand Up [{interact}]
|
||||||
dummy.name=Training Dummy
|
dummy.name=Training Dummy
|
||||||
|
|
||||||
# ── Items ────────────────────────────────────────────────────────────────────
|
# ── Items ────────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user