feat: add specsForProfession and containerCount to Trade

This commit is contained in:
dualfroz committed 2026-09-09 20:44:11 +02:00
1 parent 05b0fbcf03
commit e8d9fe3a04
1 file changed
+152
+152
View File
@@ -153,3 +153,155 @@ const OfferSpec butcherSpecs[] = {
{4, ItemId::EmeraldGem, 4, ItemId::None, 0, ItemId::CookedBeef, 8, 12, 30}, {4, ItemId::EmeraldGem, 4, ItemId::None, 0, ItemId::CookedBeef, 8, 12, 30},
}; };
const OfferSpec leatherworkerSpecs[] = {
{0, ItemId::Leaves, 26, ItemId::None, 0, ItemId::EmeraldGem, 1, 16, 2},
{0, ItemId::EmeraldGem, 2, ItemId::None, 0, ItemId::Planks, 8, 16, 1},
{1, ItemId::RottenFlesh, 26, ItemId::None, 0, ItemId::EmeraldGem, 1, 16, 5},
{1, ItemId::EmeraldGem, 3, ItemId::None, 0, ItemId::Ladder, 4, 16, 5},
{2, ItemId::Snow, 24, ItemId::None, 0, ItemId::EmeraldGem, 1, 12, 10},
{2, ItemId::EmeraldGem, 4, ItemId::None, 0, ItemId::Ice, 6, 12, 10},
{3, ItemId::EmeraldGem, 5, ItemId::None, 0, ItemId::Chest, 2, 12, 15},
{4, ItemId::EmeraldGem, 6, ItemId::IronIngot, 2, ItemId::Furnace, 2, 9, 30},
};
const OfferSpec masonSpecs[] = {
{0, ItemId::Stone, 20, ItemId::None, 0, ItemId::EmeraldGem, 1, 16, 2},
{0, ItemId::EmeraldGem, 1, ItemId::None, 0, ItemId::Cobblestone, 10, 16, 1},
{1, ItemId::Sand, 20, ItemId::None, 0, ItemId::EmeraldGem, 1, 16, 5},
{1, ItemId::EmeraldGem, 1, ItemId::None, 0, ItemId::Sandstone, 6, 16, 5},
{2, ItemId::Gravel, 20, ItemId::None, 0, ItemId::EmeraldGem, 1, 12, 10},
{2, ItemId::EmeraldGem, 2, ItemId::None, 0, ItemId::Obsidian, 1, 12, 10},
{3, ItemId::EmeraldGem, 3, ItemId::None, 0, ItemId::Glowstone, 2, 12, 15},
{4, ItemId::EmeraldGem, 5, ItemId::None, 0, ItemId::Sandstone, 12, 9, 30},
};
const OfferSpec* specsForProfession(VillagerProfession profession, int& outCount) {
switch (profession) {
case VillagerProfession::Farmer: outCount = 8; return farmerSpecs;
case VillagerProfession::Fisherman: outCount = 8; return fishermanSpecs;
case VillagerProfession::Shepherd: outCount = 8; return shepherdSpecs;
case VillagerProfession::Fletcher: outCount = 8; return fletcherSpecs;
case VillagerProfession::Librarian: outCount = 8; return librarianSpecs;
case VillagerProfession::Cartographer: outCount = 8; return cartographerSpecs;
case VillagerProfession::Cleric: outCount = 8; return clericSpecs;
case VillagerProfession::Armorer: outCount = 8; return armorerSpecs;
case VillagerProfession::Weaponsmith: outCount = 8; return weaponsmithSpecs;
case VillagerProfession::Toolsmith: outCount = 8; return toolsmithSpecs;
case VillagerProfession::Butcher: outCount = 8; return butcherSpecs;
case VillagerProfession::Leatherworker: outCount = 8; return leatherworkerSpecs;
case VillagerProfession::Mason: outCount = 8; return masonSpecs;
default: break;
}
outCount = 0;
return nullptr;
}
int containerCount(const Container& container, ItemId item) {
int total = 0;
for (int i = 0; i < container.size(); ++i) {
const ItemSlot& slot = container.slot(i);
if (!slot.empty() && slot.item == item) total += slot.count;
}
return total;
}
bool containerHasRoom(const Container& container, ItemId item, int count) {
int capacity = 0;
int limit = maxStackSize(item);
for (int i = 0; i < container.size(); ++i) {
const ItemSlot& slot = container.slot(i);
if (slot.empty()) capacity += limit;
else if (slot.item == item && slot.count < limit) capacity += limit - slot.count;
if (capacity >= count) return true;
}
return capacity >= count;
}
} // namespace
const char* tradeTierName(TradeTier tier) {
switch (tier) {
case TradeTier::Novice: return "novice";
case TradeTier::Apprentice: return "apprentice";
case TradeTier::Journeyman: return "journeyman";
case TradeTier::Expert: return "expert";
case TradeTier::Master: return "master";
case TradeTier::Count: break;
}
return "unknown";
}
TradeTier tierForLevel(int level) {
int clamped = clampInt(level, 1, villagerMaxLevel);
return static_cast<TradeTier>(clamped - 1);
}
int levelForTier(TradeTier tier) { return static_cast<int>(tier) + 1; }
int PriceModifier::totalPercent() const {
int percent = reputationPercent + heroLevel * heroDiscountPercentPerLevel -
hitCount * hitSurchargePercent;
return clampInt(percent, -300, 90);
}
PriceModifier priceModifierFor(const ReputationView& reputation, int heroLevel, int hitCount) {
PriceModifier modifier;
modifier.reputationPercent = reputation.discountPercent();
modifier.heroLevel = heroLevel < 0 ? 0 : heroLevel;
modifier.hitCount = hitCount < 0 ? 0 : hitCount;
return modifier;
}
int villagerHitCount(const GossipLedger& gossip) {
return gossip.valueOf(GossipKind::MinorNegative) / gossipMinorNegativeGain;
}
int offerPrice(const TradeOffer& offer, const PriceModifier& modifier) {
int base = offer.basePrice();
int demandDelta = static_cast<int>(
std::lround(static_cast<double>(offer.demand) * offer.priceMultiplier * base));
if (demandDelta < 0) demandDelta = 0;
int raw = base + demandDelta;
int shift = static_cast<int>(
std::lround(static_cast<double>(raw) * modifier.totalPercent() / 100.0));
int adjusted = raw - shift;
return adjusted < 1 ? 1 : adjusted;
}
bool TradeOfferList::unlocked(int index, int level) const {
if (index < 0 || index >= size()) return false;
return static_cast<int>(offers[index].tier) < clampInt(level, 1, villagerMaxLevel);
}
int TradeOfferList::unlockedCount(int level) const {
int count = 0;
for (int i = 0; i < size(); ++i)
if (unlocked(i, level)) ++count;
return count;
}
int TradeOfferList::countForTier(TradeTier tier) const {
int count = 0;
for (const TradeOffer& offer : offers)
if (offer.tier == tier) ++count;
return count;
}
bool TradeOfferList::allExhausted(int level) const {
int reachable = 0;
for (int i = 0; i < size(); ++i) {
if (!unlocked(i, level)) continue;
++reachable;
if (!offers[i].exhausted()) return false;
}
return reachable > 0;
}
TradeOfferList buildTradeOffers(VillagerProfession profession, uint32_t seed) {
TradeOfferList list;
list.profession = profession;
int specCount = 0;
const OfferSpec* specs = specsForProfession(profession, specCount);
if (specs == nullptr) return list;