Kampfsystem für Zweihänder erweitert
This commit is contained in:
BIN
assets/imported/animations/Draw A Great Sword.fbx
Normal file
BIN
assets/imported/animations/Draw A Great Sword.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Attack 1.fbx
Normal file
BIN
assets/imported/animations/Great Sword Attack 1.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Attack 2.fbx
Normal file
BIN
assets/imported/animations/Great Sword Attack 2.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Attack 3.fbx
Normal file
BIN
assets/imported/animations/Great Sword Attack 3.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Block Impact.fbx
Normal file
BIN
assets/imported/animations/Great Sword Block Impact.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Blocking Idle.fbx
Normal file
BIN
assets/imported/animations/Great Sword Blocking Idle.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Blocking to Idle.fbx
Normal file
BIN
assets/imported/animations/Great Sword Blocking to Idle.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Heavy Attack.fbx
Normal file
BIN
assets/imported/animations/Great Sword Heavy Attack.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Idle boring 2.fbx
Normal file
BIN
assets/imported/animations/Great Sword Idle boring 2.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Idle boring 3.fbx
Normal file
BIN
assets/imported/animations/Great Sword Idle boring 3.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Idle boring.fbx
Normal file
BIN
assets/imported/animations/Great Sword Idle boring.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Idle to Blocking.fbx
Normal file
BIN
assets/imported/animations/Great Sword Idle to Blocking.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Idle.fbx
Normal file
BIN
assets/imported/animations/Great Sword Idle.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Impact.fbx
Normal file
BIN
assets/imported/animations/Great Sword Impact.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Jump.fbx
Normal file
BIN
assets/imported/animations/Great Sword Jump.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Parade.fbx
Normal file
BIN
assets/imported/animations/Great Sword Parade.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Run.fbx
Normal file
BIN
assets/imported/animations/Great Sword Run.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Running Jump.fbx
Normal file
BIN
assets/imported/animations/Great Sword Running Jump.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Strafe Right.fbx
Normal file
BIN
assets/imported/animations/Great Sword Strafe Right.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Strafe left.fbx
Normal file
BIN
assets/imported/animations/Great Sword Strafe left.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Walk backwards.fbx
Normal file
BIN
assets/imported/animations/Great Sword Walk backwards.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Great Sword Walk.fbx
Normal file
BIN
assets/imported/animations/Great Sword Walk.fbx
Normal file
Binary file not shown.
BIN
assets/imported/animations/Sheath A Great Sword.fbx
Normal file
BIN
assets/imported/animations/Sheath A Great Sword.fbx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -37,7 +37,30 @@
|
||||
"Sword And Shield Idle boring 3",
|
||||
"Sword And Shield Jump Running",
|
||||
"Sword And Shield Jump",
|
||||
"Sword And Shield Slash"
|
||||
"Sword And Shield Slash",
|
||||
"Draw A Great Sword",
|
||||
"Great Sword Attack 1",
|
||||
"Great Sword Attack 2",
|
||||
"Great Sword Attack 3",
|
||||
"Great Sword Block Impact",
|
||||
"Great Sword Blocking Idle",
|
||||
"Great Sword Blocking to Idle",
|
||||
"Great Sword Heavy Attack",
|
||||
"Great Sword Idle",
|
||||
"Great Sword Idle boring",
|
||||
"Great Sword Idle boring 2",
|
||||
"Great Sword Idle boring 3",
|
||||
"Great Sword Idle to Blocking",
|
||||
"Great Sword Impact",
|
||||
"Great Sword Jump",
|
||||
"Great Sword Parade",
|
||||
"Great Sword Run",
|
||||
"Great Sword Running Jump",
|
||||
"Great Sword Strafe Right",
|
||||
"Great Sword Strafe left",
|
||||
"Great Sword Walk",
|
||||
"Great Sword Walk backwards",
|
||||
"Sheath A Great Sword"
|
||||
],
|
||||
"actionMap": {
|
||||
"DEFAULT": "Idle",
|
||||
@@ -82,7 +105,32 @@
|
||||
"SWORD_IDLE_BORED": "Sword And Shield Idle boring",
|
||||
"SWORD_IDLE_BORED_2": "Sword And Shield Idle boring 2",
|
||||
"SWORD_IDLE_BORED_3": "Sword And Shield Idle boring 3",
|
||||
"STAND_TO_CROUCH": "Standing To Crouched"
|
||||
"STAND_TO_CROUCH": "Standing To Crouched",
|
||||
"TWO_HAND_DRAW": "Draw A Great Sword",
|
||||
"TWO_HAND_SHEATHE": "Sheath A Great Sword",
|
||||
"TWO_HAND_BLOCK": "Great Sword Blocking Idle",
|
||||
"TWO_HAND_ATTACK_1": "Great Sword Attack 1",
|
||||
"TWO_HAND_ATTACK_2": "Great Sword Attack 2",
|
||||
"TWO_HAND_ATTACK_3": "Great Sword Attack 3",
|
||||
"TWO_HAND_HEAVY_ATTACK": "Great Sword Heavy Attack",
|
||||
"TWO_HAND_IDLE_TO_BLOCK": "Great Sword Idle to Blocking",
|
||||
"TWO_HAND_PARADE": "Great Sword Parade",
|
||||
"TWO_HAND_BLOCK_IMPACT": "Great Sword Block Impact",
|
||||
"TWO_HAND_IDLE_IMPACT": "Great Sword Impact",
|
||||
"TWO_HAND_IDLE": "Great Sword Idle",
|
||||
"TWO_HAND_WALK": "Great Sword Walk",
|
||||
"TWO_HAND_RUN": "Great Sword Run",
|
||||
"TWO_HAND_BLOCK_MOVE": "Great Sword Walk",
|
||||
"TWO_HAND_BLOCK_MOVE_BACKWARD": "Great Sword Walk backwards",
|
||||
"TWO_HAND_BLOCK_STRAFE_LEFT": "Great Sword Strafe left",
|
||||
"TWO_HAND_BLOCK_STRAFE_RIGHT": "Great Sword Strafe Right",
|
||||
"TWO_HAND_JUMP": "Great Sword Jump",
|
||||
"TWO_HAND_RUNNING_JUMP": "Great Sword Running Jump",
|
||||
"TWO_HAND_IDLE_BORED": "Great Sword Idle boring",
|
||||
"TWO_HAND_IDLE_BORED_2": "Great Sword Idle boring 2",
|
||||
"TWO_HAND_IDLE_BORED_3": "Great Sword Idle boring 3",
|
||||
"TWO_HAND_DEATH": "Sword And Shield Death",
|
||||
"TWO_HAND_DEATH_2": "Sword And Shield Death 2"
|
||||
},
|
||||
"previewModelPath": "Characters/Models/hero.j3o",
|
||||
"animOffsets": {
|
||||
|
||||
@@ -664,18 +664,10 @@ public class AnimPreviewState extends BaseAppState {
|
||||
// snap passiert ausschließlich zur Laufzeit im Spiel via AnimationLibrary.
|
||||
boolean isJ3oSource = animAssetPath.matches("(?i).*\\.j3o$");
|
||||
|
||||
// FBX-Import erfordert ein geladenes Charakter-Modell.
|
||||
// Ohne Ziel-Armatur kann normalizeAndRetargetClip die $AssimpFbx$_Translation-Orphan-Joints
|
||||
// nicht in den Hips-Track falten → Hips-Translation geht verloren → Charakter schrumpft.
|
||||
if (!isJ3oSource && srcArm != null && dstArm == null) {
|
||||
input.animPreviewStatus =
|
||||
"Fehler: FBX-Import erfordert ein geladenes Charakter-Modell. " +
|
||||
"Bitte zuerst das Modell laden, dann die Animation importieren.";
|
||||
LOG.warn("[AnimPreview] FBX-Import ohne Charakter-Modell abgebrochen: {}", animAssetPath);
|
||||
return;
|
||||
}
|
||||
|
||||
com.jme3.anim.Armature snapArm = dstArm != null ? dstArm : srcArm;
|
||||
// Kein Charakter-Modell geladen: Normalisierung gegen eigenes Skelett (Orphan-Fold + Scale-Strip).
|
||||
// Das erzeugte j3o ist später per redirectAnimClipByName auf jedes kompatible Rig anwendbar.
|
||||
com.jme3.anim.Armature effectiveDst = dstArm != null ? dstArm : srcArm;
|
||||
com.jme3.anim.Armature snapArm = effectiveDst;
|
||||
|
||||
int saved = 0;
|
||||
for (AnimClip clip : sourceAC.getAnimClips()) {
|
||||
@@ -688,7 +680,7 @@ public class AnimPreviewState extends BaseAppState {
|
||||
}
|
||||
}
|
||||
AnimClip result;
|
||||
if (!retarget) {
|
||||
if (!retarget && (isJ3oSource || srcArm == null)) {
|
||||
result = clip;
|
||||
} else if (isJ3oSource) {
|
||||
// j3o-Clips wurden bereits beim FBX-Import normalisiert (in Modell-Raum gespeichert).
|
||||
@@ -696,12 +688,15 @@ public class AnimPreviewState extends BaseAppState {
|
||||
result = redirectAnimClipByName(clip, dstArm);
|
||||
} else {
|
||||
// FBX-Import: Normalisierung in Modell-Raum (Orphan-Fold + Bind-Pose-Delta).
|
||||
// Das resultierende j3o ist dann mit plain redirectAnimClipByName kompatibel.
|
||||
result = normalizeAndRetargetClip(clip, dstArm);
|
||||
// Kein Charakter-Modell → effectiveDst == srcArm → reine Normalisierung ohne Retarget.
|
||||
result = normalizeAndRetargetClip(clip, effectiveDst);
|
||||
if (result == null) {
|
||||
LOG.warn("[AnimPreview] normalizeAndRetarget: keine Joints gemappt, versuche Retargeting");
|
||||
result = de.blight.game.animation.RetargetingSystem.retarget(clip, srcArm, dstArm);
|
||||
if (dstArm != null) {
|
||||
result = de.blight.game.animation.RetargetingSystem.retarget(clip, srcArm, dstArm);
|
||||
}
|
||||
}
|
||||
if (result == null) result = clip;
|
||||
}
|
||||
if (result == null) continue;
|
||||
|
||||
|
||||
@@ -997,6 +997,11 @@ public class AnimSetController implements ToolController {
|
||||
return;
|
||||
}
|
||||
|
||||
// ── Aktions-Combo mit Suchfeld ────────────────────────────────────────
|
||||
javafx.scene.control.TextField actionSearch = new javafx.scene.control.TextField();
|
||||
actionSearch.setPromptText("Aktion suchen…");
|
||||
actionSearch.setMaxWidth(Double.MAX_VALUE);
|
||||
|
||||
ComboBox<de.blight.game.animation.AnimationAction> actionCombo = new ComboBox<>();
|
||||
actionCombo.getItems().addAll(available);
|
||||
javafx.util.Callback<javafx.scene.control.ListView<de.blight.game.animation.AnimationAction>,
|
||||
@@ -1013,29 +1018,85 @@ public class AnimSetController implements ToolController {
|
||||
actionCombo.setMaxWidth(Double.MAX_VALUE);
|
||||
actionCombo.getSelectionModel().selectFirst();
|
||||
|
||||
ComboBox<String> clipCombo = new ComboBox<>();
|
||||
actionSearch.textProperty().addListener((obs, ov, nv) -> {
|
||||
String lower = nv == null ? "" : nv.toLowerCase();
|
||||
actionCombo.getItems().setAll(available.stream()
|
||||
.filter(a -> lower.isEmpty()
|
||||
|| a.displayName().toLowerCase().contains(lower)
|
||||
|| a.name().toLowerCase().contains(lower))
|
||||
.collect(java.util.stream.Collectors.toList()));
|
||||
if (!actionCombo.getItems().isEmpty()) actionCombo.getSelectionModel().selectFirst();
|
||||
});
|
||||
|
||||
// ── Clip-Combo mit Suchfeld ───────────────────────────────────────────
|
||||
java.util.List<String> allClipNames = new java.util.ArrayList<>(animSetClipListView.getItems());
|
||||
for (String subName : editableSubClips.keySet()) {
|
||||
if (!allClipNames.contains(subName)) allClipNames.add(subName);
|
||||
}
|
||||
|
||||
javafx.scene.control.TextField clipSearch = new javafx.scene.control.TextField();
|
||||
clipSearch.setPromptText("Clip suchen…");
|
||||
clipSearch.setMaxWidth(Double.MAX_VALUE);
|
||||
|
||||
ComboBox<String> clipCombo = new ComboBox<>();
|
||||
clipCombo.getItems().addAll(allClipNames);
|
||||
clipCombo.setMaxWidth(Double.MAX_VALUE);
|
||||
clipCombo.getSelectionModel().selectFirst();
|
||||
|
||||
clipSearch.textProperty().addListener((obs, ov, nv) -> {
|
||||
String lower = nv == null ? "" : nv.toLowerCase();
|
||||
clipCombo.getItems().setAll(allClipNames.stream()
|
||||
.filter(c -> lower.isEmpty() || c.toLowerCase().contains(lower))
|
||||
.collect(java.util.stream.Collectors.toList()));
|
||||
if (!clipCombo.getItems().isEmpty()) clipCombo.getSelectionModel().selectFirst();
|
||||
});
|
||||
|
||||
// ── Layout ────────────────────────────────────────────────────────────
|
||||
VBox actionBox = new VBox(4, actionSearch, actionCombo);
|
||||
VBox clipBox = new VBox(4, clipSearch, clipCombo);
|
||||
|
||||
javafx.scene.layout.GridPane grid = new javafx.scene.layout.GridPane();
|
||||
grid.setHgap(8); grid.setVgap(6);
|
||||
grid.add(new Label("Aktion:"), 0, 0); grid.add(actionCombo, 1, 0);
|
||||
grid.add(new Label("Clip:"), 0, 1); grid.add(clipCombo, 1, 1);
|
||||
grid.setHgap(8); grid.setVgap(8);
|
||||
grid.add(new Label("Aktion:"), 0, 0); grid.add(actionBox, 1, 0);
|
||||
grid.add(new Label("Clip:"), 0, 1); grid.add(clipBox, 1, 1);
|
||||
javafx.scene.layout.ColumnConstraints cc = new javafx.scene.layout.ColumnConstraints();
|
||||
cc.setHgrow(Priority.ALWAYS);
|
||||
grid.getColumnConstraints().addAll(new javafx.scene.layout.ColumnConstraints(), cc);
|
||||
|
||||
CheckBox weitereCB = new CheckBox("Weitere zuordnen");
|
||||
|
||||
VBox content = new VBox(12, grid, weitereCB);
|
||||
Dialog<ButtonType> dlg = new Dialog<>();
|
||||
dlg.initOwner(ctx.stageSupplier().get());
|
||||
dlg.setTitle("Aktion zuordnen");
|
||||
ButtonType ok = new ButtonType("Hinzufügen", ButtonBar.ButtonData.OK_DONE);
|
||||
dlg.getDialogPane().getButtonTypes().addAll(ok, ButtonType.CANCEL);
|
||||
dlg.getDialogPane().setContent(grid);
|
||||
dlg.getDialogPane().setContent(content);
|
||||
dlg.getDialogPane().setPrefWidth(480);
|
||||
|
||||
// Wenn "Weitere zuordnen" aktiv: Dialog offen lassen, nächste Aktion auto-selektieren.
|
||||
javafx.scene.Node okNode = dlg.getDialogPane().lookupButton(ok);
|
||||
okNode.addEventFilter(javafx.event.ActionEvent.ACTION, ev -> {
|
||||
if (!weitereCB.isSelected()) return;
|
||||
ev.consume();
|
||||
var action = actionCombo.getValue();
|
||||
var clip = clipCombo.getValue();
|
||||
if (action == null || clip == null) return;
|
||||
String entry = action.name() + " → " + clip;
|
||||
if (!animSetActionListView.getItems().contains(entry)) {
|
||||
animSetActionListView.getItems().add(entry);
|
||||
autoSaveAnimSet();
|
||||
}
|
||||
available.remove(action);
|
||||
String lower = actionSearch.getText() == null ? "" : actionSearch.getText().toLowerCase();
|
||||
actionCombo.getItems().setAll(available.stream()
|
||||
.filter(a -> lower.isEmpty()
|
||||
|| a.displayName().toLowerCase().contains(lower)
|
||||
|| a.name().toLowerCase().contains(lower))
|
||||
.collect(java.util.stream.Collectors.toList()));
|
||||
if (!actionCombo.getItems().isEmpty()) actionCombo.getSelectionModel().selectFirst();
|
||||
});
|
||||
|
||||
dlg.showAndWait().ifPresent(bt -> {
|
||||
if (bt != ok) return;
|
||||
var action = actionCombo.getValue();
|
||||
|
||||
@@ -53,10 +53,13 @@ public enum AnimationAction {
|
||||
AXE_IDLE, AXE_WALK, AXE_RUN,
|
||||
|
||||
// ── Zweihänder ────────────────────────────────────────────────────────────
|
||||
TWO_HAND_DRAW, TWO_HAND_SHEATHE, TWO_HAND_BLOCK,
|
||||
TWO_HAND_DRAW, TWO_HAND_SHEATHE, TWO_HAND_BLOCK,
|
||||
TWO_HAND_ATTACK_1, TWO_HAND_ATTACK_2, TWO_HAND_ATTACK_3, TWO_HAND_HEAVY_ATTACK,
|
||||
TWO_HAND_IDLE_TO_BLOCK, TWO_HAND_PARADE, TWO_HAND_BLOCK_IMPACT, TWO_HAND_IDLE_IMPACT,
|
||||
TWO_HAND_IDLE, TWO_HAND_WALK, TWO_HAND_RUN,
|
||||
TWO_HAND_IDLE, TWO_HAND_WALK, TWO_HAND_RUN, TWO_HAND_JUMP, TWO_HAND_RUNNING_JUMP,
|
||||
TWO_HAND_IDLE_BORED, TWO_HAND_IDLE_BORED_2, TWO_HAND_IDLE_BORED_3,
|
||||
TWO_HAND_DEATH, TWO_HAND_DEATH_2,
|
||||
TWO_HAND_BLOCK_MOVE, TWO_HAND_BLOCK_MOVE_BACKWARD, TWO_HAND_BLOCK_STRAFE_LEFT, TWO_HAND_BLOCK_STRAFE_RIGHT,
|
||||
|
||||
// ── Bogen ─────────────────────────────────────────────────────────────────
|
||||
BOW_DRAW, BOW_SHEATHE, BOW_HIT_TAKEN,
|
||||
@@ -157,8 +160,19 @@ public enum AnimationAction {
|
||||
case TWO_HAND_BLOCK_IMPACT -> TextRegistry.resolve(null, key, "Block-Treffer (Zweihänder)");
|
||||
case TWO_HAND_IDLE_IMPACT -> TextRegistry.resolve(null, key, "Treffer (Zweihänder)");
|
||||
case TWO_HAND_IDLE -> TextRegistry.resolve(null, key, "Stehen (Zweihänder)");
|
||||
case TWO_HAND_WALK -> TextRegistry.resolve(null, key, "Gehen (Zweihänder)");
|
||||
case TWO_HAND_RUN -> TextRegistry.resolve(null, key, "Laufen (Zweihänder)");
|
||||
case TWO_HAND_WALK -> TextRegistry.resolve(null, key, "Gehen (Zweihänder)");
|
||||
case TWO_HAND_RUN -> TextRegistry.resolve(null, key, "Laufen (Zweihänder)");
|
||||
case TWO_HAND_JUMP -> TextRegistry.resolve(null, key, "Sprung (Zweihänder)");
|
||||
case TWO_HAND_RUNNING_JUMP -> TextRegistry.resolve(null, key, "Sprung Laufend (Zweihänder)");
|
||||
case TWO_HAND_IDLE_BORED -> TextRegistry.resolve(null, key, "Idle Gelangweilt (Zweihänder)");
|
||||
case TWO_HAND_IDLE_BORED_2 -> TextRegistry.resolve(null, key, "Idle Gelangweilt 2 (Zweihänder)");
|
||||
case TWO_HAND_IDLE_BORED_3 -> TextRegistry.resolve(null, key, "Idle Gelangweilt 3 (Zweihänder)");
|
||||
case TWO_HAND_DEATH -> TextRegistry.resolve(null, key, "Tod (Zweihänder)");
|
||||
case TWO_HAND_DEATH_2 -> TextRegistry.resolve(null, key, "Tod 2 (Zweihänder)");
|
||||
case TWO_HAND_BLOCK_MOVE -> TextRegistry.resolve(null, key, "Block Vorwärts (Zweihänder)");
|
||||
case TWO_HAND_BLOCK_MOVE_BACKWARD -> TextRegistry.resolve(null, key, "Block Rückwärts (Zweihänder)");
|
||||
case TWO_HAND_BLOCK_STRAFE_LEFT -> TextRegistry.resolve(null, key, "Block Strafe Links (Zweihänder)");
|
||||
case TWO_HAND_BLOCK_STRAFE_RIGHT -> TextRegistry.resolve(null, key, "Block Strafe Rechts (Zweihänder)");
|
||||
|
||||
case BOW_DRAW -> TextRegistry.resolve(null, key, "Ziehen (Bogen)");
|
||||
case BOW_SHEATHE -> TextRegistry.resolve(null, key, "Wegstecken (Bogen)");
|
||||
|
||||
@@ -31,9 +31,8 @@ public final class SwordShieldCombat extends CombatStyleBase {
|
||||
public static AnimationAction resolveIdleImpactAction(ItemSubCategory weaponType) {
|
||||
if (weaponType == null) { return AnimationAction.SWORD_IDLE_IMPACT; }
|
||||
return switch (weaponType) {
|
||||
case AXE -> AnimationAction.AXE_IDLE_IMPACT;
|
||||
case TWO_HANDED_SWORD -> AnimationAction.TWO_HAND_IDLE_IMPACT;
|
||||
default -> AnimationAction.SWORD_IDLE_IMPACT;
|
||||
case AXE -> AnimationAction.AXE_IDLE_IMPACT;
|
||||
default -> AnimationAction.SWORD_IDLE_IMPACT;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -44,9 +43,8 @@ public final class SwordShieldCombat extends CombatStyleBase {
|
||||
public static AnimationAction resolveBlockImpactAction(ItemSubCategory weaponType) {
|
||||
if (weaponType == null) { return AnimationAction.SWORD_BLOCK_IMPACT; }
|
||||
return switch (weaponType) {
|
||||
case AXE -> AnimationAction.AXE_BLOCK_IMPACT;
|
||||
case TWO_HANDED_SWORD -> AnimationAction.TWO_HAND_BLOCK_IMPACT;
|
||||
default -> AnimationAction.SWORD_BLOCK_IMPACT;
|
||||
case AXE -> AnimationAction.AXE_BLOCK_IMPACT;
|
||||
default -> AnimationAction.SWORD_BLOCK_IMPACT;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -57,9 +55,8 @@ public final class SwordShieldCombat extends CombatStyleBase {
|
||||
public static AnimationAction resolveParadeAction(ItemSubCategory weaponType) {
|
||||
if (weaponType == null) { return AnimationAction.SWORD_PARADE; }
|
||||
return switch (weaponType) {
|
||||
case AXE -> AnimationAction.AXE_PARADE;
|
||||
case TWO_HANDED_SWORD -> AnimationAction.TWO_HAND_PARADE;
|
||||
default -> AnimationAction.SWORD_PARADE;
|
||||
case AXE -> AnimationAction.AXE_PARADE;
|
||||
default -> AnimationAction.SWORD_PARADE;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
package de.blight.game.combat.twohanded;
|
||||
|
||||
import de.blight.game.animation.AnimationAction;
|
||||
import de.blight.game.combat.CombatStyleBase;
|
||||
|
||||
/**
|
||||
* Gemeinsame Kampflogik für den Zweihänder-Stil.
|
||||
*
|
||||
* Wird von beiden Seiten genutzt:
|
||||
* – {@code PlayerInputControl} (Held)
|
||||
* – {@code WorldNpcsState} (Gegner-KI)
|
||||
*
|
||||
* Alle Animations-Auflösungen (welche Animation für welches Ereignis?) und
|
||||
* alle Reaktions-Anwendungen (Impact, Stagger, Konterparade) sind hier
|
||||
* zentralisiert. Bug-Fixes gelten damit automatisch für Held und NPC.
|
||||
*
|
||||
* Neuer Kampfstil: erbt von {@link CombatStyleBase} und folgt demselben
|
||||
* Muster in einem eigenen Unter-Package.
|
||||
*/
|
||||
public final class TwoHandedCombat extends CombatStyleBase {
|
||||
|
||||
private TwoHandedCombat() {}
|
||||
|
||||
// ── Animations-Auflösung ──────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Impact-Animation in Idle/Kampf-Haltung (Entität wurde getroffen).
|
||||
* Identisch für Spieler und NPC.
|
||||
*/
|
||||
public static AnimationAction resolveIdleImpactAction() {
|
||||
return AnimationAction.TWO_HAND_IDLE_IMPACT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Block-Impact-Animation (Entität hat einen Treffer abgeblockt).
|
||||
* Identisch für Spieler und NPC.
|
||||
*/
|
||||
public static AnimationAction resolveBlockImpactAction() {
|
||||
return AnimationAction.TWO_HAND_BLOCK_IMPACT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Konterangriff-Animation nach erfolgreicher Parade.
|
||||
* Identisch für Spieler und NPC.
|
||||
*/
|
||||
public static AnimationAction resolveParadeAction() {
|
||||
return AnimationAction.TWO_HAND_PARADE;
|
||||
}
|
||||
|
||||
// ── Reaktionen anwenden ───────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Normaler Treffer-Impact (Entität wurde getroffen, kein Block).
|
||||
* Für Held: {@code PlayerInputControl} implementiert {@link AnimReceiver}.
|
||||
* Für NPC: {@code WorldNpcsState.npcAnimReceiver(s)} liefert den Adapter.
|
||||
*/
|
||||
public static void applyNormalImpact(AnimReceiver receiver) {
|
||||
receiver.receiveBlockingAnim(resolveIdleImpactAction(), IMPACT_SPEED_NORMAL, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Konterparade-Impact (Angreifer wird durch Parade gestoppt – Slow Motion).
|
||||
* Wird aufgerufen auf dem ANGREIFER, nicht auf dem Parierenden.
|
||||
*/
|
||||
public static void applyParriedImpact(AnimReceiver receiver) {
|
||||
receiver.receiveBlockingAnim(resolveIdleImpactAction(), IMPACT_SPEED_PARRIED, null);
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import de.blight.game.combat.CombatStyleBase;
|
||||
import de.blight.game.combat.MeleeAttackCallback;
|
||||
import de.blight.game.combat.WeaponCombatState;
|
||||
import de.blight.game.combat.swordshield.SwordShieldCombat;
|
||||
import de.blight.game.combat.twohanded.TwoHandedCombat;
|
||||
import de.blight.game.navigation.CharacterNavigator;
|
||||
import de.blight.game.state.TerrainChunkState;
|
||||
|
||||
@@ -1254,10 +1255,11 @@ public class PlayerInputControl implements CombatStyleBase.AnimReceiver {
|
||||
} else if (blockPhase == BlockPhase.HOLDING && !combatAnimActive) {
|
||||
// Beim Blocken: Bewegungsanimation nach Taste, Strafe hat Vorrang
|
||||
AnimationAction blockMoveAnim;
|
||||
if (right) blockMoveAnim = AnimationAction.SWORD_BLOCK_STRAFE_RIGHT;
|
||||
else if (left) blockMoveAnim = AnimationAction.SWORD_BLOCK_STRAFE_LEFT;
|
||||
else if (forward) blockMoveAnim = AnimationAction.SWORD_BLOCK_MOVE;
|
||||
else if (backward) blockMoveAnim = AnimationAction.SWORD_BLOCK_MOVE_BACKWARD;
|
||||
boolean twoHand = drawnWeaponType == ItemSubCategory.TWO_HANDED_SWORD;
|
||||
if (right) blockMoveAnim = twoHand ? AnimationAction.TWO_HAND_BLOCK_STRAFE_RIGHT : AnimationAction.SWORD_BLOCK_STRAFE_RIGHT;
|
||||
else if (left) blockMoveAnim = twoHand ? AnimationAction.TWO_HAND_BLOCK_STRAFE_LEFT : AnimationAction.SWORD_BLOCK_STRAFE_LEFT;
|
||||
else if (forward) blockMoveAnim = twoHand ? AnimationAction.TWO_HAND_BLOCK_MOVE : AnimationAction.SWORD_BLOCK_MOVE;
|
||||
else if (backward) blockMoveAnim = twoHand ? AnimationAction.TWO_HAND_BLOCK_MOVE_BACKWARD : AnimationAction.SWORD_BLOCK_MOVE_BACKWARD;
|
||||
else blockMoveAnim = getMeleeBlockAction(drawnWeaponType);
|
||||
|
||||
if (blockMoveAnim != currentAnim) {
|
||||
@@ -1861,10 +1863,12 @@ public class PlayerInputControl implements CombatStyleBase.AnimReceiver {
|
||||
}
|
||||
|
||||
public static AnimationAction getMeleeBlockImpactAction(ItemSubCategory s) {
|
||||
if (s == ItemSubCategory.TWO_HANDED_SWORD) { return TwoHandedCombat.resolveBlockImpactAction(); }
|
||||
return SwordShieldCombat.resolveBlockImpactAction(s);
|
||||
}
|
||||
|
||||
public static AnimationAction getMeleeIdleImpactAction(ItemSubCategory s) {
|
||||
if (s == ItemSubCategory.TWO_HANDED_SWORD) { return TwoHandedCombat.resolveIdleImpactAction(); }
|
||||
return SwordShieldCombat.resolveIdleImpactAction(s);
|
||||
}
|
||||
|
||||
@@ -2143,7 +2147,9 @@ public class PlayerInputControl implements CombatStyleBase.AnimReceiver {
|
||||
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";
|
||||
SWORD_BLOCK_STRAFE_LEFT, SWORD_BLOCK_STRAFE_RIGHT,
|
||||
TWO_HAND_BLOCK_MOVE, TWO_HAND_BLOCK_MOVE_BACKWARD,
|
||||
TWO_HAND_BLOCK_STRAFE_LEFT, TWO_HAND_BLOCK_STRAFE_RIGHT -> "walking";
|
||||
case RUN,
|
||||
SWORD_RUN, AXE_RUN, TWO_HAND_RUN,
|
||||
BOW_RUN, CROSSBOW_RUN, CRYSTAL_RUN -> "running";
|
||||
|
||||
@@ -55,6 +55,7 @@ import de.blight.game.combat.DamageCalculator;
|
||||
import de.blight.game.combat.MeleeAttackCallback;
|
||||
import de.blight.game.combat.WeaponCombatState;
|
||||
import de.blight.game.combat.swordshield.SwordShieldCombat;
|
||||
import de.blight.game.combat.twohanded.TwoHandedCombat;
|
||||
import de.blight.game.npc.AggroEvent;
|
||||
import de.blight.game.npc.NpcAggroBehavior;
|
||||
import de.blight.lang.TextResolver;
|
||||
@@ -2036,7 +2037,12 @@ public class WorldNpcsState extends BaseAppState implements MeleeAttackCallback
|
||||
s.combat.phase = WeaponCombatState.Phase.COUNTER_WINDOW;
|
||||
s.combat.timer = dur * 2f;
|
||||
s.paradeFromParry = true;
|
||||
SwordShieldCombat.applyParriedImpact(playerInput, playerInput.getDrawnWeaponType());
|
||||
ItemSubCategory pwt = playerInput.getDrawnWeaponType();
|
||||
if (pwt == ItemSubCategory.TWO_HANDED_SWORD) {
|
||||
TwoHandedCombat.applyParriedImpact(playerInput);
|
||||
} else {
|
||||
SwordShieldCombat.applyParriedImpact(playerInput, pwt);
|
||||
}
|
||||
return;
|
||||
}
|
||||
s.konterParadeAttackId = -1; // Entscheidung war kein Konter → zurücksetzen
|
||||
@@ -2501,11 +2507,19 @@ public class WorldNpcsState extends BaseAppState implements MeleeAttackCallback
|
||||
}
|
||||
|
||||
private void staggerNpc(SpawnedNpc s) {
|
||||
SwordShieldCombat.applyNormalImpact(npcAnimReceiver(s), s.combatWeaponType);
|
||||
if (s.combatWeaponType == ItemSubCategory.TWO_HANDED_SWORD) {
|
||||
TwoHandedCombat.applyNormalImpact(npcAnimReceiver(s));
|
||||
} else {
|
||||
SwordShieldCombat.applyNormalImpact(npcAnimReceiver(s), s.combatWeaponType);
|
||||
}
|
||||
}
|
||||
|
||||
private void staggerNpcSlow(SpawnedNpc s) {
|
||||
SwordShieldCombat.applyParriedImpact(npcAnimReceiver(s), s.combatWeaponType);
|
||||
if (s.combatWeaponType == ItemSubCategory.TWO_HANDED_SWORD) {
|
||||
TwoHandedCombat.applyParriedImpact(npcAnimReceiver(s));
|
||||
} else {
|
||||
SwordShieldCombat.applyParriedImpact(npcAnimReceiver(s), s.combatWeaponType);
|
||||
}
|
||||
}
|
||||
|
||||
private void setNpcCurrentActionSpeed(SpawnedNpc s, float speed) {
|
||||
@@ -2575,15 +2589,18 @@ public class WorldNpcsState extends BaseAppState implements MeleeAttackCallback
|
||||
s.paradeFromParry, playerInput.isBlocking());
|
||||
|
||||
if (isHeavy) {
|
||||
ItemSubCategory pwtH = playerInput.getDrawnWeaponType();
|
||||
if (playerInput.isBlocking()) {
|
||||
// Schwerer Angriff bricht Block — kein HP-Verlust, aber Stagger
|
||||
if (weaponSoundSystem != null) { weaponSoundSystem.play("impact"); }
|
||||
playerInput.triggerBlockImpact();
|
||||
SwordShieldCombat.applyNormalImpact(playerInput, playerInput.getDrawnWeaponType());
|
||||
if (pwtH == ItemSubCategory.TWO_HANDED_SWORD) { TwoHandedCombat.applyNormalImpact(playerInput); }
|
||||
else { SwordShieldCombat.applyNormalImpact(playerInput, pwtH); }
|
||||
} else {
|
||||
if (weaponSoundSystem != null) { weaponSoundSystem.play("impact"); }
|
||||
applyPlayerDamage(s, Math.max(1, base * 3));
|
||||
SwordShieldCombat.applyNormalImpact(playerInput, playerInput.getDrawnWeaponType());
|
||||
if (pwtH == ItemSubCategory.TWO_HANDED_SWORD) { TwoHandedCombat.applyNormalImpact(playerInput); }
|
||||
else { SwordShieldCombat.applyNormalImpact(playerInput, pwtH); }
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -2600,7 +2617,9 @@ public class WorldNpcsState extends BaseAppState implements MeleeAttackCallback
|
||||
// Konterparade: Spieler wurde bereits per applyParriedImpact gestaggert → keine zweite Animation.
|
||||
// Block+Konter: Spieler war NICHT gestaggert → normale Impact-Animation abspielen.
|
||||
if (!s.paradeFromParry) {
|
||||
SwordShieldCombat.applyNormalImpact(playerInput, playerInput.getDrawnWeaponType());
|
||||
ItemSubCategory pwtN = playerInput.getDrawnWeaponType();
|
||||
if (pwtN == ItemSubCategory.TWO_HANDED_SWORD) { TwoHandedCombat.applyNormalImpact(playerInput); }
|
||||
else { SwordShieldCombat.applyNormalImpact(playerInput, pwtN); }
|
||||
}
|
||||
s.paradeFromParry = false;
|
||||
}
|
||||
@@ -2686,14 +2705,17 @@ public class WorldNpcsState extends BaseAppState implements MeleeAttackCallback
|
||||
}
|
||||
|
||||
private AnimationAction resolveNpcBlockImpactAction(SpawnedNpc s) {
|
||||
if (s.combatWeaponType == ItemSubCategory.TWO_HANDED_SWORD) { return TwoHandedCombat.resolveBlockImpactAction(); }
|
||||
return SwordShieldCombat.resolveBlockImpactAction(s.combatWeaponType);
|
||||
}
|
||||
|
||||
private AnimationAction resolveNpcParadeAction(SpawnedNpc s) {
|
||||
if (s.combatWeaponType == ItemSubCategory.TWO_HANDED_SWORD) { return TwoHandedCombat.resolveParadeAction(); }
|
||||
return SwordShieldCombat.resolveParadeAction(s.combatWeaponType);
|
||||
}
|
||||
|
||||
private AnimationAction resolveNpcStaggerAction(SpawnedNpc s) {
|
||||
if (s.combatWeaponType == ItemSubCategory.TWO_HANDED_SWORD) { return TwoHandedCombat.resolveIdleImpactAction(); }
|
||||
return SwordShieldCombat.resolveIdleImpactAction(s.combatWeaponType);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user