Skip to content

Instantly share code, notes, and snippets.

@SalatielSauer
Last active August 22, 2024 23:40
Show Gist options
  • Save SalatielSauer/7a12c5c5e6df4f92cefe904057242390 to your computer and use it in GitHub Desktop.
Save SalatielSauer/7a12c5c5e6df4f92cefe904057242390 to your computer and use it in GitHub Desktop.
CubeScript workaround to deform edges of cubes via commands in Sauerbraten's map editor.

Since the current Sauerbraten version does not have commands to select a specific face or an edge of a cube, this script uses some workarounds to achieve a similar result using the existing commands, not requiring a modified client (thus being slower than it would be if implemented natively).

Installation

Just download and place the cubedeformer.cfg file in the Sauerbraten root folder and run /exec cubedeformer.cfg ingame, the cubedeformer_examples.cfg file is optional.

Face & Edge commands

  • gotoface f [c] s Teleports to face f (0..3) and executes [c]. s is an optional delay that gives time for the gotoface to finish before running another gotoface (otherwise some sync problems may occur), default: 500ms when there is an nested gotoface or 1ms when there is none.

  • gotoedge e [c] b Teleports to the edge e (0..3) of the face being looked at and executes [c]. b (0/1) is optional and determines whether the gridpower should be changed to an equivalent of the selected edge.

  • moveseldo x y z c Adds x, y and z to the position of the current selection, executing [c] every move.

  • getlookatface Returns the face the camera is looking at relative to the world (not the selected face).

Native commands

The commands below are already available in the Sauerbraten 2020 edition natively.

  • editface d t t determines whether the face (0), selection (1) or edge (2) of a cube will be pushed to d.

  • flip Inverts selected geometry.

  • movesel a d d determines whether the selection will be moved to x (0), y (1) or z (2) a times.

  • rotate d Rotates once left (-1) or right (1) relative to the selected face.

  • setselpos x y z Sets the position of the current selection to x y and z or creates a new one.

Limitations and their workarounds

