Raise and lower terrain brush script

Discussion in 'CraftScripts' started by blockhead, May 29, 2012.

  1. blockhead

    blockhead New Member

    I wrote a script that implements a new brush. It allows you to raise or lower terrain:

    [​IMG]

    It takes one or two parameters. The first is brush radius and the second is strength (how many blocks the centre will displace vertically; defaults to 3). Positive values for strength raise terrain and negative values lower terrain. When lowering terrain under water, the gap is filled with water instead of air.


    Code (Text):

    importPackage(Packages.com.sk89q.worldedit);
    importPackage(Packages.com.sk89q.worldedit.blocks);
    importPackage(Packages.com.sk89q.worldedit.tools.brushes);
     
     
     
    context.checkArgs(1,2,"<size> [strength]");
    var size = parseInt(argv[1]);
    var strength = argv.length > 2 ? parseInt(argv[2]) : 3;
     
    var maxRadius = context.getConfiguration().maxBrushRadius;
    if(size > maxRadius) {
        player.printError("Maximum allowed brush radius: " + maxRadius);
    }else if(strength == 0) {
        player.printError("strength cannot be zero");
    } else {
     
    var tool = context.getSession().getBrushTool(player.getItemInHand());
    tool.setSize(size);
    var brush = new Brush({
        strength : strength,
        build : function(editSession,pos,mat,size) {
            var maxY = player.getWorld().getMaxY();
           
            function maybeSetBlock(pos,block) {
                // don't overwrite solid blocks
                if(BlockType.canPassThrough(editSession.getBlockType(pos))) {
                    editSession.setBlock(pos,block);
                    return true;
                }
                return false;
            }
               
            function moveColumn(z,x,limit) {
                var blockH = editSession.getHighestTerrainBlock(
                    pos.getBlockX() + x,
                    pos.getBlockZ() + z,
                    Math.max(0,pos.getBlockY() - size - 1),
                    Math.min(maxY,pos.getBlockY() + size + 1),
                    false);
     
                var bpos = new Vector(pos.getBlockX() + x,blockH,pos.getBlockZ() + z);
                var btype = editSession.getBlock(bpos);
               
                var target = blockH;
               
                /* skip this coordinate if the highest block isn't in our range or
                  is air */
                if(blockH >= (pos.getBlockY() - size) && blockH <= (pos.getBlockY() + size) && !btype.isAir()) {
                    // a parabolic curve
                    var xb = x/size;
                    var zb = z/size;
                    var s = Math.abs(this.strength);
                    var move = Math.max(0,Math.round(-(s-0.5) * (xb*xb + zb*zb) + s));
                   
                    if(this.strength > 0) {
                        // don't push past the top
                        target = Math.min(blockH + move,limit !== null ? limit : maxY);
                       
                        if(target > blockH) {
                            var cap = editSession.getBlock(bpos.setY(blockH + 1));
                            if(maybeSetBlock(bpos.setY(target),btype)) {
                               
                                /* if there is something on top of the block other
                                  than water, lift it too (since
                                  getHighestTerrainBlock was used, we know it's a
                                  non-solid block) */
                                if(target < maxY) {
                                    if(cap.getType() != BlockID.AIR &&
                                            cap.getType() != BlockID.WATER &&
                                            cap.getType() != BlockID.STATIONARY_WATER) {
                                        maybeSetBlock(bpos.setY(target + 1),cap);
                                    }
                                }
                            }
                           
                            /* fill the remainder with whatever was underneath the
                              block */
                            var filler = blockH > 0 ? editSession.getBlock(bpos.setY(blockH - 1)) : BaseBlock(BlockID.AIR);
                            for(var y = blockH; y < target; ++y)
                                editSession.setBlock(bpos.setY(y),filler);
                        } else target = blockH;
                    } else {
                        // don't push past the bottom
                        target = Math.max(blockH - move,limit !== null ? limit : 0);
                       
                        if(target < blockH) {
                            editSession.setBlock(bpos.setY(target),btype);
                           
                            if(blockH < maxY) {
                                var cap = editSession.getBlock(bpos.setY(blockH + 1));
                                editSession.setBlock(bpos.setY(target + 1),cap);
                               
                                var filler = (cap.getType() == BlockID.WATER ||
                                    cap.getType() == BlockID.STATIONARY_WATER) ?
                                    cap : BaseBlock(BlockID.AIR);
                               
                                for(var y=target+2; y<=blockH; ++y)
                                    editSession.setBlock(bpos.setY(y),filler);
                            }
                        } else target = blockH;
                    }
                }
               
                return target;
            }
       
            /* blocks are not moved past where the middle block is moved, otherwise
              using the brush multiple times makes things very bumpy */
            var limit = moveColumn(0,0,null);
           
            for(var z = -size; z <= size; ++z) {
                for(var x = -size; x <= size; ++x) {
                    if(z || x) moveColumn(z,x,limit);
                }
            }
        }
    });
     
    tool.setBrush(brush,"worldedit.brush.elevate");
     
    }
     
  2. Toops

    Toops New Member

    This. This right here.
    You deserve accolades. Works perfectly so far. Thank you so much!
  3. blockhead

    blockhead New Member

    Glad you like it
  4. inHaze

    inHaze New Member

    I had to sign up just to say how awesome this script is! It works great! I had always wondered if it was possible to make custom brushes through a script but had no idea where to start, really glad to see that you can!
  5. BlueII

    BlueII New Member

    that says the right person ^^ Your scripts are really cool Haze! But ur right, this script is it, too. It's easier to use than normal brush :)
  6. Koolio

    Koolio New Member

    This awesome script no longer works with the newer worldedit 6.0.0 :(((

    Maybe some can fix it.....

    Failed to execute:
    ReferenceError: "Brush" is not defined.
    (plugins/WorldEdit/craftscripts/terrain.js#20)
    plugins/WorldEdit/craftscripts/terrain.js at line number 20
  7. blockhead

    blockhead New Member

    Heh, I still get e-mail notifications for this old thread.

    Anyway, I can't find any single-player mods that come with worldedit 6, and before I go through the inconvenience of setting up a server just to test a script, try the following for me:

    Change the line
    importPackage(Packages.com.sk89q.worldedit.tools.brushes);
    to
    importPackage(Packages.com.sk89q.worldedit.command.tool.brush);
  8. Koolio

    Koolio New Member

    That works!

    btw always wondered if you could add in a way for the brush to just delete/ignore leaves,log, grass (id 31, not the block) when using the brush... that way it doesn't make a mess with those blocks and only edits the actual terrain stuff like stone, dirt etc mainly.
  9. Koolio

    Koolio New Member

    or just way to make it only effect dirt,stone,grass and ignore moving other blocks?
  10. PseudoKnight

    PseudoKnight Well-Known Member

    Do masks not work with scripts?
  11. blockhead

    blockhead New Member

    Here is an updated version of the script. It has an optional third parameter that takes "true" or "false" (I can't use the "-x" flag style that other commands use because the "cs" command gobbles them up). The default is false. When true, it only considers natural terrain blocks as the ground and treats every other block like it's non-solid (when in the way, pushes it up if it can without overwriting another block, otherwise just removes it). WorldEdit defines natural terrain blocks as stone, grass (the block kind), dirt, bedrock, sand, gravel, clay (including stained clay), mycelium, packed ice, netherrack, soul sand and all ores.

    It also checks WorldEdit's version and should work on version 6 as well as older versions.

    Code (Text):
    importPackage(Packages.com.sk89q.worldedit);
    importPackage(Packages.com.sk89q.worldedit.blocks);

    var v = WorldEdit.getVersion().split(".")
    if(v[0] >= 6) importPackage(Packages.com.sk89q.worldedit.command.tool.brush);
    else importPackage(Packages.com.sk89q.worldedit.tools.brushes);


    context.checkArgs(1,3,"<size> [strength] [natural only: true/false]");
    var size = parseInt(argv[1]);
    var strength = argv.length > 2 ? parseInt(argv[2]) : 3;
    var natOnly = false;
    if(argv.length > 3) {
        natOnly = argv[3].toLowerCase();
        if(natOnly == "1" || natOnly == "true") natOnly = true;
        else if(natOnly != "0" && natOnly != "false") natOnly = null;
    }

    var maxRadius = context.getConfiguration().maxBrushRadius;

    if(natOnly === null) {
        player.printError("third argument, if provided, must be \"true\" or \"false\"");
    } else if(size > maxRadius) {
        player.printError("Maximum allowed brush radius: " + maxRadius);
    } else if(strength == 0) {
        player.printError("strength cannot be zero");
    } else {

    var tool = context.getSession().getBrushTool(player.getItemInHand());
    tool.setSize(size);
    var brush = new Brush({
        strength : strength,
        natOnly : natOnly,
        build : function(editSession,pos,mat,size) {
            var maxY = player.getWorld().getMaxY();
         
            function maybeSetBlock(pos,block) {
                // don't overwrite solid blocks
                if(BlockType.canPassThrough(editSession.getBlockType(pos))) {
                    editSession.setBlock(pos,block);
                    return true;
                }
                return false;
            }
             
            function moveColumn(z,x,limit) {
                var blockH = editSession.getHighestTerrainBlock(
                    pos.getBlockX() + x,
                    pos.getBlockZ() + z,
                    Math.max(0,pos.getBlockY() - size - 1),
                    Math.min(maxY,pos.getBlockY() + size + 1),
                    this.natOnly);

                var bpos = new Vector(pos.getBlockX() + x,blockH,pos.getBlockZ() + z);
                var btype = editSession.getBlock(bpos);
             
                var target = blockH;
             
                /* skip this coordinate if the highest block isn't in our range or
                   is air */
                if(blockH >= (pos.getBlockY() - size) && blockH <= (pos.getBlockY() + size) && !btype.isAir()) {
                    // a parabolic curve
                    var xb = x/size;
                    var zb = z/size;
                    var s = Math.abs(this.strength);
                    var move = Math.max(0,Math.round(-(s-0.5) * (xb*xb + zb*zb) + s));
                 
                    if(this.strength > 0) {
                        // don't push past the top
                        target = Math.min(blockH + move,limit !== null ? limit : maxY);
                     
                        if(target > blockH) {
                            var cap = editSession.getBlock(bpos.setY(blockH + 1));
                            if(maybeSetBlock(bpos.setY(target),btype)) {
                             
                                /* if there is something on top of the block other
                                   than water, lift it too (since
                                   getHighestTerrainBlock was used, we know it's a
                                   non-solid block) */
                                if(target < maxY) {
                                    if(cap.getType() != BlockID.AIR &&
                                            cap.getType() != BlockID.WATER &&
                                            cap.getType() != BlockID.STATIONARY_WATER) {
                                        maybeSetBlock(bpos.setY(target + 1),cap);
                                    }
                                }
                            }
                         
                            /* fill the remainder with whatever was underneath the
                               block */
                            var filler = blockH > 0 ? editSession.getBlock(bpos.setY(blockH - 1)) : BaseBlock(BlockID.AIR);
                            for(var y = blockH; y < target; ++y)
                                editSession.setBlock(bpos.setY(y),filler);
                        } else target = blockH;
                    } else {
                        // don't push past the bottom
                        target = Math.max(blockH - move,limit !== null ? limit : 0);
                     
                        if(target < blockH) {
                            editSession.setBlock(bpos.setY(target),btype);
                         
                            if(blockH < maxY) {
                                var cap = editSession.getBlock(bpos.setY(blockH + 1));
                                editSession.setBlock(bpos.setY(target + 1),cap);
                             
                                var filler = (cap.getType() == BlockID.WATER ||
                                    cap.getType() == BlockID.STATIONARY_WATER) ?
                                    cap : BaseBlock(BlockID.AIR);
                             
                                for(var y=target+2; y<=blockH; ++y)
                                    editSession.setBlock(bpos.setY(y),filler);
                            }
                        } else target = blockH;
                    }
                }
             
                return target;
            }
     
            /* blocks are not moved past where the middle block is moved, otherwise
               using the brush multiple times makes things very bumpy */
            var limit = moveColumn(0,0,null);
         
            for(var z = -size; z <= size; ++z) {
                for(var x = -size; x <= size; ++x) {
                    if(z || x) moveColumn(z,x,limit);
                }
            }
        }
    });

    tool.setBrush(brush,"worldedit.brush.elevate");

    var msg = "elevate brush equipped (size " + size + ", strength " + strength;
    if(natOnly) msg += ", natural blocks only";
    msg += ")";
    player.print(msg);

    }
  12. Koolio

    Koolio New Member

    This bit does not seem to work
    var v = WorldEdit.getVersion().split(".")
    if(v[0] >= 6) importPackage(Packages.com.sk89q.worldedit.command.tool.brush);
    else importPackage(Packages.com.sk89q.worldedit.tools.brushes);

    get the old error where it uses importPackage(Packages.com.sk89q.worldedit.tools.brushes);

    and if i remove it, and just use importPackage(Packages.com.sk89q.worldedit.command.tool.brush);

    then it works.. but

    the true and false bit does not completely work...well if I set it to 'true' then it works.. but if I set to 'false' then the brush just doesn't work at all..
  13. Koolio

    Koolio New Member

    WorldEdit version 6.0.0-SNAPSHOT.3267-5dd7b83
  14. blockhead

    blockhead New Member

    My bad. I had forgotten that methods defined in Java return Java strings and not native JavaScript strings. Both classes happen to have "split" methods but they work differently. The second problem was just me being sloppy.

    You should note that the third argument is optional, meaning you can just enter two arguments (or even one, since the second argument is also optional) and the third argument will automatically get a value of false (the second argument has a default of 3). I actually neglected to test the script with an explicit false value before posting it last time.

    Here is a fixed version:
    Code (Text):
    importPackage(Packages.com.sk89q.worldedit);
    importPackage(Packages.com.sk89q.worldedit.blocks);

    var v = String(WorldEdit.getVersion()).split(".");
    if(v[0] >= 6) importPackage(Packages.com.sk89q.worldedit.command.tool.brush);
    else importPackage(Packages.com.sk89q.worldedit.tools.brushes);


    context.checkArgs(1,3,"<size> [strength] [natural only: true/false]");
    var size = parseInt(argv[1]);
    var strength = argv.length > 2 ? parseInt(argv[2]) : 3;
    var natOnly = false;
    if(argv.length > 3) {
        var arg = argv[3].toLowerCase();
        if(arg == "1" || arg == "true") natOnly = true;
        else if(arg != "0" && arg != "false") natOnly = null;
    }

    var maxRadius = context.getConfiguration().maxBrushRadius;

    if(natOnly === null) {
        player.printError("third argument, if provided, must be \"true\" or \"false\"");
    } else if(size > maxRadius) {
        player.printError("Maximum allowed brush radius: " + maxRadius);
    } else if(strength == 0) {
        player.printError("strength cannot be zero");
    } else {

    var tool = context.getSession().getBrushTool(player.getItemInHand());
    tool.setSize(size);
    var brush = new Brush({
        strength : strength,
        natOnly : natOnly,
        build : function(editSession,pos,mat,size) {
            var maxY = player.getWorld().getMaxY();
         
            function maybeSetBlock(pos,block) {
                // don't overwrite solid blocks
                if(BlockType.canPassThrough(editSession.getBlockType(pos))) {
                    editSession.setBlock(pos,block);
                    return true;
                }
                return false;
            }
             
            function moveColumn(z,x,limit) {
                var blockH = editSession.getHighestTerrainBlock(
                    pos.getBlockX() + x,
                    pos.getBlockZ() + z,
                    Math.max(0,pos.getBlockY() - size - 1),
                    Math.min(maxY,pos.getBlockY() + size + 1),
                    this.natOnly);

                var bpos = new Vector(pos.getBlockX() + x,blockH,pos.getBlockZ() + z);
                var btype = editSession.getBlock(bpos);
             
                var target = blockH;
             
                /* skip this coordinate if the highest block isn't in our range or
                   is air */
                if(blockH >= (pos.getBlockY() - size) && blockH <= (pos.getBlockY() + size) && !btype.isAir()) {
                    // a parabolic curve
                    var xb = x/size;
                    var zb = z/size;
                    var s = Math.abs(this.strength);
                    var move = Math.max(0,Math.round(-(s-0.5) * (xb*xb + zb*zb) + s));
                 
                    if(this.strength > 0) {
                        // don't push past the top
                        target = Math.min(blockH + move,limit !== null ? limit : maxY);
                     
                        if(target > blockH) {
                            var cap = editSession.getBlock(bpos.setY(blockH + 1));
                            if(maybeSetBlock(bpos.setY(target),btype)) {
                             
                                /* if there is something on top of the block other
                                   than water, lift it too (since
                                   getHighestTerrainBlock was used, we know it's a
                                   non-solid block) */
                                if(target < maxY) {
                                    if(cap.getType() != BlockID.AIR &&
                                            cap.getType() != BlockID.WATER &&
                                            cap.getType() != BlockID.STATIONARY_WATER) {
                                        maybeSetBlock(bpos.setY(target + 1),cap);
                                    }
                                }
                            }
                         
                            /* fill the remainder with whatever was underneath the
                               block */
                            var filler = blockH > 0 ? editSession.getBlock(bpos.setY(blockH - 1)) : BaseBlock(BlockID.AIR);
                            for(var y = blockH; y < target; ++y)
                                editSession.setBlock(bpos.setY(y),filler);
                        } else target = blockH;
                    } else {
                        // don't push past the bottom
                        target = Math.max(blockH - move,limit !== null ? limit : 0);
                     
                        if(target < blockH) {
                            editSession.setBlock(bpos.setY(target),btype);
                         
                            if(blockH < maxY) {
                                var cap = editSession.getBlock(bpos.setY(blockH + 1));
                                editSession.setBlock(bpos.setY(target + 1),cap);
                             
                                var filler = (cap.getType() == BlockID.WATER ||
                                    cap.getType() == BlockID.STATIONARY_WATER) ?
                                    cap : BaseBlock(BlockID.AIR);
                             
                                for(var y=target+2; y<=blockH; ++y)
                                    editSession.setBlock(bpos.setY(y),filler);
                            }
                        } else target = blockH;
                    }
                }
             
                return target;
            }
     
            /* blocks are not moved past where the middle block is moved, otherwise
               using the brush multiple times makes things very bumpy */
            var limit = moveColumn(0,0,null);
         
            for(var z = -size; z <= size; ++z) {
                for(var x = -size; x <= size; ++x) {
                    if(z || x) moveColumn(z,x,limit);
                }
            }
        }
    });

    tool.setBrush(brush,"worldedit.brush.elevate");

    var msg = "elevate brush equipped (size " + size + ", strength " + strength;
    if(natOnly) msg += ", natural blocks only";
    msg += ")";
    player.print(msg);

    }