I've been working for some time on improving Timeslip's Script Editor, as well as sslc compiler and Anchorite's int2ssl decompiler.
Many cool new features were already implemented by Timeslip, some didn't work though, so I had to fix them.
Now I want to introduce some of the new SSL capabilities to all scripters out there.
There are quite a few features added, so I guess it's a different language now (btw. SSL stands for "Star Trek Scripting Language")
Following features are vanilla-compatible, does not require Sfall to be interpreted by the game:
Arrays-related, sfall required:
You can read all about array syntax here. Only recent addition to that stuff was a chained array dereferencing using a sequence of dots and brackets. This way you can use multi-dimensional arrays easily.
Now some details about most notable (IMO) features.
Answer to "what the hell is this?" can be found here. In Fallout scripts, unlike most languages, logical operators AND, OR were compiled just like any other arithmetic operator. That means every part of such expressions was evaluated, even when result is already known.
Why this is important? It's simple, consider this real example from Restoration Project code, dude_obj script:
What is wrong with this code? Nothing, it looks pretty logical. However, it breaks the script and produces hundreds of errors every minute in debug.log. Why? Because all parts are evaluated, even when current map is not New Reno, it still calls "map_var", which fails with message "map var out of range". The problem is in the compiler itself.
This new feature fixes this and makes such conditions work like you expect. It is currently optional in compile.exe and disabled by default. It can be enabled by -s parameter (or a checkbox in latest Script Editor settings dialog).
As a side effect of how this works, AND, OR operators now return one of the arguments as a result of expression, instead of always 1 or 0:
Because it was taken from python, you can read everything here. It works exactly the same in SSL+, providing you with a little convenience and another option to branch code execution within expressions.
Break/Continue statements
Another important thing, IMO. I really missed this on several occasions, even added "foreach (x in y while z)" syntax in foreach loops, just because there were no break. Later I discovered that it is possible, and pretty easy to implement, using existing fallout scripting opcodes.
Example:
NOTE: continue, like in other languages, will properly respect increment operator in for and foreach loops so it will be executed before next iteration starts.
"For" Loops
Well, one example of such loop is above. Should be pretty obvious for everyone who used C++/C#/PHP/JavaScript or similar language. It was originally made by Timeslip, I only fixed a couple of bugs related to them and made it possible to use parentheses around parameter list.
"Switch" Statements
Nothing new on my part here. I never honestly used them in my scripts, but still they might improve your code when you need to compare one value against large number of constants:
Variable Initialization with Expressions
I find it very useful when writing hook scripts for sfall:
Previously it would fail to compile. You can use any complex expressions here. Only restriction - if it starts with a constant number or a string, you will need to enclose it in parentheses.
Optional Arguments
Another thing common in other languages that I missed. It works only for user-defined procedures. You need to specify default values for arguments so they could be omitted. Default values should be simple constants. Doesn't work for inline functions (not that anyone cares to use them anyway )
How this works: it basically puts the constants you provide in place of omitted arguments. No fancy tricks here.
Fixes for procedures inside arguments to engine functions
This is relevant if you have a user-defined procedure without arguments and let's say you want to call it like this:
What you wanted is to call this function so it return line number, but instead, compiler will place this function ordinal number into the compiled script. Good luck finding this bug.
With new compiler, only procedures at appropriate argument positions will be treated as procedure references (second argument in above example). Also, decompiler will place empty parentheses after procedures call, so it will be more obvious what has compiled:
Procedure Stringify Operator
Basically it puts string with procedure name in it's place. So @Node006 will be compiled as "Node006". As simple as that.
Now why the hell do we need it? Well, in fallout scripts you can call procedures by their names contained in a string. You can pass this string around and call it from other places. This basically a ghetto version of passing functions by reference (sadly, no anonymous functions... yet).
This operator helps to use this feature better, as it:
Again, all this features were done using existing low-level scripting operators. Scripts compiled with them will work on any Fallout version, even on new engine (if it will properly implement all basic opcodes).
int2ssl was updated to support all this syntax (along with sfall opcodes), to properly decompile new scripts.
There were numerous small-to-medium fixes and improvements, so I won't list all of them, only the big and recent ones.
Parser
It shows a list of all procedures and global variables in current script. This list can be found in the right part of the main window. Clicking on an item will bring you to it's location in code.
This is Timeslip's work, but I've updated code parser to support all latest syntax and sfall scripting functions and tried to make it look somewhat more friendly.
Hover Tooltips
Hovering over a scripting function, will bring you help tooltip like this:
Code Completion
As you write function name, a list of suggestions will show up, you can browse it with arrow keys and select with Enter:
It still ugly and primitive, but I find it extremely helpful. Not only to see names of scripting functions (not having to open reference every time), but also to see constants like PID_LEATHER_ARMOR. Don't guess the name, don't open define.h, just start typing and select it.
Hope this will inspire someone to start/continue scripting.
Script Editor with latest compiler/decompiler: download v3.5.4.3
Many cool new features were already implemented by Timeslip, some didn't work though, so I had to fix them.
Now I want to introduce some of the new SSL capabilities to all scripters out there.
SSL+
There are quite a few features added, so I guess it's a different language now (btw. SSL stands for "Star Trek Scripting Language")
Following features are vanilla-compatible, does not require Sfall to be interpreted by the game:
- short-circuit evaluation of logical operators: AND, OR
- python-inspired conditional expressions (ternary operator)
- break/continue statements
- for loops
- increment/decrement operators (++, --)
- switch statements
- variable initialization with expressions
- optional arguments in user-defined procedures
- procedure stringify operator
- fixes for using procedures inside arguments to engine functions
Arrays-related, sfall required:
- arrays in local variables
- bracket syntax
- dot syntax, chaining
- array expressions: lists and maps
- foreach loops
You can read all about array syntax here. Only recent addition to that stuff was a chained array dereferencing using a sequence of dots and brackets. This way you can use multi-dimensional arrays easily.
Now some details about most notable (IMO) features.
Short-Circuit Evaluation
Answer to "what the hell is this?" can be found here. In Fallout scripts, unlike most languages, logical operators AND, OR were compiled just like any other arithmetic operator. That means every part of such expressions was evaluated, even when result is already known.
Why this is important? It's simple, consider this real example from Restoration Project code, dude_obj script:
Code:
if (cur_map_index == MAP_NEW_RENO_1 and (map_var(MVAR_Kitty_Drug) > 0) and (map_var(MVAR_Kitty_Drug) < 4)) then ...
What is wrong with this code? Nothing, it looks pretty logical. However, it breaks the script and produces hundreds of errors every minute in debug.log. Why? Because all parts are evaluated, even when current map is not New Reno, it still calls "map_var", which fails with message "map var out of range". The problem is in the compiler itself.
This new feature fixes this and makes such conditions work like you expect. It is currently optional in compile.exe and disabled by default. It can be enabled by -s parameter (or a checkbox in latest Script Editor settings dialog).
As a side effect of how this works, AND, OR operators now return one of the arguments as a result of expression, instead of always 1 or 0:
Code:
y := 0;
x := y or z; // x will hold the value of z, because y is 0
Conditional Expressions
Because it was taken from python, you can read everything here. It works exactly the same in SSL+, providing you with a little convenience and another option to branch code execution within expressions.
Break/Continue statements
Another important thing, IMO. I really missed this on several occasions, even added "foreach (x in y while z)" syntax in foreach loops, just because there were no break. Later I discovered that it is possible, and pretty easy to implement, using existing fallout scripting opcodes.
Example:
Code:
for (obj := tile_get_next_critter(tile, elevation, 0); obj != 0; obj := tile_get_next_critter(tile, elevation, obj)) begin
// do some exciting stuff
if (did_all_I_wanted) then
break; // exit from loop prematurely
end
"For" Loops
"Switch" Statements
Nothing new on my part here. I never honestly used them in my scripts, but still they might improve your code when you need to compare one value against large number of constants:
Code:
switch (obj_pid(obj_being_used_with)) begin
case PID_KNIFE: display_msg("You hit it with your knife to no result");
case PID_CROWBAR: display_msg("You hit it with your crowbar. Still, it won't budge.");
case PID_SLEDGEHAMMER: display_msg("You hit it with your hammer. Not a scratch. What's wrong with this thing?");
default:
display_msg("This won't help here.");
end
Variable Initialization with Expressions
I find it very useful when writing hook scripts for sfall:
Code:
procedure keypress_hook begin
variable pressed := get_sfall_arg,
key := get_sfall_arg;
// ...
end
Optional Arguments
Code:
procedure myproc(variable a, variable b := "sdsd", variable c := 0) begin
display_msg( a + b + c );
end
// someplace else
call myproc(5); // valid
call myproc(7, "no"); // valid
call myproc(); // not valid, need at least 1 argument
Fixes for procedures inside arguments to engine functions
Code:
NOption(get_line_number, Node002a, 004);
With new compiler, only procedures at appropriate argument positions will be treated as procedure references (second argument in above example). Also, decompiler will place empty parentheses after procedures call, so it will be more obvious what has compiled:
Code:
giq_option(4, 205, get_line_number(), Node002a, 0);
Procedure Stringify Operator
Basically it puts string with procedure name in it's place. So @Node006 will be compiled as "Node006". As simple as that.
Now why the hell do we need it? Well, in fallout scripts you can call procedures by their names contained in a string. You can pass this string around and call it from other places. This basically a ghetto version of passing functions by reference (sadly, no anonymous functions... yet).
This operator helps to use this feature better, as it:
- prevents optimizer from deleting your procedure, because it will be properly referenced
- allows to see such procedure references in Script Editor
Code:
procedure get_name(variable a) begin
if not(a) then return "NULL";
return obj_name(a);
end
procedure array_map_func(variable arr, variable callback) begin
variable k, v, i;
foreach (k: v in arr)
arr[k] := callback(v);
return arr;
end
// this will display names of all party members
display_array( array_map_func(party_member_list) );
// (display_array is a user procedure which print array contents)
Again, all this features were done using existing low-level scripting operators. Scripts compiled with them will work on any Fallout version, even on new engine (if it will properly implement all basic opcodes).
int2ssl was updated to support all this syntax (along with sfall opcodes), to properly decompile new scripts.
Script Editor
There were numerous small-to-medium fixes and improvements, so I won't list all of them, only the big and recent ones.
Parser
This is Timeslip's work, but I've updated code parser to support all latest syntax and sfall scripting functions and tried to make it look somewhat more friendly.
Hover Tooltips
Code Completion
It still ugly and primitive, but I find it extremely helpful. Not only to see names of scripting functions (not having to open reference every time), but also to see constants like PID_LEATHER_ARMOR. Don't guess the name, don't open define.h, just start typing and select it.
Hope this will inspire someone to start/continue scripting.
Script Editor with latest compiler/decompiler: download v3.5.4.3
Last edited: