From 0863a2a595028e89e1e2fe72b314aafa359f7948 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Fri, 11 Sep 2026 18:04:13 +0200 Subject: [PATCH] test: cover withEnchant and fullDiamond --- tests/testEquipment.cpp | 159 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100644 tests/testEquipment.cpp diff --git a/tests/testEquipment.cpp b/tests/testEquipment.cpp new file mode 100644 index 0000000..8abb21a --- /dev/null +++ b/tests/testEquipment.cpp @@ -0,0 +1,159 @@ +#include "testHarness.h" +#include "vf/Equipment.h" + +#include +#include + +using namespace vf; + +namespace { + +const CraftingRegistry& sharedRegistry() { + static CraftingRegistry registry; + return registry; +} + +EnchantSet withEnchant(EnchantKind kind, int level) { + EnchantSet set; + addEnchant(set, kind, level); + return set; +} + +Equipment fullDiamond() { + Equipment gear; + gear.equipArmor(makeArmor(Armor::Material::Diamond, Armor::Slot::Helmet)); + gear.equipArmor(makeArmor(Armor::Material::Diamond, Armor::Slot::Chestplate)); + gear.equipArmor(makeArmor(Armor::Material::Diamond, Armor::Slot::Leggings)); + gear.equipArmor(makeArmor(Armor::Material::Diamond, Armor::Slot::Boots)); + return gear; +} + +float modifierTotal(const EquipmentStats& stats, AttributeModifier::Attribute attribute) { + float total = 0.0f; + for (const AttributeModifier& modifier : stats.modifiers) { + if (modifier.attribute == attribute) total += modifier.amount; + } + return total; +} + +CombatStats readyToSwing() { + CombatStats stats; + stats.attackChargeTicks = 200; + return stats; +} + +} // namespace + +VF_TEST(everyEquipSlotAndAttributeHasAName) { + for (int i = 0; i < static_cast(EquipSlot::Count); ++i) { + CHECK(std::string(equipSlotName(static_cast(i))) != std::string("unknown")); + } + for (int i = 0; i < static_cast(AttributeModifier::Attribute::Count); ++i) { + CHECK(std::string(attributeName(static_cast(i))) != + std::string("unknown")); + } + CHECK(isArmorSlot(EquipSlot::Head)); + CHECK(isArmorSlot(EquipSlot::Feet)); + CHECK(!isArmorSlot(EquipSlot::MainHand)); + CHECK(isHandSlot(EquipSlot::OffHand)); + CHECK(armorSlotFor(EquipSlot::Legs) == Armor::Slot::Leggings); + CHECK(armorSlotFor(EquipSlot::MainHand) == Armor::Slot::Count); + CHECK(equipSlotFor(Armor::Slot::Boots) == EquipSlot::Feet); + CHECK(equipSlotFor(Armor::Slot::Count) == EquipSlot::Count); +} + +VF_TEST(anArmorSlotRefusesAnyItemStackBecauseNoItemIsWearable) { + Equipment gear; + MetaStack helmetShaped = makeStack(ItemId::Stone, 1); + CHECK(!gear.accepts(EquipSlot::Head, helmetShaped)); + CHECK(!gear.equip(EquipSlot::Head, helmetShaped)); + CHECK(!gear.equip(EquipSlot::Feet, makeTool(ItemId::DiamondSword))); + CHECK(gear.armorPiece(EquipSlot::Head).material == Armor::Material::None); + + CHECK(gear.equip(EquipSlot::MainHand, makeTool(ItemId::DiamondSword))); + CHECK(gear.mainHand().item == ItemId::DiamondSword); + CHECK(!gear.equip(EquipSlot::MainHand, MetaStack{})); + CHECK(gear.mainHand().item == ItemId::DiamondSword); + + CHECK(gear.armorPiece(EquipSlot::MainHand).material == Armor::Material::None); + CHECK(gear.hand(EquipSlot::Head).empty()); +} + +VF_TEST(aPieceOfArmorOnlyGoesOnTheSlotItWasMadeFor) { + Equipment gear; + Armor helmet = makeArmor(Armor::Material::Iron, Armor::Slot::Helmet); + + CHECK(!gear.equipArmorAt(EquipSlot::Chest, helmet)); + CHECK(!gear.equipArmorAt(EquipSlot::MainHand, helmet)); + CHECK(gear.equipArmorAt(EquipSlot::Head, helmet)); + CHECK(gear.armorPiece(EquipSlot::Head).material == Armor::Material::Iron); + CHECK_EQ(gear.wornPieces(), 1); + CHECK(!gear.empty()); + + CHECK(!gear.equipArmor(Armor{})); + + Armor removed = gear.unequipArmor(EquipSlot::Head); + CHECK(removed.material == Armor::Material::Iron); + CHECK_EQ(gear.wornPieces(), 0); + CHECK(gear.empty()); + CHECK(gear.unequipArmor(EquipSlot::OffHand).material == Armor::Material::None); +} + +VF_TEST(statsSumArmorPointsAndToughnessAcrossTheWholeSet) { + Equipment gear = fullDiamond(); + EquipmentStats stats = gear.stats(); + + CHECK_EQ(stats.armorPoints, gear.armor().totalPoints()); + CHECK_EQ(stats.armorPoints, 20); + CHECK_NEAR(stats.armorToughness, 8.0f, 0.0001f); + CHECK_NEAR(modifierTotal(stats, AttributeModifier::Attribute::ArmorPoints), 20.0f, 0.0001f); + CHECK_NEAR(modifierTotal(stats, AttributeModifier::Attribute::ArmorToughness), 8.0f, 0.0001f); + + Equipment bare; + EquipmentStats naked = bare.stats(); + CHECK_EQ(naked.armorPoints, 0); + CHECK_NEAR(naked.armorToughness, 0.0f, 0.0001f); + CHECK(naked.modifiers.empty()); +} + +VF_TEST(equipmentStatsFeedTheCombatPipelineDamageAlreadyUnderstands) { + CombatStats bare; + CombatStats armored; + fullDiamond().stats().applyTo(armored); + + DamageEvent blow; + blow.source = meleeSource(1, Vec3{0.0f, 0.0f, 0.0f}, false); + blow.amount = 12.0f; + + EffectSet none; + uint32_t rngBare = 42u; + uint32_t rngArmored = 42u; + CombatResult unprotected = applyDamage(bare, none, blow, rngBare); + CombatResult protectedHit = applyDamage(armored, none, blow, rngArmored); + + CHECK(unprotected.applied); + CHECK(protectedHit.applied); + CHECK(protectedHit.healthLost < unprotected.healthLost); + CHECK(protectedHit.armorDurabilityLost > 0); + CHECK_EQ(unprotected.armorDurabilityLost, 0); + CHECK(armored.armor.totalPoints() > 0); +} + +VF_TEST(theHeldToolDecidesAttackDamageSpeedAndReach) { + CHECK_NEAR(weaponAttackDamage(ItemId::DiamondSword), 7.0f, 0.0001f); + CHECK_NEAR(weaponAttackDamage(ItemId::WoodSword), 4.0f, 0.0001f); + CHECK_NEAR(weaponAttackDamage(ItemId::DiamondAxe), 9.0f, 0.0001f); + CHECK_NEAR(weaponAttackDamage(ItemId::Stone), unarmedAttackDamage, 0.0001f); + CHECK_NEAR(weaponAttackSpeed(ItemId::DiamondSword), 1.6f, 0.0001f); + CHECK(weaponAttackSpeed(ItemId::DiamondAxe) < weaponAttackSpeed(ItemId::DiamondSword)); + CHECK(weaponAttackReach(ItemId::DiamondSword) > weaponAttackReach(ItemId::DiamondPickaxe)); + CHECK_NEAR(weaponAttackReach(ItemId::Stone), unarmedAttackReach, 0.0001f); + + Equipment gear; + gear.equip(EquipSlot::MainHand, makeTool(ItemId::DiamondSword)); + EquipmentStats stats = gear.stats(); + CHECK_NEAR(stats.attackDamage, 7.0f, 0.0001f); + CHECK_NEAR(stats.attackSpeed, 1.6f, 0.0001f); + CHECK_NEAR(stats.attackReach, unarmedAttackReach + 0.5f, 0.0001f); + CHECK_NEAR(modifierTotal(stats, AttributeModifier::Attribute::AttackDamage), 6.0f, 0.0001f); +