Skip to content

Commit 8ea4f82

Browse files
authored
Clean up build item collection logic (#50)
2 parents ef7b06c + 6b86988 commit 8ea4f82

File tree

5 files changed

+301
-155
lines changed

5 files changed

+301
-155
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
package fr.hugman.build_rush.build;
2+
3+
import fr.hugman.build_rush.BuildRush;
4+
import fr.hugman.build_rush.mixin.CandleCakeBlockAccessor;
5+
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
6+
import net.minecraft.block.BlockState;
7+
import net.minecraft.block.Blocks;
8+
import net.minecraft.block.BrewingStandBlock;
9+
import net.minecraft.block.CampfireBlock;
10+
import net.minecraft.block.CandleBlock;
11+
import net.minecraft.block.CandleCakeBlock;
12+
import net.minecraft.block.ChiseledBookshelfBlock;
13+
import net.minecraft.block.EndPortalFrameBlock;
14+
import net.minecraft.block.FlowerPotBlock;
15+
import net.minecraft.block.LecternBlock;
16+
import net.minecraft.block.LeveledCauldronBlock;
17+
import net.minecraft.block.MultifaceGrowthBlock;
18+
import net.minecraft.block.RespawnAnchorBlock;
19+
import net.minecraft.block.SlabBlock;
20+
import net.minecraft.block.VineBlock;
21+
import net.minecraft.block.entity.BlockEntity;
22+
import net.minecraft.block.enums.BedPart;
23+
import net.minecraft.block.enums.DoubleBlockHalf;
24+
import net.minecraft.block.enums.SlabType;
25+
import net.minecraft.fluid.Fluids;
26+
import net.minecraft.item.BlockItem;
27+
import net.minecraft.item.Item;
28+
import net.minecraft.item.ItemConvertible;
29+
import net.minecraft.item.ItemStack;
30+
import net.minecraft.item.Items;
31+
import net.minecraft.item.PlayerHeadItem;
32+
import net.minecraft.nbt.NbtCompound;
33+
import net.minecraft.potion.PotionUtil;
34+
import net.minecraft.potion.Potions;
35+
import net.minecraft.registry.tag.BlockTags;
36+
import net.minecraft.server.world.ServerWorld;
37+
import net.minecraft.state.property.IntProperty;
38+
import net.minecraft.state.property.Properties;
39+
import net.minecraft.state.property.Property;
40+
import net.minecraft.util.math.BlockPos;
41+
import org.jetbrains.annotations.Nullable;
42+
43+
import java.util.ArrayList;
44+
import java.util.Comparator;
45+
import java.util.HashMap;
46+
import java.util.List;
47+
import java.util.Map;
48+
import java.util.function.Consumer;
49+
50+
public class BuildItemCollector {
51+
private static final Comparator<ItemStack> COMPARATOR = Comparator.comparingInt(ItemStack::getCount).reversed();
52+
53+
private final List<ItemStack> additionalStacks = new ArrayList<>();
54+
private final Object2IntOpenHashMap<Item> counts = new Object2IntOpenHashMap<>();
55+
private final Map<Item, ItemStack> singletonStacks = new HashMap<>();
56+
57+
private void add(ItemStack stack) {
58+
if (!stack.isEmpty()) {
59+
if (stack.hasNbt()) {
60+
this.additionalStacks.add(stack);
61+
} else {
62+
this.addCount(stack.getItem(), stack.getCount());
63+
}
64+
}
65+
}
66+
67+
private void addCount(ItemConvertible item, int count) {
68+
this.counts.addTo(item.asItem(), count);
69+
}
70+
71+
private void addSingletonStack(Item item, Consumer<ItemStack> creator) {
72+
this.singletonStacks.computeIfAbsent(item, i -> {
73+
var stack = new ItemStack(i);
74+
creator.accept(stack);
75+
return stack;
76+
});
77+
}
78+
79+
public void accept(ServerWorld world, BlockPos pos) {
80+
var state = world.getBlockState(pos);
81+
82+
if (state.hasBlockEntity()) {
83+
var blockEntity = world.getBlockEntity(pos);
84+
85+
if (blockEntity == null) {
86+
BuildRush.LOGGER.warn("Block entity was null for " + state.getBlock() + " even though the game said it had one");
87+
}
88+
89+
this.accept(world, pos, state, blockEntity);
90+
} else {
91+
this.accept(world, pos, state, null);
92+
}
93+
}
94+
95+
public void accept(ServerWorld world, BlockPos pos, BlockState state, @Nullable BlockEntity blockEntity) {
96+
if (isIgnored(state)) return;
97+
98+
var block = state.getBlock();
99+
var pickStack = block.getPickStack(world, pos, state);
100+
101+
// Blocks requiring tools
102+
if (needsFlintAndSteel(state)) {
103+
this.addSingletonStack(Items.FLINT_AND_STEEL, BuildItemCollector::setUnbreakable);
104+
}
105+
106+
if (block instanceof CampfireBlock && !state.get(CampfireBlock.LIT)) {
107+
this.addSingletonStack(Items.IRON_SHOVEL, BuildItemCollector::setUnbreakable);
108+
}
109+
110+
// Fluids
111+
var fluid = state.getFluidState().getFluid();
112+
113+
if (fluid == Fluids.WATER) {
114+
this.addCount(Items.WATER_BUCKET, 1);
115+
}
116+
117+
if (block == Blocks.WATER_CAULDRON) {
118+
for (int i = 0; i < state.get(LeveledCauldronBlock.LEVEL); i++) {
119+
this.add(PotionUtil.setPotion(new ItemStack(Items.POTION), Potions.WATER));
120+
}
121+
}
122+
123+
if (fluid == Fluids.LAVA || block == Blocks.LAVA_CAULDRON) {
124+
this.addCount(Items.LAVA_BUCKET, 1);
125+
}
126+
127+
// Multiblocks
128+
setCount(pickStack, state, Properties.EGGS);
129+
setCount(pickStack, state, Properties.CANDLES);
130+
setCount(pickStack, state, Properties.FLOWER_AMOUNT);
131+
setCount(pickStack, state, Properties.LAYERS);
132+
setCount(pickStack, state, Properties.PICKLES);
133+
134+
if (block instanceof MultifaceGrowthBlock) {
135+
pickStack.setCount(MultifaceGrowthBlock.collectDirections(state).size());
136+
}
137+
138+
if (block instanceof SlabBlock && state.get(SlabBlock.TYPE) == SlabType.DOUBLE) {
139+
pickStack.setCount(pickStack.getCount() * 2);
140+
}
141+
142+
if (block instanceof VineBlock) {
143+
int count = 0;
144+
145+
if (state.get(VineBlock.UP)) count++;
146+
if (state.get(VineBlock.NORTH)) count++;
147+
if (state.get(VineBlock.EAST)) count++;
148+
if (state.get(VineBlock.SOUTH)) count++;
149+
if (state.get(VineBlock.WEST)) count++;
150+
151+
pickStack.setCount(count);
152+
}
153+
154+
// Blocks containing items
155+
if (block instanceof BrewingStandBlock) {
156+
for (var property : BrewingStandBlock.BOTTLE_PROPERTIES) {
157+
this.addCount(Items.GLASS_BOTTLE, state.get(property) ? 1 : 0);
158+
}
159+
}
160+
161+
if (block instanceof CandleCakeBlockAccessor cake) {
162+
this.addCount(cake.buildrush$getCandle(), 1);
163+
}
164+
165+
if (block instanceof ChiseledBookshelfBlock) {
166+
for (var property : ChiseledBookshelfBlock.SLOT_OCCUPIED_PROPERTIES) {
167+
this.addCount(Items.BOOK, state.get(property) ? 1 : 0);
168+
}
169+
}
170+
171+
if (block instanceof EndPortalFrameBlock && state.get(EndPortalFrameBlock.EYE)) {
172+
this.addCount(Items.ENDER_EYE, 1);
173+
}
174+
175+
if (block instanceof FlowerPotBlock && block != Blocks.FLOWER_POT) {
176+
this.addCount(Items.FLOWER_POT, 1);
177+
}
178+
179+
if (block instanceof LecternBlock && state.get(LecternBlock.HAS_BOOK)) {
180+
this.addCount(Items.BOOK, 1);
181+
}
182+
183+
if (block instanceof RespawnAnchorBlock) {
184+
this.addCount(Items.GLOWSTONE, state.get(RespawnAnchorBlock.CHARGES));
185+
}
186+
187+
// Block entities
188+
if (blockEntity != null) {
189+
BuildItemCollector.addBlockEntityNbt(pickStack, blockEntity);
190+
}
191+
192+
this.add(pickStack);
193+
}
194+
195+
public List<ItemStack> getStacks() {
196+
var stacks = new ArrayList<ItemStack>();
197+
198+
for (var entry : this.counts.object2IntEntrySet()) {
199+
var item = entry.getKey();
200+
int count = entry.getIntValue();
201+
202+
while (count > 0) {
203+
ItemStack stack = new ItemStack(item, Math.min(count, item.getMaxCount()));
204+
count -= stack.getCount();
205+
206+
stacks.add(stack);
207+
}
208+
}
209+
210+
stacks.addAll(this.additionalStacks);
211+
stacks.addAll(this.singletonStacks.values());
212+
213+
stacks.sort(COMPARATOR);
214+
return stacks;
215+
}
216+
217+
public boolean isSingletonStack(ItemStack stack) {
218+
return this.singletonStacks.containsValue(stack);
219+
}
220+
221+
private static boolean isIgnored(BlockState state) {
222+
if (state.isAir()) return true;
223+
224+
// Multipart blocks
225+
if (state.isOf(Blocks.PISTON_HEAD)) return true;
226+
227+
// Multipart block states
228+
if (isPropertyValue(state, Properties.DOUBLE_BLOCK_HALF, DoubleBlockHalf.LOWER)) return true;
229+
if (isPropertyValue(state, Properties.BED_PART, BedPart.FOOT)) return true;
230+
231+
return false;
232+
}
233+
234+
private static <T extends Comparable<T>> boolean isPropertyValue(BlockState state, Property<T> property, T value) {
235+
return state.contains(property) && state.get(property) == value;
236+
}
237+
238+
private static boolean needsFlintAndSteel(BlockState state) {
239+
var block = state.getBlock();
240+
241+
if (block instanceof CandleBlock) return state.get(CandleBlock.LIT);
242+
if (block instanceof CandleCakeBlock) return state.get(CandleCakeBlock.LIT);
243+
244+
return state.isIn(BlockTags.PORTALS) || state.isIn(BlockTags.FIRE);
245+
}
246+
247+
private static void setCount(ItemStack stack, BlockState state, IntProperty property) {
248+
if (state.contains(property)) {
249+
stack.setCount(state.get(property));
250+
}
251+
}
252+
253+
private static void setUnbreakable(ItemStack stack) {
254+
stack.getOrCreateNbt().putBoolean("Unbreakable", true);
255+
}
256+
257+
public static void addBlockEntityNbt(ItemStack stack, BlockEntity blockEntity) {
258+
NbtCompound nbtCompound = blockEntity.createNbtWithIdentifyingData();
259+
BlockItem.setBlockEntityNbt(stack, blockEntity.getType(), nbtCompound);
260+
if (stack.getItem() instanceof PlayerHeadItem && nbtCompound.contains("SkullOwner")) {
261+
NbtCompound nbtCompound2 = nbtCompound.getCompound("SkullOwner");
262+
NbtCompound nbtCompound3 = stack.getOrCreateNbt();
263+
nbtCompound3.put("SkullOwner", nbtCompound2);
264+
NbtCompound nbtCompound4 = nbtCompound3.getCompound("BlockEntityTag");
265+
nbtCompound4.remove("SkullOwner");
266+
nbtCompound4.remove("x");
267+
nbtCompound4.remove("y");
268+
nbtCompound4.remove("z");
269+
}
270+
}
271+
}

0 commit comments

Comments
 (0)