feat: add refreshOffers and takeOffer to Enchant

This commit is contained in:
dualfroz committed 2026-09-07 17:18:49 +02:00
1 parent c3794851a2
commit 6a21f1d332
1 file changed
+110
+110
View File
@@ -298,3 +298,113 @@ EnchantSet rollEnchantments(Enchantment::Target target, int enchantability, int
float spread = 1.0f + (unitFloat(rng) + unitFloat(rng) - 1.0f) * 0.15f;
modified = std::max(1, static_cast<int>(std::lround(static_cast<float>(modified) * spread)));
std::vector<Enchantment> candidates;
for (int slot = 0; slot < 3; ++slot) {
candidates.clear();
int totalWeight = 0;
for (int k = 0; k < static_cast<int>(EnchantKind::Count); ++k) {
EnchantKind kind = static_cast<EnchantKind>(k);
if (!enchantApplies(kind, target)) continue;
if (chosen.levelOf(kind) > 0 || chosen.conflictsWith(kind)) continue;
for (int level = maxEnchantLevel(kind); level >= 1; --level) {
if (modified < enchantMinCost(kind, level)) continue;
if (modified > enchantMaxCost(kind, level)) continue;
candidates.push_back({kind, level});
totalWeight += enchantWeight(kind);
break;
}
}
if (candidates.empty()) break;
int pick = rollBelow(rng, totalWeight);
Enchantment picked = candidates.back();
for (const Enchantment& candidate : candidates) {
pick -= enchantWeight(candidate.kind);
if (pick < 0) {
picked = candidate;
break;
}
}
chosen.entries.push_back(picked);
if (slot == 2) break;
modified = modified * 4 / 5;
if (rollBelow(rng, 50) > modified) break;
}
return chosen;
}
void refreshOffers(EnchantTableState& state) {
uint32_t rng = state.seed != 0 ? state.seed : 0x9e3779b9u;
int shelves = std::min(std::max(state.bookshelves, 0), maxBookshelfPower);
int base = 1 + rollBelow(rng, 8) + shelves / 2 + rollBelow(rng, shelves + 1);
int slotLevels[3] = {
std::max(base / 3, 1),
(base * 2) / 3 + 1,
std::max(base, shelves * 2),
};
for (int slot = 0; slot < 3; ++slot) {
EnchantOffer offer;
offer.lapisCost = slot + 1;
offer.requiredLevels = slotLevels[slot];
uint32_t slotRng = rng ^ (0x85ebca6bu * static_cast<uint32_t>(slot + 1));
offer.enchants = rollEnchantments(state.target, state.enchantability, slotLevels[slot],
slotRng);
if (offer.enchants.empty()) offer.requiredLevels = 0;
state.offers[static_cast<std::size_t>(slot)] = offer;
}
}
EnchantOffer takeOffer(EnchantTableState& state, int slot, int playerLevels, int lapisCount) {
if (slot < 0 || slot > 2) return {};
const EnchantOffer& offer = state.offers[static_cast<std::size_t>(slot)];
if (!offer.valid()) return {};
if (playerLevels < offer.requiredLevels || lapisCount < offer.lapisCost) return {};
EnchantOffer granted = offer;
state.seed = state.seed * 1664525u + 1013904223u + static_cast<uint32_t>(slot);
refreshOffers(state);
return granted;
}
int anvilRepairCost(int priorWork) {
int clamped = std::min(std::max(priorWork, 0), 30);
return (1 << clamped) - 1;
}
EnchantOffer anvilCombine(const EnchantSet& target, int targetPriorWork, const EnchantSet& sacrifice,
int sacrificePriorWork) {
EnchantOffer merged;
merged.enchants = target;
merged.priorWork = std::max(targetPriorWork, sacrificePriorWork) + 1;
int cost = anvilRepairCost(targetPriorWork) + anvilRepairCost(sacrificePriorWork);
for (const Enchantment& incoming : sacrifice.entries) {
int existing = merged.enchants.levelOf(incoming.kind);
if (existing == 0) {
if (merged.enchants.conflictsWith(incoming.kind)) {
cost += 1;
continue;
}
merged.enchants.entries.push_back(incoming);
cost += anvilCostMultiplier(incoming.kind) * incoming.level;
continue;
}
int newLevel = existing;
if (incoming.level > existing) {
newLevel = incoming.level;
} else if (incoming.level == existing) {
newLevel = std::min(existing + 1, maxEnchantLevel(incoming.kind));
}
for (Enchantment& entry : merged.enchants.entries) {
if (entry.kind == incoming.kind) entry.level = newLevel;
}
cost += anvilCostMultiplier(incoming.kind) * newLevel;
}
merged.requiredLevels = cost;
merged.tooExpensive = cost >= anvilTooExpensiveCost;
return merged;
}
} // namespace vf