🎯 Mecánicas de Combate

Mecánica Descripción Archivo Clave
💥 Daño Base DPS o ABSOLUTO con variación aleatoria DamageCalculator.java
🛡️ Reducción por Armadura Modificadores ADITIVO y MULTIPLICATIVO DamageEntityInteraction.java
🔄 Knockback 3 tipos: Directional, Point, Force Knockback.java
⚡ Stamina Consumo por bloquear/wield, stamina broken StaminaModule.java
🛡️ Bloqueo WieldingInteraction con BlockedEffects WieldingInteraction.java
✨ Invulnerabilidad Componente Invulnerable, iframes por efectos InvulnerableSystems.java
📐 Daño Angular Diferente daño según ángulo de ataque AngledDamage
🎯 Daño por Zona Headshots, weak points específicos TargetedDamage

💥 Cálculo de Daño

Fórmula Principal

// Fórmula simplificada del daño
dañoFinal = (dañoBase + armorFlatModifier) * armorMultiplierModifier * randomModifier

// Donde:
dañoBase = baseDamage * (tipo == DPS ? duración : 1.0)
randomModifier = 1.0 + random(-randomPercentageModifier, +randomPercentageModifier)
armorFlatModifier = suma de todos los ADDITIVE de armadura
armorMultiplierModifier = producto de todos los MULTIPLICATIVE de armadura

DamageCalculator

public class DamageCalculator {
    enum Type { 
        DPS,       // Daño = baseDamage * duración 
        ABSOLUTE   // Daño = baseDamage fijo
    }
    
    DamageClass damageClass;              // MELEE, RANGED, etc.
    Map<DamageCause, Float> baseDamage;   // {"Physical": 10.0}
    float sequentialModifierStep;          // Reducción por hits secuenciales
    float sequentialModifierMinimum;       // Mínimo del modificador
    float randomPercentageModifier;        // Variación ±X%
}
💡
Sequential Modifier
Reduce el daño en ataques rápidos consecutivos. Ejemplo: si step=0.1 y minimum=0.5, el primer hit hace 100%, segundo 90%, tercero 80%... hasta mínimo 50%.

🏷️ Tipos de Daño (DamageCause)

public class DamageCause {
    String id;               // "Physical", "Fall", "Drowning"
    boolean durabilityLoss;   // ¿Daña durabilidad del arma?
    boolean staminaLoss;      // ¿Consume stamina al bloquear?
    boolean bypassResistances;// ¿Ignora resistencias?
    String damageTextColor;  // Color del número de daño
    String animationId;      // Animación al recibir daño
    String deathAnimationId; // Animación de muerte
}

Tipos Predefinidos

Tipo Durability Stamina Bypass Color
Physical Rojo
Projectile Naranja
Fall Blanco
Drowning Azul
Environment Gris
Command Púrpura

🔄 Sistema de Knockback

Tipos de Knockback

abstract class Knockback {
    float force;           // Fuerza del empuje
    float duration;        // Duración (0 = instantáneo)
    ChangeVelocityType velocityType; // Add, Set, Scale
    
    abstract Vector3d calculateVector(attackerPos, yaw, targetPos);
}

// 3 Implementaciones:
class DirectionalKnockback // Dirección fija (hacia donde mira)
class PointKnockback       // Desde punto de impacto
class ForceKnockback       // Fuerza personalizada

Modificadores de Knockback

// El knockback se modifica por:
knockbackFinal = knockbackBase * effectMultiplier * armorEnhancement

// effectMultiplier: multiplicador de efectos activos
// armorEnhancement: bonus/reducción por armadura

🛡️ Sistema de Armadura

Modificadores de Armadura

// La armadura tiene dos tipos de modificadores:

enum CalculationType {
    ADDITIVE,      // Suma/resta al daño base
    MULTIPLICATIVE // Multiplica el daño final
}

// Ejemplo: Armadura con -5 ADDITIVE y 0.8 MULTIPLICATIVE
dañoFinal = (20 + (-5)) * 0.8 = 12

Cálculo de Reducción

for (ItemStack armorPiece : armorSlots) {
    if (armorPiece.hasArmor()) {
        // Aplicar modificadores por tipo de daño
        StaticModifier[] mods = armor.getDamageEnhancementValues().get(damageCause);
        for (StaticModifier mod : mods) {
            if (mod.getCalculationType() == ADDITIVE) {
                flatModifier += mod.getAmount();
            } else {
                multiplierModifier += mod.getAmount();
            }
        }
        
        // Aplicar modificadores por clase de daño (MELEE, RANGED...)
        StaticModifier[] classMods = armor.getDamageClassEnhancement().get(damageClass);
        // ... mismo proceso
    }
}

⚡ Sistema de Stamina

Consumo de Stamina al Bloquear

class StaminaCost {
    enum CostType {
        MAX_HEALTH_PERCENTAGE, // 1 stamina = X% de salud máxima
        DAMAGE                 // 1 stamina = X puntos de daño
    }
    
    CostType costType = MAX_HEALTH_PERCENTAGE;
    float value = 0.04f; // 4% por defecto
    
    float computeStaminaAmountToConsume(damageRaw, entityStatMap) {
        return costType == DAMAGE 
            ? damageRaw / value 
            : damageRaw / (entityStatMap.getMaxHealth() * value);
    }
}

Stamina Broken Effect

// Cuando la stamina llega a 0 al bloquear:
if (staminaAfterBlock <= 0) {
    applyEffect(combatConfig.getStaminaBrokenEffectId()); // "Stamina_Broken"
    // El jugador queda vulnerable por un tiempo
}
⚠️
Stamina Drain Multiplier
Los efectos de daño pueden tener staminaDrainMultiplier para aumentar el consumo de stamina del defensor. Valor por defecto: 1.0

