diff --git a/src/main/java/com/trunksbomb/minetriad/blockentity/DuelTableBlockEntity.java b/src/main/java/com/trunksbomb/minetriad/blockentity/DuelTableBlockEntity.java index 6824763..a2fa219 100644 --- a/src/main/java/com/trunksbomb/minetriad/blockentity/DuelTableBlockEntity.java +++ b/src/main/java/com/trunksbomb/minetriad/blockentity/DuelTableBlockEntity.java @@ -48,9 +48,11 @@ public class DuelTableBlockEntity extends BlockEntity { private static final int CAPTURE_DURATION = 10; private static final int OVERVIEW_SWEEP_DURATION = 18; private static final int CLEAR_WAVE_DURATION = 10; + private static final int REWARD_TAKE_DURATION = 14; private final NonNullList boardCards = NonNullList.withSize(SLOT_COUNT, ItemStack.EMPTY); private final NonNullList rewardCards = NonNullList.withSize(REWARD_SLOT_COUNT, ItemStack.EMPTY); + private final List rewardTakeAnimations = new ArrayList<>(); private final int[] ownerSlots = new int[SLOT_COUNT]; private final int[] slotAges = new int[SLOT_COUNT]; private final ArrayDeque pendingAnimations = new ArrayDeque<>(); @@ -88,6 +90,10 @@ public class DuelTableBlockEntity extends BlockEntity { return count; } + public List rewardTakeAnimations() { + return List.copyOf(rewardTakeAnimations); + } + public int ownerAt(int slot) { return ownerSlots[slot]; } @@ -253,6 +259,15 @@ public class DuelTableBlockEntity extends BlockEntity { if (taken.isEmpty()) { return ItemStack.EMPTY; } + int visibleIndex = visibleRewardIndex(slot); + int rewardCountBeforeTake = rewardCardCount(); + if (visibleIndex >= 0 && rewardCountBeforeTake > 0) { + BoardLocalSpace.SlotCenter center = BoardLocalSpace.rewardCenter( + visibleIndex, + getBlockState().getValue(DuelTableBlock.FACING), + rewardCountBeforeTake); + rewardTakeAnimations.add(new RewardTakeAnimation(taken.copyWithCount(1), center.x(), center.z(), 0, REWARD_TAKE_DURATION)); + } rewardCards.set(slot, ItemStack.EMPTY); refreshRewardInteractions(); sync(); @@ -263,6 +278,7 @@ public class DuelTableBlockEntity extends BlockEntity { for (int index = 0; index < rewardCards.size(); index++) { rewardCards.set(index, ItemStack.EMPTY); } + rewardTakeAnimations.clear(); removeRewardInteractions(); } @@ -274,6 +290,7 @@ public class DuelTableBlockEntity extends BlockEntity { rewardCards.set(slot, ItemStack.EMPTY); } } + rewardTakeAnimations.clear(); removeRewardInteractions(); } @@ -431,6 +448,21 @@ public class DuelTableBlockEntity extends BlockEntity { blockEntity.slotAges[slot] = 0; } } + + if (!blockEntity.rewardTakeAnimations.isEmpty()) { + boolean changed = false; + for (int index = blockEntity.rewardTakeAnimations.size() - 1; index >= 0; index--) { + RewardTakeAnimation animation = blockEntity.rewardTakeAnimations.get(index); + animation.age++; + if (animation.age >= animation.duration) { + blockEntity.rewardTakeAnimations.remove(index); + changed = true; + } + } + if (!level.isClientSide && changed) { + blockEntity.sync(); + } + } } @Override @@ -440,6 +472,7 @@ public class DuelTableBlockEntity extends BlockEntity { CompoundTag rewardTag = new CompoundTag(); ContainerHelper.saveAllItems(rewardTag, rewardCards, registries); tag.put("RewardCards", rewardTag); + saveRewardTakeAnimations(tag, registries); tag.putIntArray("OwnerSlots", ownerSlots); tag.putIntArray("SlotAges", slotAges); saveAnimation(tag); @@ -460,6 +493,7 @@ public class DuelTableBlockEntity extends BlockEntity { if (tag.contains("RewardCards", Tag.TAG_COMPOUND)) { ContainerHelper.loadAllItems(tag.getCompound("RewardCards"), rewardCards, registries); } + loadRewardTakeAnimations(tag, registries); loadOwnerData(tag); loadAnimation(tag); loadTableAnimation(tag); @@ -472,6 +506,7 @@ public class DuelTableBlockEntity extends BlockEntity { CompoundTag rewardTag = new CompoundTag(); ContainerHelper.saveAllItems(rewardTag, rewardCards, registries); tag.put("RewardCards", rewardTag); + saveRewardTakeAnimations(tag, registries); tag.putIntArray("OwnerSlots", ownerSlots); tag.putIntArray("SlotAges", slotAges); saveAnimation(tag); @@ -499,6 +534,7 @@ public class DuelTableBlockEntity extends BlockEntity { if (tag.contains("RewardCards", Tag.TAG_COMPOUND)) { ContainerHelper.loadAllItems(tag.getCompound("RewardCards"), rewardCards, registries); } + loadRewardTakeAnimations(tag, registries); loadOwnerData(tag); loadAnimation(tag); loadTableAnimation(tag); @@ -531,6 +567,44 @@ public class DuelTableBlockEntity extends BlockEntity { secondParticipantId = tag.hasUUID("SecondParticipantId") ? tag.getUUID("SecondParticipantId") : null; } + private void saveRewardTakeAnimations(CompoundTag tag, HolderLookup.Provider registries) { + CompoundTag animationsTag = new CompoundTag(); + for (int index = 0; index < rewardTakeAnimations.size(); index++) { + RewardTakeAnimation animation = rewardTakeAnimations.get(index); + CompoundTag animationTag = new CompoundTag(); + animationTag.put("Stack", animation.stack.saveOptional(registries)); + animationTag.putFloat("LocalX", animation.localX); + animationTag.putFloat("LocalZ", animation.localZ); + animationTag.putInt("Age", animation.age); + animationTag.putInt("Duration", animation.duration); + animationsTag.put("Animation" + index, animationTag); + } + animationsTag.putInt("Count", rewardTakeAnimations.size()); + tag.put("RewardTakeAnimations", animationsTag); + } + + private void loadRewardTakeAnimations(CompoundTag tag, HolderLookup.Provider registries) { + rewardTakeAnimations.clear(); + if (!tag.contains("RewardTakeAnimations", Tag.TAG_COMPOUND)) { + return; + } + CompoundTag animationsTag = tag.getCompound("RewardTakeAnimations"); + int count = animationsTag.getInt("Count"); + for (int index = 0; index < count; index++) { + CompoundTag animationTag = animationsTag.getCompound("Animation" + index); + ItemStack stack = ItemStack.parseOptional(registries, animationTag.getCompound("Stack")); + if (stack.isEmpty()) { + continue; + } + rewardTakeAnimations.add(new RewardTakeAnimation( + stack, + animationTag.getFloat("LocalX"), + animationTag.getFloat("LocalZ"), + animationTag.getInt("Age"), + animationTag.getInt("Duration"))); + } + } + private void saveAnimation(CompoundTag tag) { tag.putString("AnimationType", animationType.name()); tag.putIntArray("AnimationSlots", animationSlots); @@ -672,4 +746,54 @@ public class DuelTableBlockEntity extends BlockEntity { private record AnimationStep(AnimationType type, int[] slots, int targetOwner) { } + + public static final class RewardTakeAnimation { + private final ItemStack stack; + private final float localX; + private final float localZ; + private int age; + private final int duration; + + private RewardTakeAnimation(ItemStack stack, float localX, float localZ, int age, int duration) { + this.stack = stack; + this.localX = localX; + this.localZ = localZ; + this.age = age; + this.duration = duration; + } + + public ItemStack stack() { + return stack; + } + + public float localX() { + return localX; + } + + public float localZ() { + return localZ; + } + + public int age() { + return age; + } + + public int duration() { + return duration; + } + } + + private int visibleRewardIndex(int slot) { + int visibleIndex = 0; + for (int index = 0; index < rewardCards.size(); index++) { + if (rewardCards.get(index).isEmpty()) { + continue; + } + if (index == slot) { + return visibleIndex; + } + visibleIndex++; + } + return -1; + } } diff --git a/src/main/java/com/trunksbomb/minetriad/client/render/DuelTableBlockEntityRenderer.java b/src/main/java/com/trunksbomb/minetriad/client/render/DuelTableBlockEntityRenderer.java index 391c071..988d683 100644 --- a/src/main/java/com/trunksbomb/minetriad/client/render/DuelTableBlockEntityRenderer.java +++ b/src/main/java/com/trunksbomb/minetriad/client/render/DuelTableBlockEntityRenderer.java @@ -78,6 +78,7 @@ public class DuelTableBlockEntityRenderer implements BlockEntityRenderer