Skip to content

Commit 33c864d

Browse files
committed
Add scroll controls
1 parent 32db7c0 commit 33c864d

File tree

11 files changed

+240
-25
lines changed

11 files changed

+240
-25
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package io.github.drakonkinst.worldsinger.gui.tooltip;
2+
3+
import io.github.drakonkinst.worldsinger.item.itemcontainer.ItemContainerInteractions;
4+
import io.github.drakonkinst.worldsinger.network.packet.ItemContainerItemSelectedPayload;
5+
import io.github.drakonkinst.worldsinger.registry.ModDataComponentTypes;
6+
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
7+
import net.minecraft.client.MinecraftClient;
8+
import net.minecraft.client.gui.tooltip.TooltipSubmenuHandler;
9+
import net.minecraft.client.input.Scroller;
10+
import net.minecraft.item.ItemStack;
11+
import net.minecraft.screen.slot.Slot;
12+
import net.minecraft.screen.slot.SlotActionType;
13+
import org.joml.Vector2i;
14+
15+
public class ItemContainerTooltipSubmenuHandler implements TooltipSubmenuHandler {
16+
17+
private final MinecraftClient client;
18+
private final Scroller scroller;
19+
20+
public ItemContainerTooltipSubmenuHandler(MinecraftClient client) {
21+
this.client = client;
22+
this.scroller = new Scroller();
23+
}
24+
25+
@Override
26+
public boolean isApplicableTo(Slot slot) {
27+
return slot.getStack().contains(ModDataComponentTypes.ITEM_CONTAINER);
28+
}
29+
30+
@Override
31+
public boolean onScroll(double horizontal, double vertical, int slotId, ItemStack item) {
32+
int numStacksShown = ItemContainerInteractions.getNumberOfStacksShown(item);
33+
if (numStacksShown <= 0) {
34+
return false;
35+
}
36+
Vector2i delta = this.scroller.update(horizontal, vertical);
37+
int amount = delta.y == 0 ? -delta.x : delta.y;
38+
if (amount != 0) {
39+
int currentSelectedIndex = ItemContainerInteractions.getSelectedStackIndex(item);
40+
int nextSelectedIndex = Scroller.scrollCycling(amount, currentSelectedIndex,
41+
numStacksShown);
42+
if (currentSelectedIndex != nextSelectedIndex) {
43+
this.sendPacket(item, slotId, nextSelectedIndex);
44+
}
45+
}
46+
47+
return true;
48+
}
49+
50+
@Override
51+
public void reset(Slot slot) {
52+
this.reset(slot.getStack(), slot.id);
53+
}
54+
55+
@Override
56+
public void onMouseClick(Slot slot, SlotActionType actionType) {
57+
if (actionType == SlotActionType.QUICK_MOVE || actionType == SlotActionType.SWAP) {
58+
this.reset(slot.getStack(), slot.id);
59+
}
60+
}
61+
62+
private void sendPacket(ItemStack item, int slotId, int selectedItemIndex) {
63+
if (this.client.getNetworkHandler() != null
64+
&& selectedItemIndex < ItemContainerInteractions.getNumberOfStacksShown(item)) {
65+
ItemContainerInteractions.setSelectedStackIndex(item, selectedItemIndex);
66+
ClientPlayNetworking.send(
67+
new ItemContainerItemSelectedPayload(slotId, selectedItemIndex));
68+
}
69+
}
70+
71+
public void reset(ItemStack item, int slotId) {
72+
this.sendPacket(item, slotId, -1);
73+
}
74+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
package io.github.drakonkinst.worldsinger.mixin.client.gui;
2+
3+
import io.github.drakonkinst.worldsinger.gui.tooltip.ItemContainerTooltipSubmenuHandler;
4+
import net.minecraft.client.gui.screen.Screen;
5+
import net.minecraft.client.gui.screen.ingame.HandledScreen;
6+
import net.minecraft.client.gui.screen.ingame.ScreenHandlerProvider;
7+
import net.minecraft.client.gui.tooltip.TooltipSubmenuHandler;
8+
import net.minecraft.screen.ScreenHandler;
9+
import net.minecraft.text.Text;
10+
import org.spongepowered.asm.mixin.Mixin;
11+
import org.spongepowered.asm.mixin.Shadow;
12+
import org.spongepowered.asm.mixin.injection.At;
13+
import org.spongepowered.asm.mixin.injection.Inject;
14+
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
15+
16+
@Mixin(HandledScreen.class)
17+
public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen implements
18+
ScreenHandlerProvider<T> {
19+
20+
protected HandledScreenMixin(Text title) {
21+
super(title);
22+
}
23+
24+
@Shadow
25+
protected abstract void addTooltipSubmenuHandler(TooltipSubmenuHandler handler);
26+
27+
@Inject(method = "init", at = @At("TAIL"))
28+
private void addCustomSubmenuHandlers(CallbackInfo ci) {
29+
this.addTooltipSubmenuHandler(new ItemContainerTooltipSubmenuHandler(this.client));
30+
}
31+
}

src/client/resources/worldsinger.client.mixins.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"entity.render.state.LivingEntityRenderStateMixin",
2424
"gui.CreateWorldScreenMixin",
2525
"gui.DebugHudMixin",
26+
"gui.HandledScreenMixin",
2627
"gui.InGameHudMixin",
2728
"gui.InGameOverlayRendererPossessionMixin",
2829
"item.ItemModelManagerMixin",

src/main/java/io/github/drakonkinst/worldsinger/item/ModItems.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@ public final class ModItems {
192192
// Test Items
193193
public static final Item QUIVER = register("quiver", new Settings().maxCount(1)
194194
.component(ModDataComponentTypes.ITEM_CONTAINER,
195-
new ItemContainerComponent(256, ModItemTags.CAN_BE_QUIVERED,
195+
new ItemContainerComponent(256, ModItemTags.CAN_BE_QUIVERED, false,
196196
ItemContainerSettings.QUIVER, Collections.emptyList())));
197197

198198
// Admin

src/main/java/io/github/drakonkinst/worldsinger/item/component/ItemContainerComponent.java

Lines changed: 40 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,21 +38,22 @@ public class ItemContainerComponent implements TooltipData {
3838
public static final Codec<ItemContainerComponent> CODEC = ItemContainerComponentData.CODEC.flatXmap(
3939
ItemContainerComponent::validateWeight, component -> DataResult.success(
4040
new ItemContainerComponentData(component.getMaxItemCount(),
41-
component.getValidItems(), component.getSettings(),
42-
component.getStacks())));
41+
component.getValidItems(), component.isAutoPickupDisabled(),
42+
component.getSettings(), component.getStacks())));
4343
public static final PacketCodec<RegistryByteBuf, ItemContainerComponent> PACKET_CODEC = ItemContainerComponentData.PACKET_CODEC.xmap(
44-
data -> new ItemContainerComponent(data.maxItemCount, data.validItems, data.settings,
45-
data.stacks),
44+
data -> new ItemContainerComponent(data.maxItemCount, data.validItems,
45+
data.disableAutoPickup, data.settings, data.stacks),
4646
component -> new ItemContainerComponentData(component.getMaxItemCount(),
47-
component.getValidItems(), component.getSettings(), component.getStacks()));
47+
component.getValidItems(), component.isAutoPickupDisabled(),
48+
component.getSettings(), component.getStacks()));
4849

