mirror of
https://github.com/PixelGuys/Cubyz.git
synced 2025-08-03 11:17:05 -04:00
Add /replace
command (#1424)
## Description This pull request adds `/replace` command. The main difference from `/set` with `/mask global` is that mask specified for `/replace` is inlined, local and positive, i.e. only blocks matching the mask specified as part of `/replace` command will be affected. `/replace` ignores global masks. ``` /replace <old> <new> ``` `<old>` - expression following mask syntax (#1284), blocks which match will be affected `<new>` - expression following pattern syntax (#1237) used to fill blocks matched by `<old>` ## Examples `/replace cubyz:air cubyz:void` `/replace $leaf|cubyz:air cubyz:stone,cubyz:grass` ## Links Related to: #1214 Depends on: #1337 Depends on: #1284 --------- Co-authored-by: IntegratedQuantum <43880493+IntegratedQuantum@users.noreply.github.com>
This commit is contained in:
parent
c52b231983
commit
b79784a0aa
@ -315,12 +315,14 @@ pub const Blueprint = struct {
|
||||
},
|
||||
}
|
||||
}
|
||||
pub fn set(self: *Blueprint, pattern: Pattern, mask: ?Mask) void {
|
||||
pub fn replace(self: *Blueprint, whitelist: ?Mask, blacklist: ?Mask, newBlocks: Pattern) void {
|
||||
for(0..self.blocks.width) |x| {
|
||||
for(0..self.blocks.depth) |y| {
|
||||
for(0..self.blocks.height) |z| {
|
||||
if(mask) |_mask| if(_mask.match(self.blocks.get(x, y, z))) continue;
|
||||
self.blocks.set(x, y, z, pattern.blocks.sample(&main.seed).block);
|
||||
const current = self.blocks.get(x, y, z);
|
||||
if(whitelist) |m| if(!m.match(current)) continue;
|
||||
if(blacklist) |m| if(m.match(current)) continue;
|
||||
self.blocks.set(x, y, z, newBlocks.blocks.sample(&main.seed).block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -17,3 +17,4 @@ pub const blueprint = @import("worldedit/blueprint.zig");
|
||||
pub const rotate = @import("worldedit/rotate.zig");
|
||||
pub const set = @import("worldedit/set.zig");
|
||||
pub const mask = @import("worldedit/mask.zig");
|
||||
pub const replace = @import("worldedit/replace.zig");
|
||||
|
61
src/server/command/worldedit/replace.zig
Normal file
61
src/server/command/worldedit/replace.zig
Normal file
@ -0,0 +1,61 @@
|
||||
const std = @import("std");
|
||||
|
||||
const main = @import("main");
|
||||
const Vec3i = main.vec.Vec3i;
|
||||
const User = main.server.User;
|
||||
|
||||
const Block = main.blocks.Block;
|
||||
const Blueprint = main.blueprint.Blueprint;
|
||||
const Pattern = main.blueprint.Pattern;
|
||||
const Mask = main.blueprint.Mask;
|
||||
|
||||
pub const description = "Replace blocks in the world edit selection.";
|
||||
pub const usage = "/replace <old mask> <new pattern>";
|
||||
|
||||
pub fn execute(args: []const u8, source: *User) void {
|
||||
var argsSplit = std.mem.splitScalar(u8, args, ' ');
|
||||
const oldMaskString = argsSplit.next() orelse {
|
||||
return source.sendMessage("#ff0000Missing required <old> argument.", .{});
|
||||
};
|
||||
const newPatternString = argsSplit.next() orelse {
|
||||
return source.sendMessage("#ff0000Missing required <new> argument.", .{});
|
||||
};
|
||||
|
||||
const pos1 = source.worldEditData.selectionPosition1 orelse {
|
||||
return source.sendMessage("#ff0000Position 1 isn't set", .{});
|
||||
};
|
||||
const pos2 = source.worldEditData.selectionPosition2 orelse {
|
||||
return source.sendMessage("#ff0000Position 2 isn't set", .{});
|
||||
};
|
||||
|
||||
const oldMask = Mask.initFromString(main.stackAllocator, oldMaskString) catch |err| {
|
||||
return source.sendMessage("#ff0000Error parsing mask: {s}", .{@errorName(err)});
|
||||
};
|
||||
defer oldMask.deinit(main.stackAllocator);
|
||||
|
||||
const newPattern = Pattern.initFromString(main.stackAllocator, newPatternString) catch |err| {
|
||||
return source.sendMessage("#ff0000Error parsing pattern: {s}", .{@errorName(err)});
|
||||
};
|
||||
defer newPattern.deinit(main.stackAllocator);
|
||||
|
||||
const posStart: Vec3i = @min(pos1, pos2);
|
||||
const posEnd: Vec3i = @max(pos1, pos2);
|
||||
|
||||
const selection = Blueprint.capture(main.globalAllocator, posStart, posEnd);
|
||||
|
||||
switch(selection) {
|
||||
.success => |blueprint| {
|
||||
source.worldEditData.undoHistory.push(.init(blueprint, posStart, "replace"));
|
||||
source.worldEditData.redoHistory.clear();
|
||||
|
||||
var modifiedBlueprint = blueprint.clone(main.stackAllocator);
|
||||
defer modifiedBlueprint.deinit(main.stackAllocator);
|
||||
|
||||
modifiedBlueprint.replace(oldMask, null, newPattern);
|
||||
modifiedBlueprint.paste(posStart, .{.preserveVoid = true});
|
||||
},
|
||||
.failure => |err| {
|
||||
source.sendMessage("#ff0000Error: Could not capture selection. (at {}, {s})", .{err.pos, err.message});
|
||||
},
|
||||
}
|
||||
}
|
@ -41,7 +41,7 @@ pub fn execute(args: []const u8, source: *User) void {
|
||||
var modifiedBlueprint = blueprint.clone(main.stackAllocator);
|
||||
defer modifiedBlueprint.deinit(main.stackAllocator);
|
||||
|
||||
modifiedBlueprint.set(pattern, source.worldEditData.mask);
|
||||
modifiedBlueprint.replace(null, source.worldEditData.mask, pattern);
|
||||
modifiedBlueprint.paste(posStart, .{.preserveVoid = true});
|
||||
},
|
||||
.failure => |err| {
|
||||
|
Loading…
x
Reference in New Issue
Block a user