test: cover testTrade

This commit is contained in:
dualfroz committed 2026-09-13 21:24:03 +02:00
1 parent 5fba3c338a
commit f2782c8ffb
1 file changed
+156
+156
View File
@@ -157,3 +157,159 @@ VF_TEST(restockRefillsUsesAndLetsDemandAndPriceFallBack) {
Container wallet = makeWallet(400); Container wallet = makeWallet(400);
TradeSession session(villager, offers, wallet); TradeSession session(villager, offers, wallet);
for (int i = 0; i < 10; ++i) CHECK(session.trade(comparatorOffer).success);
int busyPrice = session.priceOf(comparatorOffer);
int busyDemand = offers.at(comparatorOffer).demand;
CHECK_EQ(offers.at(comparatorOffer).uses, 10);
CHECK(restockOffers(offers));
CHECK_EQ(offers.at(comparatorOffer).uses, 0);
CHECK_EQ(offers.at(comparatorOffer).demand, busyDemand - tradeDemandDecayPerRestock);
CHECK(session.priceOf(comparatorOffer) <= busyPrice);
CHECK(session.available(comparatorOffer));
for (int i = 0; i < 20; ++i) restockOffers(offers);
CHECK_EQ(offers.at(comparatorOffer).demand, 0);
CHECK_EQ(session.priceOf(comparatorOffer), offers.at(comparatorOffer).basePrice());
CHECK(!restockOffers(offers));
}
VF_TEST(theVillagerRestocksAtMostTwiceADayAndOnlyAtItsOwnWorkstation) {
World world = makeShopWorld();
PoiRegistry pois;
pois.scan(world, villageCenter, 10, 4);
Villager villager = makeLibrarian(3);
TradeOfferList offers = buildTradeOffers(VillagerProfession::Librarian, 1u);
Container wallet = makeWallet(400);
TradeSession session(villager, offers, wallet);
VillagerSchedule schedule;
TimeOfDay clock(static_cast<WorldTick>(0.35 * 24000.0));
CHECK(session.trade(comparatorOffer).success);
CHECK(!tickVillagerRestock(villager, offers, clock));
for (int tick = 0; tick < 400; ++tick)
tickVillager(world, villager, pois, clock, schedule, villageCenter, false, 0.05f);
CHECK(villager.activity == VillagerActivity::Work);
CHECK(villager.state == VillagerState::AtTarget);
CHECK_EQ(pois.at(villager.workstationIndex).block, Block::Lectern);
CHECK(tickVillagerRestock(villager, offers, clock));
CHECK_EQ(villager.restocksToday, 1);
CHECK(!tickVillagerRestock(villager, offers, clock));
CHECK(session.trade(comparatorOffer).success);
CHECK(tickVillagerRestock(villager, offers, clock));
CHECK_EQ(villager.restocksToday, tradeRestocksPerDay);
CHECK(session.trade(comparatorOffer).success);
CHECK(!tickVillagerRestock(villager, offers, clock));
clock.setTick(static_cast<WorldTick>(24000 + 0.35 * 24000.0));
CHECK(tickVillagerRestock(villager, offers, clock));
CHECK_EQ(villager.restocksToday, 1);
CHECK_EQ(villager.lastRestockDay, 1);
}
VF_TEST(tradingWithoutEnoughEmeraldsFailsAndLeavesTheInventoryAlone) {
Villager villager = makeLibrarian(3);
TradeOfferList offers = buildTradeOffers(VillagerProfession::Librarian, 1u);
Container wallet = makeWallet(2);
TradeSession session(villager, offers, wallet);
CHECK(session.available(comparatorOffer));
CHECK(!session.canAfford(comparatorOffer));
TradeResult outcome = session.trade(comparatorOffer);
CHECK(!outcome.success);
CHECK(outcome.reason == TradeResult::Reason::MissingInputA);
CHECK_EQ(stockOf(wallet, ItemId::EmeraldGem), 2);
CHECK_EQ(stockOf(wallet, ItemId::Comparator), 0);
CHECK_EQ(offers.at(comparatorOffer).uses, 0);
CHECK_EQ(villager.experience, villagerExperienceForLevel(3));
CHECK(session.trade(-1).reason == TradeResult::Reason::NoSuchOffer);
CHECK(session.trade(offers.size()).reason == TradeResult::Reason::NoSuchOffer);
CHECK(!session.available(99));
CHECK_EQ(session.priceOf(99), 0);
}
VF_TEST(aDeadOrUnemployedVillagerRefusesToTradeAtAll) {
Villager villager = makeLibrarian(3);
TradeOfferList offers = buildTradeOffers(VillagerProfession::Librarian, 1u);
Container wallet = makeWallet(400);
TradeSession session(villager, offers, wallet);
CHECK(session.trade(comparatorOffer).success);
villager.alive = false;
TradeResult dead = session.trade(comparatorOffer);
CHECK(!dead.success);
CHECK(dead.reason == TradeResult::Reason::VillagerUnavailable);
villager.alive = true;
villager.profession = VillagerProfession::Unemployed;
CHECK(session.trade(comparatorOffer).reason == TradeResult::Reason::VillagerUnavailable);
CHECK_EQ(session.tradesCompleted(), 1);
}
VF_TEST(heroOfTheVillageCutsPricesWhileHittingAVillagerRaisesThem) {
Villager villager = makeLibrarian(3);
TradeOfferList offers = buildTradeOffers(VillagerProfession::Librarian, 1u);
Container wallet = makeWallet(400);
TradeSession session(villager, offers, wallet);
int listPrice = session.priceOf(comparatorOffer);
CHECK_EQ(listPrice, offers.at(comparatorOffer).basePrice());
ReputationView neutral = buildReputation(villager.gossip, {});
session.setPriceModifier(priceModifierFor(neutral, 1, 0));
int heroPrice = session.priceOf(comparatorOffer);
CHECK(heroPrice < listPrice);
CHECK_EQ(session.priceModifier().totalPercent(), heroDiscountPercentPerLevel);
villager.gossip.add(GossipKind::MinorNegative, gossipMinorNegativeGain);
CHECK_EQ(villagerHitCount(villager.gossip), 1);
session.refreshPrices(buildReputation(villager.gossip, {}), 0);
int angryPrice = session.priceOf(comparatorOffer);
CHECK(angryPrice > listPrice);
CHECK(session.priceModifier().totalPercent() < 0);
session.refreshPrices(buildReputation(villager.gossip, {}), 1);
CHECK(session.priceOf(comparatorOffer) < angryPrice);
}
VF_TEST(priceNeverDropsBelowOneNoMatterHowLargeTheDiscount) {
TradeOffer offer;
offer.inputA = ItemId::EmeraldGem;
offer.inputACount = 2;
offer.output = ItemId::Bread;
offer.outputCount = 4;
PriceModifier generous;
generous.heroLevel = 5;
CHECK_EQ(generous.totalPercent(), 90);
CHECK_EQ(offerPrice(offer, generous), 1);
PriceModifier absurd;
absurd.reputationPercent = 60;
absurd.heroLevel = 5;
CHECK_EQ(absurd.totalPercent(), 90);
CHECK_EQ(offerPrice(offer, absurd), 1);
PriceModifier hostile;
hostile.hitCount = 8;
CHECK_EQ(hostile.totalPercent(), -160);
CHECK_EQ(offerPrice(offer, hostile), 5);
offer.demand = 40;
CHECK(offerPrice(offer, PriceModifier{}) > 2);
CHECK_EQ(offerPrice(offer, generous), 1);
}
VF_TEST(aCompletedTradeMovesBothSidesAndFeedsTheVillagerExperience) {
Villager villager = makeLibrarian(1);
villager.experience = 0;
TradeOfferList offers = buildTradeOffers(VillagerProfession::Librarian, 1u);
Container wallet = makeWallet(40);
TradeSession session(villager, offers, wallet);