River Pebble Script

Emoticone11

The Dark Lord Sauron
Staff member
Hey everyone, I made a new script which automatically places pebble layers on river banks- with nothing more than a single flat placeholder!

Check out the result:

5d99a19056.png


07732f7a2a.png


The script is pretty flexible and allows for you to put in various parameters. Here's a brief explanation of them:

1. Placeholder

Simple enough- give the block ID/metadata pair of the blocks you want to put pebble layers above. Note: the placeholder here should not be a wool block (35) as those are used in the script! In the example picture I used gold (41).

2. Type

The type of the terrainset you want. The script can also do sand as well. The options are (in terrainset order): 'easternislands', 'ironislands', 'vale', 'north', 'ocean', 'dorne', 'river', 'westerlands', 'arbor', 'stormlands', 'reach', and then 'sand'.

3. Cutoff

This represents how high the dry/wet cutoff point should be. You should give the script an integer number between 0 and 9. If you give 0 it will make the entire pebble bank dry, whereas 9 will make the entire bank wet. A value of 4 or 5 will make the cutoff roughly in the middle.

4. Height profile

There are various preset "height profiles" preprogrammed into the script- a height profile just represents a distribution of the pebble layers. This should take an integer number between 0 and 6. The presets are listed as follows:

0 is a special value- if you want more flexibility with the height profile and wet/dry mix in the pebbles you should give 0 as the value for this. It will cause the script to terminate just after making the rainbow gradient, in which case all you have to do is mask over each wool color to set each pebble layer.

1 gives a "normal" linear slope, with each wool step increasing in height by one layer -->
:0, :1, :2, :3, :4, :5, :6.

2 gives a shallow slope, with every other wool step increasing in height by one layer -->
:0, :0, :1, :1, :2, :2, :3.

3 gives an even shallower slope, with the same pattern as the previous one but offset by one block -->
air, :0, :0, :1, :1, :2, :2

4 gives a convex slope (i.e. "pointier" at the top) -->
:0, :0, :1, :1, :2, :4, :6

5 gives a concave slope (i.e. "flatter" at the top) -->
:0, :1, :2, :3, :3, :4, :4

6 gives a steeper concave slope -->
:0, :2, :4, :5, :6, :7, :7

-------------------------------------------------------------------------

Please feel free to give feedback and suggestions on the script! One issue I know of already is that the pebbles on the outermost banks don't "transition" to the higher land, they form the same symmetric curve as the rest of the banks. Not sure if it's possible to fix this with the script but I'll work on it.

Download the script here

(Alternate download here)
 

Emoticone11

The Dark Lord Sauron
Staff member
This would be fun to try with snow and sand too, great work!

I've tried making an automatic snow script to help with stuff in the North, but it's quite a bit more challenging. The problem here is that the snow layer gradient depends on slope. I've tried doing one where the user puts two placeholders, one for steep areas and one for flat areas, but then the script leaves jagged edges where the areas merge.

Very cool! Can this be modified to work with sand layers as well?

Yeah, you can put 'sand' when it asks for pebble type and it'll do it with sand.
 

Emoticone11

The Dark Lord Sauron
Staff member
Yup! It's actually not too complicated, it's all the conditionals which make the script that long. The key thing to the script is the adjacency mask, which has the format //gmask ~[X][min][max], where X is an arbitrary mask (any mask syntax is valid) selecting which adjacent blocks to check, min is the minimum amount of such adjacent blocks necessary to mask the current block, and max is the maximum.

So the script steps are something like the following:

1. Let wetid and dryid be the IDs of the wet & dry pebble layers selected by the "type" parameter

2. //gmask ~[X][4][8] (where X is the ID of the single placeholder),
//set red

3. Loop for each prevcolor, color in [red, orange, yellow, green, blue, purple, pink], starting at prevcolor=red, color=orange:
//gmask ~[prevcolor][4][8]
//set color

By the end of this you should have a rainbow gradient which is used in setting the pebble layers.

4. Let H be the pattern (array) of pebble heights (i.e. metadata values) denoted by the height profile parameter, indexed by color.
Loop for each color in [red, orange, yellow, green, blue, purple, pink]:
//gmask 0&>[color]
//set dryid:H[color]

5. Omitting some details on the last step, since the conditionals are pretty messy, but basically loop through each color again, replacing dryid with wetid, and terminate once an incremented value is greater than the cutoff parameter.

6. Replace X (the placeholder) and all wool blocks with the dryidf (the full block ID), or wetidf, depending on the conditionals.
 

CashBanks

