Reimplement stack distribution and new WIP placement order preview
Some checks failed
build / build (push) Has been cancelled

This commit is contained in:
Dakedres 2025-04-18 23:35:48 -06:00
parent 662aa41c63
commit f588ac5de8
6 changed files with 255 additions and 38 deletions

View File

@ -153,15 +153,19 @@ public abstract class OrderedContainer extends BlockEntity implements Inventory
markDirty(); markDirty();
} }
public void pushStack(ItemStack stack) { public ItemStack pushStack(ItemStack stack) {
if(stack.isEmpty()) { if(stack.isEmpty()) {
return; return stack;
} }
if(hasRoom()) { if(hasRoom()) {
inventory.add(stack); inventory.add(stack);
distributeStack(stack);
markDirty(); markDirty();
return ItemStack.EMPTY;
} }
return stack;
} }
public ItemStack pullStack() { public ItemStack pullStack() {
@ -212,4 +216,73 @@ public abstract class OrderedContainer extends BlockEntity implements Inventory
public ItemStack topStack() { public ItemStack topStack() {
return this.inventory.getLast(); return this.inventory.getLast();
} }
// public ItemStack topStackAlike(ItemStack comparisonStack) {
// for(ItemStack stack : inventory) {
// if(ItemStack.areItemsAndComponentsEqual(stack, comparisonStack)) {
// return stack;
// }
// }
//
// return ItemStack.EMPTY;
// }
public List<ItemStack> similarStacks(ItemStack comparisonStack) {
var out = new ArrayList<ItemStack>();
for(int i = 0; i < filledSlots(); i++) {
ItemStack stack = getStack(i);
if(ItemStack.areItemsAndComponentsEqual(stack, comparisonStack)) {
out.add(stack);
}
}
return out;
}
public void distributeStack(ItemStack target) {
distributeStack(target, 0);
}
public void distributeStack(ItemStack target, int amount) {
List<ItemStack> candidates = similarStacks(target);
if(candidates.isEmpty()) {
return;
}
int total = amount;
int maxTotal = 0;
for(ItemStack candidate : candidates) {
total += candidate.getCount();
maxTotal += candidate.getMaxCount();
candidate.setCount(0);
}
int remainder = 0;
if(total > maxTotal) {
remainder = total - maxTotal;
total = maxTotal;
}
int share = total / candidates.size();
for(ItemStack candidate : candidates) {
candidate.setCount(share);
total -= share;
}
int i = 0;
while(total > 0) {
candidates.get(i % candidates.size()).increment(1);
i++;
total--;
}
if(amount != 0) {
target.decrement(amount);
}
}
} }

View File

