Workbench (REPL) widgets

SkookumScript commands can be typed and run on live projects on any platform using SkookumIDE command consoles called Workbenches – a form of a “read evaluate print loop” (REPL).

More sophisticated SkookumScript commands are often grown and tested in a Workbench before they are wrapped up into reusable routines. People also swap around these handy code snippets via chat, email, posting online, etc.

The initial Workbench

The Workbench widgets in their initial state. Note the lock symbol on the Tutorial Workbench icon indicating that it is read-only.

The basics

Several of the script examples on this page assume that you are using the SkookumScript Unreal Engine 4 Plugin and the example SkookumDemo project that comes with it though many of the examples will work for any project. See the Unreal Engine 4 Plugin installation and setup instructions for more information.

Ensure that the runtime is started when you evaluate commands. If the runtime is not started, various game data may not be available or the commands may queue until it starts.

In a long-standing programming language manual tradition, we begin with the minimal amount of code needed to print something:

"Hello, world!"

The SkookumIDE Workbench widgets allow you to run code snippets on any connected and running project. Type (or copy and paste) "Hello World!" into the Workbench (REPL), then open the Build menu option and select Execute Selection on Runtime.

In the Log widget you should see the following printed out:

"Hello, world!"

Simple, eh? No includes, mucking about with files, making classes, nor other typical language red-tape or bookkeeping is needed.

SkookumScript prints out the result of any script that is evaluated (or executed, run, called, invoked – whatever term you prefer).

NOTE You may have noticed that there are two Workbench widget tabs - the REPL and the Tutorial. The REPL Workbench is writeable, saves your changes and reloads with each launch of the SkookumIDE. The Tutorial Workbench is read-only since the example code snippets that it contains are frequently changed by the mad computer scientists on the SkookumScript team and any of your own custom code snippets would be lost with each update. You can still evaluate the Tutorial example code snippets or copy them over to the REPL Workbench if you want to edit them.

TIP: F4 is a keyboard shortcut for evaluating code. You can place the edit caret anywhere on a line and press F4 to evaluate the whole line or select some text even across multiple lines and whatever is selected is evaluated when you press F4. Using these mechanisms, you can evaluate any selection or line of text that parses correctly.

You can enter math into a Workbench. The SkookumIDE is a great calculator.

6 * 7

42

This is actually really useful. We mad computer scientists use it all the time.

NOTE Only the last expression is displayed as a result in the Log widget. Any results from expressions before the last expression will be ignored. If you want to use or see results from expressions that come before the last expression, you will have to store them in variables, pass them as arguments to routines or print them out.

All commands entered into a Workbench are run on an instance of the SkookumScript project’s default Master Mind object - it is the inferred this for any command. So when you evaluate code, it looks for routines and data in the project’s default Master Mind object and then its superclass and then the superclass of its superclass and so on. Every project can have its own customized Mind object.

Also see Mind objects and the Master Mind for more information on mind objects.

TIP: Since a result is often something you want to reuse, there are some hotkeys to help you copy it around:

  • Alt+Insert pastes the most recent result string into the currently focused Workbench widget at the caret position
  • Alt+Ctrl+Insert copies the result string from the currently focused Workbench widget into the system clipboard

What is a REPL?

Many languages call the ability to interactively evaluate code a read-eval-print loop or REPL and it is traditionally accomplished with a simple interactive command line prompt >. These REPLs read in a command, evaluate it, print out the result (usually in the same console) and then loop all over again with any new command. Since this is the 21st Century, SkookumScript’s Workbench widgets use a more sophisticated text edit box that can accommodate multiple lines of commands and easily allow you to scroll around. It also saves all your commands for later use, which can be very handy. While this is more than a traditional REPL, we still call it a REPL since it serves essentially the same purpose and people looking for a “REPL” will find it when searching information about the SkookumIDE.

You can also call SkookumScript on the command prompt if you need to – for example to evaluate SkookumScript commands from non-SkookumSript applications such as text editors. See the SkookumIDE Command line arguments page.

Printing to the Log

If you would like to print something other than a result to the Log widget, you can put it in a println() command, which prints out one or more objects and adds a line break (hence the ln for “line” at the end). Printing is an essential and versatile tool for any programmer’s toolbox.

