From c29b9dba4a350945486460196fbeec7296ba3851 Mon Sep 17 00:00:00 2001 From: DualFroz Date: Mon, 7 Sep 2026 19:53:25 +0200 Subject: [PATCH] feat: add transferAll and splitOff to ItemStack --- core/src/ItemStack.cpp | 160 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/core/src/ItemStack.cpp b/core/src/ItemStack.cpp index 8e2fff6..7aed888 100644 --- a/core/src/ItemStack.cpp +++ b/core/src/ItemStack.cpp @@ -158,3 +158,163 @@ StackTransfer transferStack(MetaStack& from, MetaStack& into, int amount) { return transfer; } +StackTransfer transferAll(MetaStack& from, MetaStack& into) { + return transferStack(from, into, from.count); +} + +MetaStack splitOff(MetaStack& stack, int amount) { + MetaStack taken; + if (stack.empty() || amount <= 0) return taken; + int moved = std::min(amount, stack.count); + taken.item = stack.item; + taken.meta = stack.meta; + taken.count = moved; + stack.count -= moved; + if (stack.count <= 0) stack.clear(); + return taken; +} + +MetaStack splitHalf(MetaStack& stack) { + if (stack.empty()) return MetaStack{}; + return splitOff(stack, (stack.count + 1) / 2); +} + +int defragmentStacks(std::vector& stacks) { + int freedBefore = 0; + for (const MetaStack& stack : stacks) { + if (stack.empty()) ++freedBefore; + } + + for (std::size_t target = 0; target < stacks.size(); ++target) { + if (stacks[target].empty() || stacks[target].full()) continue; + for (std::size_t source = target + 1; source < stacks.size(); ++source) { + if (stacks[source].empty()) continue; + if (!stacksMergeable(stacks[target], stacks[source])) continue; + transferAll(stacks[source], stacks[target]); + if (stacks[target].full()) break; + } + } + + std::stable_partition(stacks.begin(), stacks.end(), + [](const MetaStack& stack) { return !stack.empty(); }); + + int freedAfter = 0; + for (const MetaStack& stack : stacks) { + if (stack.empty()) ++freedAfter; + } + return freedAfter - freedBefore; +} + +int totalCountOf(const std::vector& stacks, ItemId item) { + int total = 0; + for (const MetaStack& stack : stacks) { + if (!stack.empty() && stack.item == item) total += stack.count; + } + return total; +} + +bool stackOrderLess(const MetaStack& a, const MetaStack& b) { + if (a.empty() != b.empty()) return b.empty(); + if (a.empty()) return false; + if (a.item != b.item) return a.item < b.item; + bool aWorn = a.meta.durability.used > 0; + bool bWorn = b.meta.durability.used > 0; + if (aWorn != bWorn) return bWorn; + if (a.count != b.count) return a.count > b.count; + return a.meta.customName < b.meta.customName; +} + +void sortStacks(std::vector& stacks) { + std::stable_sort(stacks.begin(), stacks.end(), stackOrderLess); +} + +uint32_t stackRandom(uint32_t& state) { + if (state == 0) state = 0x9e3779b9u; + state ^= state << 13; + state ^= state >> 17; + state ^= state << 5; + return state; +} + +bool unbreakingSkipsWear(int unbreakingLevel, bool armorPiece, uint32_t& rng) { + if (unbreakingLevel <= 0) return false; + float levels = static_cast(unbreakingLevel + 1); + float ignoreChance = armorPiece ? 0.6f + 0.4f / levels + : static_cast(unbreakingLevel) / levels; + float roll = static_cast(stackRandom(rng)) / 4294967295.0f; + return roll < ignoreChance; +} + +WearResult damageStack(MetaStack& stack, int amount, uint32_t& rng) { + if (stack.empty() || !stack.meta.durability.damageable()) return WearResult::NotDamageable; + if (stack.meta.unbreakable || amount <= 0) return WearResult::Absorbed; + + int unbreaking = stack.meta.enchants.levelOf(EnchantKind::Unbreaking); + int applied = 0; + for (int i = 0; i < amount; ++i) { + if (unbreakingSkipsWear(unbreaking, false, rng)) continue; + ++applied; + } + if (applied == 0) return WearResult::Absorbed; + + stack.meta.durability.used += applied; + if (!stack.meta.durability.broken()) return WearResult::Damaged; + + if (stack.meta.enchants.has(EnchantKind::Mending)) { + stack.meta.durability.used = stack.meta.durability.max - 1; + return WearResult::Survived; + } + stack.clear(); + return WearResult::Broke; +} + +int mendingRepair(MetaStack& stack, int experience) { + if (stack.empty() || experience <= 0) return 0; + if (!stack.meta.durability.damageable()) return 0; + if (!stack.meta.enchants.has(EnchantKind::Mending)) return 0; + int repair = std::min(experience * 2, stack.meta.durability.used); + stack.meta.durability.used -= repair; + return repair; +} + +MetaStack craftingRemainder(const MetaStack& ingredient) { + if (ingredient.empty()) return MetaStack{}; + if (!ingredient.meta.durability.damageable()) return MetaStack{}; + + MetaStack leftover = ingredient; + leftover.count = 1; + if (leftover.meta.unbreakable) return leftover; + leftover.meta.durability.used += 1; + if (leftover.meta.durability.broken()) return MetaStack{}; + return leftover; +} + +ItemSlot toItemSlot(const MetaStack& stack) { + if (stack.empty()) return ItemSlot{}; + ItemSlot slot; + slot.item = stack.item; + slot.count = stack.count; + slot.durability = stack.meta.durability.remaining(); + return slot; +} + +MetaStack fromItemSlot(const ItemSlot& slot) { + MetaStack stack; + if (slot.empty()) return stack; + stack.item = slot.item; + stack.count = slot.count; + int max = isTool(slot.item) ? toolMaxDurability(slot.item) : 0; + if (max > 0) { + int left = std::min(std::max(slot.durability, 0), max); + stack.meta.durability = {max - left, max}; + } + return stack; +} + +NbtTag stackToNbt(const MetaStack& stack) { + NbtTag tag = NbtTag::makeCompound(); + if (stack.empty()) { + tag.put("Id", NbtTag::makeShort(0)); + return tag; + } +