Files
voxelforge/tests/testEquipment.cpp
T

160 lines
6.0 KiB
C++

#include "testHarness.h"
#include "vf/Equipment.h"
#include <string>
#include <vector>
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<int>(EquipSlot::Count); ++i) {
CHECK(std::string(equipSlotName(static_cast<EquipSlot>(i))) != std::string("unknown"));
}
for (int i = 0; i < static_cast<int>(AttributeModifier::Attribute::Count); ++i) {
CHECK(std::string(attributeName(static_cast<AttributeModifier::Attribute>(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);