Skip to content

Commit 47cf114

Browse files
Add item frame filter expansion (#4796)
Filter items in glow item frames are expanded into a list of items if they are a bundle, shulker box, or similar item with non-empty content. - Hopperhock, Rannuncarpus, and Interceptor filter for the exact container item *with the same contents* if it's in a regular item frame, and for the contents items if it's in a glow item frame - Corporea Funnel treats item counts of expanded filter lists as random weights, similar to having multiple frames on the funnel block. Rotation of the item still defines the number of items to request. --------- Co-authored-by: Artemis System <[email protected]>
1 parent 84a08c3 commit 47cf114

File tree

13 files changed

+268
-47
lines changed

13 files changed

+268
-47
lines changed

Xplat/src/main/java/vazkii/botania/client/core/helper/RenderHelper.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -613,8 +613,8 @@ private static MultiBufferSource wrapBuffer(MultiBufferSource buffer, int alpha,
613613
* another box that is 2px bigger in each direction
614614
*/
615615
public static void renderHUDBox(GuiGraphics gui, int startX, int startY, int endX, int endY) {
616-
gui.fill(startX, startY, endX, endY, 0x44000000);
617-
gui.fill(startX - 2, startY - 2, endX + 2, endY + 2, 0x44000000);
616+
gui.fill(startX, startY, endX, endY, 0x40000000);
617+
gui.fill(startX - 2, startY - 2, endX + 2, endY + 2, 0x40000000);
618618
}
619619

620620
/*

Xplat/src/main/java/vazkii/botania/client/integration/shared/LocaleHelper.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,25 @@ public static NumberFormat getPercentageFormat(int fractionDigits) {
2121
return formatter;
2222
}
2323

24+
@NotNull
25+
public static NumberFormat getDecimalFractionFormat(int fractionDigits) {
26+
final NumberFormat formatter = NumberFormat.getNumberInstance(Proxy.INSTANCE.getLocale());
27+
formatter.setMinimumFractionDigits(fractionDigits);
28+
formatter.setMaximumFractionDigits(fractionDigits);
29+
formatter.setRoundingMode(RoundingMode.HALF_UP);
30+
return formatter;
31+
}
32+
2433
public static String formatAsPercentage(double value, int fractionDigits) {
2534
final NumberFormat formatter = getPercentageFormat(fractionDigits);
2635
final double minValue = Math.pow(10, -fractionDigits) / 100;
2736
return (value < minValue
2837
? "< " + formatter.format(minValue)
2938
: formatter.format(value)).replace('\u00a0', ' ');
3039
}
40+
41+
public static String formatAsDecimalFraction(double value, int fractionDigits) {
42+
final NumberFormat formatter = getDecimalFractionFormat(fractionDigits);
43+
return formatter.format(value).replace('\u00a0', ' ');
44+
}
3145
}

Xplat/src/main/java/vazkii/botania/common/block/block_entity/corporea/CorporeaFunnelBlockEntity.java

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import net.minecraft.core.BlockPos;
1212
import net.minecraft.core.Direction;
13+
import net.minecraft.util.random.WeightedRandomList;
1314
import net.minecraft.world.entity.LivingEntity;
1415
import net.minecraft.world.entity.decoration.ItemFrame;
1516
import net.minecraft.world.entity.item.ItemEntity;
@@ -24,23 +25,28 @@
2425
import vazkii.botania.api.corporea.CorporeaRequestor;
2526
import vazkii.botania.api.corporea.CorporeaSpark;
2627
import vazkii.botania.common.block.block_entity.BotaniaBlockEntities;
28+
import vazkii.botania.common.helper.FilterHelper;
2729
import vazkii.botania.common.helper.InventoryHelper;
2830
import vazkii.botania.xplat.XplatAbstractions;
2931

3032
import java.util.ArrayList;
3133
import java.util.List;
3234

3335
public class CorporeaFunnelBlockEntity extends BaseCorporeaBlockEntity implements CorporeaRequestor {
36+
private static final int[] ROTATION_TO_STACK_SIZE = { 1, 2, 4, 8, 16, 32, 48, 64 };
37+
3438
public CorporeaFunnelBlockEntity(BlockPos pos, BlockState state) {
3539
super(BotaniaBlockEntities.CORPOREA_FUNNEL, pos, state);
3640
}
3741

3842
public void doRequest() {
3943
CorporeaSpark spark = getSpark();
4044
if (spark != null && spark.getMaster() != null) {
41-
List<ItemStack> filter = getFilter();
45+
WeightedRandomList<FilterHelper.WeightedItemStack> filter = getFilter();
4246
if (!filter.isEmpty()) {
43-
ItemStack stack = filter.get(level.random.nextInt(filter.size()));
47+
ItemStack stack = filter.getRandom(level.random)
48+
.map(FilterHelper.WeightedItemStack::stack)
49+
.orElse(ItemStack.EMPTY);
4450

4551
if (!stack.isEmpty()) {
4652
var matcher = CorporeaHelper.instance().createMatcher(stack, true);
@@ -50,28 +56,26 @@ public void doRequest() {
5056
}
5157
}
5258

53-
public List<ItemStack> getFilter() {
54-
List<ItemStack> filter = new ArrayList<>();
55-
56-
final int[] rotationToStackSize = new int[] {
57-
1, 2, 4, 8, 16, 32, 48, 64
58-
};
59+
public WeightedRandomList<FilterHelper.WeightedItemStack> getFilter() {
60+
List<FilterHelper.WeightedItemStack> filter = new ArrayList<>();
5961

6062
for (Direction dir : Direction.values()) {
6163
List<ItemFrame> frames = level.getEntitiesOfClass(ItemFrame.class, new AABB(worldPosition.relative(dir), worldPosition.relative(dir).offset(1, 1, 1)));
6264
for (ItemFrame frame : frames) {
6365
Direction orientation = frame.getDirection();
6466
if (orientation == dir) {
65-
ItemStack stack = frame.getItem();
66-
if (!stack.isEmpty()) {
67-
ItemStack copy = stack.copyWithCount(rotationToStackSize[frame.getRotation()]);
68-
filter.add(copy);
67+
List<ItemStack> filterStacks = FilterHelper.getFilterItems(frame);
68+
if (!filterStacks.isEmpty()) {
69+
int stackSize = ROTATION_TO_STACK_SIZE[frame.getRotation()];
70+
filterStacks.stream()
71+
.map(s -> FilterHelper.WeightedItemStack.of(s.copyWithCount(stackSize), s.getCount()))
72+
.forEach(filter::add);
6973
}
7074
}
7175
}
7276
}
7377

74-
return filter;
78+
return WeightedRandomList.create(filter);
7579
}
7680

7781
@Override

Xplat/src/main/java/vazkii/botania/common/block/block_entity/corporea/CorporeaInterceptorBlockEntity.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import vazkii.botania.api.corporea.CorporeaRequestMatcher;
2323
import vazkii.botania.api.corporea.CorporeaSpark;
2424
import vazkii.botania.common.block.block_entity.BotaniaBlockEntities;
25+
import vazkii.botania.common.helper.FilterHelper;
2526

2627
import java.util.ArrayList;
2728
import java.util.List;
@@ -82,10 +83,7 @@ private List<ItemStack> getFilter() {
8283
for (ItemFrame frame : frames) {
8384
Direction orientation = frame.getDirection();
8485
if (orientation == dir) {
85-
ItemStack stack = frame.getItem();
86-
if (!stack.isEmpty()) {
87-
filter.add(stack);
88-
}
86+
filter.addAll(FilterHelper.getFilterItems(frame));
8987
}
9088
}
9189
}

Xplat/src/main/java/vazkii/botania/common/block/flower/functional/HopperhockBlockEntity.java

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,7 @@
3131
import vazkii.botania.api.block_entity.FunctionalFlowerBlockEntity;
3232
import vazkii.botania.api.block_entity.RadiusDescriptor;
3333
import vazkii.botania.common.block.BotaniaFlowerBlocks;
34-
import vazkii.botania.common.helper.DelayHelper;
35-
import vazkii.botania.common.helper.EntityHelper;
36-
import vazkii.botania.common.helper.InventoryHelper;
37-
import vazkii.botania.common.helper.ItemNBTHelper;
34+
import vazkii.botania.common.helper.*;
3835
import vazkii.botania.common.internal_caps.ItemFlagsComponent;
3936
import vazkii.botania.xplat.XplatAbstractions;
4037

@@ -191,7 +188,7 @@ public static List<ItemStack> getFilterForInventory(Level level, BlockPos pos, b
191188
List<ItemFrame> frames = level.getEntitiesOfClass(ItemFrame.class, aabb);
192189
for (ItemFrame frame : frames) {
193190
if (frame.getDirection() == dir) {
194-
filter.add(frame.getItem());
191+
filter.addAll(FilterHelper.getFilterItems(frame));
195192
}
196193
}
197194
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package vazkii.botania.common.helper;
2+
3+
import net.minecraft.nbt.CompoundTag;
4+
import net.minecraft.nbt.ListTag;
5+
import net.minecraft.nbt.Tag;
6+
import net.minecraft.util.random.Weight;
7+
import net.minecraft.util.random.WeightedEntry;
8+
import net.minecraft.world.entity.decoration.GlowItemFrame;
9+
import net.minecraft.world.entity.decoration.ItemFrame;
10+
import net.minecraft.world.item.BlockItem;
11+
import net.minecraft.world.item.ItemStack;
12+
import net.minecraft.world.item.Items;
13+
14+
import org.jetbrains.annotations.NotNull;
15+
import org.jetbrains.annotations.Nullable;
16+
17+
import vazkii.botania.mixin.BundleItemAccessor;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
22+
public class FilterHelper {
23+
24+
public static final String ITEMS_TAG = "Items";
25+
26+
public static List<ItemStack> getFilterItems(ItemFrame filterFrame) {
27+
ItemStack filterStack = filterFrame.getItem();
28+
if (filterStack.isEmpty()) {
29+
return List.of();
30+
}
31+
return filterFrame instanceof GlowItemFrame ? getFilterStacks(filterStack) : List.of(filterStack);
32+
}
33+
34+
/**
35+
* Expands the given filter item into the list of filter items it represents. This is NOT recursive.
36+
* Non-empty container items stand for their contents, not for themselves.
37+
*/
38+
public static List<ItemStack> getFilterStacks(ItemStack filterStack) {
39+
if (filterStack.is(Items.BUNDLE)) {
40+
// get bundle content
41+
List<ItemStack> bundledItems = BundleItemAccessor.call_getContents(filterStack).toList();
42+
if (!bundledItems.isEmpty()) {
43+
return bundledItems;
44+
}
45+
} else {
46+
// BlockItems (especially shulker boxes) can contain BlockEntity data, which may include an inventory.
47+
// Otherwise, items may represent an inventory themselves (e.g. Flower Pouch or Bauble Box)
48+
CompoundTag tag = filterStack.getItem() instanceof BlockItem
49+
? BlockItem.getBlockEntityData(filterStack)
50+
: filterStack.getTag();
51+
if (tag != null && tag.contains(ITEMS_TAG, Tag.TAG_LIST)) {
52+
// item might contain an inventory
53+
List<ItemStack> items = getItemStacks(tag);
54+
if (items != null) {
55+
return items;
56+
}
57+
}
58+
}
59+
return List.of(filterStack);
60+
}
61+
62+
@Nullable
63+
private static List<ItemStack> getItemStacks(CompoundTag tag) {
64+
try {
65+
ListTag contents = tag.getList(ITEMS_TAG, CompoundTag.TAG_COMPOUND);
66+
List<ItemStack> items = new ArrayList<>(contents.size());
67+
for (int i = 0; i < contents.size(); i++) {
68+
CompoundTag entry = contents.getCompound(i);
69+
ItemStack stack = ItemStack.of(entry);
70+
if (!stack.isEmpty()) {
71+
items.add(stack);
72+
}
73+
}
74+
if (!items.isEmpty()) {
75+
return items;
76+
}
77+
} catch (ClassCastException ce) {
78+
// apparently not a typical container inventory
79+
}
80+
return null;
81+
}
82+
83+
public record WeightedItemStack(ItemStack stack, Weight weight) implements WeightedEntry {
84+
public static WeightedItemStack of(ItemStack stack, int weight) {
85+
return new WeightedItemStack(stack, Weight.of(weight));
86+
}
87+
88+
@NotNull
89+
@Override
90+
public Weight getWeight() {
91+
return weight;
92+
}
93+
}
94+
}

0 commit comments

Comments
 (0)