println("Hello, world!")

Evaluates to:

Hello, world!

nil

While the result of the simple 6 * 7 earlier was the number 42, the result of a println() command is nil. nil is an object that essentially represents nothing or the absence of a value.

Nihilistic notation

nil, or something like it, is common in many languages (Smalltalk, Lisp, Objective-C, Ruby). It goes by several different names, such as NULL (C), nullptr (C++), and null (JavaScript).

TIP Those with a sharp eye may have noted that the println() command prints out one or more objects.

println("The answer: " 42 " and running on object: " this)

The answer: 42 and running on object: 'SkookumDemo'

nil

You can cram in as many arguments to println() as you like and they are all converted to strings and joined together. All SkookumScript objects know how to convert themselves to a string to get a textual representation of themselves.

The println() command above is called with 3 arguments: a string, a number and another string. SkookumScript arguments do not need to be separated with commas , – the parser automatically figures out where one argument ends and another begins. You can use commas if you think it makes things easier to read, though they aren’t necessary.

When you evaluate several commands at once, they will be called one after the other (in sequence).

println("Hello, world!")
println("I'll be your megalomaniacal overlord for today.")

SkookumScript also does not need an end of line or end of expression / statement delimiter such as a semi-colon ; like most C/C++ languages. Some amount of whitespace between each expression is all that is needed.

Select both lines with the mouse or keyboard, and then press F4.

Hello, world!
I'll be your megalomaniacal overlord for today.

nil

Also check out the Debug class in the SkookumIDE by selecting it using the Classes widget and look at its members in the Members widget for more routines that print out to the Log widget.

Workbench Files

The file that stores the text for the Workbench (REPL) widget is located at SkookumIDE\Scripts\workbench.sk and it is saved with your latest changes every time you evaluate a line of code or after some time passes since it was changed. You can also save it manually by pressing Ctrl+S or by shutting down the SkookumIDE.

If you open the context menu for a Workbench, you can select Show in Explorer (or use the hotkey Cltr+Shift+E) to open up the file in the Windows File Explorer or Open in External Editor (or use the hotkey Cltr+E) to open up the file in Your Favorite Editor™. It will use whatever default application that you associate with the SkookumScript .sk file extension.

The file for the Tutorial Workbench is located at SkookumIDE\Scripts\tutorial-workbench.sk and it can also be opened up in an external editor or via the Windows File Explorer by using the context menu or hotkeys.

All these handy Workbench code snippets are reloaded and ready to be reused the next time you run the SkookumIDE.

Language warnings and errors

Errors are something you must embrace if you wish to become an awesome coder. No matter how awesome you get, you will always have errors—though your errors will become progressively more interesting.

The SkookumIDE will helpfully alert you whenever it detects what may be an error and helps visualize it with, different colorizations, red underlines, mouse hover info and more.

Since errors will be a frequent companion and patient teacher, we recommend that you take a look at the Parser error checking page and then return filled with knowledge of error notification goodness.

Work through the “Tutorial Workbench”

Here is a simple command to ensure your project is running in the UE4 editor and it is ready to receive commands from the SkookumIDE:

player_pawn.actor_location

Paste/type the command into the Workbench (REPL), have the caret on the line or select/highlight the text and press F4. The location of the player should be printed in the Log widget and will look something like this:

(770, -345, 322.25)

Now take a moment and read through these examples illustrating some basic concepts of SkookumScript. All these examples can be found already entered in the Tutorial Workbench.

Core SkookumScript Principles
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Simple Math

// Select this expression (or just have the caret on the line)
// and press F4 (to evaluate on game runtime)
4 + 5

// The result will be printed in the IDE log window.
// Alt+Insert         - pastes the last result
// Alt+Control+Insert - copies the last result to the clipboard

// Do the same with any of these:
3.0 + 4.5
6 < 4
7 ~= 8 // Not equal

// SkookumScript has no operator precedence!
// Use [square] brackets to group sub-expressions.
[2 + 2] = 4
Variables!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// The exclamation mark ! indicates that something “new” is
// being created. This creates variable 'bob' and binds it
// to the Integer 9 and bob has the type Integer
!bob : 9

// re-assigns the value 7 to bob, bob stays an Integer
bob := 7

