BSL:Manual: Difference between revisions

1,192 bytes added ,  6 December 2015
scope bug confirmed; consistent strong syntax in examples; example for "!" operator
(→‎if: oops, forgot to mention "else if" here)
(scope bug confirmed; consistent strong syntax in examples; example for "!" operator)
Line 1: Line 1:
Like any typical scripting or programming language, BSL scripts consist of plain-text files which employ branching logic and various operators according to certain rules of syntax in order to process data using functions that act upon variables. If any of those terms are not familiar to you now, keep reading. If you found that sentence boringly obvious, then you should be experienced enough to get by with [[BSL:Introduction]].
Oni's scripting language is called [[BSL]]. Like any typical scripting or programming language, BSL scripts consist of plain-text files which employ branching logic and various operators according to certain rules of syntax in order to process data using functions that act upon variables. If any of those terms are not familiar to you now, keep reading. If you found that sentence boringly obvious, then you should be experienced enough to get by with [[BSL:Introduction]].
{{TOClimit|3}}
{{TOClimit|3}}
==Files==
==Files==
Line 33: Line 33:


  {
  {
     dmsg("Statement 1")
     dmsg("Statement 1");
     dmsg("Statement 2")
     dmsg("Statement 2");
     dmsg("Statement 3")
     dmsg("Statement 3");
  }
  }


Line 48: Line 48:


  '''# The following block of code calculates the meaning of life'''
  '''# The following block of code calculates the meaning of life'''
  if [block of code begins here]
  if (...)
{
    ...
}