🛡️ Sistema de Bloqueo (Wielding)

class WieldingInteraction {
    Map<DamageCause, StaticModifier[]> damageModifiers;  // Reducción de daño
    Map<DamageCause, Float> knockbackModifiers;          // Reducción de knockback
    AngledWielding[] angledWielding;                     // Bloqueo direccional
    StaminaCost staminaCost;                             // Consumo de stamina
    DamageEffects blockedEffects;                        // Efectos al bloquear
    String failed;                                       // Interacción si falla
}

Flujo de Bloqueo

// 1. Verificar si el jugador está en posición de bloqueo
if (isWielding \u0026\u0026 !staminaBroken) {
    // 2. Calcular ángulo entre atacante y defensor
    float angleBetween = calculateAngle(attacker, defender);
    
    // 3. Verificar si el ataque viene de frente (ángulo válido)
    if (isWithinBlockAngle(angleBetween, wieldingConfig)) {
        // 4. Aplicar reducción de daño
        damage.setAmount(damage.getAmount() * blockMultiplier);
        damage.putMetaObject(Damage.BLOCKED, true);
        
        // 5. Consumir stamina
        staminaToConsume = staminaCost.compute(damage);
        entityStats.subtract(STAMINA, staminaToConsume);
        
        // 6. Aplicar efectos de bloqueo (sonido, partículas)
        blockedEffects.addToDamage(damage);
    }
}

✨ Sistema de Invulnerabilidad

Componente Invulnerable

// Una entidad es invulnerable si:
boolean isInvulnerable = 
    archetype.contains(Invulnerable.getComponentType()) ||  // Componente fijo
    effectController.isInvulnerable();                      // Efecto temporal

// Verificación antes de aplicar daño
if (invulnerable \u0026\u0026 !damageCause.doesBypassResistances()) {
    damage.setCancelled(true);
    return;
}

Invulnerabilidad por Efectos

// Los EntityEffects pueden dar iframes
class EntityEffect {
    ApplicationEffects applicationEffects;
    
    class ApplicationEffects {
        boolean invulnerable;          // El efecto da invulnerabilidad
        float knockbackMultiplier;      // Modificador de knockback
    }
}

// El sistema combina todos los efectos activos
float knockbackMult = activeEffects.stream()
    .filter(e -\u003e e.getApplicationEffects() != null)
    .mapToDouble(e -\u003e e.getApplicationEffects().getKnockbackMultiplier())
    .reduce(1.0, (a, b) -\u003e a * b);

📐 Daño Angular y Dirigido

AngledDamage (Golpes Traseros)

class AngledDamage extends TargetedDamage {
    float angleRad;          // Ángulo central (ej: 180° = espalda)
    float angleDistanceRad;  // Tolerancia (ej: ±30°)
    DamageCalculator damageCalculator;  // Daño específico
    DamageEffects damageEffects;        // Efectos específicos
}

// Ejemplo: Backstab con 50% más daño
{
    "Angle": 180,
    "AngleDistance": 45,
    "DamageCalculator": {
        "Type": "ABSOLUTE",
        "BaseDamage": { "Physical": 15.0 }  // vs 10.0 normal
    }
}

TargetedDamage (Headshots)

// Daño diferente según la zona del hitbox
"TargetedDamage": {
    "Head": {
        "DamageCalculator": {
            "BaseDamage": { "Physical": 25.0 }  // 2.5x daño
        },
        "DamageEffects": {
            "CameraEffectId": "HeadshotEffect"
        }
    },
    "Torso": {
        "DamageCalculator": {
            "BaseDamage": { "Physical": 10.0 }
        }
    }
}

⚙️ Configuración Global de Combate

class CombatConfig {
    Duration outOfCombatDelay = 5000ms;  // Tiempo para salir de combate
    String staminaBrokenEffectId = "Stamina_Broken";
    boolean displayHealthBars = true;    // Mostrar barras de vida
    boolean displayCombatText = true;    // Mostrar números de daño
    boolean disableNpcIncomingDamage = false;    // NPCs invulnerables
    boolean disablePlayerIncomingDamage = false; // Jugadores invulnerables
}

📊 EntityStatOnHit (Lifesteal, etc.)

class EntityStatOnHit {
    String entityStatId;  // Ej: "Health" para lifesteal
    float amount;          // Cantidad base
    float[] multipliersPerEntitiesHit = {1.0, 0.6, 0.4, 0.2, 0.1};
    float multiplierPerExtraEntityHit = 0.05;
}

// Ejemplo: Lifesteal que da 5 HP por hit
// 1 enemigo: 5 * 1.0 = 5 HP
// 2 enemigos: 5 * 0.6 = 3 HP cada uno
// 6+ enemigos: 5 * 0.05 = 0.25 HP cada uno

📁 Archivos Clave

  • 📄 DamageEntityInteraction.java 551 líneas - Interacción principal de ataque
  • 📄 DamageSystems.java 1451 líneas - Sistema principal de daño
  • 📄 DamageCalculator.java 171 líneas - Cálculo de daño
  • 📄 Damage.java 287 líneas - Evento de daño
  • 📄 DamageCause.java 149 líneas - Tipos de daño
  • 📄 Knockback.java 62 líneas - Sistema de knockback
  • 📄 WieldingInteraction.java Sistema de bloqueo y wielding
  • 📄 CombatConfig.java 78 líneas - Configuración global
  • 📄 StaminaModule.java Sistema de stamina