From f189e6d81f1ead11aa1485e5d966cb09bbc1d359 Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 26 Oct 2026 12:09:37 +0100 Subject: [PATCH] =?UTF-8?q?Kampfsystem=20-=20hier:=20Anker=20beim=20Kurzsc?= =?UTF-8?q?hwert,=20sowohl=20f=C3=BCr=20Helden=20als=20auch=20NPC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/weapon_attachment.json | 6 +-- .../common/model/ShieldAttachmentIO.java | 38 +++++++++++++++- .../common/model/WeaponAttachmentIO.java | 43 ++++++++++++++++++- .../blight/editor/state/ModelEditorState.java | 39 +++++++++++++++++ .../control/NpcWeaponAttachmentControl.java | 17 ++++++-- .../game/control/PlayerInputControl.java | 6 +++ 6 files changed, 139 insertions(+), 10 deletions(-) diff --git a/blight-assets/src/main/resources/weapon_attachment.json b/blight-assets/src/main/resources/weapon_attachment.json index 14d00ea..058a693 100644 --- a/blight-assets/src/main/resources/weapon_attachment.json +++ b/blight-assets/src/main/resources/weapon_attachment.json @@ -1,8 +1,8 @@ { "SWORD": { - "handOffX": 0.095, - "handOffY": 0.045, - "handOffZ": 0.645, + "handOffX": 0.085, + "handOffY": 0.115, + "handOffZ": -0.095, "hand2OffX": 0.0, "hand2OffY": 0.0, "hand2OffZ": 0.0 diff --git a/blight-common/src/main/java/de/blight/common/model/ShieldAttachmentIO.java b/blight-common/src/main/java/de/blight/common/model/ShieldAttachmentIO.java index 117fb77..f033d58 100644 --- a/blight-common/src/main/java/de/blight/common/model/ShieldAttachmentIO.java +++ b/blight-common/src/main/java/de/blight/common/model/ShieldAttachmentIO.java @@ -9,11 +9,14 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; /** * Lädt und speichert per-Kategorie Schild-Attachment-Konfiguration (shield_attachment.json). + * Siehe WeaponAttachmentIO: liest vom Dateisystem (Dev-Umgebung) und prüft bei jedem Zugriff + * die mtime der Datei, damit Editor-Änderungen ohne Neustart ankommen. */ public final class ShieldAttachmentIO { @@ -21,8 +24,17 @@ public final class ShieldAttachmentIO { private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); private static final String CLASSPATH_FILE = "shield_attachment.json"; + private static final String[] ASSET_BASES = { + "blight-assets/src/main/resources", + "../blight-assets/src/main/resources", + "assets", + ".", + }; + private static final Map CONFIGS = new HashMap<>(); - private static boolean loaded = false; + private static boolean loaded = false; + private static Path cachedFile = null; + private static long cachedMtime = -1; private ShieldAttachmentIO() {} @@ -77,9 +89,33 @@ public final class ShieldAttachmentIO { } private static synchronized void ensureLoaded() { + Path file = resolveFile(); + if (file != null) { + try { + long mtime = Files.getLastModifiedTime(file).toMillis(); + if (!file.equals(cachedFile) || mtime != cachedMtime) { + parse(Files.readString(file, StandardCharsets.UTF_8)); + cachedFile = file; + cachedMtime = mtime; + } + loaded = true; + return; + } catch (IOException e) { + log.warn("[ShieldAttachmentIO] Ladefehler: {}", e.getMessage()); + } + } if (!loaded) loadFromClasspath(); } + /** Sucht {@code shield_attachment.json} im Dateisystem der Dev-Umgebung (Editor/Gradle-Run). */ + private static Path resolveFile() { + for (String base : ASSET_BASES) { + Path p = Paths.get(base, CLASSPATH_FILE); + if (Files.isRegularFile(p)) return p; + } + return null; + } + private static void parse(String json) { CONFIGS.clear(); JsonObject obj = JsonParser.parseString(json).getAsJsonObject(); diff --git a/blight-common/src/main/java/de/blight/common/model/WeaponAttachmentIO.java b/blight-common/src/main/java/de/blight/common/model/WeaponAttachmentIO.java index 85a24c4..0f96c4d 100644 --- a/blight-common/src/main/java/de/blight/common/model/WeaponAttachmentIO.java +++ b/blight-common/src/main/java/de/blight/common/model/WeaponAttachmentIO.java @@ -9,12 +9,16 @@ import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.HashMap; import java.util.Map; /** * Lädt und speichert per-Kategorie Waffen-Attachment-Konfiguration (weapon_attachment.json). - * Singleton-Map: wird beim ersten Zugriff automatisch vom Classpath geladen. + * Singleton-Map: wird vom Dateisystem geladen (Dev-Umgebung, siehe ASSET_BASES) und bei jedem + * Zugriff gegen die mtime der Datei geprüft, damit Änderungen aus dem Editor ohne Neustart + * ankommen. Nur wenn keine Quelldatei auffindbar ist (z.B. gepackter Build), wird einmalig + * vom Classpath geladen. */ public final class WeaponAttachmentIO { @@ -22,8 +26,19 @@ public final class WeaponAttachmentIO { private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); private static final String CLASSPATH_FILE = "weapon_attachment.json"; + /** Siehe AnimationLibrary.ASSET_BASES (blight-game) – hier dupliziert, da blight-common + * keine Abhängigkeit auf blight-game haben darf. */ + private static final String[] ASSET_BASES = { + "blight-assets/src/main/resources", + "../blight-assets/src/main/resources", + "assets", + ".", + }; + private static final Map CONFIGS = new HashMap<>(); - private static boolean loaded = false; + private static boolean loaded = false; + private static Path cachedFile = null; + private static long cachedMtime = -1; private WeaponAttachmentIO() {} @@ -84,9 +99,33 @@ public final class WeaponAttachmentIO { } private static synchronized void ensureLoaded() { + Path file = resolveFile(); + if (file != null) { + try { + long mtime = Files.getLastModifiedTime(file).toMillis(); + if (!file.equals(cachedFile) || mtime != cachedMtime) { + parse(Files.readString(file, StandardCharsets.UTF_8)); + cachedFile = file; + cachedMtime = mtime; + } + loaded = true; + return; + } catch (IOException e) { + log.warn("[WeaponAttachmentIO] Ladefehler: {}", e.getMessage()); + } + } if (!loaded) loadFromClasspath(); } + /** Sucht {@code weapon_attachment.json} im Dateisystem der Dev-Umgebung (Editor/Gradle-Run). */ + private static Path resolveFile() { + for (String base : ASSET_BASES) { + Path p = Paths.get(base, CLASSPATH_FILE); + if (Files.isRegularFile(p)) return p; + } + return null; + } + private static void parse(String json) { CONFIGS.clear(); JsonObject obj = JsonParser.parseString(json).getAsJsonObject(); diff --git a/blight-editor/src/main/java/de/blight/editor/state/ModelEditorState.java b/blight-editor/src/main/java/de/blight/editor/state/ModelEditorState.java index e815e1c..52ec95c 100644 --- a/blight-editor/src/main/java/de/blight/editor/state/ModelEditorState.java +++ b/blight-editor/src/main/java/de/blight/editor/state/ModelEditorState.java @@ -296,6 +296,13 @@ public class ModelEditorState extends BaseAppState { updateCharPreviewWeapon(); } + // Orbit-Kamera im Charakter-Preview immer um den aktuellen Hand-Ankerpunkt drehen + // (statt um die Bounding-Box der isolierten Waffe, die am Boden/"Fuß" verankert ist) + if (charPreviewNode != null) { + Vector3f anchor = computeCharAnchorWorld(); + if (anchor != null) orbitCenter = anchor; + } + if (input.modelEditorEmbedLodsRequest) { input.modelEditorEmbedLodsRequest = false; embedLodsAndSave(input.modelEditorEmbedLodsPath); @@ -2133,6 +2140,38 @@ public class ModelEditorState extends BaseAppState { if (modelWrapper != null) modelWrapper.setCullHint(Spatial.CullHint.Inherit); if (gripGizmoNode != null) gripGizmoNode.setCullHint(Spatial.CullHint.Always); input.gripEditorCharPreviewReady = false; + updateBounds(); // Orbit-Zentrum zurück auf die Bounding-Box der isolierten Waffe + } + + /** + * Liefert die aktuelle Position des Hand-Ankerpunkts (Waffen-Griff bzw. Schild-Anker) + * in Weltkoordinaten, oder null wenn kein Charakter-Preview aktiv ist. + */ + private Vector3f computeCharAnchorWorld() { + if (charPreviewNode == null) return null; + Quaternion charWorldRot = charPreviewNode.getWorldRotation(); + Quaternion charWorldRotInv = charWorldRot.inverse(); + Vector3f charWorldPos = charPreviewNode.getWorldTranslation(); + + if (input.shieldEditorActive) { + if (charLeftHandNode == null) return null; + Vector3f leftBoneInChar = charWorldRotInv.mult( + charLeftHandNode.getWorldTranslation().subtract(charWorldPos)); + Quaternion leftBoneRotInChar = charWorldRotInv.mult(charLeftHandNode.getWorldRotation()); + Vector3f anchorInChar = leftBoneInChar.add( + leftBoneRotInChar.mult(new Vector3f(0f, -input.shieldEditorElbowOffset, 0f))); + return charWorldPos.add(charWorldRot.mult(anchorInChar)); + } + + if (charHandNode == null) return null; + Vector3f boneInChar = charWorldRotInv.mult( + charHandNode.getWorldTranslation().subtract(charWorldPos)); + Quaternion boneRotInChar = charWorldRotInv.mult(charHandNode.getWorldRotation()); + Vector3f palmOff = charPalmOffset; + Vector3f attachInChar = (palmOff != null) + ? boneInChar.add(boneRotInChar.mult(palmOff.mult(0.5f))) + : boneInChar; + return charWorldPos.add(charWorldRot.mult(attachInChar)); } private void updateCharPreviewWeapon() { diff --git a/blight-game/src/main/java/de/blight/game/control/NpcWeaponAttachmentControl.java b/blight-game/src/main/java/de/blight/game/control/NpcWeaponAttachmentControl.java index eddfdfa..317fc43 100644 --- a/blight-game/src/main/java/de/blight/game/control/NpcWeaponAttachmentControl.java +++ b/blight-game/src/main/java/de/blight/game/control/NpcWeaponAttachmentControl.java @@ -217,7 +217,10 @@ public class NpcWeaponAttachmentControl extends AbstractControl { Quaternion animDelta = (handBindRotInChar != null) ? boneRotInChar.mult(handBindRotInChar.inverse()) : boneRotInChar; - boolean bladeAtMinusZ = grip != null && grip.getGrip2PosZ() != 0f && grip.getGrip2PosZ() > grip.getPosZ(); + boolean bladeAtMinusZ = grip != null && ( + (grip.getGrip2PosX() != 0f || grip.getGrip2PosY() != 0f || grip.getGrip2PosZ() != 0f) + ? grip.getGrip2PosZ() > grip.getPosZ() + : grip.getPosZ() > 0f); Quaternion bladeFlip = bladeAtMinusZ ? new Quaternion().fromAngles(com.jme3.math.FastMath.PI, 0f, 0f) : new Quaternion(); @@ -227,6 +230,9 @@ public class NpcWeaponAttachmentControl extends AbstractControl { : new Vector3f(cfg.getHandOffX(), cfg.getHandOffY(), cfg.getHandOffZ()); Vector3f weaponPos = bonePosInChar.subtract(weaponRot.mult(gripPos)); weaponPos = weaponPos.add(weaponRot.mult(handOff)); + if (grip != null && (grip.getHandOffsetX() != 0f || grip.getHandOffsetY() != 0f || grip.getHandOffsetZ() != 0f)) { + weaponPos = weaponPos.add(grip.getHandOffsetX(), grip.getHandOffsetY(), grip.getHandOffsetZ()); + } s.setLocalRotation(weaponRot); s.setLocalTranslation(weaponPos); } @@ -288,17 +294,20 @@ public class NpcWeaponAttachmentControl extends AbstractControl { DEG_TO_RAD * grip.getRotY(), DEG_TO_RAD * grip.getRotZ()) : new Quaternion(); - boolean bladeAtMinusZ2 = grip != null && grip.getGrip2PosZ() != 0f && grip.getGrip2PosZ() > grip.getPosZ(); + boolean bladeAtMinusZ2 = grip != null && ( + (grip.getGrip2PosX() != 0f || grip.getGrip2PosY() != 0f || grip.getGrip2PosZ() != 0f) + ? grip.getGrip2PosZ() > grip.getPosZ() + : grip.getPosZ() > 0f); Quaternion bladeFlip2 = bladeAtMinusZ2 ? new Quaternion().fromAngles(com.jme3.math.FastMath.PI, 0f, 0f) : new Quaternion(); Quaternion weaponRot = boneRotInChar.mult(BACK_ROT_NORM).mult(bladeFlip2).mult(gripRot.inverse()); Vector3f target = boneInChar.add(boneRotInChar.mult( twoHanded ? TWO_HAND_BACK_GRIP_OFFSET : BACK_GRIP_OFFSET)); + // X=0: Waffenmitte (nicht handspezifischer Griffpunkt) auf Wirbelsäule zentrieren Vector3f gripPos = grip != null - ? new Vector3f(grip.getPosX(), grip.getPosY(), grip.getPosZ()) : Vector3f.ZERO; + ? new Vector3f(0f, grip.getPosY(), grip.getPosZ()) : Vector3f.ZERO; Vector3f backPos = target.subtract(weaponRot.mult(gripPos)); - if (bladeAtMinusZ2) backPos = backPos.add(0.1f, 0f, 0f); s.setLocalRotation(weaponRot); s.setLocalTranslation(backPos); } diff --git a/blight-game/src/main/java/de/blight/game/control/PlayerInputControl.java b/blight-game/src/main/java/de/blight/game/control/PlayerInputControl.java index 4f1d335..9fba104 100644 --- a/blight-game/src/main/java/de/blight/game/control/PlayerInputControl.java +++ b/blight-game/src/main/java/de/blight/game/control/PlayerInputControl.java @@ -1429,6 +1429,12 @@ public class PlayerInputControl implements CombatStyleBase.AnimReceiver { private void handleCombatAlt(boolean isPressed) { if (!weaponDrawn) return; if (isMeleeType(drawnWeaponType)) { + // Schwert blockt per Schild – ohne Schild ist Blocken nicht möglich. + // Zweihänder/Axt blocken mit der Waffe selbst, brauchen keinen Schild. + if (drawnWeaponType == ItemSubCategory.SWORD && !hasShieldEquipped()) { + rightMouseHeld = false; + return; + } if (isPressed) { rightMouseHeld = true; // Sofort blocken nur wenn keine andere Animation läuft