diff --git a/src/Inventory.zig b/src/Inventory.zig index c34c0ea44..94e3a57eb 100644 --- a/src/Inventory.zig +++ b/src/Inventory.zig @@ -13,6 +13,7 @@ const Vec3d = vec.Vec3d; const Vec3f = vec.Vec3f; const Vec3i = vec.Vec3i; const ZonElement = main.ZonElement; +const Neighbor = main.chunk.Neighbor; const Gamemode = main.game.Gamemode; @@ -1555,9 +1556,80 @@ pub const Command = struct { // MARK: Command const UpdateBlock = struct { // MARK: UpdateBlock source: InventoryAndSlot, pos: Vec3i, + dropLocation: BlockDropLocation, oldBlock: Block, newBlock: Block, + const half = @as(Vec3f, @splat(0.5)); + const itemHitBoxMargin: f32 = @floatCast(main.itemdrop.ItemDropManager.radius); + const itemHitBoxMarginVec: Vec3f = @splat(itemHitBoxMargin); + + const BlockDropLocation = struct { + dir: Neighbor, + min: Vec3f, + max: Vec3f, + + pub fn drop(self: BlockDropLocation, pos: Vec3i, newBlock: Block, _drop: main.blocks.BlockDrop) void { + if(newBlock.collide()) { + self.dropOutside(pos, _drop); + } else { + self.dropInside(pos, _drop); + } + } + fn dropInside(self: BlockDropLocation, pos: Vec3i, _drop: main.blocks.BlockDrop) void { + for(_drop.items) |itemStack| { + main.server.world.?.drop(itemStack.clone(), self.insidePos(pos), self.dropDir(), self.dropVelocity()); + } + } + fn insidePos(self: BlockDropLocation, _pos: Vec3i) Vec3d { + const pos: Vec3d = @floatFromInt(_pos); + return pos + self.randomOffset(); + } + fn randomOffset(self: BlockDropLocation) Vec3f { + const max = @min(@as(Vec3f, @splat(1.0)) - itemHitBoxMarginVec, @max(itemHitBoxMarginVec, self.max - itemHitBoxMarginVec)); + const min = @min(max, @max(itemHitBoxMarginVec, self.min + itemHitBoxMarginVec)); + const center = (max + min)*half; + const width = (max - min)*half; + return center + width*main.random.nextFloatVectorSigned(3, &main.seed)*half; + } + fn dropOutside(self: BlockDropLocation, pos: Vec3i, _drop: main.blocks.BlockDrop) void { + for(_drop.items) |itemStack| { + main.server.world.?.drop(itemStack.clone(), self.outsidePos(pos), self.dropDir(), self.dropVelocity()); + } + } + fn outsidePos(self: BlockDropLocation, _pos: Vec3i) Vec3d { + const pos: Vec3d = @floatFromInt(_pos); + return pos + self.randomOffset()*self.minor() + self.directionOffset()*self.major() + self.direction()*itemHitBoxMarginVec; + } + fn directionOffset(self: BlockDropLocation) Vec3d { + return half + self.direction()*half; + } + inline fn direction(self: BlockDropLocation) Vec3d { + return @floatFromInt(self.dir.relPos()); + } + inline fn major(self: BlockDropLocation) Vec3d { + return @floatFromInt(@abs(self.dir.relPos())); + } + inline fn minor(self: BlockDropLocation) Vec3d { + return @floatFromInt(self.dir.orthogonalComponents()); + } + fn dropDir(self: BlockDropLocation) Vec3f { + const randomnessVec: Vec3f = main.random.nextFloatVectorSigned(3, &main.seed)*@as(Vec3f, @splat(0.25)); + const directionVec: Vec3f = @as(Vec3f, @floatCast(self.direction())) + randomnessVec; + const z: f32 = directionVec[2]; + return vec.normalize(Vec3f{ + directionVec[0], + directionVec[1], + if(z < -0.5) 0 else if(z < 0.0) (z + 0.5)*4.0 else z + 2.0, + }); + } + fn dropVelocity(self: BlockDropLocation) f32 { + const velocity = 3.5 + main.random.nextFloatSigned(&main.seed)*0.5; + if(self.direction()[2] < -0.5) return velocity*0.333; + return velocity; + } + }; + fn run(self: UpdateBlock, allocator: NeverFailingAllocator, cmd: *Command, side: Side, user: ?*main.server.User, gamemode: Gamemode) error{serverFailure}!void { if(self.source.inv.type != .normal) return; @@ -1610,7 +1682,7 @@ pub const Command = struct { // MARK: Command for(0..amount) |_| { for(self.newBlock.blockDrops()) |drop| { if(drop.chance == 1 or main.random.nextFloat(&main.seed) < drop.chance) { - blockDrop(self.pos, drop); + self.dropLocation.drop(self.pos, self.newBlock, drop); } } } @@ -1621,25 +1693,18 @@ pub const Command = struct { // MARK: Command if(side == .server and gamemode != .creative and self.oldBlock.typ != self.newBlock.typ and shouldDropSourceBlockOnSuccess) { for(self.oldBlock.blockDrops()) |drop| { if(drop.chance == 1 or main.random.nextFloat(&main.seed) < drop.chance) { - blockDrop(self.pos, drop); + self.dropLocation.drop(self.pos, self.newBlock, drop); } } } } - fn blockDrop(pos: Vec3i, drop: main.blocks.BlockDrop) void { - for(drop.items) |itemStack| { - const dropPos = @as(Vec3d, @floatFromInt(pos)) + @as(Vec3d, @splat(0.5)) + main.random.nextDoubleVectorSigned(3, &main.seed)*@as(Vec3d, @splat(0.5 - main.itemdrop.ItemDropManager.radius)); - const dir = vec.normalize(main.random.nextFloatVectorSigned(3, &main.seed)); - main.server.world.?.drop(itemStack.clone(), dropPos, dir, main.random.nextFloat(&main.seed)*1.5); - } - } - fn serialize(self: UpdateBlock, writer: *utils.BinaryWriter) void { self.source.write(writer); - writer.writeInt(i32, self.pos[0]); - writer.writeInt(i32, self.pos[1]); - writer.writeInt(i32, self.pos[2]); + writer.writeVec(Vec3i, self.pos); + writer.writeEnum(Neighbor, self.dropLocation.dir); + writer.writeVec(Vec3f, self.dropLocation.min); + writer.writeVec(Vec3f, self.dropLocation.max); writer.writeInt(u32, @as(u32, @bitCast(self.oldBlock))); writer.writeInt(u32, @as(u32, @bitCast(self.newBlock))); } @@ -1647,10 +1712,11 @@ pub const Command = struct { // MARK: Command fn deserialize(reader: *utils.BinaryReader, side: Side, user: ?*main.server.User) !UpdateBlock { return .{ .source = try InventoryAndSlot.read(reader, side, user), - .pos = .{ - try reader.readInt(i32), - try reader.readInt(i32), - try reader.readInt(i32), + .pos = try reader.readVec(Vec3i), + .dropLocation = .{ + .dir = try reader.readEnum(Neighbor), + .min = try reader.readVec(Vec3f), + .max = try reader.readVec(Vec3f), }, .oldBlock = @bitCast(try reader.readInt(u32)), .newBlock = @bitCast(try reader.readInt(u32)), diff --git a/src/renderer.zig b/src/renderer.zig index 490173a18..71d4e40cb 100644 --- a/src/renderer.zig +++ b/src/renderer.zig @@ -716,6 +716,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection var currentSwingTime: f32 = 0; var selectionMin: Vec3f = undefined; var selectionMax: Vec3f = undefined; + var selectionFace: chunk.Neighbor = undefined; var lastPos: Vec3d = undefined; var lastDir: Vec3f = undefined; pub fn select(pos: Vec3d, _dir: Vec3f, item: ?main.items.Item) void { @@ -750,6 +751,7 @@ pub const MeshSelection = struct { // MARK: MeshSelection selectedBlockPos = voxelPos; selectionMin = intersection.min; selectionMax = intersection.max; + selectionFace = intersection.face; break; } } @@ -927,7 +929,19 @@ pub const MeshSelection = struct { // MARK: MeshSelection } fn updateBlockAndSendUpdate(source: main.items.Inventory, slot: u32, x: i32, y: i32, z: i32, oldBlock: blocks.Block, newBlock: blocks.Block) void { - main.items.Inventory.Sync.ClientSide.executeCommand(.{.updateBlock = .{.source = .{.inv = source, .slot = slot}, .pos = .{x, y, z}, .oldBlock = oldBlock, .newBlock = newBlock}}); + main.items.Inventory.Sync.ClientSide.executeCommand(.{ + .updateBlock = .{ + .source = .{.inv = source, .slot = slot}, + .pos = .{x, y, z}, + .dropLocation = .{ + .dir = selectionFace, + .min = selectionMin, + .max = selectionMax, + }, + .oldBlock = oldBlock, + .newBlock = newBlock, + }, + }); mesh_storage.updateBlock(x, y, z, newBlock); } diff --git a/src/rotation.zig b/src/rotation.zig index 21a7d6099..5bdfa8e04 100644 --- a/src/rotation.zig +++ b/src/rotation.zig @@ -18,6 +18,7 @@ pub const RayIntersectionResult = struct { distance: f64, min: Vec3f, max: Vec3f, + face: Neighbor, }; pub const Degrees = enum(u2) { @@ -65,10 +66,27 @@ pub const RotationMode = struct { // MARK: RotationMode const boxTMin = @reduce(.Max, @min(t1, t2)); const boxTMax = @reduce(.Min, @max(t1, t2)); if(boxTMin <= boxTMax and boxTMax > 0) { + var face: Neighbor = undefined; + if(boxTMin == t1[0]) { + face = Neighbor.dirNegX; + } else if(boxTMin == t1[1]) { + face = Neighbor.dirNegY; + } else if(boxTMin == t1[2]) { + face = Neighbor.dirDown; + } else if(boxTMin == t2[0]) { + face = Neighbor.dirPosX; + } else if(boxTMin == t2[1]) { + face = Neighbor.dirPosY; + } else if(boxTMin == t2[2]) { + face = Neighbor.dirUp; + } else { + unreachable; + } return .{ .distance = boxTMin, .min = min, .max = max, + .face = face, }; } return null; diff --git a/src/rotation/stairs.zig b/src/rotation/stairs.zig index e8841c889..5799cb45e 100644 --- a/src/rotation/stairs.zig +++ b/src/rotation/stairs.zig @@ -296,15 +296,12 @@ pub fn rayIntersection(block: Block, item: ?main.items.Item, relativePlayerPos: if(item) |_item| { switch(_item) { .baseItem => |baseItem| { - if(std.mem.eql(u8, baseItem.id, "cubyz:chisel")) { // Select only one eigth of a block + if(std.mem.eql(u8, baseItem.id, "cubyz:chisel")) { // Select only one eighth of a block if(intersectionPos(block, relativePlayerPos, playerDir)) |intersection| { const offset: Vec3f = @floatFromInt(intersection.minPos); const half: Vec3f = @splat(0.5); - return .{ - .distance = intersection.minT, - .min = half*offset, - .max = half + half*offset, - }; + const fullIntersection = RotationMode.DefaultFunctions.rayIntersection(block, item, relativePlayerPos, playerDir) orelse unreachable; + return .{.distance = intersection.minT, .min = half*offset, .max = half + half*offset, .face = fullIntersection.face}; } return null; }