Impact System ausgearbeitet
This commit is contained in:
BIN
assets/imported/models/wooden straw man 3d model.glb
Normal file
BIN
assets/imported/models/wooden straw man 3d model.glb
Normal file
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 7.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 7.7 KiB |
BIN
blight-assets/src/main/resources/Models/imported/kampfdummy.j3o
Normal file
BIN
blight-assets/src/main/resources/Models/imported/kampfdummy.j3o
Normal file
Binary file not shown.
@@ -0,0 +1,30 @@
|
|||||||
|
#Tue Sep 22 20:54:03 CEST 2026
|
||||||
|
attachedEmitters.count=0
|
||||||
|
attachedLights.count=0
|
||||||
|
attachedSounds.count=0
|
||||||
|
castShadow=true
|
||||||
|
category=
|
||||||
|
cullDistance=120.0
|
||||||
|
footstepSurface=
|
||||||
|
interactableOffsetX=0.0
|
||||||
|
interactableOffsetY=0.5
|
||||||
|
interactableOffsetZ=0.0
|
||||||
|
interactableRotY=0.0
|
||||||
|
interactableType=NONE
|
||||||
|
lod1Distance=30.0
|
||||||
|
lod1Path=
|
||||||
|
lod2Distance=80.0
|
||||||
|
lod2Path=
|
||||||
|
modelCategory=DUMMY
|
||||||
|
name=kampfdummy
|
||||||
|
pivotOffsetY=0.0
|
||||||
|
placementOffsetY=0.0
|
||||||
|
randomScaleMax=1.0
|
||||||
|
randomScaleMin=1.0
|
||||||
|
receiveShadow=true
|
||||||
|
scaleX=1.0
|
||||||
|
scaleY=1.0
|
||||||
|
scaleZ=1.0
|
||||||
|
solid=true
|
||||||
|
tags=
|
||||||
|
uniformScale=true
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
#Tue Sep 22 20:52:39 CEST 2026
|
||||||
|
attachedEmitters.count=0
|
||||||
|
attachedLights.count=0
|
||||||
|
attachedSounds.count=0
|
||||||
|
castShadow=true
|
||||||
|
category=
|
||||||
|
cullDistance=120.0
|
||||||
|
footstepSurface=
|
||||||
|
interactableOffsetX=0.0
|
||||||
|
interactableOffsetY=0.5
|
||||||
|
interactableOffsetZ=0.0
|
||||||
|
interactableRotY=0.0
|
||||||
|
interactableType=NONE
|
||||||
|
lod1Distance=30.0
|
||||||
|
lod1Path=
|
||||||
|
lod2Distance=80.0
|
||||||
|
lod2Path=
|
||||||
|
modelCategory=NONE
|
||||||
|
name=wooden straw man 3d model
|
||||||
|
pivotOffsetY=0.0
|
||||||
|
placementOffsetY=0.0
|
||||||
|
randomScaleMax=1.0
|
||||||
|
randomScaleMin=1.0
|
||||||
|
receiveShadow=true
|
||||||
|
scaleX=1.0
|
||||||
|
scaleY=1.0
|
||||||
|
scaleZ=1.0
|
||||||
|
solid=true
|
||||||
|
tags=
|
||||||
|
uniformScale=true
|
||||||
@@ -3,16 +3,19 @@ package de.blight.game.combat;
|
|||||||
import com.jme3.math.Vector3f;
|
import com.jme3.math.Vector3f;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wird aufgerufen wenn der Spieler einen Nahkampfangriff auslöst.
|
* Wird pro Frame während des Treffer-Fensters einer Nahkampf-Animation aufgerufen.
|
||||||
* Implementierende Klassen können z.B. Treffererkennung auf Zielobjekten durchführen.
|
* Jeder Schwung hat eine eindeutige attackId; hitThisAttack muss pro ID zurückgesetzt werden.
|
||||||
*/
|
*/
|
||||||
public interface MeleeAttackCallback {
|
public interface MeleeAttackCallback {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param charPosition Weltposition des Charakters beim Angriff
|
* @param attackId Eindeutige ID pro Schwung (inkrementiert bei jedem Angriff)
|
||||||
* @param charForward Blickrichtung des Charakters (normiert, Y=0)
|
* @param gripPos Weltposition des Griffes (Hand-Bone)
|
||||||
* @param baseDamage Basisschaden der Waffe (0 = unbewaffnet)
|
* @param bladeDir Normierter Klingen-Richtungsvektor in Weltkoordinaten
|
||||||
* @param critMultiplier Schadens-Multiplikator (1.0 = normal, 2.0 = Kombo-5, 1.5–3.0 = Schwerer Angriff)
|
* @param bladeLength Länge der Klinge in Metern (Ray-Reichweite)
|
||||||
|
* @param baseDamage Basisschaden der Waffe (0 = unbewaffnet → Fallback auf 5)
|
||||||
|
* @param critMultiplier Schadens-Multiplikator (1.0 = normal, 2.0 = Kombo-5, 3.0 = Schwerer Angriff)
|
||||||
*/
|
*/
|
||||||
void onMeleeAttack(Vector3f charPosition, Vector3f charForward, int baseDamage, float critMultiplier);
|
void onMeleeAttack(int attackId, Vector3f gripPos, Vector3f bladeDir,
|
||||||
|
float bladeLength, int baseDamage, float critMultiplier);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,6 +132,14 @@ public class PlayerInputControl {
|
|||||||
/** Gesamtdauer der aktuellen Kampfanim — für Kombo-Fenster-Timing (öffnet bei 50%). */
|
/** Gesamtdauer der aktuellen Kampfanim — für Kombo-Fenster-Timing (öffnet bei 50%). */
|
||||||
private float combatAnimDuration = 0f;
|
private float combatAnimDuration = 0f;
|
||||||
private MeleeAttackCallback meleeAttackCallback = null;
|
private MeleeAttackCallback meleeAttackCallback = null;
|
||||||
|
private WeaponAttachmentControl weaponAttach = null;
|
||||||
|
private static final float HIT_WINDOW_START = 0.20f;
|
||||||
|
private static final float HIT_WINDOW_END = 0.75f;
|
||||||
|
private static final float WEAPON_BLADE_LENGTH = 1.0f;
|
||||||
|
private int attackIdCounter = 0;
|
||||||
|
private int currentAttackId = -1;
|
||||||
|
private boolean hitWindowActive = false;
|
||||||
|
private float hitWindowCritMult = 1.0f;
|
||||||
// Draw/Sheath Animation System
|
// Draw/Sheath Animation System
|
||||||
private boolean drawAnimActive = false;
|
private boolean drawAnimActive = false;
|
||||||
private float drawAnimTimer = 0f;
|
private float drawAnimTimer = 0f;
|
||||||
@@ -373,6 +381,7 @@ public class PlayerInputControl {
|
|||||||
public boolean isLastAttackCritical() { return lastAttackCritical; }
|
public boolean isLastAttackCritical() { return lastAttackCritical; }
|
||||||
public com.jme3.anim.SkinningControl getSkinningControl() { return skinningControl; }
|
public com.jme3.anim.SkinningControl getSkinningControl() { return skinningControl; }
|
||||||
public void setMeleeAttackCallback(MeleeAttackCallback cb) { this.meleeAttackCallback = cb; }
|
public void setMeleeAttackCallback(MeleeAttackCallback cb) { this.meleeAttackCallback = cb; }
|
||||||
|
public void setWeaponAttachmentControl(WeaponAttachmentControl w) { this.weaponAttach = w; }
|
||||||
|
|
||||||
public boolean isPaused() { return paused; }
|
public boolean isPaused() { return paused; }
|
||||||
|
|
||||||
@@ -833,6 +842,7 @@ public class PlayerInputControl {
|
|||||||
physicsChar.setWalkDirection(inLungeWindow ? heavyLungeVel : Vector3f.ZERO);
|
physicsChar.setWalkDirection(inLungeWindow ? heavyLungeVel : Vector3f.ZERO);
|
||||||
if (combatAnimRemaining <= 0f) {
|
if (combatAnimRemaining <= 0f) {
|
||||||
combatAnimActive = false;
|
combatAnimActive = false;
|
||||||
|
hitWindowActive = false;
|
||||||
heavyLungeVel = null;
|
heavyLungeVel = null;
|
||||||
heavyLungeStartRemaining = 0f;
|
heavyLungeStartRemaining = 0f;
|
||||||
heavyLungeEndRemaining = 0f;
|
heavyLungeEndRemaining = 0f;
|
||||||
@@ -856,6 +866,15 @@ public class PlayerInputControl {
|
|||||||
comboWindowActive = true;
|
comboWindowActive = true;
|
||||||
log.info("[Combat] Kombo-Fenster geöffnet (step={}, remaining={}s)", comboStep, combatAnimRemaining);
|
log.info("[Combat] Kombo-Fenster geöffnet (step={}, remaining={}s)", comboStep, combatAnimRemaining);
|
||||||
}
|
}
|
||||||
|
// Per-Frame Waffen-Treffercheck während Hit-Window
|
||||||
|
if (hitWindowActive && isMeleeType(drawnWeaponType) && combatAnimDuration > 0f) {
|
||||||
|
float t = (combatAnimDuration - combatAnimRemaining) / combatAnimDuration;
|
||||||
|
if (t >= HIT_WINDOW_START && t <= HIT_WINDOW_END) {
|
||||||
|
fireWeaponCallback(hitWindowCritMult);
|
||||||
|
} else if (t > HIT_WINDOW_END) {
|
||||||
|
hitWindowActive = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
pollFootsteps(tpf);
|
pollFootsteps(tpf);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -990,7 +1009,9 @@ public class PlayerInputControl {
|
|||||||
boolean isComboFollow = comboWindowActive; // war Kombo-Fenster offen?
|
boolean isComboFollow = comboWindowActive; // war Kombo-Fenster offen?
|
||||||
AnimationAction atk = getMeleeComboAction(drawnWeaponType, step);
|
AnimationAction atk = getMeleeComboAction(drawnWeaponType, step);
|
||||||
lastAttackCritical = (step == 4);
|
lastAttackCritical = (step == 4);
|
||||||
fireMeleeCallback(lastAttackCritical ? 2.0f : 1.0f);
|
currentAttackId = ++attackIdCounter;
|
||||||
|
hitWindowActive = true;
|
||||||
|
hitWindowCritMult = lastAttackCritical ? 2.0f : 1.0f;
|
||||||
comboStep = (step + 1) % 5;
|
comboStep = (step + 1) % 5;
|
||||||
comboWindowActive = false;
|
comboWindowActive = false;
|
||||||
float dur = resolveClipLength(atk, 0.6f);
|
float dur = resolveClipLength(atk, 0.6f);
|
||||||
@@ -1302,21 +1323,20 @@ public class PlayerInputControl {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fireMeleeCallback(float critMultiplier) {
|
private void fireWeaponCallback(float critMultiplier) {
|
||||||
if (meleeAttackCallback == null || physicsChar == null) {
|
if (meleeAttackCallback == null || weaponAttach == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
com.jme3.scene.Spatial ws = weaponAttach.getWeaponSpatial();
|
||||||
|
com.jme3.scene.Node hn = weaponAttach.getHandNode();
|
||||||
|
if (ws == null || hn == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int base = (mainCharacter != null && mainCharacter.getEquippedMeleeWeapon() != null)
|
int base = (mainCharacter != null && mainCharacter.getEquippedMeleeWeapon() != null)
|
||||||
? mainCharacter.getEquippedMeleeWeapon().getDamage() : 0;
|
? mainCharacter.getEquippedMeleeWeapon().getDamage() : 0;
|
||||||
Vector3f pos = physicsChar.getPhysicsLocation();
|
Vector3f grip = hn.getWorldTranslation().clone();
|
||||||
Vector3f fwd = (visual != null)
|
Vector3f bladeDir = ws.getWorldRotation().mult(com.jme3.math.Vector3f.UNIT_Y).normalizeLocal();
|
||||||
? visual.getLocalRotation().mult(new Vector3f(0f, 0f, -1f))
|
meleeAttackCallback.onMeleeAttack(currentAttackId, grip, bladeDir, WEAPON_BLADE_LENGTH, base, critMultiplier);
|
||||||
: new Vector3f(0f, 0f, -1f);
|
|
||||||
fwd.y = 0f;
|
|
||||||
if (fwd.lengthSquared() > 0.001f) {
|
|
||||||
fwd.normalizeLocal();
|
|
||||||
}
|
|
||||||
meleeAttackCallback.onMeleeAttack(pos, fwd, base, critMultiplier);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private float computeHeavyMultiplier() {
|
private float computeHeavyMultiplier() {
|
||||||
@@ -1368,8 +1388,9 @@ public class PlayerInputControl {
|
|||||||
if (!weaponDrawn || drawnWeaponType == null || !isMeleeType(drawnWeaponType)) {
|
if (!weaponDrawn || drawnWeaponType == null || !isMeleeType(drawnWeaponType)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
currentAttackId = ++attackIdCounter;
|
||||||
float mult = computeHeavyMultiplier();
|
float mult = computeHeavyMultiplier();
|
||||||
fireMeleeCallback(mult);
|
fireWeaponCallback(mult);
|
||||||
log.info("[Combat] Schwerer Angriff ausgelöst | Haltezeit={}s Multiplikator={}x",
|
log.info("[Combat] Schwerer Angriff ausgelöst | Haltezeit={}s Multiplikator={}x",
|
||||||
String.format("%.2f", attackHoldTimer), String.format("%.2f", mult));
|
String.format("%.2f", attackHoldTimer), String.format("%.2f", mult));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,9 @@ public class WeaponAttachmentControl extends AbstractControl {
|
|||||||
this.mc = mc;
|
this.mc = mc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Spatial getWeaponSpatial() { return weaponSpatial; }
|
||||||
|
public Node getHandNode() { return handNode; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void controlUpdate(float tpf) {
|
protected void controlUpdate(float tpf) {
|
||||||
if (skinCtrl == null) {
|
if (skinCtrl == null) {
|
||||||
|
|||||||
@@ -362,7 +362,9 @@ public class WorldScene extends BaseAppState {
|
|||||||
MainCharacter mc = findMainCharacter();
|
MainCharacter mc = findMainCharacter();
|
||||||
if (mc != null) {
|
if (mc != null) {
|
||||||
playerInput.setMainCharacter(mc);
|
playerInput.setMainCharacter(mc);
|
||||||
character.addControl(new de.blight.game.control.WeaponAttachmentControl(assetManager, playerInput, mc));
|
var wac = new de.blight.game.control.WeaponAttachmentControl(assetManager, playerInput, mc);
|
||||||
|
character.addControl(wac);
|
||||||
|
playerInput.setWeaponAttachmentControl(wac);
|
||||||
// SaveGameState mit Charakter und Positions-Lieferant verknüpfen
|
// SaveGameState mit Charakter und Positions-Lieferant verknüpfen
|
||||||
de.blight.game.state.SaveGameState saveState =
|
de.blight.game.state.SaveGameState saveState =
|
||||||
app.getStateManager().getState(de.blight.game.state.SaveGameState.class);
|
app.getStateManager().getState(de.blight.game.state.SaveGameState.class);
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import com.jme3.app.Application;
|
|||||||
import com.jme3.app.SimpleApplication;
|
import com.jme3.app.SimpleApplication;
|
||||||
import com.jme3.app.state.BaseAppState;
|
import com.jme3.app.state.BaseAppState;
|
||||||
import com.jme3.asset.AssetManager;
|
import com.jme3.asset.AssetManager;
|
||||||
|
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.util.CollisionShapeFactory;
|
||||||
import com.jme3.font.BitmapFont;
|
import com.jme3.font.BitmapFont;
|
||||||
import com.jme3.font.BitmapText;
|
import com.jme3.font.BitmapText;
|
||||||
import com.jme3.input.InputManager;
|
import com.jme3.input.InputManager;
|
||||||
@@ -44,7 +47,9 @@ import org.slf4j.LoggerFactory;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.FloatBuffer;
|
import java.nio.FloatBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verwaltet Kampfpuppen auf der Karte: HP-Tracking, Treffer-Erkennung,
|
* Verwaltet Kampfpuppen auf der Karte: HP-Tracking, Treffer-Erkennung,
|
||||||
@@ -55,8 +60,6 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
|||||||
private static final Logger log = LoggerFactory.getLogger(WorldDummyState.class);
|
private static final Logger log = LoggerFactory.getLogger(WorldDummyState.class);
|
||||||
|
|
||||||
// ── Spielwelt-Konstanten ──────────────────────────────────────────────────
|
// ── Spielwelt-Konstanten ──────────────────────────────────────────────────
|
||||||
private static final float ATTACK_RANGE = 2.5f;
|
|
||||||
private static final float ATTACK_CONE_DOT = 0.2f;
|
|
||||||
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;
|
||||||
@@ -87,9 +90,12 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
|||||||
private Node rootNode;
|
private Node rootNode;
|
||||||
private Node guiNode;
|
private Node guiNode;
|
||||||
private Camera cam;
|
private Camera cam;
|
||||||
|
private BulletAppState bulletAppState;
|
||||||
|
|
||||||
// ── Laufzeit-Zustand ─────────────────────────────────────────────────────
|
// ── Laufzeit-Zustand ─────────────────────────────────────────────────────
|
||||||
private final List<DummyRuntime> dummies = new ArrayList<>();
|
private final List<DummyRuntime> dummies = new ArrayList<>();
|
||||||
|
private int lastAttackId = -1;
|
||||||
|
private final Set<String> hitThisAttack = new HashSet<>();
|
||||||
private int hoveredDummyIdx = -1; // gefallene Puppe in Aufstell-Reichweite
|
private int hoveredDummyIdx = -1; // gefallene Puppe in Aufstell-Reichweite
|
||||||
private int focusedDummyIdx = -1; // nächste Puppe in Fokus-Reichweite (Waffe gezückt)
|
private int focusedDummyIdx = -1; // nächste Puppe in Fokus-Reichweite (Waffe gezückt)
|
||||||
|
|
||||||
@@ -121,6 +127,7 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
|||||||
this.rootNode = sapp.getRootNode();
|
this.rootNode = sapp.getRootNode();
|
||||||
this.guiNode = sapp.getGuiNode();
|
this.guiNode = sapp.getGuiNode();
|
||||||
this.cam = app.getCamera();
|
this.cam = app.getCamera();
|
||||||
|
this.bulletAppState = app.getStateManager().getState(BulletAppState.class);
|
||||||
|
|
||||||
loadDummies();
|
loadDummies();
|
||||||
buildEnemyHud();
|
buildEnemyHud();
|
||||||
@@ -149,6 +156,9 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
|||||||
@Override
|
@Override
|
||||||
protected void cleanup(Application app) {
|
protected void cleanup(Application app) {
|
||||||
for (DummyRuntime dr : dummies) {
|
for (DummyRuntime dr : dummies) {
|
||||||
|
if (dr.rbc != null && bulletAppState != null) {
|
||||||
|
bulletAppState.getPhysicsSpace().remove(dr.rbc);
|
||||||
|
}
|
||||||
rootNode.detachChild(dr.dummyNode);
|
rootNode.detachChild(dr.dummyNode);
|
||||||
}
|
}
|
||||||
dummies.clear();
|
dummies.clear();
|
||||||
@@ -203,10 +213,22 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
|||||||
dummyNode.setLocalScale(m.scale());
|
dummyNode.setLocalScale(m.scale());
|
||||||
rootNode.attachChild(dummyNode);
|
rootNode.attachChild(dummyNode);
|
||||||
|
|
||||||
|
RigidBodyControl rbc = null;
|
||||||
|
if (m.solid() && bulletAppState != null) {
|
||||||
|
try {
|
||||||
|
rbc = new RigidBodyControl(CollisionShapeFactory.createMeshShape(model), 0f);
|
||||||
|
model.addControl(rbc);
|
||||||
|
bulletAppState.getPhysicsSpace().add(rbc);
|
||||||
|
} catch (Exception pe) {
|
||||||
|
log.warn("[WorldDummy] Kollisions-Shape für '{}' fehlgeschlagen: {}", m.modelPath(), pe.getMessage());
|
||||||
|
rbc = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Geometry hpFg = (Geometry) ((Node) hpBarNode.getChild(0)).getChild(1);
|
Geometry hpFg = (Geometry) ((Node) hpBarNode.getChild(0)).getChild(1);
|
||||||
return new DummyRuntime(
|
return new DummyRuntime(
|
||||||
m.interactableId() != null ? m.interactableId() : "?",
|
m.interactableId() != null ? m.interactableId() : "?",
|
||||||
m.x(), m.y(), m.z(), dummyNode, hpBarNode, hpFg, maxHp);
|
m.x(), m.y(), m.z(), dummyNode, hpBarNode, hpFg, maxHp, rbc);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Spatial loadModel(PlacedModel m) {
|
private Spatial loadModel(PlacedModel m) {
|
||||||
@@ -374,35 +396,32 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
|||||||
// ── MeleeAttackCallback ───────────────────────────────────────────────────
|
// ── MeleeAttackCallback ───────────────────────────────────────────────────
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMeleeAttack(Vector3f charPos, Vector3f charForward, int baseDamage, float critMultiplier) {
|
public void onMeleeAttack(int attackId, Vector3f gripPos, Vector3f bladeDir,
|
||||||
|
float bladeLength, int baseDamage, float critMultiplier) {
|
||||||
|
if (attackId != lastAttackId) {
|
||||||
|
lastAttackId = attackId;
|
||||||
|
hitThisAttack.clear();
|
||||||
|
}
|
||||||
for (DummyRuntime dr : dummies) {
|
for (DummyRuntime dr : dummies) {
|
||||||
if (dr.fallen) {
|
if (dr.fallen || hitThisAttack.contains(dr.id)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
float dx = dr.worldX - charPos.x;
|
// Distanz vom Griff zum Dummy-Zentrum (Torso-Höhe ~0.9m)
|
||||||
float dz = dr.worldZ - charPos.z;
|
float dx = dr.worldX - gripPos.x;
|
||||||
float distSq = dx * dx + dz * dz;
|
float dy = (dr.worldY + 0.9f) - gripPos.y;
|
||||||
if (distSq > ATTACK_RANGE * ATTACK_RANGE) {
|
float dz = dr.worldZ - gripPos.z;
|
||||||
continue;
|
if (dx * dx + dy * dy + dz * dz <= bladeLength * bladeLength) {
|
||||||
}
|
hitThisAttack.add(dr.id);
|
||||||
float len = (float) Math.sqrt(distSq);
|
|
||||||
if (len > 0.05f) {
|
|
||||||
float dot = (dx / len) * charForward.x + (dz / len) * charForward.z;
|
|
||||||
if (dot < ATTACK_CONE_DOT) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
applyDamage(dr, DamageCalculator.calculate(baseDamage, critMultiplier));
|
applyDamage(dr, DamageCalculator.calculate(baseDamage, critMultiplier));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ── Schaden / Zustand ─────────────────────────────────────────────────────
|
// ── Schaden / Zustand ─────────────────────────────────────────────────────
|
||||||
|
|
||||||
private void applyDamage(DummyRuntime dr, int damage) {
|
private void applyDamage(DummyRuntime dr, int damage) {
|
||||||
dr.currentHp = Math.max(0, dr.currentHp - damage);
|
dr.currentHp = Math.max(0, dr.currentHp - damage);
|
||||||
dr.restoreTimer = 0f;
|
dr.restoreTimer = 0f;
|
||||||
dr.hpBarTimer = HP_BAR_SHOW_TIME;
|
|
||||||
dr.hpBarNode.setCullHint(Spatial.CullHint.Inherit);
|
|
||||||
refreshWorldHpBar(dr);
|
refreshWorldHpBar(dr);
|
||||||
log.info("[Dummy {}] -{} HP → {}/{}{}", dr.id, damage, dr.currentHp, dr.maxHp,
|
log.info("[Dummy {}] -{} HP → {}/{}{}", dr.id, damage, dr.currentHp, dr.maxHp,
|
||||||
dr.currentHp <= 0 ? " K.O.!" : "");
|
dr.currentHp <= 0 ? " K.O.!" : "");
|
||||||
@@ -556,6 +575,7 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
|||||||
final Node dummyNode;
|
final Node dummyNode;
|
||||||
final Node hpBarNode;
|
final Node hpBarNode;
|
||||||
final Geometry hpBarFg;
|
final Geometry hpBarFg;
|
||||||
|
final RigidBodyControl rbc;
|
||||||
int currentHp;
|
int currentHp;
|
||||||
final int maxHp;
|
final int maxHp;
|
||||||
float restoreTimer = 0f;
|
float restoreTimer = 0f;
|
||||||
@@ -564,7 +584,8 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
|||||||
float fallenLerp = 0f;
|
float fallenLerp = 0f;
|
||||||
|
|
||||||
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,
|
||||||
|
RigidBodyControl rbc) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.worldX = wx;
|
this.worldX = wx;
|
||||||
this.worldY = wy;
|
this.worldY = wy;
|
||||||
@@ -574,6 +595,7 @@ public class WorldDummyState extends BaseAppState implements MeleeAttackCallback
|
|||||||
this.hpBarFg = hpBarFg;
|
this.hpBarFg = hpBarFg;
|
||||||
this.maxHp = maxHp;
|
this.maxHp = maxHp;
|
||||||
this.currentHp = maxHp;
|
this.currentHp = maxHp;
|
||||||
|
this.rbc = rbc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,3 +14,4 @@ Models/northcoast/alte_fischerhytte.j3o 322.73001 2.00000 -856.09003 -1.83155 1.
|
|||||||
Models/imported/feuerstelle1.j3o 1.46137 2.00000 -1.08840 0.00000 1.00000 0.00000 0.00000 true true true 30.00000 80.00000 120.00000 NONE
|
Models/imported/feuerstelle1.j3o 1.46137 2.00000 -1.08840 0.00000 1.00000 0.00000 0.00000 true true true 30.00000 80.00000 120.00000 NONE
|
||||||
Models/trees/palm/palm_20260914_195434.j3o 226.76038 1.25643 -893.77020 -1.59663 1.00000 -0.00000 0.00000 true true true 50.00000 150.00000 300.00000 PALM
|
Models/trees/palm/palm_20260914_195434.j3o 226.76038 1.25643 -893.77020 -1.59663 1.00000 -0.00000 0.00000 true true true 50.00000 150.00000 300.00000 PALM
|
||||||
Models/trees/willow/willow_20260914_210538.j3o 256.04678 5.84959 -887.66675 0.00000 1.00000 0.00000 0.00000 true true true 50.00000 150.00000 300.00000 TREE
|
Models/trees/willow/willow_20260914_210538.j3o 256.04678 5.84959 -887.66675 0.00000 1.00000 0.00000 0.00000 true true true 50.00000 150.00000 300.00000 TREE
|
||||||
|
Models/imported/kampfdummy.j3o -5.95449 2.00000 -11.12527 0.00000 1.00000 0.00000 0.00000 true true true 30.00000 80.00000 120.00000 DUMMY
|
||||||
|
|||||||
Reference in New Issue
Block a user