Kampfsystem für Zweihänder erweitert

This commit is contained in:
2026-10-06 21:48:44 +02:00
parent 62931e3273
commit 9ff911eaa6
54 changed files with 258 additions and 47 deletions

View File

@@ -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)");

View File

@@ -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;
};
}

View File

@@ -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);
}
}

View File

@@ -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";

View File

@@ -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);
}