feat: add armorToughness and armorEnchantability to Damage
This commit is contained in:
1 parent
0cc5b9527b
commit
468a60e9d2
1 file changed
+159
@@ -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;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user