mirror of
https://github.com/plexusorg/Plex-FAWE.git
synced 2024-12-22 17:27:38 +00:00
Implement a "sensitivity" setting, a mask, and an option to only distinguish air vs blocks to blendball (#1832)
This commit is contained in:
parent
d498996cbd
commit
100288ada5
@ -1,5 +1,7 @@
|
|||||||
package com.fastasyncworldedit.core.command.tool.brush;
|
package com.fastasyncworldedit.core.command.tool.brush;
|
||||||
|
|
||||||
|
import com.fastasyncworldedit.core.function.mask.CachedMask;
|
||||||
|
import com.fastasyncworldedit.core.math.MutableBlockVector3;
|
||||||
import com.sk89q.worldedit.EditSession;
|
import com.sk89q.worldedit.EditSession;
|
||||||
import com.sk89q.worldedit.MaxChangedBlocksException;
|
import com.sk89q.worldedit.MaxChangedBlocksException;
|
||||||
import com.sk89q.worldedit.command.tool.brush.Brush;
|
import com.sk89q.worldedit.command.tool.brush.Brush;
|
||||||
@ -8,10 +10,40 @@ import com.sk89q.worldedit.math.BlockVector3;
|
|||||||
import com.sk89q.worldedit.world.block.BlockState;
|
import com.sk89q.worldedit.world.block.BlockState;
|
||||||
import com.sk89q.worldedit.world.block.BlockTypes;
|
import com.sk89q.worldedit.world.block.BlockTypes;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
public class BlendBall implements Brush {
|
public class BlendBall implements Brush {
|
||||||
|
|
||||||
|
private static final BlockState AIR = BlockTypes.AIR.getDefaultState();
|
||||||
|
|
||||||
|
private final int minFreqDiff;
|
||||||
|
private final boolean onlyAir;
|
||||||
|
@Nullable private final CachedMask mask;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link Brush} instance with default settings.
|
||||||
|
*/
|
||||||
|
public BlendBall() {
|
||||||
|
this(1, false, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new {@link Brush} instance.
|
||||||
|
*
|
||||||
|
* @param minFreqDiff Minimum difference between nearby blocks (3x3x3 cuboid centered on position) to alter the current block
|
||||||
|
* at a position
|
||||||
|
* @param onlyAir Only consider air for comparing existing blocks, and for altering existing blocks
|
||||||
|
* @param mask Mask to limit the blocks being considered for alteration. Will also limit blocks types able to be
|
||||||
|
* placed, and will consider blocks not meeting the mask as air
|
||||||
|
* @since TODO
|
||||||
|
*/
|
||||||
|
public BlendBall(int minFreqDiff, boolean onlyAir, @Nullable CachedMask mask) {
|
||||||
|
this.minFreqDiff = minFreqDiff;
|
||||||
|
this.onlyAir = onlyAir;
|
||||||
|
this.mask = mask;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double size) throws
|
public void build(EditSession editSession, BlockVector3 position, Pattern pattern, double size) throws
|
||||||
MaxChangedBlocksException {
|
MaxChangedBlocksException {
|
||||||
@ -27,41 +59,75 @@ public class BlendBall implements Brush {
|
|||||||
int maxY = editSession.getMaxY();
|
int maxY = editSession.getMaxY();
|
||||||
int minY = editSession.getMinY();
|
int minY = editSession.getMinY();
|
||||||
|
|
||||||
|
MutableBlockVector3 mutable = new MutableBlockVector3();
|
||||||
for (int x = -outsetSize; x <= outsetSize; x++) {
|
for (int x = -outsetSize; x <= outsetSize; x++) {
|
||||||
int x0 = x + tx;
|
int x0 = x + tx;
|
||||||
|
int xx = x * x;
|
||||||
for (int y = -outsetSize; y <= outsetSize; y++) {
|
for (int y = -outsetSize; y <= outsetSize; y++) {
|
||||||
int y0 = y + ty;
|
int y0 = y + ty;
|
||||||
|
if (y0 + 1 < minY || y0 - 1 > maxY) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int yy = y * y;
|
||||||
|
int xxyy = xx + yy;
|
||||||
|
if (xxyy >= brushSizeSquared) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
for (int z = -outsetSize; z <= outsetSize; z++) {
|
for (int z = -outsetSize; z <= outsetSize; z++) {
|
||||||
if (x * x + y * y + z * z >= brushSizeSquared) {
|
int z0 = z + tz;
|
||||||
|
if (xxyy + z * z >= brushSizeSquared || maskFails(editSession, mutable.setComponents(x0, y0, z0))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
int z0 = z + tz;
|
int highest = 1, currentBlockFrequency = 1;
|
||||||
int highest = 1;
|
|
||||||
BlockState currentState = editSession.getBlock(x0, y0, z0);
|
BlockState currentState = editSession.getBlock(x0, y0, z0);
|
||||||
BlockState highestState = currentState;
|
BlockState highestState = currentState;
|
||||||
|
int currentStateID = currentState.getInternalBlockTypeId();
|
||||||
Arrays.fill(frequency, 0);
|
Arrays.fill(frequency, 0);
|
||||||
|
int air = 0;
|
||||||
|
int total = 26;
|
||||||
boolean tie = false;
|
boolean tie = false;
|
||||||
for (int ox = -1; ox <= 1; ox++) {
|
for (int ox = -1; ox <= 1; ox++) {
|
||||||
for (int oz = -1; oz <= 1; oz++) {
|
for (int oz = -1; oz <= 1; oz++) {
|
||||||
for (int oy = -1; oy <= 1; oy++) {
|
for (int oy = -1; oy <= 1; oy++) {
|
||||||
if (oy + y0 < minY || oy + y0 > maxY) {
|
if (ox == 0 && oy == 0 && oz == 0) {
|
||||||
|
continue;
|
||||||
|
} else if (oy + y0 < minY || oy + y0 > maxY) {
|
||||||
|
total--;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
BlockState state = editSession.getBlock(x0 + ox, y0 + oy, z0 + oz);
|
boolean masked = maskFails(editSession, mutable.setComponents(x0 + ox, y0 + oy, z0 + oz));
|
||||||
int count = frequency[state.getInternalBlockTypeId()];
|
BlockState state = masked ? AIR : editSession.getBlock(x0 + ox, y0 + oy, z0 + oz);
|
||||||
|
if (state.getBlockType().getMaterial().isAir()) {
|
||||||
|
air++;
|
||||||
|
}
|
||||||
|
int internalID = state.getInternalBlockTypeId();
|
||||||
|
int count = frequency[internalID];
|
||||||
|
if (internalID == currentStateID) {
|
||||||
|
currentBlockFrequency++;
|
||||||
|
}
|
||||||
count++;
|
count++;
|
||||||
if (count > highest) {
|
if (count - highest >= minFreqDiff) {
|
||||||
highest = count;
|
highest = count;
|
||||||
highestState = state;
|
highestState = state;
|
||||||
tie = false;
|
tie = false;
|
||||||
} else if (count == highest) {
|
} else if (count == highest) {
|
||||||
tie = true;
|
tie = true;
|
||||||
}
|
}
|
||||||
frequency[state.getInternalBlockTypeId()] = count;
|
frequency[internalID] = count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!tie && currentState != highestState) {
|
if (onlyAir) {
|
||||||
|
if (air * 2 - total >= minFreqDiff) {
|
||||||
|
if (!currentState.isAir()) {
|
||||||
|
editSession.setBlock(x0, y0, z0, AIR);
|
||||||
|
}
|
||||||
|
} else if (currentState.isAir() && total - 2 * air >= minFreqDiff) {
|
||||||
|
editSession.setBlock(x0, y0, z0, highestState);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (highest - currentBlockFrequency >= minFreqDiff && !tie && currentState != highestState) {
|
||||||
editSession.setBlock(x0, y0, z0, highestState);
|
editSession.setBlock(x0, y0, z0, highestState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -69,5 +135,8 @@ public class BlendBall implements Brush {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean maskFails(EditSession editSession, MutableBlockVector3 mutable) {
|
||||||
|
return mask == null || !mask.test(editSession, mutable);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
package com.fastasyncworldedit.core.function.mask;
|
package com.fastasyncworldedit.core.function.mask;
|
||||||
|
|
||||||
import com.fastasyncworldedit.core.math.BlockVectorSet;
|
import com.fastasyncworldedit.core.math.BlockVectorSet;
|
||||||
|
import com.fastasyncworldedit.core.math.LocalBlockVectorSet;
|
||||||
|
import com.fastasyncworldedit.core.util.collection.BlockVector3Set;
|
||||||
import com.sk89q.worldedit.extent.Extent;
|
import com.sk89q.worldedit.extent.Extent;
|
||||||
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
|
import com.sk89q.worldedit.function.mask.AbstractExtentMask;
|
||||||
import com.sk89q.worldedit.function.mask.Mask;
|
import com.sk89q.worldedit.function.mask.Mask;
|
||||||
@ -11,12 +13,30 @@ import javax.annotation.Nullable;
|
|||||||
public class CachedMask extends AbstractDelegateMask implements ResettableMask {
|
public class CachedMask extends AbstractDelegateMask implements ResettableMask {
|
||||||
|
|
||||||
private final boolean hasExtent;
|
private final boolean hasExtent;
|
||||||
private transient BlockVectorSet cache_checked = new BlockVectorSet();
|
private transient BlockVector3Set cache_checked;
|
||||||
private transient BlockVectorSet cache_results = new BlockVectorSet();
|
private transient BlockVector3Set cache_results;
|
||||||
|
|
||||||
public CachedMask(Mask mask) {
|
public CachedMask(Mask mask) {
|
||||||
|
this(mask, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new CachedMask instance for the given mask
|
||||||
|
*
|
||||||
|
* @param mask Mask to cache results of
|
||||||
|
* @param local If the area will be small
|
||||||
|
* @since TODO
|
||||||
|
*/
|
||||||
|
public CachedMask(Mask mask, boolean local) {
|
||||||
super(mask);
|
super(mask);
|
||||||
hasExtent = mask instanceof AbstractExtentMask;
|
hasExtent = mask instanceof AbstractExtentMask;
|
||||||
|
if (local) {
|
||||||
|
cache_checked = new LocalBlockVectorSet();
|
||||||
|
cache_results = new LocalBlockVectorSet();
|
||||||
|
} else {
|
||||||
|
cache_checked = new BlockVectorSet();
|
||||||
|
cache_results = new BlockVectorSet();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CachedMask cache(Mask mask) {
|
public static CachedMask cache(Mask mask) {
|
||||||
@ -95,7 +115,7 @@ public class CachedMask extends AbstractDelegateMask implements ResettableMask {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Mask copy() {
|
public Mask copy() {
|
||||||
return new CachedMask(getMask().copy());
|
return new CachedMask(getMask().copy(), cache_checked instanceof LocalBlockVectorSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ import com.fastasyncworldedit.core.command.tool.sweep.SweepBrush;
|
|||||||
import com.fastasyncworldedit.core.configuration.Caption;
|
import com.fastasyncworldedit.core.configuration.Caption;
|
||||||
import com.fastasyncworldedit.core.configuration.Settings;
|
import com.fastasyncworldedit.core.configuration.Settings;
|
||||||
import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder;
|
import com.fastasyncworldedit.core.extent.clipboard.MultiClipboardHolder;
|
||||||
|
import com.fastasyncworldedit.core.function.mask.CachedMask;
|
||||||
import com.fastasyncworldedit.core.function.mask.IdMask;
|
import com.fastasyncworldedit.core.function.mask.IdMask;
|
||||||
import com.fastasyncworldedit.core.function.mask.SingleBlockTypeMask;
|
import com.fastasyncworldedit.core.function.mask.SingleBlockTypeMask;
|
||||||
import com.fastasyncworldedit.core.limit.FaweLimit;
|
import com.fastasyncworldedit.core.limit.FaweLimit;
|
||||||
@ -138,6 +139,7 @@ import java.nio.file.FileSystems;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
import static com.sk89q.worldedit.internal.command.CommandUtil.checkCommandArgument;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Commands to set brush shape.
|
* Commands to set brush shape.
|
||||||
@ -175,10 +177,20 @@ public class BrushCommands {
|
|||||||
public void blendBallBrush(
|
public void blendBallBrush(
|
||||||
InjectedValueAccess context,
|
InjectedValueAccess context,
|
||||||
@Arg(desc = "The radius to sample for blending", def = "5")
|
@Arg(desc = "The radius to sample for blending", def = "5")
|
||||||
Expression radius
|
Expression radius,
|
||||||
|
@Arg(desc = "Minimum difference in frequency to change block", def = "1")
|
||||||
|
int minFreqDiff,
|
||||||
|
@Switch(name = 'a', desc = "Compare only air vs existing blocks")
|
||||||
|
boolean onlyAir,
|
||||||
|
@ArgFlag(name = 'm', desc = "Mask to limit blocks being considered", def = "")
|
||||||
|
Mask mask
|
||||||
) throws WorldEditException {
|
) throws WorldEditException {
|
||||||
worldEdit.checkMaxBrushRadius(radius);
|
worldEdit.checkMaxBrushRadius(radius);
|
||||||
set(context, new BlendBall(), "worldedit.brush.blendball").setSize(radius);
|
checkCommandArgument(minFreqDiff >= 0 && minFreqDiff <= 26, "minFreqDiff not in range 0 <= value <= 26");
|
||||||
|
if (mask != null && !(mask instanceof CachedMask)) {
|
||||||
|
mask = new CachedMask(mask, false);
|
||||||
|
}
|
||||||
|
set(context, new BlendBall(minFreqDiff, onlyAir, (CachedMask) mask), "worldedit.brush.blendball").setSize(radius);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Command(
|
@Command(
|
||||||
|
Loading…
Reference in New Issue
Block a user