4950
private static DataResult<ItemContainerComponent> validateWeight(
5051
ItemContainerComponentData data) {
5152
try {
5253
Fraction weight = calculateWeight(data.stacks());
5354
return DataResult.success(
5455
new ItemContainerComponent(data.maxItemCount(), data.validItems(),
55-
data.settings(), data.stacks(), weight, -1));
56+
data.disableAutoPickup(), data.settings(), data.stacks(), weight, -1));
5657
} catch (ArithmeticException var2) {
5758
return DataResult.error(() -> "Excessive total item container weight");
5859
}
@@ -87,25 +88,30 @@ public static Fraction getStackWeight(ItemStack stack) {
8788

8889
private final int maxItemCount;
8990
private final TagKey<Item> validItems;
91+
private final boolean disableAutoPickup;
9092
private final ItemContainerSettings settings;
9193
private final List<ItemStack> stacks;
94+
95+
// These two do not necessarily need to be saved
9296
private final Fraction weight;
9397
private final int selectedStackIndex;
9498

9599
protected ItemContainerComponent(int maxItemCount, TagKey<Item> validItems,
96-
ItemContainerSettings settings, List<ItemStack> stacks, Fraction weight,
97-
int selectedStackIndex) {
100+
boolean disableAutoPickup, ItemContainerSettings settings, List<ItemStack> stacks,
101+
Fraction weight, int selectedStackIndex) {
98102
this.maxItemCount = maxItemCount;
99103
this.validItems = validItems;
104+
this.disableAutoPickup = disableAutoPickup;
100105
this.settings = settings;
101106
this.stacks = stacks;
102107
this.weight = weight;
103108
this.selectedStackIndex = selectedStackIndex;
104109
}
105110

106111
public ItemContainerComponent(int maxItemCount, TagKey<Item> validItems,
107-
ItemContainerSettings settings, List<ItemStack> stacks) {
108-
this(maxItemCount, validItems, settings, stacks, calculateWeight(stacks), -1);
112+
boolean disableAutoPickup, ItemContainerSettings settings, List<ItemStack> stacks) {
113+
this(maxItemCount, validItems, disableAutoPickup, settings, stacks, calculateWeight(stacks),
114+
-1);
109115
}
110116

111117
public int getNumberOfStacksShown() {
@@ -170,8 +176,13 @@ public int getSelectedStackIndex() {
170176
return selectedStackIndex;
171177
}
172178

173-
public boolean shouldAutoSort() {
174-
return settings.shouldAutoSort();
179+
public boolean shouldAutoPickup() {
180+
return settings.shouldAutoPickup() && !disableAutoPickup;
181+
}
182+
183+
// This boolean only matters if it would be enabled by default
184+
public boolean isAutoPickupDisabled() {
185+
return settings.shouldAutoPickup() && disableAutoPickup;
175186
}
176187

177188
public boolean canBeStored(ItemStack stack) {
@@ -182,6 +193,7 @@ public static class Builder {
182193

183194
private int maxItemCount;
184195
private TagKey<Item> validItems;
196+
private boolean disableAutoPickup;
185197
private ItemContainerSettings settings;
186198
private final List<ItemStack> stacks;
187199
private Fraction weight;
@@ -289,13 +301,24 @@ public Fraction getWeight() {
289301
return weight;
290302
}
291303

304+
public boolean toggleAutoPickup() {
305+
disableAutoPickup = !disableAutoPickup;
306+
return disableAutoPickup;
307+
}
308+
309+
public boolean isAutoPickupDisabled() {
310+
return disableAutoPickup;
311+
}
312+
292313
public ItemContainerComponent build() {
293-
return new ItemContainerComponent(this.maxItemCount, this.validItems, this.settings,
294-
List.copyOf(this.stacks), this.weight, this.selectedStackIndex);
314+
return new ItemContainerComponent(this.maxItemCount, this.validItems,
315+
this.disableAutoPickup, this.settings, List.copyOf(this.stacks), this.weight,
316+
this.selectedStackIndex);
295317
}
296318
}
297319

298320
public record ItemContainerComponentData(int maxItemCount, TagKey<Item> validItems,
321+
boolean disableAutoPickup,
299322
ItemContainerSettings settings,
300323
List<ItemStack> stacks) {
301324

@@ -305,6 +328,8 @@ public record ItemContainerComponentData(int maxItemCount, TagKey<Item> validIte
305328
TagKey.codec(RegistryKeys.ITEM)
306329
.fieldOf("valid_items")
307330
.forGetter(ItemContainerComponentData::validItems),
331+
Codec.BOOL.optionalFieldOf("disable_auto_pickup", false)
332+
.forGetter(ItemContainerComponentData::disableAutoPickup),
308333
ItemContainerSettings.CODEC.fieldOf("settings")
309334
.forGetter(ItemContainerComponentData::settings),
310335
ItemStack.CODEC.listOf()
@@ -314,6 +339,7 @@ public record ItemContainerComponentData(int maxItemCount, TagKey<Item> validIte
314339
public static final PacketCodec<RegistryByteBuf, ItemContainerComponentData> PACKET_CODEC = PacketCodec.tuple(
315340
PacketCodecs.VAR_INT, ItemContainerComponentData::maxItemCount,
316341
TagKey.packetCodec(RegistryKeys.ITEM), ItemContainerComponentData::validItems,
342+
PacketCodecs.BOOLEAN, ItemContainerComponentData::disableAutoPickup,
317343
ItemContainerSettings.PACKET_CODEC, ItemContainerComponentData::settings,
318344
ItemStack.PACKET_CODEC.collect(PacketCodecs.toList()),
319345
ItemContainerComponentData::stacks, ItemContainerComponentData::new);

src/main/java/io/github/drakonkinst/worldsinger/item/itemcontainer/ItemContainerInteractions.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,22 @@ public static float getAmountFilled(ItemStack stack) {
2929
return 0.0f;
3030
}
3131

32+
public static int getSelectedStackIndex(ItemStack stack) {
33+
ItemContainerComponent component = stack.get(ModDataComponentTypes.ITEM_CONTAINER);
34+
if (component != null) {
35+
return component.getSelectedStackIndex();
36+
}
37+
return -1;
38+
}
39+
40+
public static int getNumberOfStacksShown(ItemStack stack) {
41+
ItemContainerComponent component = stack.get(ModDataComponentTypes.ITEM_CONTAINER);
42+
if (component != null) {
43+
return component.getNumberOfStacksShown();
44+
}
45+
return 0;
46+
}
47+
3248
public static boolean onItemContainerStackClicked(ItemStack stack, Slot slot,
3349
ClickType clickType, PlayerEntity player) {
3450
ItemContainerComponent component = stack.get(ModDataComponentTypes.ITEM_CONTAINER);
@@ -104,7 +120,7 @@ public static boolean onItemContainerClicked(ItemStack stack, ItemStack otherSta
104120
}
105121
}
106122

107-
private static void setSelectedStackIndex(ItemStack stack, int selectedStackIndex) {
123+
public static void setSelectedStackIndex(ItemStack stack, int selectedStackIndex) {
108124
ItemContainerComponent component = stack.get(ModDataComponentTypes.ITEM_CONTAINER);
109125
if (component != null) {
110126
ItemContainerComponent.Builder builder = new Builder(component);

src/main/java/io/github/drakonkinst/worldsinger/item/itemcontainer/ItemContainerSettings.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,19 +31,19 @@ public enum ItemContainerSettings implements StringIdentifiable {
3131
// TODO: Add more settings, like canBeNested, singleStackSlots, showAllEmptySlots
3232
private final int id;
3333
private final String name;
34-
private final boolean autoSort;
34+
private final boolean autoPickup;
3535
private final Text emptyDescription;
3636
private final Supplier<SoundEvent> insertSound;
3737
private final Supplier<SoundEvent> insertFailSound;
3838
private final Supplier<SoundEvent> removeOneSound;
3939
private final Supplier<SoundEvent> dropContentsSound;
4040

41-
ItemContainerSettings(int id, String name, boolean autoSort, Text emptyDescription,
41+
ItemContainerSettings(int id, String name, boolean autoPickup, Text emptyDescription,
4242
Supplier<SoundEvent> insertSound, Supplier<SoundEvent> insertFailSound,
4343
Supplier<SoundEvent> removeOneSound, Supplier<SoundEvent> dropAllSound) {
4444
this.id = id;
4545
this.name = name;
46-
this.autoSort = autoSort;
46+
this.autoPickup = autoPickup;
4747
this.emptyDescription = emptyDescription;
4848
this.insertSound = insertSound;
4949
this.insertFailSound = insertFailSound;
@@ -55,8 +55,8 @@ public int getId() {
5555
return id;
5656
}
5757

58-
public boolean shouldAutoSort() {
59-
return autoSort;
58+
public boolean shouldAutoPickup() {
59+
return autoPickup;
6060
}
6161

6262
public Text getEmptyDescription() {

src/main/java/io/github/drakonkinst/worldsinger/mixin/entity/player/ItemEntityPickupMixin.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,22 @@
44
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
55
import io.github.drakonkinst.worldsinger.item.component.ItemContainerComponent;
66
import io.github.drakonkinst.worldsinger.registry.ModDataComponentTypes;
7+
import net.minecraft.entity.Entity;
8+
import net.minecraft.entity.EntityType;
79
import net.minecraft.entity.ItemEntity;
10+
import net.minecraft.entity.Ownable;
811
import net.minecraft.entity.player.PlayerInventory;
912
import net.minecraft.item.ItemStack;
13+
import net.minecraft.world.World;
1014
import org.spongepowered.asm.mixin.Mixin;
1115
import org.spongepowered.asm.mixin.injection.At;
1216

1317
@Mixin(ItemEntity.class)
14-
public abstract class ItemEntityPickupMixin {
18+
public abstract class ItemEntityPickupMixin extends Entity implements Ownable {
19+
20+
public ItemEntityPickupMixin(EntityType<?> type, World world) {
21+
super(type, world);
22+
}
1523

1624
@WrapOperation(method = "onPlayerCollision", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/PlayerInventory;insertStack(Lnet/minecraft/item/ItemStack;)Z"))
1725
private boolean autoSortIntoItemContainers(PlayerInventory instance, ItemStack stackToInsert,
@@ -23,7 +31,7 @@ private boolean autoSortIntoItemContainers(PlayerInventory instance, ItemStack s
2331
boolean anyAccepted = false;
2432
for (ItemStack stack : instance) {
2533
ItemContainerComponent component = stack.get(ModDataComponentTypes.ITEM_CONTAINER);
26-
if (component == null || !component.shouldAutoSort() || !component.canBeStored(
34+
if (component == null || !component.shouldAutoPickup() || !component.canBeStored(
2735
stackToInsert)) {
2836
continue;
2937
}

0 commit comments

Comments
 (0)