// re-binds bob to "Hello World!"
// bob type is now a String!
bob : "Hello World!"

// Note that all the lines must be selected. The `bob`
// variable will only exist in a selected code snippet
// that has `!bob`.
Randomness, class types and lists
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// SkookumScript is a strongly typed language, BUT variables
// can have multiple possible types! The compiler will
// remember the possible types a variable can have, and make
// sure all types are always compatible.

// Note that when a method (function) takes no arguments,
// you can omit the parentheses: method() = method
!var: if @@random.coin_toss [42] else ["stuff"]
// The type of var is now <Integer|String>

// Lists of stuff - separated by whitespace (comma optional)

// Reverses the order of this list's items
{1 2 3 4 5}.reverse

// Applies the method 'negate' to all items of the list,
// returns the result
{1 -2 3 -4}%negate

// Runs some code - a 'closure' - on each item of the list
{1 -2 3 -4}.do[item.negate]

// unless is short for 'if not'
{1 2 3}.do[item++ unless item = 2]

// Make new list based on a selection criteria (again that
// code in square brackets is a closure)
{3 4 5 8}.select[item.pow2?]

// Make new list by rejecting some items of the original list
{3 4 5 8}.reject[item.pow2?]

// Select random item from list
{"A" "B" "C" "D"}.any

// Test if _any_ item from list satisfies a condition
{3 4 5}.any?[item.pow2?]

// Test if _all_ items from list satisfies a condition,
// false here
{3 4 5}.all?[item.pow2?]

// Same, true in this case
{2 4 8}.all?[item.pow2?]
Loops and control statements
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Do something four times
4.do[println("Hello World!")]

// Conditional
if 6>9 [println("Six is greater than nine!")]

// if statement can be used as an expression
// (so can everything)
println(if 5>4 ["Correct"] else ["Not correct"])

// "elseif" is done by using a single if with
// multiple clauses
!a: 8
if a=7  [println("a is 7")]
   a=8  [println("a is 8")]
   else [println("a is neither 7 nor 8")]

// when is another way of expressing a conditional    
println("Tail!") when @@random.coin_toss

// unless = "when not"
println("Head!") unless @@random.coin_toss
Coroutines!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Coroutines are the secret sauce of SkookumScript.
// IMPORTANT: The IDE must be connected to the
// started project for this to work when pressing F4.

// Waits 2 seconds then returns
_wait(2)

// Waits 1 second four times and prints "Tick!"
// after each wait
4._do [_wait(1) println("Tick!")]

// Wait random time between .1 and 2 seconds
// Note that commas are not required between arguments
_wait(@@random.uniform_range(.1 2))

// Wait one simulation frame
_wait
Vector math examples
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// SkookumScript comes with built-in vector math types
// Check out the classes Vector2, Vector3, Vector4,
// Rotation, RotationAngles, Transform, Color

// Creates a 3D vector with x=1,y=2,z=3
Vector3!xyz(1 2 3)

// Adds two vectors
Vector3!xyz(1 2 3) + Vector3!xyz(2 3 4)

// A handy class constant (static data member) of Vector3
Vector3.@@direction_up

// Creates a set of Euler angles to specify a rotation
RotationAngles!yaw_pitch_roll(60 0 0)

// A transform has a position, a rotation and a scale:
Transform!position_rotation_scale(
  Vector3!xyz(1 2 3)
  RotationAngles!yaw_pitch_roll(60 0 0).Rotation
  Vector3!xyz(1 1 1))

// We've got colors, too
Color.@@yellow
UE4 Engine bindings
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// Ok now let's have some fun!
// NOTE: Make sure you have the project SkookumDemo running!

// Select the code below and press F4 multiple times!!!
// Gets list of all Pawns in the world, and runs the
// coroutine _boom on them
Pawn.instances%_boom

// Click on the word _boom and press Alt-G to see how it is
// implemented! Use your mouse previous and next buttons or
// Alt+<- Alt+-> to navigate back and fourth in the history.

// The currently active game world
// (or nil if the game is not running)
@@world

// Is the game running?
not @@world.nil?

// List of levels of the game world
@@world.@levels

// Get the player pawn
player_pawn

// Instruct RoboChar1 to path towards the player
Enemy@'RoboChar1'._path_to_actor(player_pawn)