Do not using trailing comments when not ending statements with semicolons (see [[#Old vs. new syntax|Old vs. new syntax]] for explanation).
Do not using trailing comments when not ending statements with semicolons (see [[#Old vs. new syntax|Old vs. new syntax]] for explanation).
Line 77: Line 80:
  var int a = 3 # ...this line is not (even without this comment!)
  var int a = 3 # ...this line is not (even without this comment!)


Thus, it's recommended to consistently use the "new" style of BSL. It requires a bit more typing, but it creates safer and more readable code.
Thus, it's recommended to consistently use the "new" style of BSL. It requires a bit more typing, but it creates safer and more readable code. For consistency, all code on this page is in new-style syntax besides the old-style examples above.


===Coding style===
===Coding style===
There are other aspects of the language which are flexible, and yet not connected to the old-style/new-style division. For instance, the quotes around "Hello" can be left out in both syntax versions of the above calls. You can also omit function types (with "void" being assumed in their absence). You also do not need to enclose code after an if/else statement in curly braces if there is only a single line. None of these syntax choices are in conflict with the "new style" syntax.
There are other aspects of the language which are flexible, and yet not connected to the old-style/new-style division. For instance, the quotes around "Hello" can be left out in both syntax versions of the above calls. You can also omit function types (with "void" being assumed in their absence). You also do not need to enclose code after an if/else statement in curly braces if there is only a single line. None of these syntax choices are in conflict with the "new style" syntax.


Additionally, you can use whitespace in different ways:
Additionally, you can use whitespace and newlines in different ways:


  if (counter eq 3) {
  if (counter eq 3) {
Line 105: Line 108:


===Declaration===
===Declaration===
Before using a function or a variable for the first time, you need to give some information on it.
Before using a function or a variable for the first time, you need to declare its name and data type.


====var====
====var====
See [[#Variables|Variables]] for details on using variables.
'''var''' int a = 0;
 
See [[#Variables|Variables]] for details on declaring variables.


====func====
====func====
See [[#Functions|Functions]] for details on using functions.
'''func''' void spawn_team(void)
{
    ...
}
 
See [[#Functions|Functions]] for details on declaring functions.


===Type specification===
===Type specification===
Line 117: Line 127:


====void, bool, int, float, string====
====void, bool, int, float, string====
Besides "void" (used in function definitions to indicate "no type"), these are the types of data that can be assigned to variables, passed to functions, and returned by functions. See [[#Data types|Data types]].
var '''int''' a = 0;
func '''int''' spawn_team('''int''' flag)
{
    ...
}
 
Besides "void" (used in function definitions to indicate "no type"), these are the types of data that can be assigned to variables, passed to functions, and returned by functions. See [[#Data types|Data types]] for details on the types.


===Conditional===
===Conditional===
Line 134: Line 150:
  if (8 - 8) # evaluates to false because it is zero
  if (8 - 8) # evaluates to false because it is zero
  if (8 - 7) # evaluates to true because it is non-zero
  if (8 - 7) # evaluates to true because it is non-zero
if (7 - 8) # evaluates to true because it is non-zero


Braces are optional when you only have one line of code under the "if" (this is also true for "else" and "else if", discussed below):
Braces are optional when you only have one line of code under the "if" (this is also true for "else" and "else if", discussed below):
Line 150: Line 165:
That indentation on "spawn_considered" is misleading; when glancing at this code, you might expect that "spawn_considered" only is changed if "a" is greater than zero. It could be that you even intended to add that line to the "if" statement but forgot to add braces. Always using braces around an "if" statement's body will prevent that mistake.
That indentation on "spawn_considered" is misleading; when glancing at this code, you might expect that "spawn_considered" only is changed if "a" is greater than zero. It could be that you even intended to add that line to the "if" statement but forgot to add braces. Always using braces around an "if" statement's body will prevent that mistake.


Be aware that, even ''with'' braces, BSL does not always respect scope for blocks of code under "if" statements; for instance, a "return" statement will fire even if the surrounding "if" condition is false (see [[#Flow interrupt|Flow interrupt]] for an example). According to a note in neuro_level_logic.bsl, this may happen for variable assignments too. The workaround Bungie West used in that script for setting a variable conditionally was to make the variable global, and under the "if" statement call a function that modified that variable, since function calls will not fire unless the "if" condition is true.
Be aware that, even ''with'' braces, BSL does not always respect scope for blocks of code under "if" statements; for instance, a "return" statement will fire even if the surrounding "if" condition is false (see [[#Flow interrupt|Flow interrupt]] for an example). An even more alarming example of bad scope is this:
 
func void broken_if(void)
{
    var int one = 1;
    var int two = 2;
    var bool one_equals_two = false;
    if (one eq two) # this condition will evaluate to false, but...
      one_equals_two = true; # ...this will still run
    if (one_equals_two)
      dprint("Uh-oh.");
    else
      dprint("Phew.");
}
 
Bungie West was aware of this bug, and would work around it by making a variable global and then calling a function to modify that variable, since function calls will not fire out of scope:
 
var bool one_equals_two;
func void fixed_if(void)
{
    var int one = 1;
    var int two = 2;
    set_oet(false);
    if (one eq two)
      set_oet(true);
    if (one_equals_two)
      dprint("Uh-oh.");
    else
      dprint("Phew.");
}
func void set_oet(bool input)
{
    one_equals_two = input;
}


One very handy feature that you also won't see being used in Oni's game scripts is that you can use the logical operators "and" and "or" to string together multiple conditions:
One very handy feature that you also won't see in Oni's game scripts is that you can use the logical operators "and" and "or" to string together multiple conditions:


  if ((a < b) and (c > d))
  if ((a < b) and (c > d))
Line 158: Line 212:
     # ...
     # ...
  }
  }
If you want to express the opposite of some condition, you can use the "!" operator (also unused by Bungie West). The following two statements have the same meaning:
if (!one_equals_two)
if (one_equals_two eq false)
There's no functional need for the "!"; it just saves space.


====else====
====else====
Line 282: Line 343:
|}
|}


You'll note that there is no multiplication or division. Bungie West was only concerned with creating enough scripting power to drive Oni, and they did not need "higher math" for this.
You'll note that there is no multiplication or division. Bungie West was only concerned with creating enough scripting power to drive Oni, and they did not need "higher math" for this. See [[#Data types|Data types]] to learn the details of how math works between different data types.
 
See [[#Data types|Data types]] to learn the details of how operators work between different data types.


===Relational===
===Relational===
Line 337: Line 396:
| Logical Or.
| Logical Or.
|}
|}
See the [[#if|"if" section]] to learn how to use the logical operators.


==Data types==
==Data types==
Line 358: Line 419:
  var bool example = 1;
  var bool example = 1;
  func bool are_we_there_yet(void)
  func bool are_we_there_yet(void)
{
    ...
}


Giving a bool any value other than 0 will set it to 1. For instance, assigning a float to a bool will make the bool "true" unless the float value is 0.0, which will become "false".
Giving a bool any value other than 0 will set it to 1. For instance, assigning a float to a bool will make the bool "true" unless the float value is 0.0, which will become "false".
Line 366: Line 430:
  var int example = -1000;
  var int example = -1000;
  func int get_enemy_count(void)
  func int get_enemy_count(void)
{
    ...
}


Note that the number wraps around, which means that once it passes its maximum or minimum value, it starts over from the other end. For instance, the function...
Note that the number wraps around, which means that once it passes its maximum or minimum value, it starts over from the other end. For instance, the function...
Line 539: Line 606:
  func int count_enemies(void)
  func int count_enemies(void)
  {
  {
     return 3
     return(3);
  }
  }


  func void wait_for_a_second(void)
  func void wait_for_a_second(void)
  {
  {
     sleep 60
     sleep(60);
  }
  }


Line 556: Line 623:
Allows you to schedule a function call a certain number of ticks in the future. Unused in Oni's scripts, but here's a working example:
Allows you to schedule a function call a certain number of ticks in the future. Unused in Oni's scripts, but here's a working example:


  schedule dmsg("BOOM") at 300
  schedule dmsg("BOOM") at 300;
  schedule dmsg("1...") at 240
  schedule dmsg("1...") at 240;
  schedule dmsg("2...") at 180
  schedule dmsg("2...") at 180;
  schedule dmsg("3...") at 120
  schedule dmsg("3...") at 120;
  schedule dmsg("4...") at 60
  schedule dmsg("4...") at 60;
  dmsg("5...")
  dmsg("5...");


You are not allowed to set a variable to the return value of a scheduled function call, just like you cannot use "sleep" in a function that returns a value. This is presumably to avoid unpredictable changes being made to variables that share scope with other code.
You are not allowed to set a variable to the return value of a scheduled function call, just like you cannot use "sleep" in a function that returns a value. This is presumably to avoid unpredictable changes being made to variables that share scope with other code.