Skip to content

Commit addd09b

Browse files
shift clicking
fixes #1799
1 parent e9e22de commit addd09b

File tree

5 files changed

+134
-23
lines changed

5 files changed

+134
-23
lines changed

src/Inventory.zig

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,7 @@ pub const Command = struct { // MARK: Command
533533
drop = 5,
534534
fillFromCreative = 6,
535535
depositOrDrop = 7,
536+
depositToAny = 11,
536537
clear = 8,
537538
updateBlock = 9,
538539
addHealth = 10,
@@ -546,6 +547,7 @@ pub const Command = struct { // MARK: Command
546547
drop: Drop,
547548
fillFromCreative: FillFromCreative,
548549
depositOrDrop: DepositOrDrop,
550+
depositToAny: DepositToAny,
549551
clear: Clear,
550552
updateBlock: UpdateBlock,
551553
addHealth: AddHealth,
@@ -1082,13 +1084,12 @@ pub const Command = struct { // MARK: Command
10821084
return true;
10831085
}
10841086

1085-
fn tryCraftingTo(self: *Command, allocator: NeverFailingAllocator, dest: InventoryAndSlot, source: InventoryAndSlot, side: Side, user: ?*main.server.User) void { // MARK: tryCraftingTo()
1087+
fn tryCraftingTo(self: *Command, allocator: NeverFailingAllocator, dest: Inventory, source: InventoryAndSlot, side: Side, user: ?*main.server.User) void { // MARK: tryCraftingTo()
10861088
std.debug.assert(source.inv.type == .crafting);
1087-
std.debug.assert(dest.inv.type == .normal);
1089+
std.debug.assert(dest.type == .normal);
10881090
if(source.slot != source.inv._items.len - 1) return;
1089-
if(dest.ref().item != null and !std.meta.eql(dest.ref().item, source.ref().item)) return;
1091+
if(!dest.canHold(source.ref().*)) return;
10901092
if(source.ref().item == null) return; // Can happen if the we didn't receive the inventory information from the server yet.
1091-
if(dest.ref().amount + source.ref().amount > source.ref().item.?.stackSize()) return;
10921093

10931094
const playerInventory: Inventory = switch(side) {
10941095
.client => main.game.Player.inventory,
@@ -1138,11 +1139,21 @@ pub const Command = struct { // MARK: Command
11381139
}
11391140
std.debug.assert(remainingAmount == 0);
11401141
}
1141-
self.executeBaseOperation(allocator, .{.create = .{
1142-
.dest = dest,
1143-
.amount = source.ref().amount,
1144-
.item = source.ref().item,
1145-
}}, side);
1142+
1143+
var remainingAmount: u16 = source.ref().amount;
1144+
for(dest._items, 0..) |*destStack, destSlot| {
1145+
if(std.meta.eql(destStack.item, source.ref().item) or destStack.item == null) {
1146+
const amount = @min(source.ref().item.?.stackSize() - destStack.amount, remainingAmount);
1147+
self.executeBaseOperation(allocator, .{.create = .{
1148+
.dest = .{.inv = dest, .slot = @intCast(destSlot)},
1149+
.amount = amount,
1150+
.item = source.ref().item,
1151+
}}, side);
1152+
remainingAmount -= amount;
1153+
if(remainingAmount == 0) break;
1154+
}
1155+
}
1156+
std.debug.assert(remainingAmount == 0);
11461157
}
11471158

11481159
const Open = struct { // MARK: Open
@@ -1281,7 +1292,7 @@ pub const Command = struct { // MARK: Command
12811292
return;
12821293
}
12831294
if(self.dest.inv.type == .crafting) {
1284-
cmd.tryCraftingTo(allocator, self.source, self.dest, side, user);
1295+
cmd.tryCraftingTo(allocator, self.source.inv, self.dest, side, user);
12851296
return;
12861297
}
12871298
if(self.dest.inv.type == .workbench and self.dest.slot != 25 and self.dest.inv.type.workbench.slotInfos()[self.dest.slot].disabled) return;
@@ -1392,7 +1403,7 @@ pub const Command = struct { // MARK: Command
13921403
return;
13931404
}
13941405
if(self.source.inv.type == .crafting) {
1395-
cmd.tryCraftingTo(allocator, self.dest, self.source, side, user);
1406+
cmd.tryCraftingTo(allocator, self.dest.inv, self.source, side, user);
13961407
return;
13971408
}
13981409
if(self.source.inv.type == .workbench and self.source.slot != 25 and self.source.inv.type.workbench.slotInfos()[self.source.slot].disabled) return;
@@ -1458,7 +1469,7 @@ pub const Command = struct { // MARK: Command
14581469
.source = undefined,
14591470
.callbacks = .{},
14601471
};
1461-
cmd.tryCraftingTo(allocator, .{.inv = temp, .slot = 0}, self.source, side, user);
1472+
cmd.tryCraftingTo(allocator, temp, self.source, side, user);
14621473
std.debug.assert(cmd.baseOperations.pop().create.dest.inv._items.ptr == temp._items.ptr); // Remove the extra step from undo list (we cannot undo dropped items)
14631474
if(_items[0].item != null) {
14641475
if(side == .server) {
@@ -1613,6 +1624,55 @@ pub const Command = struct { // MARK: Command
16131624
}
16141625
};
16151626

1627+
const DepositToAny = struct { // MARK: DepositToAny
1628+
dest: Inventory,
1629+
source: InventoryAndSlot,
1630+
amount: u16,
1631+
1632+
fn run(self: DepositToAny, allocator: NeverFailingAllocator, cmd: *Command, side: Side, user: ?*main.server.User, _: Gamemode) error{serverFailure}!void {
1633+
if(self.dest.type == .creative) return;
1634+
if(self.dest.type == .crafting) return;
1635+
if(self.dest.type == .workbench) return;
1636+
if(self.source.inv.type == .crafting) {
1637+
cmd.tryCraftingTo(allocator, self.dest, self.source, side, user);
1638+
return;
1639+
}
1640+
const sourceStack = self.source.ref();
1641+
if(sourceStack.item == null) return;
1642+
if(self.amount > sourceStack.amount) return;
1643+
if(!self.dest.canHold(.{.item = sourceStack.item, .amount = self.amount})) return;
1644+
1645+
var remainingAmount = self.amount;
1646+
for(self.dest._items, 0..) |*destStack, destSlot| {
1647+
if(std.meta.eql(destStack.item, sourceStack.item) or destStack.item == null) {
1648+
const amount = @min(sourceStack.item.?.stackSize() - destStack.amount, remainingAmount);
1649+
cmd.executeBaseOperation(allocator, .{.move = .{
1650+
.dest = .{.inv = self.dest, .slot = @intCast(destSlot)},
1651+
.source = self.source,
1652+
.amount = amount,
1653+
}}, side);
1654+
remainingAmount -= amount;
1655+
if(remainingAmount == 0) break;
1656+
}
1657+
}
1658+
}
1659+
1660+
fn serialize(self: DepositToAny, writer: *utils.BinaryWriter) void {
1661+
writer.writeEnum(InventoryId, self.dest.id);
1662+
self.source.write(writer);
1663+
writer.writeInt(u16, self.amount);
1664+
}
1665+
1666+
fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !DepositToAny {
1667+
const destId = try reader.readEnum(InventoryId);
1668+
return .{
1669+
.dest = Sync.getInventory(destId, side, user) orelse return error.InventoryNotFound,
1670+
.source = try InventoryAndSlot.read(reader, side, user),
1671+
.amount = try reader.readInt(u16),
1672+
};
1673+
}
1674+
};
1675+
16161676
const Clear = struct { // MARK: Clear
16171677
inv: Inventory,
16181678

@@ -2014,6 +2074,10 @@ pub fn depositOrDrop(dest: Inventory, source: Inventory) void {
20142074
Sync.ClientSide.executeCommand(.{.depositOrDrop = .{.dest = dest, .source = source}});
20152075
}
20162076

2077+
pub fn depositToAny(source: Inventory, sourceSlot: u32, dest: Inventory, amount: u16) void {
2078+
Sync.ClientSide.executeCommand(.{.depositToAny = .{.dest = dest, .source = .{.inv = source, .slot = sourceSlot}, .amount = amount}});
2079+
}
2080+
20172081
pub fn dropStack(source: Inventory, sourceSlot: u32) void {
20182082
Sync.ClientSide.executeCommand(.{.drop = .{.source = .{.inv = source, .slot = sourceSlot}}});
20192083
}
@@ -2054,6 +2118,20 @@ pub fn getAmount(self: Inventory, slot: usize) u16 {
20542118
return self._items[slot].amount;
20552119
}
20562120

2121+
pub fn canHold(self: Inventory, sourceStack: ItemStack) bool {
2122+
if(sourceStack.amount == 0) return true;
2123+
2124+
var remainingAmount = sourceStack.amount;
2125+
for(self._items) |*destStack| {
2126+
if(std.meta.eql(destStack.item, sourceStack.item) or destStack.item == null) {
2127+
const amount = @min(sourceStack.item.?.stackSize() - destStack.amount, remainingAmount);
2128+
remainingAmount -= amount;
2129+
if(remainingAmount == 0) return true;
2130+
}
2131+
}
2132+
return false;
2133+
}
2134+
20572135
// TODO: Remove after #480
20582136
pub fn loadFromZon(self: Inventory, zon: ZonElement) void {
20592137
for(self._items, 0..) |*stack, i| {

src/graphics/Window.zig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,7 @@ pub const GamepadAxis = struct {
271271
pub const Key = struct { // MARK: Key
272272
name: []const u8,
273273
pressed: bool = false,
274+
modsOnPress: Modifiers = .{},
274275
value: f32 = 0.0,
275276
key: c_int = c.GLFW_KEY_UNKNOWN,
276277
gamepadAxis: ?GamepadAxis = null,
@@ -429,6 +430,7 @@ pub const Key = struct { // MARK: Key
429430
fn setPressed(self: *Key, newPressed: bool, isGrabbed: bool, mods: Modifiers, textKeyPressedInTextField: bool) void {
430431
if(newPressed != self.pressed) {
431432
self.pressed = newPressed;
433+
self.modsOnPress = mods;
432434
self.value = @floatFromInt(@intFromBool(newPressed));
433435
if(newPressed) {
434436
self.action(.press, isGrabbed, mods, textKeyPressedInTextField);
@@ -617,6 +619,7 @@ fn releaseButtonsOnGrabChange(grab: bool) void {
617619
for(&main.KeyBoard.keys) |*key| {
618620
if(key.notifyRequirement == state and key.pressed) {
619621
key.pressed = false;
622+
key.modsOnPress = .{};
620623
if(key.releaseAction) |rel| rel();
621624
}
622625
}

src/gui/GuiWindow.zig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ closeIfMouseIsGrabbed: bool = false,
6262
closeable: bool = true,
6363
isHud: bool = false,
6464

65+
shiftClickableInventory: ?main.items.Inventory = null,
66+
6567
/// Called every frame.
6668
renderFn: *const fn() void = &defaultFunction,
6769
/// Called every frame before rendering.

src/gui/gui.zig

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,7 @@ pub const inventory = struct { // MARK: inventory
608608
var carriedItemSlot: *ItemSlot = undefined;
609609
var leftClickSlots: List(*ItemSlot) = undefined;
610610
var rightClickSlots: List(*ItemSlot) = undefined;
611+
var recipeItem: ?main.items.Item = null;
611612
var initialized: bool = false;
612613
const minCraftingCooldown = 20;
613614
const maxCraftingCooldown = 400;
@@ -658,8 +659,14 @@ pub const inventory = struct { // MARK: inventory
658659
fn update() void {
659660
if(!initialized) return;
660661
if(hoveredItemSlot) |itemSlot| {
662+
const mainGuiButton = main.KeyBoard.key("mainGuiButton");
663+
const secondaryGuiButton = main.KeyBoard.key("secondaryGuiButton");
661664
if(itemSlot.inventory.type == .crafting and itemSlot.mode == .takeOnly) {
662-
if(main.KeyBoard.key("mainGuiButton").pressed) {
665+
if(mainGuiButton.pressed) {
666+
if(recipeItem == null and itemSlot.inventory._items[itemSlot.itemSlot].item != null) {
667+
recipeItem = itemSlot.inventory._items[itemSlot.itemSlot].item.?.clone();
668+
}
669+
if(!std.meta.eql(itemSlot.inventory._items[itemSlot.itemSlot].item, recipeItem)) return;
663670
const time = std.time.milliTimestamp();
664671
if(!startedCrafting) {
665672
startedCrafting = true;
@@ -669,24 +676,42 @@ pub const inventory = struct { // MARK: inventory
669676
while(time -% nextCraftingAction >= 0) {
670677
nextCraftingAction +%= craftingCooldown;
671678
craftingCooldown -= (craftingCooldown - minCraftingCooldown)*craftingCooldown/1000;
672-
itemSlot.inventory.depositOrSwap(itemSlot.itemSlot, carried);
679+
if(mainGuiButton.modsOnPress.shift) {
680+
itemSlot.inventory.depositToAny(itemSlot.itemSlot, main.game.Player.inventory, itemSlot.inventory.getAmount(itemSlot.itemSlot));
681+
} else {
682+
itemSlot.inventory.depositOrSwap(itemSlot.itemSlot, carried);
683+
}
673684
}
674685
return;
675686
}
676687
}
677688
if(itemSlot.mode != .normal) return;
678689

679-
if(carried.getAmount(0) == 0) return;
680-
if(main.KeyBoard.key("mainGuiButton").pressed) {
681-
for(leftClickSlots.items) |deliveredSlot| {
682-
if(itemSlot == deliveredSlot) {
683-
return;
690+
if(mainGuiButton.pressed) {
691+
if(mainGuiButton.modsOnPress.shift) {
692+
if(itemSlot.inventory.id == main.game.Player.inventory.id) {
693+
var iterator = std.mem.reverseIterator(openWindows.items);
694+
while(iterator.next()) |window| {
695+
if(window.shiftClickableInventory) |inv| {
696+
itemSlot.inventory.depositToAny(itemSlot.itemSlot, inv, itemSlot.inventory.getAmount(itemSlot.itemSlot));
697+
break;
698+
}
699+
}
700+
} else {
701+
itemSlot.inventory.depositToAny(itemSlot.itemSlot, main.game.Player.inventory, itemSlot.inventory.getAmount(itemSlot.itemSlot));
702+
}
703+
} else {
704+
if(carried.getAmount(0) == 0) return;
705+
for(leftClickSlots.items) |deliveredSlot| {
706+
if(itemSlot == deliveredSlot) {
707+
return;
708+
}
709+
}
710+
if(itemSlot.inventory.getItem(itemSlot.itemSlot) == null) {
711+
leftClickSlots.append(itemSlot);
684712
}
685713
}
686-
if(itemSlot.inventory.getItem(itemSlot.itemSlot) == null) {
687-
leftClickSlots.append(itemSlot);
688-
}
689-
} else if(main.KeyBoard.key("secondaryGuiButton").pressed) {
714+
} else if(secondaryGuiButton.pressed) {
690715
for(rightClickSlots.items) |deliveredSlot| {
691716
if(itemSlot == deliveredSlot) {
692717
return;
@@ -731,6 +756,8 @@ pub const inventory = struct { // MARK: inventory
731756
carried.dropOne(0);
732757
}
733758
}
759+
if(recipeItem) |item| item.deinit();
760+
recipeItem = null;
734761
}
735762

736763
fn render(mousePos: Vec2f) void {

src/gui/windows/chest.zig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ pub fn onOpen() void {
5757
list.add(row);
5858
}
5959
list.finish(.center);
60+
window.shiftClickableInventory = openInventory;
6061
window.rootComponent = list.toComponent();
6162
window.contentSize = window.rootComponent.?.pos() + window.rootComponent.?.size() + @as(Vec2f, @splat(padding));
6263
gui.updateWindowPositions();

0 commit comments

Comments
 (0)