// Get an actor by name, and find something out about it
// Note that if a class is not used before @ it assumes
// class Actor
@'RoboChar1'.transform

// Play explosion sound at a given actor location
@'TargetPoint1'._play_sound("/Game/ScriptLoaded/Audio/Explosion01.Explosion01")

// Draw text above player
player_pawn._draw_text("Play me!")

// Send random robot to player
Enemy.instances.any._path_to_actor(player_pawn)

// Play _boom effect on random enemy
Enemy.instances.any._boom

// Reset the robots to their corners
robo_reset
Time flow and concurrency
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// The secret sauce of SkookumScript - elegantly and
// concisely describe concurrent and sequential behavior

//------------------------------------------
// Time flow example 1: Sequential
// Have each robot path to player one after the
// other then have player go boom.
// Select and run the next four lines
Enemy@'RoboChar1'._path_to_actor(player_pawn)
Enemy@'RoboChar2'._path_to_actor(player_pawn)
Enemy@'RoboChar3'._path_to_actor(player_pawn)
player_pawn._boom

// Then select and reset when complete
robo_reset


//------------------------------------------
// Time flow example 2: Synchronize
// Have each robot path to player simultaneously
// then have player go boom after they *all* arrive.
// Select and run the next seven lines
sync
  [
  Enemy@'RoboChar1'._path_to_actor(player_pawn)
  Enemy@'RoboChar2'._path_to_actor(player_pawn)
  Enemy@'RoboChar3'._path_to_actor(player_pawn)
  ]
player_pawn._boom

robo_reset  // Reset the robots


//------------------------------------------
// Time flow example 3: Race
// Have each robot path to player simultaneously
// then have player go boom when the *first* arrives.
// Select and run the next eight lines
race
  [
  Enemy@'RoboChar1'._path_to_actor(player_pawn)
  Enemy@'RoboChar2'._path_to_actor(player_pawn)
  Enemy@'RoboChar3'._path_to_actor(player_pawn)
  ]
// Stop movement of other robots
Enemy.instances%path_stop
player_pawn._boom

robo_reset  // Reset the robots


//------------------------------------------
// Time flow example 4: Timeout
// Have each robot path to player simultaneously then
// have player go boom when the *first* arrives or when
// 1.5 seconds elapses.
// Select and run the next eight lines
race
  [
  _wait(1.5)
  Enemy@'RoboChar1'._path_to_actor(player_pawn)
  Enemy@'RoboChar2'._path_to_actor(player_pawn)
  Enemy@'RoboChar3'._path_to_actor(player_pawn)
  ]
// Stop movement of other robots  
Enemy.instances%path_stop
player_pawn._boom

robo_reset  // Reset the robots


//------------------------------------------
// Act on lists of objects using different timeflow

// Sequential: all robots boom one after the other
// then go to next line after the last completes
Enemy.instances._do[item._boom]

// Synchronize: All robots boom simultaneously then
// go to next line when *all* completed
Enemy.instances%_boom

// Race: All robots boom simultaneously then go to
// next line when *first* completed
Enemy.instances%>_boom


//------------------------------------------
// So here are the previous path commands rewritten
// using lists rather than specifying the robots
// individually.

// All robots path to player one after the other
// and boom player when last arrives
Enemy.instances._do[item._path_to_actor(player_pawn)]
player_pawn._boom

robo_reset  // Reset the robots


// All robots path to player simultaneously
// and boom player when the all arrive
Enemy.instances%_path_to_actor(player_pawn)
player_pawn._boom

robo_reset  // Reset the robots


// Have each robot path to player simultaneously
// and boom player when first arrives
Enemy.instances%>_path_to_actor(player_pawn)
player_pawn._boom

robo_reset  // Reset the robots
More UE4 examples
1
2
3
4
5
6
7
8
// Get averaged location of all enemies
GameLib.actor_array_average_location(Enemy.instances)

// Gets list of actors of class 'TargetPoint'
!actors: Actor{}
GameLib.all_actors_of_class(
  @@world TargetPoint.static_class actors)
actors

You can see more SkookumScript code examples at Rosetta Code which also shows the same examples in many other languages for comparison.

Get more in-depth with the SkookumScript language by going to (or returning to) the SkookumScript Primer.