feat: add transferAll and splitOff to ItemStack

This commit is contained in:
dualfroz committed 2026-09-07 19:53:25 +02:00
1 parent 0bb301ea22
commit c29b9dba4a
1 file changed
+160
+160
View File
@@ -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<MetaStack>& 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<MetaStack>& 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<MetaStack>& 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<float>(unbreakingLevel + 1);
float ignoreChance = armorPiece ? 0.6f + 0.4f / levels
: static_cast<float>(unbreakingLevel) / levels;
float roll = static_cast<float>(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;
}