A Knight at the Opera
Staff member
The key thing to the script is the adjacency mask, which has the format //gmask ~[X][min][max], where X is an arbitrary mask (any mask syntax is valid) selecting which adjacent blocks to check, min is the minimum amount of such adjacent blocks necessary to mask the current block, and max is the maximum.

WOoaahh I had no idea adjacency masks were a thing. So it's masking objects based on the number of blocks adjacent to them, can it mask based on the ID of the blocks they have adjacent to them too? E.g. like the above/below /mask <[X].
 
Last edited:
  • Like
Reactions: Thamus_Knoward

CashBanks

A Knight at the Opera
Staff member
I just learned that all these are are a thing - mind blown.
Block mask [blocks]
Example: stone,wood
Explanation: Allows any block which is already stone or wood

Angle mask /[min],[max]
Example: /-20,-5
Explanation: Allows any block where the adjacent block is between 20 and 5 blocks below

Radius mask {[min],[max]
Example: {5,20
Explanation: Restricts blocks to within a specific radius range of the initial block

Adjacent mask ~[pattern][=min[,max]]
Example: ~stone,wood
Explanation: Allows any block if it is adjacent to stone or wood Example: ~bedrock=2,4 Explanation: Allows any block if it is adjacent to between 2 and 4 bedrock

Below mask <[pattern]
Example: <stone
Explanation: Allows any block directly beneath stone

Above mask >[pattern]
Example: >stone
Explanation: Allows any block directly above stone

Biome mask $[biome]
Example: $FOREST
Explanation: Restricts to the specified biome

Random mask %[chance]
Example: %34
Explanation: 34% chance any block is allowed

Negate mask
Example: !<stone
Explanation: Can negate any mask, this will allow any block not below stone

Expression mask =[expression]
Example: =abs(z+1)%2-abs(x)%2
Explanation: Restricts to blocks where x and z coords are even numbers.
 

Emoticone11

The Dark Lord Sauron
Staff member
WOoaahh I had no idea adjacency masks were a thing. So it's masking objects based on the number of blocks adjacent to them, can it mask based on the ID of the blocks they have adjacent to them too? E.g. like the above/below /mask <[X].

Yup, so say you have red blocks, and you want to mask red blocks adjacent to four or more green blocks. Then you do:

//gmask red&~[green][4][8].

So the one inside the brackets is the ID of the blocks adjacent to the ones you want to mask.
 

Emoticone11

The Dark Lord Sauron
Staff member
BTW @CashBanks you need to keep the brackets around all of those above.

If you do:

//gmask >1,2

It will be interpreted as "mask over 1 OR mask 2".

//gmask >[1,2]

"mask over 1 OR 2"


Also, you missed a very important one:

Simplex mask #simplex[scale][min][max]
Example: #simplex[20][40][70]
Explanation: Uses simplex noise to create "blobs" rather than random noise. The 'scale' parameter changes how "zoomed in" the blobs are, the 'min' and 'max' parameters roughly change the "width" of the blobs.
 
  • Like
Reactions: CashBanks

Thamus_Knoward

Shadowbinder
Wowowowow!

Now if we got an API into the schembrush that would allow the placement of a schem above masked blocks we’d have ourselves a forest generator.

Especially with simplex allowing for natural clusters or groves of trees within a forest.

A hack that could work would be if there was a way to write a script using a random teleport command that moves the player character around in a predefined range of coordinates, makes them look down and then executes the schbr command.
 
Last edited:
I actually did find a plug-in called RandomTP on bukkit that randomly teleports a player throughout preselected coordinates. It’s got quite good functionality, even preventing players from teleporting onto certain blocks. Not sure how well it would integrate or how good it works on the small scale tho. I also saw mention of a bot plugin that makes virtual players capable of running commands. It would be quite complex to do your workaround, though it could be possible.
 

Emoticone11

The Dark Lord Sauron
Staff member
I'd rather not have players teleporting around amok without being at the helm, especially with the double-place glitch.

It seems like integration of masks into the schembrush and having a "bulk" place command (parameter representing density) should be quite doable.
 

CashBanks

A Knight at the Opera
Staff member
Stein just helped me figure out a very handy way to quickly replace snow/pebble layers.

Say you've just made a large river bank of dry pebbles, but now you want to mix in some wet pebbles close to the water line. With the #id mask, you can tell world edit to only change a block's ID, not it's metadata.

So if you wanted to quickly change those dry pebbles to wet pebbles, while maintaining their original height you could do:
/br s #id[2212] <== the ID for wet northern pebble layers
and
mask [2250] <=== the ID for the dry pebbles you're replacing.

Then just paint over the spots you want to change.

This also works for snow layers, so you can now quickly make pebble banks with the voxel snow brush /b snow then just swap it to pebbles using the #id brush.
 

Thamus_Knoward

Shadowbinder
Please feel free to give feedback and suggestions on the script! One issue I know of already is that the pebbles on the outermost banks don't "transition" to the higher land, they form the same symmetric curve as the rest of the banks. Not sure if it's possible to fix this with the script but I'll work on it.


Fixed it in this version:


Code:
$${PROMPT(&type,$$[type],type of pebbles : e.g. 'easternislands' 'vale' 'dorne' 'arbor' : 'sand' also works)}$$
$${PROMPT(#cutoff,$$[cutoff],cutoff of wet pebbles in mix : number from 0-8 : 0 = all dry : 9 = all wet)}$$
$${PROMPT(#profile,$$[profile],height profile of pebbles : number from 0-6 : see guide)}$$



$${LOG(Selecting pebble type)}$$
$${IF(%&type% == "easternislands")
SET(#iddry,2247)
SET(#idwet,2209)
SET(&iddryf,2246:0)
SET(&idwetf,2208:0)
ELSEIF(%&type% == "ironislands")
SET(#iddry,2248)
SET(#idwet,2210)
SET(&iddryf,2246:1)
SET(&idwetf,2208:1)
ELSEIF(%&type% == "vale")
SET(#iddry,2249)
SET(#idwet,2211)
SET(&iddryf,2246:2)
SET(&idwetf,2208:2)
ELSEIF(%&type% == "north")
SET(#iddry,2250)
SET(#idwet,2212)
SET(&iddryf,2246:3)
SET(&idwetf,2208:3)
ELSEIF(%&type% == "ocean")
SET(#iddry,2251)
SET(#idwet,2213)
SET(&iddryf,2246:4)
SET(&idwetf,2208:4)
ELSEIF(%&type% == "dorne")
SET(#iddry,2252)
SET(#idwet,2214)
SET(&iddryf,2246:5)
SET(&idwetf,2208:5)
ELSEIF(%&type% == "river")
SET(#iddry,2253)
SET(#idwet,2215)
SET(&iddryf,2246:6)
SET(&idwetf,2208:6)
ELSEIF(%&type% == "westerlands")
SET(#iddry,2254)
SET(#idwet,2216)
SET(&iddryf,2246:7)
SET(&idwetf,2208:7)
ELSEIF(%&type% == "arbor")
SET(#iddry,2255)
SET(#idwet,2217)
SET(&iddryf,2246:8)
SET(&idwetf,2208:8)
ELSEIF(%&type% == "stormlands")
SET(#iddry,2256)
SET(#idwet,2218)
SET(&iddryf,2246:9)
SET(&idwetf,2208:9)
ELSEIF(%&type% == "reach")
SET(#iddry,2257)
SET(#idwet,2219)
SET(&iddryf,2246:10)
SET(&idwetf,2208:10)
ELSEIF(%&type% == "sand")
SET(#iddry,2021)
SET(#idwet,2258)
SET(&iddryf,12:0)
SET(&idwetf,2012:1)
ELSE
SET(#iddry,2247)
SET(#idwet,2209)
SET(&iddryf,2246:0)
SET(&idwetf,2208:0)
ENDIF;}$$





$${LOG(Preparing gradient)}$$
$${LOG(Lower Slope)}$$
//expand 5 u,d
//replace $$[placeholder]&\|[air,$$[placeholderwater]][1][6]&<[air] red
$${WAIT(1)}$$
//replace $$[placeholder]&\|[red][1][6]&<[0] orange
$${WAIT(1)}$$
//replace $$[placeholder]&\|[orange][1][6]&<[0] yellow
$${WAIT(1)}$$
//replace $$[placeholder]&\|[yellow][1][6]&<[0] green
$${WAIT(1)}$$
$${LOG(Upper Slope)}$$
//replace $$[placeholder]&\|[![wool]][1][6]]&![\|[$$[placeholderwater]][1][5]]&![\|[air][1][5]]&<[air] pink|
$${WAIT(1)}$$
//replace $$[placeholder]&\|[pink][1][6]&<[0] purple
$${WAIT(1)}$$
//replace $$[placeholder]&\|[purple][1][6]&<[0] blue
$${WAIT(1)}$$
//replace $$[placeholder]&<[0] green
$${WAIT(1)}$$




$${LOG(Applying height profile)}$$
$${IF(%#profile% == 1)
ECHO(//replace [0&>[red]] [%#iddry%:0])
WAIT(1)
ECHO(//replace [0&>[orange]] [%#iddry%:1])
WAIT(1)
ECHO(//replace [0&>[yellow]] [%#iddry%:2])
WAIT(1)
ECHO(//replace [0&>[green]] [%#iddry%:3])
WAIT(1)
ECHO(//replace [0&>[blue]] [%#iddry%:4])
WAIT(1)
ECHO(//replace [0&>[purple]] [%#iddry%:5])
WAIT(1)
ECHO(//replace [0&>[pink]] [%#iddry%:6])
WAIT(1)

ELSEIF(%#profile% == 2)
ECHO(//replace [0&>[red]] [%#iddry%:0])
WAIT(1)
ECHO(//replace [0&>[orange]] [%#iddry%:0])
WAIT(1)
ECHO(//replace [0&>[yellow]] [%#iddry%:1])
WAIT(1)
ECHO(//replace [0&>[green]] [%#iddry%:1])
WAIT(1)
ECHO(//replace [0&>[blue]] [%#iddry%:2])
WAIT(1)
ECHO(//replace [0&>[purple]] [%#iddry%:2])
WAIT(1)
ECHO(//replace [0&>[pink]] [%#iddry%:3])
WAIT(1)

ELSEIF(%#profile% == 3)
ECHO(//replace [0&>[red]] [0])
WAIT(1)
ECHO(//replace [0&>[orange]] [%#iddry%:0])
WAIT(1)
ECHO(//replace [0&>[yellow]] [%#iddry%:0])
WAIT(1)
ECHO(//replace [0&>[green]] [%#iddry%:1])
WAIT(1)
ECHO(//replace [0&>[blue]] [%#iddry%:1])
WAIT(1)
ECHO(//replace [0&>[purple]] [%#iddry%:2])
WAIT(1)
ECHO(//replace [0&>[pink]] [%#iddry%:2])
WAIT(1)

ELSEIF(%#profile% == 4)
ECHO(//replace [0&>[red]] [%#iddry%:0])
WAIT(1)
ECHO(//replace [0&>[orange]] [%#iddry%:0])
WAIT(1)
ECHO(//replace [0&>[yellow]] [%#iddry%:1])
WAIT(1)
ECHO(//replace [0&>[green]] [%#iddry%:1])
WAIT(1)
ECHO(//replace [0&>[blue]] [%#iddry%:2])
WAIT(1)
ECHO(//replace [0&>[purple]] [%#iddry%:4])
WAIT(1)
ECHO(//replace [0&>[pink]] [%#iddry%:6])
WAIT(1)

ELSEIF(%#profile% == 5)
ECHO(//replace [0&>[red]] [%#iddry%:0])
WAIT(1)
ECHO(//replace [0&>[orange]] [%#iddry%:1])
WAIT(1)
ECHO(//replace [0&>[yellow]] [%#iddry%:2])
WAIT(1)
ECHO(//replace [0&>[green]] [%#iddry%:3])
WAIT(1)
ECHO(//replace [0&>[blue]] [%#iddry%:3])
WAIT(1)
ECHO(//replace [0&>[purple]] [%#iddry%:4])
WAIT(1)
ECHO(//replace [0&>[pink]] [%#iddry%:4])
WAIT(1)

ELSEIF(%#profile% == 6)
ECHO(//replace [0&>[red]] [%#iddry%:0])
WAIT(1)
ECHO(//replace [0&>[orange]] [%#iddry%:2])
WAIT(1)
ECHO(//replace [0&>[yellow]] [%#iddry%:4])
WAIT(1)
ECHO(//replace [0&>[green]] [%#iddry%:5])
WAIT(1)
ECHO(//replace [0&>[blue]] [%#iddry%:6])
WAIT(1)
ECHO(//replace [0&>[purple]] [%#iddry%:7])
WAIT(1)
ECHO(//replace [0&>[pink]] [%#iddry%:7])
WAIT(1)

ENDIF;}$$





$${IF(%#profile% > 0)
LOG(Applying wet mix)

IF(%#cutoff% > 2)
ECHO(//replace [%#iddry%&>[red]] #id[%#idwet%])
WAIT(1)
ENDIF;
IF(%#cutoff% > 3)
ECHO(//replace [%#iddry%&>[orange]] #id[%#idwet%])
WAIT(1)
ENDIF;
IF(%#cutoff% > 4)
ECHO(//replace [%#iddry%&>[yellow]] #id[%#idwet%])
WAIT(1)
ENDIF;
IF(%#cutoff% > 5)
ECHO(//replace [%#iddry%&>[green]] #id[%#idwet%])
WAIT(1)
ENDIF;
IF(%#cutoff% > 6)
ECHO(//replace [%#iddry%&>[blue]] #id[%#idwet%])
WAIT(1)
ENDIF;
IF(%#cutoff% > 7)
ECHO(//replace [%#iddry%&>[purple]] #id[%#idwet%])
WAIT(1)
ENDIF;
IF(%#cutoff% > 8)
ECHO(//replace [%#iddry%&>[pink]] #id[%#idwet%])
WAIT(1)
ENDIF;

WAIT(2)

IF(%#cutoff% == 0)
ECHO(//replace [$$[placeholder]] [%&iddryf%])
WAIT(1)
ECHO(//replace [35] [%&iddryf%])
WAIT(1)

ELSEIF(%#cutoff% == 1)
ECHO(//replace [$$[placeholder]] %&iddryf%\,%&idwetf%)
WAIT(1)
ECHO(//replace [35] [%&iddryf%])
WAIT(1)

ELSEIF(%#cutoff% == 2)
ECHO(//replace [$$[placeholder]] [%&idwetf%])
WAIT(1)
ECHO(//replace [%#iddry%&>[red]] #id[%#iddry%\,%#idwet%])
WAIT(2)
ECHO(//replace [red] %&iddryf%\,%&idwetf%)
WAIT(1)
ECHO(//replace [35] [%&idwetf%])
WAIT(1)

ELSEIF(%#cutoff% == 3)
ECHO(//replace [$$[placeholder]] [%&idwetf%])
WAIT(1)
ECHO(//replace [%#iddry%&>[orange]] #id[%#iddry%\,%#idwet%])
WAIT(2)
ECHO(//replace [35] [%&idwetf%])
WAIT(1)

ELSEIF(%#cutoff% == 4)
LOG(test)
ECHO(//replace [$$[placeholder]] [%&idwetf%])
WAIT(1)
ECHO(//replace [%#iddry%&>[yellow]] #id[%#iddry%\,%#idwet%])
WAIT(2)
ECHO(//replace [35] [%&idwetf%])
WAIT(1)

ELSEIF(%#cutoff% == 5)
ECHO(//replace [$$[placeholder]] [%&idwetf%])
WAIT(1)
ECHO(//replace [%#iddry%&>[green]] #id[%#iddry%\,%#idwet%])
WAIT(2)
ECHO(//replace [35] [%&idwetf%])
WAIT(1)

ELSEIF(%#cutoff% == 6)
ECHO(//replace [$$[placeholder]] [%&idwetf%])
WAIT(1)
ECHO(//replace [%#iddry%&>[blue]] #id[%#iddry%\,%#idwet%])
WAIT(2)
ECHO(//replace [35] [%&idwetf%])
WAIT(1)

ELSEIF(%#cutoff% == 7)
ECHO(//replace [$$[placeholder]] [%&idwetf%])
WAIT(1)
ECHO(//replace [%#iddry%&>[purple]] #id[%#iddry%\,%#idwet%])
WAIT(2)
ECHO(//replace [35] [%&idwetf%])
WAIT(1)

ELSEIF(%#cutoff% == 8)
ECHO(//replace [$$[placeholder]] [%&idwetf%])
WAIT(1)
ECHO(//replace [%#iddry%&>[pink]] #id[%#iddry%\,%#idwet%])
WAIT(2)
ECHO(//replace [35] [%&idwetf%])
WAIT(1)

ELSEIF(%#cutoff% == 9)
ECHO(//replace [$$[placeholder]] [%&idwetf%])
WAIT(1)
ECHO(//replace [35] [%&idwetf%])
WAIT(1)

ELSE
ECHO(//replace [$$[placeholder]] [%&iddryf%])
ECHO(//replace [35] [%&iddryf%])

ENDIF;

ENDIF;}$$





$${LOG(Script Complete)}$$
 

Thamus_Knoward

Shadowbinder
Now if you’re using the above fix, you’ll notice that I’m essentially filling large stretches with the ‘middle’ height of pebbles (green wool).

This in effect gives large flat patches after an initially steep gradient. After the middle height has been drawn one could improve this script by allowing the user to chose between a #simplex pattern or mild sine wave pattern created by generate.

That way we’d avoid these large flat sections.
 
  • Like
Reactions: CashBanks

Emoticone11

The Dark Lord Sauron
Staff member
I wonder if it’s possible to do an adaptive slope in a script. I tried doing something like that when I made the snow script, but it was very buggy so I ended up dropping it in the final version.