@ -9,7 +9,6 @@ import net.minecraft.block.BlockState;
import net.minecraft.block.CrafterBlock; import net.minecraft.block.CrafterBlock;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.inventory.CraftingResultInventory; import net.minecraft.inventory.CraftingResultInventory;
import net.minecraft.inventory.Inventory; import net.minecraft.inventory.Inventory;
import net.minecraft.inventory.RecipeInputInventory; import net.minecraft.inventory.RecipeInputInventory;
@ -18,7 +17,6 @@ import net.minecraft.nbt.NbtCompound;
import net.minecraft.recipe.RecipeMatcher; import net.minecraft.recipe.RecipeMatcher;
import net.minecraft.recipe.input.CraftingRecipeInput; import net.minecraft.recipe.input.CraftingRecipeInput;
import net.minecraft.registry.RegistryWrapper; import net.minecraft.registry.RegistryWrapper;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.screen.ScreenHandlerType; import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.screen.slot.CraftingResultSlot; import net.minecraft.screen.slot.CraftingResultSlot;
import net.minecraft.screen.slot.Slot; import net.minecraft.screen.slot.Slot;
@ -247,15 +245,23 @@ public class WorkbenchBlockEntity extends OrderedContainer {
} }
public class Gui extends SimpleGui { public class Gui extends SimpleGui {
private static final Identifier GUI_FONT_ID = Identifier.of(Grafting.MOD_ID, "workbench_gui");
private static final Identifier SLOT_ORDER_OVERLAY_FONT_ID = Identifier.of(Grafting.MOD_ID, "slot_order_overlay");
private static final char[][] SLOT_ORDER_OVERLAY_FONT_MAP = {
{ '0', '1', '2', '3', '4', '5', '6', '7' },
{ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' },
{ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n' }
};
private static final int BUTTON_COLUMN = 1; private static final int BUTTON_COLUMN = 1;
private static final int GRID_START_COLUMN = BUTTON_COLUMN + 1; private static final int GRID_START_COLUMN = BUTTON_COLUMN + 1;
private static final int RESULT_COLUMN = GRID_START_COLUMN + GRID_SIZE + 2; private static final int RESULT_COLUMN = GRID_START_COLUMN + GRID_SIZE + 2;
private static final int BASE_HEIGHT = 3; private static final int BASE_HEIGHT = 3;
private static final Identifier GUI_FONT_ID = Identifier.of(Grafting.MOD_ID, "workbench_gui");
private final CraftingResultInventory resultInventory = new CraftingResultInventory(); private final CraftingResultInventory resultInventory = new CraftingResultInventory();
private boolean attachedInventory; private AttachedInventory attachedInventory;
private ItemStack knownCursorStack = ItemStack.EMPTY;
public Gui(ServerPlayerEntity player) { public Gui(ServerPlayerEntity player) {
this(player, Optional.empty()); this(player, Optional.empty());
} }
@ -268,13 +274,10 @@ public class WorkbenchBlockEntity extends OrderedContainer {
player, player,
false false
); );
attachedInventory.ifPresent(inventory -> this.attachedInventory = inventory);
guis.add(this); guis.add(this);
setTitle( updateTitle();
Text.empty()
.append(Text.literal(attachedInventory.isEmpty() ? "-0." : "-1.").setStyle(Style.EMPTY.withFont(GUI_FONT_ID).withColor(Formatting.WHITE)))
.append(Text.translatable("container.crafting"))
);
setSlot(BUTTON_COLUMN, new RotateGridButton(Icons.ROTATE_CLOCKWISE,1)); setSlot(BUTTON_COLUMN, new RotateGridButton(Icons.ROTATE_CLOCKWISE,1));
setSlot(BUTTON_COLUMN + 9, new RotateGridButton(Icons.ROTATE_COUNTER_CLOCKWISE, -1)); setSlot(BUTTON_COLUMN + 9, new RotateGridButton(Icons.ROTATE_COUNTER_CLOCKWISE, -1));
@ -297,24 +300,109 @@ public class WorkbenchBlockEntity extends OrderedContainer {
updateResult(); updateResult();
} }
@Override public int getUnextendedSize() {
public void close() { return BASE_HEIGHT * 9;
guis.remove(this);
super.close();
} }
protected void updateResult() { protected void updateResult() {
resultInventory.setStack(0, getResult(player)); resultInventory.setStack(0, getResult(player));
} }
@Override
public void close() {
guis.remove(this);
super.close();
}
@Override
public boolean onAnyClick(int index, ClickType type, SlotActionType action) {
ItemStack cursorStack = screenHandler.getCursorStack();
Slot slot = getSlotRedirectOrPlayer(index);
if(type.equals(ClickType.MOUSE_LEFT) && slot != null) {
cursorStack = slot.getStack();
}
if(cursorStack != knownCursorStack) {
knownCursorStack = cursorStack;
updateTitle();
}
return super.onAnyClick(index, type, action);
}
private void updateTitle() {
var title = Text.empty();
title.append(
Text.literal(attachedInventory != null ? "-1." : "-0.")
.setStyle(Style.EMPTY.withFont(GUI_FONT_ID).withColor(Formatting.WHITE))
);
title.append(
Text.literal(getSlotOrderLiteral())
.setStyle(Style.EMPTY.withFont(SLOT_ORDER_OVERLAY_FONT_ID).withColor(Formatting.WHITE))
);
title.append(Text.translatable("container.crafting"));
setTitle(title);
}
private String getSlotOrderLiteral() {
StringBuilder out = new StringBuilder();
out.append(" ".repeat(GRID_START_COLUMN));
if(knownCursorStack.isEmpty()) {
return out.toString();
}
var placements = PlacementUtils.rotate(
PlacementUtils.getPlacements(knownCursorStack, (ServerWorld) world),
craftingOrientation
);
for(int i = 0; i < 3; i++) {
boolean newCol = true;
for(int j = 0; j < 3; j++) {
int order = placements[j * 3 + i] - filledSlots() - 1;
if(order < 0) {
continue;
}
if(newCol) {
newCol = false;
out.append('.');
} else {
out.append('-');
}
out.append(SLOT_ORDER_OVERLAY_FONT_MAP[j][order]);
}
if(newCol) {
out.append(' ');
}
}
out.append("___");
Grafting.LOGGER.info("Test: {}", out.toString());
return out.toString();
}
/*
* See: CraftingScreenHandler#quickMove
*/
@Override @Override
public ItemStack quickMove(int index) { public ItemStack quickMove(int index) {
ItemStack returnStack = ItemStack.EMPTY; ItemStack returnStack = ItemStack.EMPTY;
Slot slot = this.getSlotRedirectOrPlayer(index); Slot slot = this.getSlotRedirectOrPlayer(index);
boolean isResultSlot = slot instanceof CraftingResultSlot;
if(slot != null && slot.hasStack() && !(slot instanceof VirtualSlot)) { if(slot != null && slot.hasStack() && !(slot instanceof VirtualSlot)) {
ItemStack operatingStack; ItemStack operatingStack;
if(slot instanceof GridSlot) { if(slot instanceof GridSlot) {
operatingStack = topStack(); operatingStack = topStack();
} else { } else {
@ -322,12 +410,24 @@ public class WorkbenchBlockEntity extends OrderedContainer {
} }
returnStack = operatingStack.copy(); returnStack = operatingStack.copy();
if(index < this.getVirtualSize()) { if(isResultSlot) {
operatingStack.getItem().onCraftByPlayer(operatingStack, world, player);
if(!this.insertItem(operatingStack, this.getVirtualSize(), this.getVirtualSize() + 36, true)) { if(!this.insertItem(operatingStack, this.getVirtualSize(), this.getVirtualSize() + 36, true)) {
return ItemStack.EMPTY; return ItemStack.EMPTY;
} }
} else if (!this.insertItem(operatingStack, 0, this.getVirtualSize(), false)) {
return ItemStack.EMPTY; slot.onQuickTransfer(operatingStack, returnStack);
} else if(index < getUnextendedSize()) {
if(!this.insertItem(operatingStack, this.getUnextendedSize(), this.getVirtualSize() + 36, false)) {
return ItemStack.EMPTY;
}
} else {
if(similarStacks(operatingStack).isEmpty() && hasRoom()) {
pushStack(operatingStack.copyAndEmpty());
} else {
distributeStack(operatingStack, operatingStack.getCount());
}
} }
if (operatingStack.isEmpty()) { if (operatingStack.isEmpty()) {
@ -335,6 +435,15 @@ public class WorkbenchBlockEntity extends OrderedContainer {
} else { } else {
slot.markDirty(); slot.markDirty();
} }
if(returnStack.getCount() == operatingStack.getCount()) {
return ItemStack.EMPTY;
}
slot.onTakeItem(player, returnStack);
if(isResultSlot) {
player.dropItem(returnStack, false);
}
} else if (slot instanceof VirtualSlot) { } else if (slot instanceof VirtualSlot) {
return slot.getStack(); return slot.getStack();
} }
@ -349,23 +458,36 @@ public class WorkbenchBlockEntity extends OrderedContainer {
super(WorkbenchBlockEntity.this.recipeInventory, index, index, 0); super(WorkbenchBlockEntity.this.recipeInventory, index, index, 0);
} }
// @Override @Override
// public ItemStack insertStack(ItemStack stack, int count) { public ItemStack insertStack(ItemStack stack, int count) {
// if(stack.isEmpty()) {
// if(stack.isEmpty()) { return stack;
// return stack; }
// }
// int allowedCount = Math.min(count, stack.getCount());
// int allowedCount = Math.min(count, stack.getCount()); if(
// if( (getStack().isEmpty() || similarStacks(stack).isEmpty()) &&
// (!hasItemsLike(stack) || count == 1) && hasRoom()
// WorkbenchBlockEntity.this.hasRoom() ) { // Right click
// ) { pushStack(stack.split(allowedCount));
// pushStack(stack.split(allowedCount)); } else {
// } distributeStack(stack, allowedCount);
// if(stack.isEmpty()) {
// return stack; return ItemStack.EMPTY;
// } }
}
return stack;
}
@Override
public ItemStack takeStack(int amount) {
var o = super.takeStack(amount);
if(!getStack().isEmpty()) {
distributeStack(getStack());
}
return o;
}
} }
public class CraftingResultGridSlot extends CraftingResultSlot { public class CraftingResultGridSlot extends CraftingResultSlot {

View File

@ -0,0 +1,22 @@
{
"providers": [
{
"type": "space",
"advances": {
".": -1,
"-": -19,
"_": -18,
" ": 18
}
},
{
"type": "bitmap",
"file": "grafting:gui/slots.png",
"ascent": -4,
"height": 64,
"chars": [
"0123456789abcdefghijklmn"
]
}
]
}

View File

@ -3,7 +3,7 @@
{ {
"type": "space", "type": "space",
"advances": { "advances": {
".": -134, ".": -169,
"-": -8 "-": -8
} }
}, },

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB