diff --git a/core/src/Enchant.cpp b/core/src/Enchant.cpp index 7768ab6..3b4231d 100644 --- a/core/src/Enchant.cpp +++ b/core/src/Enchant.cpp @@ -156,3 +156,145 @@ int enchantMaxCost(EnchantKind kind, int level) { } } +bool enchantsConflict(EnchantKind a, EnchantKind b) { + if (a == b) return false; + if (isProtectionFamily(a) && isProtectionFamily(b)) return true; + if (isDamageFamily(a) && isDamageFamily(b)) return true; + if ((a == EnchantKind::SilkTouch && b == EnchantKind::Fortune) || + (a == EnchantKind::Fortune && b == EnchantKind::SilkTouch)) + return true; + if ((a == EnchantKind::SilkTouch && b == EnchantKind::Looting) || + (a == EnchantKind::Looting && b == EnchantKind::SilkTouch)) + return true; + if ((a == EnchantKind::Infinity && b == EnchantKind::Mending) || + (a == EnchantKind::Mending && b == EnchantKind::Infinity)) + return true; + return false; +} + +bool enchantApplies(EnchantKind kind, Enchantment::Target target) { + if (target == Enchantment::Target::Book) return true; + switch (kind) { + case EnchantKind::Unbreaking: + case EnchantKind::Mending: + return true; + case EnchantKind::Protection: + case EnchantKind::FireProtection: + case EnchantKind::BlastProtection: + case EnchantKind::ProjectileProtection: + case EnchantKind::Thorns: + return isArmourTarget(target); + case EnchantKind::FeatherFalling: + return target == Enchantment::Target::Boots; + case EnchantKind::Respiration: + case EnchantKind::AquaAffinity: + return target == Enchantment::Target::Helmet; + case EnchantKind::Knockback: + case EnchantKind::FireAspect: + case EnchantKind::Looting: + return target == Enchantment::Target::Sword; + case EnchantKind::Sharpness: + case EnchantKind::Smite: + case EnchantKind::BaneOfArthropods: + return target == Enchantment::Target::Sword || target == Enchantment::Target::Axe; + case EnchantKind::Efficiency: + case EnchantKind::SilkTouch: + case EnchantKind::Fortune: + return isDiggerTarget(target); + case EnchantKind::Power: + case EnchantKind::Punch: + case EnchantKind::Flame: + case EnchantKind::Infinity: + return target == Enchantment::Target::Bow; + default: + return false; + } +} + +int enchantabilityOf(ItemId item) { + if (!isTool(item)) return 0; + switch (itemInfo(item).toolTier) { + case ToolTier::Wood: return 15; + case ToolTier::Stone: return 5; + case ToolTier::Iron: return 14; + case ToolTier::Gold: return 22; + case ToolTier::Diamond: return 10; + default: return 0; + } +} + +Enchantment::Target enchantTargetOf(ItemId item) { + switch (itemInfo(item).toolType) { + case ToolType::Sword: return Enchantment::Target::Sword; + case ToolType::Axe: return Enchantment::Target::Axe; + case ToolType::Pickaxe: return Enchantment::Target::Pickaxe; + case ToolType::Shovel: return Enchantment::Target::Shovel; + default: return Enchantment::Target::Book; + } +} + +int EnchantSet::levelOf(EnchantKind kind) const { + for (const Enchantment& entry : entries) { + if (entry.kind == kind) return entry.level; + } + return 0; +} + +bool EnchantSet::conflictsWith(EnchantKind kind) const { + for (const Enchantment& entry : entries) { + if (enchantsConflict(entry.kind, kind)) return true; + } + return false; +} + +bool addEnchant(EnchantSet& set, EnchantKind kind, int level) { + if (level < 1 || level > maxEnchantLevel(kind)) return false; + if (set.conflictsWith(kind)) return false; + for (Enchantment& entry : set.entries) { + if (entry.kind != kind) continue; + if (level <= entry.level) return false; + entry.level = level; + return true; + } + set.entries.push_back({kind, level}); + return true; +} + +bool removeEnchant(EnchantSet& set, EnchantKind kind) { + std::size_t before = set.entries.size(); + set.entries.erase(std::remove_if(set.entries.begin(), set.entries.end(), + [kind](const Enchantment& e) { return e.kind == kind; }), + set.entries.end()); + return set.entries.size() != before; +} + +int bookshelfCount(const World& world, const Vec3i& tablePos) { + int found = 0; + for (int dx = -2; dx <= 2; ++dx) { + for (int dz = -2; dz <= 2; ++dz) { + if (std::max(std::abs(dx), std::abs(dz)) != 2) continue; + int stepX = dx > 0 ? 1 : (dx < 0 ? -1 : 0); + int stepZ = dz > 0 ? 1 : (dz < 0 ? -1 : 0); + for (int dy = 0; dy <= 1; ++dy) { + Block shelf = world.blockAt(tablePos.x + dx, tablePos.y + dy, tablePos.z + dz); + if (shelf != Block::Bookshelf) continue; + Block blocker = + world.blockAt(tablePos.x + stepX, tablePos.y + dy, tablePos.z + stepZ); + if (isOpaque(blocker)) continue; + ++found; + } + } + } + return std::min(found, maxBookshelfPower); +} + +EnchantSet rollEnchantments(Enchantment::Target target, int enchantability, int enchantLevel, + uint32_t& rng) { + EnchantSet chosen; + if (enchantLevel < 1) return chosen; + + int bonus = enchantability / 4 + 1; + int modified = enchantLevel + rollBelow(rng, bonus) + rollBelow(rng, bonus) + 1; + float spread = 1.0f + (unitFloat(rng) + unitFloat(rng) - 1.0f) * 0.15f; + modified = std::max(1, static_cast(std::lround(static_cast(modified) * spread))); +