There are two limitations you should be aware of:

  • In order for the native editface command to work properly (you will use it to push an edge or a face), the camera needs to be slightly aligned to the horizon (the camera's pitch and yaw), you don't have to worry about the camera's yaw (horizontal) value, gotoface will fix it automatically, however the only way to correct the pitch automatically is to respawn the player. You can use the gotoface_fixpitch f [c] shortcut or simply move the camera closer to the horizon manually.

  • For the same reason mentioned above, you cannot access the top or bottom face of the selection (+Z and -Z) directly with gotoface, to make changes to them you will have to rotate the geometry. Example of how to apply colors to the top and bottom face of a geometry:

gotoface 2 [ // go to a side face (2, -Y).
  rotate 1 // rotates the cube so that the top face becomes a side face (0, +X).
  gotoface 0 [ // go to the "top" face which is now temporarily on the side.
    allfaces 0 // makes sure that only the selected face has its color changed.
    vcolor 1 0 0 // sets the red color.
    flip // flips the geometry to access the "bottom" face.
    vcolor 0 0 1 // sets the blue color.
    gotoface 2 [ // go back to the side face (2, -Y).
      rotate 1 // returns the bottom and top faces to their initial rotation.
    ]
  ]
]

Regarding other limitations or problems you may run into, usually a half-second sleep fixes them :P

Shortcuts

Although handy, the commands below might not be a good idea if you want to have absolute control over gotoface and gotoedge.

  • gotoface_fixpitch f [c] Respawns the player to fix the camera orientation and calls gotoface with f and [c] as its parameters.

  • pushedge e d [c] Uses gotoedge and editface to go to an edge and push it d times, [c] is executed at the end.

  • editfaceedge f e d [c] Uses gotoface (f), pushedge (e) and editface (d), with the difference that it handles +Z (4) and -Z (5) faces automatically (rotating them back and forth).

Keep in mind that you want to avoid anything involving gotoface and gotoedge as much as possible, most of the time you only need to change faces twice and edge only once, everything else can be handled with flip and rotate.

Examples

See more examples in the cubedeformer_examples.cfg file.

// Cube Deformer functions, by @Salatiel#5274
// version: 23/01/2022
// indexes: [+x, -x, +y, -y]
// [[movesel x, movesel y], [movesel dir, movesel dim]], camera yaw
.selfacesheet = [
[[1 1][-1 1] 90]
[[0 0][1 1] -90]
[[1 0][1 0] 180]
[[0 1][-1 0] 0]
]
// indexes: [+x, -x, +y, -y]
// [movesel dim, [[TL x, TL y, TL z], [TR x, TR y, TR z], [BL x, BL y, BL z], [BR x, BR y, BR z]]]
//
// _face 3_
// |TL TR|
// | |
// | |
// |BL____BR|< this is always the origin
//
.selcornersheet = [
[[1 1 1][1 0 1][1 1 0][1 0 0]]
[[0 0 1][0 1 1][0 0 0][0 1 0]]
[[0 1 1][1 1 1][0 1 0][1 1 0]]
[[1 0 1][0 0 1][1 0 0][0 0 0]]
]
// 0: +x, 1: -x, 2: +y, 3: -y
getlookatface = [
local dir
dir = (mod (round (divf (getcamyaw) 90)) 4)
result (substr [3021] (? (= $dir -1) 3 $dir) 1)
]
editinview 0; nompedit 0 // their default values ​​prevent some commands from working properly
// gotoface <face 0..3> [command] <optional delay to fix gotoface callback (default: 500ms)>
gotoface = [
local selpos face
selpos = (getselpos)
if (|| (=s $arg1 "") (= $arg1 -1)) [arg1 = (getlookatface)]
face = (at $.selfacesheet $arg1)
if $editing [edittoggle]
edittoggle
sleep 0 [entdrop @entdrop]
entdrop 0
entcancel
newent teledest; entset teledest (at $face 2) -31349 0 0 -31349
newent teleport -31349 -1 31349 -1 -31349
edittoggle
sleep 0 [
edittoggle
entselect [|| (=s (entget) (
concat "teledest" @@(at $face 2) -31349 0 0 -31349
)) (=s (entget) (
concat "teleport" -31349 -1 31349 -1 -31349
))]
delent
setselpos @[selpos]
movesel @(at (at $face 0) 0) 1
movesel @(at (at $face 0) 1) 0
selpos = (getselpos)
gridpower (- $gridpower 1)
sleep 0 [
setselpos @[selpos]
movesel (at (at (at $.selfacesheet @@arg1) 1) 0) (at (at (at $.selfacesheet @@arg1) 1) 1)
movesel 1 2
gotosel
cancelsel
gridpower (+ $gridpower 1)
sleep 0 [
setselpos @@@[selpos]
reorient
if (!= (strstr [@@@@arg2] "gotoface") -1) [
sleep (? (< $numargs 3) @@@@arg3 500) [@@@@@arg2]
] [
sleep 0 [@@@@@arg2]
]
]
]
]
]
// gotoedge <edge 0..3> [command]
gotoedge = [
local face.index corner.index corner.xyz selpos
face.index = (getlookatface); corner.index = $arg1
corner.xyz = (at (at $.selcornersheet $face.index) $corner.index)
selsave
selpos = (getselpos)
gridpower (max (- $gridpower 1) 0)
sleep 0 [
setselpos @[selpos]
movesel @(at $corner.xyz 0) 0
movesel @(at $corner.xyz 1) 1
movesel @(at $corner.xyz 2) 2
gotoselcenter 0 [
if (!= @@arg3 1) [
gridpower (+ $gridpower 1)
setselpos @@[selpos]
selswap
]
sleep 0 [@@@arg2]
]
]
]
// shortcuts
// same as movesel, but executes a command every move.
// moveseldo x y z [command] <straight line 0/1>
moveseldo = [
local x y z
x = 0; y = 0; z = 0
loop s (+ (+ (abs $arg1) (abs $arg2)) (abs $arg3)) [
if (= $s 0) [arg4]
if (!= $x $arg1) [
movesel (? (< $arg1 0) -1 1) 0
x = (+ $x 1)
]
if (!= $y $arg2) [
movesel (? (< $arg2 0) -1 1) 1
y = (+ $y 1)
]
if (!= $z $arg3) [
movesel (? (< $arg3 0) -1 1) 2
z = (+ $z 1)
]
arg4
]
]
// shortcut for gotoedge and editface.
// pushedge <edge 0..3> <depth 0..8> [command]
pushedge = [
gotoedge $arg1 [
editface @arg2 2
@arg3
]
]
// shortcut for gotoface, gotoedge and editface which takes care of +Z(4) and -Z(5) faces automatically (by rotating the cube).
// editfaceedge <face 0..5> <edge 0..3> <depth 0..8> [command A] [command B]
// if face is 4 or 5, "command A" is executed during the process of rotating the cube to access them.
editfaceedge = [
if (<= $arg1 3) [
gotoface $arg1 [
gotoedge @arg2 [
editface @@arg3 2
sleep 0 [@@@arg4]
]
] 400
] [
.z.direction = (? (= $arg1 4) 1 -1)
gotoface 0 [
rotate $.z.direction
gotoface 3 [
gotoedge @@arg2 [
editface @@@arg3 2
@@@arg4
gotoface 0 [
rotate (* $.z.direction -1)
@@@@arg5
]
]
] 400
]
]
]
// similar to gotosel, but uses entautoview to go to the center of the selection.
// gotoselcenter <dist> [command]
gotoselcenter = [
entcancel
sleep 0 [entautoviewdist @entautoviewdist; entdrop @entdrop; @arg2]
entdrop 2
newent base -31349
entautoviewdist $arg1
entautoview
delent
]
// ¯\_(ツ)_/¯
gotoface_fixpitch = [
local face
face = (? (= $arg1 -1) (getlookatface) $arg1)
if $editing [edittoggle]
suicide
sleep 100 [respawn; sleep 100 [edittoggle; gotoface @@face [@@@arg2] @@arg3]]
]
// Cube Deformer examples, by @Salatiel#5274
// version: 23/01/2022
.cubedeformer.arrowplane = [
allfaces 0
gotoface 0 [
gotoedge 0 [
editface 4 2
flip
editface 4 2
rotate 1
editface 4 2; flip; editface 4 2
rotate 1
editface 4 2; flip; editface 4 2
gotoface 2 [
vcolor 0 0 1
gotoedge 0 [
editface 8 2
rotate 1
gotoface 1 [
gotoedge 0 [
editface 8 2
vcolor 1 0 0
copy
movesel 1 1
movesel -1 0
paste
rotate 1
gotoface 2 [
reorient
editface -2 0
flip
editface -2 0
gotoface 1 [
rotate -1
]
]
]
]
]
]
]
]
]
.cubedeformer.pyramid_slow = [
allfaces 0
local faces
faces = [[0 0 4][0 1 4][3 0 4][3 1 4][1 0 4][1 1 4][2 0 4]]
loop face 7 [
sleep (* $face 300) [
editfaceedge @(at $faces $face) [
vcolor @@(at [[1 0 0][0 1 0][0 0 1][1 0 1][1 1 0][0 1 1]] (at $faces $face))
]
]
]
]
.cubedeformer.pyramid_fast = [
gotoface 0 [
gotoedge 0 [
loop r 2 [
editface 4 2
rotate -1
editface 4 2
rotate 1
flip
]
sleep 400 [
gotoface 3 [
gotoedge 0 [
loop r 2 [
editface 4 2
flip
]
]
]
]
]
] 0
]
.cubedeformer.twistedcolumn_slow = [
allfaces 0
loop column 8 [
sleep (* $column 2400) [
loop face 4 [
sleep (* $face 600) [
gotoface @face [
gotoedge 1 [
local facecolor
facecolor = (at [[0.11 0.71 0.57][1 0.51 0.11][1 0 0.67][0.11 0.10 0.11]] @@@face)
vcolor (at $facecolor 0) (at $facecolor 1) (at $facecolor 2)
vscroll 0 10000
editface (? (= @@@@@column 0) 1 2) 2
if (= @@@face 3) [
copy; movesel 1 2; paste
gotosel
gotoface 3 [
rotate 1; rotate 1; flip
] 0
]
]
] 0
]
]
]
]
]
.cubedeformer.twistedcolumn_fast = [
gotoface 0 [
gotoedge 1 [
loop e 2 [
editface 1 2
if (= $e 0) [
loop r 3 [rotate 1]
flip
]
]
loop c 8 [
copy
movesel 1 2
paste
editface 1 2
rotate -1
editface 1 2
flip
loop r 2 [
rotate -1
editface 1 2
]
rotate -1
flip
]
movesel -8 2
sleep 500 [
gotoface 2 [
gotoedge 1 [
editface 1 2
rotate 1
flip
editface 1 2
rotate -1
flip
copy
loop c 3 [
movesel 1 2
editface (+ $c 2) 2
rotate 1
flip
editface (+ $c 2) 2
rotate 1
editface (+ $c 1) 2
flip
rotate 1
editface (+ $c 1) 2
rotate 1
]
selextend
copy
movesel 4 2
paste
rotate 1; rotate 1
copy
editdel
movesel -3 0
paste
selextend
]
]
]
]
] 0
]
.cubedeformer.ramp = [
gotoface_fixpitch 0 [
gotoedge 2 [
editface 5 2
gotoedge 3 [
editface 5 2
copy
movesel 1 0
paste
gotoedge 0 [
editface 5 2
gotoedge 1 [
editface 5 2
gotoedge 2 [
editface 8 2
gotoedge 3 [
editface 8 2
copy
movesel -2 0
movesel 1 2
paste
gotoedge 2 [
editface -8 2
gotoedge 3 [
editface -8 2
copy
movesel 1 2
paste
editface 5 0
movesel -2 2
movesel 2 0
gotoface 2 [
reorient
rotate -1
movesel -1 0
rotate -1
movesel -1 0
editface 1 1
editface -1 1
]
]
]
]
]
]
]
]
]
] 0
]
.cubedeformer.cylinder_slow = [
allfaces 1
gotoface_fixpitch 0 [
editfaceedge 0 1 2 [
editfaceedge 0 3 2 [
editfaceedge 3 0 2 [
editfaceedge 3 2 2 [
vcolor 1 0 0
copy; movesel 1 1; paste; flip
gotoface 0 [
gotoselcenter 0 [
vcolor 0 1 0
copy; movesel -1 0; paste; flip
gotoface 2 [
gotoselcenter 0 [
vcolor 0 0 1
copy; movesel -1 1; paste; flip
vcolor 1 0 1
]
] 0
]
]
]
]
]
]
]
]
.cubedeformer.cylinder_fast = [
gotoface 0 [
gotoedge 1 [
editface 2 2
rotate -1
editface 2 2
rotate 1
sleep 400 [
gotoface 3 [
gotoedge 0 [
editface 2 2
rotate 1
editface 2 2
rotate -1
copy
movesel 1 1
paste
flip
selextend
copy
movesel -1 0
paste
rotate 1; rotate 1
]
]
]
]
] 0
]
.cubedeformer.smoothercylinder = [
gotoface_fixpitch 0 [
gotoedge 1 [
gotoedge 1 [
gridpower (+ $gridpower 1); reorient
sleep 0 [
editface 8 2
rotate -1
editface 8 2
rotate -1
editface 2 2
rotate -1
editface 2 2
rotate -1
movesel 1 1
editface 2 2
rotate -1
editface 2 2
rotate 1
sleep 300 [
gotoface 3 [
selextend
gotoedge 1 [
editface 2 2
rotate -1
editface 2 2
rotate 1
movesel -1 0
editface 2 2
rotate -1
editface 2 2
rotate -1
movesel 1 1
selextend
copy
movesel 2 1
paste
flip
selextend
copy
movesel -2 0
movesel -1 2
paste
rotate 1; rotate 1
selextend
copy
movesel -1 2
paste
]
]
]
]
] 1
] 1
] 0
]
.cubedeformer.emerald = [
allfaces 1
gotoface_fixpitch 0 [
gotoedge 0 [
editface 4 2
gotoedge 1 [
editface 4 2
gotoedge 3 [
editface 4 2
gotoface 1 [
gotoedge 0 [
editface 4 2
gotoedge 1 [
editface 4 2
gotoedge 2 [
editface 4 2
gotoface 3 [
gotoedge 0 [
editface 8 2
gotoedge 2 [
editface 4 2
alpha; valpha 0.8 1; vscroll 0 999
flip; rotate 1; rotate 1; copy; vcolor 1 0 0
movesel -1 1; paste; flip; copy; vcolor 0 1 0
gotoselcenter 0
movesel 1 2; paste; rotate 1; rotate 1; copy; vcolor 0 0 1
movesel 1 1; paste; flip; vcolor 1 0 1
]
]
] 300
]
]
]
] 300
]
]
]
] 300
]
.cubedeformer.pankubox = [
allfaces 1
gotoface_fixpitch 0 [
vcolor 0 0 1
editface 4 0
gotoedge 0 [
editface -4 2
gotoedge 3 [
editface 4 2
rotate 1
gotoface 3 [
gotoedge 2 [
allfaces 0
vcolor 1 1 0
editface 3 2
flip
vcolor 0 1 0
flip
gotoface 0 [
rotate -1
gotoface 3 [
gotoedge 0 [
reorient
editface 4 2
gotoedge 2 [
editface 4 2
copy
movesel 1 1
paste
flip
selextend
copy
movesel -1 0
paste
gotoface 1 [
movesel 1 1
selextend
flip
movesel 1 0
selextend
copy
movesel 1 2
paste
rotate 1; rotate 1
copy
editdel
movesel -1 2
paste
] 150
]
]
] 300
] 500
]
] 400
]
]
] 300
]
.cubedeformer.halfclip = [
gotoface_fixpitch 0 [
gotoedge 1 [
editface 7 2
gotoedge 3 [
editface 7 2
gotoface 3 [
gotoedge 0 [
editface 8 2
gotoedge 2 [
editface 8 2
gotoface 0 [
gotoedge 1 [
editface 1 2
gotoedge 3 [
editface 1 2
rotate -1
allfaces 1
alpha
valpha 0.001
sleep 0 [
movesel -1 0
editface 1 1; editface -1 1; vcolor 0.2 0.2 0.2; editface 7 0
allfaces 0
vcolor 1 0 0; vscroll 0 999; flip
vcolor 0 1 0; vscroll 999
]
]
]
]
]
]
] 400
]
]
] 400
]
@SalatielSauer
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment