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 Strafe Left",
|
||||
"Sword And Shield Strafe Right",
|
||||
"Sword And Shield Heavy Attack"
|
||||
"Sword And Shield Heavy Attack",
|
||||
"Picking Up",
|
||||
"Roll"
|
||||
],
|
||||
"actionMap": {
|
||||
"DEFAULT": "Idle",
|
||||
@@ -51,7 +53,9 @@
|
||||
"SWORD_BLOCK_MOVE_BACKWARD": "Sword And Shield Block Walk Backwards",
|
||||
"SWORD_BLOCK_STRAFE_LEFT": "Sword And Shield Strafe Left",
|
||||
"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",
|
||||
"animOffsets": {
|
||||
|
||||
@@ -14,6 +14,7 @@ public enum AnimationAction {
|
||||
SPRINT,
|
||||
JUMP,
|
||||
RUNNING_JUMP,
|
||||
ROLL,
|
||||
DUCK,
|
||||
PICK_UP,
|
||||
LIE_DOWN,
|
||||
@@ -78,6 +79,7 @@ public enum AnimationAction {
|
||||
case SPRINT -> TextRegistry.resolve(null, key, "Sprint");
|
||||
case JUMP -> TextRegistry.resolve(null, key, "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 PICK_UP -> TextRegistry.resolve(null, key, "Pick up");
|
||||
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_HEAVY_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. */
|
||||
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
|
||||
@@ -194,6 +196,8 @@ public class PlayerInputControl {
|
||||
|
||||
private com.jme3.anim.SkinningControl skinningControl = null;
|
||||
private com.jme3.anim.Armature armature = null;
|
||||
private com.jme3.anim.Joint hipsJoint = null;
|
||||
|
||||
|
||||
// ── Fußgeräusche ─────────────────────────────────────────────────────────
|
||||
// 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. */
|
||||
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()
|
||||
private final Vector3f scratchCamDir = new Vector3f();
|
||||
private final Vector3f scratchCamLeft = new Vector3f();
|
||||
@@ -270,10 +285,10 @@ public class PlayerInputControl {
|
||||
private final ActionListener actionListener = (name, isPressed, tpf) -> {
|
||||
if (paused) return;
|
||||
switch (name) {
|
||||
case "Forward" -> forward = isPressed;
|
||||
case "Backward" -> backward = isPressed;
|
||||
case "Left" -> left = isPressed;
|
||||
case "Right" -> right = isPressed;
|
||||
case "Forward" -> { if (isPressed) detectRollTap(1); forward = isPressed; }
|
||||
case "Backward" -> { if (isPressed) detectRollTap(2); backward = isPressed; }
|
||||
case "Left" -> { if (isPressed) detectRollTap(3); left = isPressed; }
|
||||
case "Right" -> { if (isPressed) detectRollTap(4); right = isPressed; }
|
||||
case "Sprint" -> sprint = isPressed;
|
||||
case "Walk" -> walk = isPressed;
|
||||
case "Jump" -> { if (isPressed && physicsChar != null) { physicsChar.jump(); jumpFrames = 12; } }
|
||||
@@ -283,19 +298,7 @@ public class PlayerInputControl {
|
||||
case ACT_COMBAT_ATK -> {
|
||||
if (isPressed) {
|
||||
if (!weaponDrawn || blockingAnimActive || drawAnimActive || isBlocking()) break;
|
||||
if (combatAnimActive) {
|
||||
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;
|
||||
}
|
||||
// Immer Hold-Timer starten – auch mitten in einer Kombo
|
||||
attackHoldPending = true;
|
||||
attackHoldTimer = 0f;
|
||||
} else {
|
||||
@@ -303,12 +306,20 @@ public class PlayerInputControl {
|
||||
attackHoldPending = false;
|
||||
if (attackHoldTimer > QUICK_TAP_THRESHOLD) {
|
||||
handleHeavyAttack();
|
||||
} else {
|
||||
// Schneller Klick → schwere Anim abbrechen, normale Kombo starten
|
||||
combatAnimActive = false;
|
||||
combatAnimRemaining = 0f;
|
||||
} else if (combatAnimActive && comboWindowActive) {
|
||||
// Schneller Klick im Kombo-Fenster → Kombo fortsetzen
|
||||
combatAnimActive = false;
|
||||
combatAnimRemaining = 0f;
|
||||
combatAnimSkipComboWindow = false;
|
||||
heavyLungeVel = null;
|
||||
heavyLungeStartRemaining = 0f;
|
||||
heavyLungeEndRemaining = 0f;
|
||||
handleCombatAttack();
|
||||
} else if (!combatAnimActive) {
|
||||
// Schneller Klick außerhalb Kombo → normaler Angriff
|
||||
handleCombatAttack();
|
||||
}
|
||||
// Schneller Klick während Kombo aber außerhalb Fenster → ignorieren
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -355,6 +366,7 @@ public class PlayerInputControl {
|
||||
if (skinningControl != null) {
|
||||
armature = skinningControl.getArmature();
|
||||
logJointNames(armature);
|
||||
hipsJoint = armature.getJoint("mixamorig:Hips");
|
||||
jointRightArm = armature.getJoint("mixamorig:RightArm");
|
||||
jointRightForeArm = armature.getJoint("mixamorig:RightForeArm");
|
||||
jointLeftArm = armature.getJoint("mixamorig:LeftArm");
|
||||
@@ -685,7 +697,7 @@ public class PlayerInputControl {
|
||||
runningClip = animPlayer.getCurrentClip();
|
||||
}
|
||||
|
||||
if (visual != null && animOffsetElapsed < AnimPlayer.BLEND_DURATION) {
|
||||
if (visual != null && animOffsetElapsed < AnimPlayer.BLEND_DURATION && !rollActive) {
|
||||
animOffsetElapsed += tpf;
|
||||
float t = Math.min(1f, animOffsetElapsed / AnimPlayer.BLEND_DURATION);
|
||||
animOffsetCurrent.set(animOffsetFrom);
|
||||
@@ -704,6 +716,29 @@ public class PlayerInputControl {
|
||||
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
|
||||
if (navigator != null && navigator.isActive()) {
|
||||
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)
|
||||
if (turnActive) {
|
||||
turnRemaining -= tpf;
|
||||
@@ -838,9 +887,20 @@ public class PlayerInputControl {
|
||||
// Hold-Timer muss jede Frame akkumulieren, unabhängig vom Anim-Status
|
||||
if (attackHoldPending) {
|
||||
attackHoldTimer += tpf;
|
||||
// Erst wenn klar ist dass es ein schwerer Angriff wird → Animation starten
|
||||
if (attackHoldTimer > QUICK_TAP_THRESHOLD && !combatAnimActive) {
|
||||
startHeavyCharge();
|
||||
if (attackHoldTimer > QUICK_TAP_THRESHOLD) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1271,6 +1331,41 @@ public class PlayerInputControl {
|
||||
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() {
|
||||
return mainCharacter != null && mainCharacter.getEquippedShield() != null;
|
||||
}
|
||||
|
||||
@@ -409,7 +409,7 @@ public class WorldScene extends BaseAppState {
|
||||
WorldNpcsState npcsState = new WorldNpcsState(keyBindings, physicsChar, playerInput, mc);
|
||||
npcsState.setThirdPersonCamera(thirdPersonCam);
|
||||
app.getStateManager().attach(npcsState);
|
||||
app.getStateManager().attach(new InteractionHudState());
|
||||
app.getStateManager().attach(new InteractionHudState(keyBindings));
|
||||
inventoryState = new InventoryState(mc, keyBindings);
|
||||
inventoryState.setEnabled(false);
|
||||
app.getStateManager().attach(inventoryState);
|
||||
|
||||
@@ -10,6 +10,8 @@ import com.jme3.math.Vector3f;
|
||||
import com.jme3.renderer.Camera;
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.scene.Spatial;
|
||||
import de.blight.game.config.KeyBindings;
|
||||
import de.blight.game.config.KeyNames;
|
||||
import de.blight.lang.TextResolver;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -33,6 +35,10 @@ public class InteractionHudState extends BaseAppState {
|
||||
private DialogHudState dialogHud;
|
||||
|
||||
private BitmapText labelText;
|
||||
private KeyBindings keyBindings;
|
||||
|
||||
public InteractionHudState() {}
|
||||
public InteractionHudState(KeyBindings keyBindings) { this.keyBindings = keyBindings; }
|
||||
|
||||
// ── Lifecycle ─────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -134,6 +140,9 @@ public class InteractionHudState extends BaseAppState {
|
||||
}
|
||||
|
||||
String text = TextResolver.get().resolveId(labelKey);
|
||||
if (keyBindings != null) {
|
||||
text = text.replace("{interact}", KeyNames.of(keyBindings.interact));
|
||||
}
|
||||
labelText.setText(text);
|
||||
float textW = labelText.getLineWidth();
|
||||
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.RigidBodyControl;
|
||||
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.MouseInput;
|
||||
import com.jme3.input.controls.ActionListener;
|
||||
@@ -37,6 +35,7 @@ import de.blight.common.PlacedModel;
|
||||
import de.blight.common.PlacedModelIO;
|
||||
import de.blight.common.model.CombatDummy;
|
||||
import de.blight.common.model.CombatDummyIO;
|
||||
import de.blight.game.animation.AnimationAction;
|
||||
import de.blight.game.combat.DamageCalculator;
|
||||
import de.blight.game.combat.MeleeAttackCallback;
|
||||
import de.blight.game.config.KeyBindings;
|
||||
@@ -62,11 +61,12 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
||||
// ── Spielwelt-Konstanten ──────────────────────────────────────────────────
|
||||
private static final float FOCUS_RANGE = 6f;
|
||||
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 int DEFAULT_MAX_HP = 250;
|
||||
private static final float FALL_DURATION = 0.35f;
|
||||
private static final float MODEL_HALF_H = 0.75f;
|
||||
private static final int DEFAULT_MAX_HP = 10;
|
||||
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 ────────────────────────────────────────────────
|
||||
private static final float WB_WIDTH = 0.8f;
|
||||
@@ -103,8 +103,6 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
||||
// ── Feind-HUD ─────────────────────────────────────────────────────────────
|
||||
private Node enemyHudNode;
|
||||
private Geometry enemyHpFill;
|
||||
private BitmapText enemyNameText;
|
||||
private BitmapText enemyHpText;
|
||||
/** x-Koordinate des linken Randes der Feind-HP-Bar (Screen-Pixel). */
|
||||
private float ehbX;
|
||||
/** 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);
|
||||
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);
|
||||
enemyHudNode.setCullHint(Spatial.CullHint.Always);
|
||||
}
|
||||
@@ -375,23 +354,8 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
||||
}
|
||||
|
||||
private void updateEnemyHudContent(DummyRuntime dr) {
|
||||
float sw = cam.getWidth();
|
||||
float ratio = dr.maxHp > 0 ? (float) dr.currentHp / dr.maxHp : 0f;
|
||||
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 ───────────────────────────────────────────────────
|
||||
@@ -437,14 +401,19 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
||||
}
|
||||
|
||||
private void knockout(DummyRuntime dr) {
|
||||
dr.fallen = true;
|
||||
dr.fallenLerp = 0f;
|
||||
dr.fallen = true;
|
||||
dr.fallenLerp = 0f;
|
||||
dr.knockAngle = 0f;
|
||||
dr.knockAngVel = KNOCK_INITIAL_VEL;
|
||||
dr.hpBarNode.setCullHint(Spatial.CullHint.Always);
|
||||
}
|
||||
|
||||
private void standUp(DummyRuntime dr) {
|
||||
playerInput.requestAnimation(AnimationAction.PICK_UP, 0f, null);
|
||||
dr.fallen = false;
|
||||
dr.fallenLerp = 0f;
|
||||
dr.knockAngle = 0f;
|
||||
dr.knockAngVel = 0f;
|
||||
dr.currentHp = dr.maxHp;
|
||||
dr.restoreTimer = 0f;
|
||||
dr.dummyNode.setLocalRotation(new Quaternion());
|
||||
@@ -476,13 +445,18 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
||||
for (int i = 0; i < dummies.size(); i++) {
|
||||
DummyRuntime dr = dummies.get(i);
|
||||
|
||||
// Umfall-Animation
|
||||
// Umfall-Animation (physikalisch: Schwerkraft-Drehmoment nimmt mit cos(Winkel) ab)
|
||||
if (dr.fallen && dr.fallenLerp < 1f) {
|
||||
dr.fallenLerp = Math.min(1f, dr.fallenLerp + tpf / FALL_DURATION);
|
||||
Quaternion target = new Quaternion().fromAngles(0f, 0f, -FastMath.HALF_PI);
|
||||
Quaternion cur = new Quaternion();
|
||||
cur.slerp(Quaternion.IDENTITY, target, dr.fallenLerp);
|
||||
dr.dummyNode.setLocalRotation(cur);
|
||||
float gravAccel = KNOCK_GRAVITY * FastMath.cos(dr.knockAngle);
|
||||
dr.knockAngVel += gravAccel * tpf;
|
||||
dr.knockAngle += dr.knockAngVel * tpf;
|
||||
if (dr.knockAngle >= FastMath.HALF_PI) {
|
||||
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
|
||||
@@ -588,6 +562,8 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
||||
float hpBarTimer = 0f;
|
||||
boolean fallen = false;
|
||||
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,
|
||||
Node dummyNode, Node hpBarNode, Geometry hpBarFg, int maxHp,
|
||||
|
||||
@@ -7,7 +7,7 @@ dialog.speaker.player=Spieler
|
||||
# ── Interactables ─────────────────────────────────────────────────────────────
|
||||
interactable.bench.name=Bank
|
||||
interactable.bed.name=Bett
|
||||
dummy.stand_up=Aufstellen [F]
|
||||
dummy.stand_up=Aufstellen [{interact}]
|
||||
dummy.name=Kampfpuppe
|
||||
|
||||
# ── Items ────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -7,7 +7,7 @@ dialog.speaker.player=Player
|
||||
# ── Interactables ─────────────────────────────────────────────────────────────
|
||||
interactable.bench.name=Bench
|
||||
interactable.bed.name=Bed
|
||||
dummy.stand_up=Stand Up [F]
|
||||
dummy.stand_up=Stand Up [{interact}]
|
||||
dummy.name=Training Dummy
|
||||
|
||||
# ── Items ────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user