feat: add armorToughness and armorEnchantability to Damage

This commit is contained in:
dualfroz committed 2026-09-06 23:13:21 +02:00
1 parent 0cc5b9527b
commit 468a60e9d2
1 file changed
+159
+159
View File
@@ -175,3 +175,162 @@ int armorPoints(const Armor& piece) {
}
}
float armorToughness(const Armor& piece) {
if (!piece.worn()) return 0.0f;
return piece.material == Armor::Material::Diamond ? 2.0f : 0.0f;
}
int armorEnchantability(Armor::Material material) {
switch (material) {
case Armor::Material::Leather: return 15;
case Armor::Material::Chain: return 12;
case Armor::Material::Iron: return 9;
case Armor::Material::Gold: return 25;
case Armor::Material::Diamond: return 10;
default: return 0;
}
}
Enchantment::Target armorTarget(Armor::Slot slot) {
switch (slot) {
case Armor::Slot::Helmet: return Enchantment::Target::Helmet;
case Armor::Slot::Chestplate: return Enchantment::Target::Chestplate;
case Armor::Slot::Leggings: return Enchantment::Target::Leggings;
default: return Enchantment::Target::Boots;
}
}
int ArmorSet::totalPoints() const {
int total = 0;
for (const Armor& piece : pieces) total += armorPoints(piece);
return total;
}
float ArmorSet::totalToughness() const {
float total = 0.0f;
for (const Armor& piece : pieces) total += armorToughness(piece);
return total;
}
int ArmorSet::protectionPoints(DamageType type) const {
int total = 0;
for (const Armor& piece : pieces) {
if (!piece.worn()) continue;
total += protectionPointsFor(piece.enchants, type);
}
return total;
}
int ArmorSet::levelOf(EnchantKind kind) const {
int best = 0;
for (const Armor& piece : pieces) {
if (!piece.worn()) continue;
best = std::max(best, piece.enchants.levelOf(kind));
}
return best;
}
float armorReduction(int points, float toughness, float damage) {
if (points <= 0) return damage;
float pointsF = static_cast<float>(points);
float defence = std::max(pointsF / 5.0f, pointsF - damage / (2.0f + toughness / 4.0f));
defence = std::min(20.0f, defence);
return damage * (1.0f - defence / 25.0f);
}
float protectionFactor(int points) {
int capped = std::min(points, 20);
if (capped <= 0) return 1.0f;
return 1.0f - static_cast<float>(capped) * 0.04f;
}
float effectiveMaxHealth(const CombatStats& stats, const EffectSet& effects) {
return stats.baseMaxHealth + effectModifiers(effects).maxHealthBonus;
}
int attackCooldownTicks(float attackSpeed) {
if (attackSpeed <= 0.0f) return ticksPerSecond;
int ticks = static_cast<int>(std::lround(static_cast<float>(ticksPerSecond) / attackSpeed));
return std::max(1, ticks);
}
float attackCharge(const CombatStats& stats) {
int cooldown = attackCooldownTicks(stats.attackSpeed);
float charge = static_cast<float>(stats.attackChargeTicks) / static_cast<float>(cooldown);
return std::min(1.0f, std::max(0.0f, charge));
}
void registerSwing(CombatStats& stats) { stats.attackChargeTicks = 0; }
EffectSet::Pending tickCombat(CombatStats& stats, EffectSet& effects) {
if (stats.invulnerableTicks > 0) --stats.invulnerableTicks;
if (stats.invulnerableTicks == 0) stats.lastDamageTaken = 0.0f;
int cooldown = attackCooldownTicks(stats.attackSpeed);
if (stats.attackChargeTicks < cooldown) ++stats.attackChargeTicks;
EffectSet::Pending pending = tickEffects(effects);
EffectModifier modifier = effectModifiers(effects);
stats.absorption = std::max(stats.absorption, modifier.absorptionHearts);
float maxHealth = stats.baseMaxHealth + modifier.maxHealthBonus;
applyEffectTick(pending, stats.health, maxHealth);
if (stats.health <= 0.0f) {
stats.health = 0.0f;
stats.alive = false;
}
return pending;
}
float meleeAttackDamage(const CombatStats& attacker, const EnchantSet& weapon,
const EffectSet& attackerEffects, bool undeadTarget, bool arthropodTarget,
bool critical) {
float charge = attackCharge(attacker);
float damage = attacker.baseAttackDamage * (0.2f + charge * charge * 0.8f);
damage += effectModifiers(attackerEffects).attackDamageBonus;
damage = std::max(0.0f, damage);
float enchantBonus = 0.0f;
int sharpness = weapon.levelOf(EnchantKind::Sharpness);
if (sharpness > 0) enchantBonus += 1.0f + 0.5f * static_cast<float>(sharpness - 1);
if (undeadTarget) enchantBonus += 2.5f * static_cast<float>(weapon.levelOf(EnchantKind::Smite));
if (arthropodTarget) {
enchantBonus += 2.5f * static_cast<float>(weapon.levelOf(EnchantKind::BaneOfArthropods));
}
// A partially charged swing only lands a fraction of the enchantment bonus.
damage += enchantBonus * charge;
if (critical && charge >= 0.9f) damage *= criticalMultiplier;
return damage;
}
float fallDamage(float fallDistance, const EffectSet& effects) {
float safeDistance = 3.0f + static_cast<float>(effectLevel(effects, EffectKind::JumpBoost));
float excess = fallDistance - safeDistance;
if (excess <= 0.0f) return 0.0f;
return std::floor(excess);
}
Vec3 knockbackVector(const Vec3& attackerPos, const Vec3& targetPos, float strength) {
return knockbackAlong(targetPos - attackerPos, strength);
}
int wearArmor(ArmorSet& armor, float incomingDamage, uint32_t& rng) {
int wear = std::max(1, static_cast<int>(std::floor(incomingDamage / 4.0f)));
int spent = 0;
for (Armor& piece : armor.pieces) {
if (!piece.worn()) continue;
int unbreaking = piece.enchants.levelOf(EnchantKind::Unbreaking);
int applied = 0;
for (int i = 0; i < wear; ++i) {
if (unbreakingBlocksWear(unbreaking, rng)) continue;
++applied;
}
applied = std::min(applied, piece.durability);
piece.durability -= applied;
spent += applied;
if (piece.durability <= 0) piece = Armor{};
}
return spent;
}