Jump to content

BSL:Manual: Difference between revisions

1,352 bytes added ,  16 May 2017
small copy-edit from top to bottom
(copy-edited through end of Conditional section)
(small copy-edit from top to bottom)
Line 2: Line 2:
{{TOClimit|3}}
{{TOClimit|3}}
==Files==
==Files==
When Oni loads a level's resources, it also loads and parses all .bsl files in the level's corresponding folder in [[IGMD]] (the name of the folder being specified by the level's [[ONLV]] resource). The code automatically begins running with the function called "main", regardless of which .bsl file it is found in (Oni's scripts always place it in a file ending in "_main.bsl"). The code then flows through whichever functions are called by "main". You can also manually call any of those functions from the [[Developer Mode|developer console]] -- however, you cannot actually define variables or functions through the console, so scripts cannot be written this way.
When Oni loads a level's resources, it also loads and parses all .bsl files in the level's corresponding folder in [[IGMD]] (the name of this folder being specified by the level's [[ONLV]] resource). The code automatically begins running with the function called "main", regardless of which .bsl file it is found in (Oni's scripts always place it in a file ending in "_main.bsl"). The code then flows through whichever functions are called by "main". You can also manually call any of those functions from the [[Developer Mode|developer console]] -- however, you cannot actually define variables or functions through the console, so scripts cannot be written this way.


Besides the automatic starting point represented by the "main" function, there are types of game data which specify function names that are to be called upon certain events. For instance, characters can call script functions upon death, and trigger volumes can call functions when they are entered or exited. See [[CHAR]], [[OBD:BINA/OBJC/CONS|CONS (BINA)]], [[OBD:BINA/OBJC/DOOR|DOOR (BINA)]], [[NEUT]], and [[TRGV]] for all known places in the game data that can trigger BSL functions.
Besides the automatic starting point represented by the "main" function, there are types of game data which specify function names that are to be called upon certain events. For instance, characters can call script functions upon death, and trigger volumes can call functions when they are entered or exited. See [[CHAR]], [[OBD:BINA/OBJC/CONS|CONS (BINA)]], [[OBD:BINA/OBJC/DOOR|DOOR (BINA)]], [[NEUT]], and [[TRGV]] for all known places in the game data that can trigger BSL functions.
Line 32: Line 32:


===Compound statements===
===Compound statements===
Compound statements are groups of statements grouped together by a pair of curly braces:
Compound statements are series of statements grouped together by a pair of curly braces:


  <b>{</b>
  <b>{</b>
Line 57: Line 57:
Do not use a trailing comment unless you end the statement with a semicolon (see [[#Old vs. new syntax|Old vs. new syntax]] for explanation).
Do not use a trailing comment unless you end the statement with a semicolon (see [[#Old vs. new syntax|Old vs. new syntax]] for explanation).


In documentation outside of source code or script files, such as this page's BSL samples, comments are sometimes used to tell the reader something in a way that won't break the actual code if the user copies the whole line into a script file, comments and all.
In documentation outside of source code or script files, such as this page's BSL samples, comments are sometimes used to tell the reader something in a way that won't break the actual code if the user copies the whole block of text into a script file, comments and all.


===Old vs. new syntax===
===Old vs. new syntax===
Line 64: Line 64:
  dprint "Hello"
  dprint "Hello"


and the more strict style, called "new style":
and the more strict style, called "new style", which resembles the syntax of [[wikipedia:C_(programming_language)|C]]:


  dprint("Hello");
  dprint("Hello");


This dual syntax can make it more complicated to talk about the language, and you can also generate errors if you accidentally mix syntaxes. For instance, if you try to end a function call with a semicolon, but you don't use a C-style function call...
This dual syntax can make it more complicated to talk about the language, and you can also generate errors if you accidentally mix syntaxes. For instance, if you try to end a function call with a semicolon, but you don't use the new-style parentheses around the function arguments...


  dprint "Hello";
  dprint "Hello";
Line 85: Line 85:


===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 to dprint() and dmsg(). You can also omit stating the type and parameters of a function (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 of code intended to be in that scope. 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 to dprint() and dmsg(). You can also omit stating the type and parameters of a function, with "void" being assumed in their absence (see [[#Functions|Functions]] if you need further explanation). You also do not need to use curly braces to enclose code that falls under an if/else statement if there is only a single line of code intended to be in that scope (see [[#Conditional|Conditional]] for examples). None of these syntax choices are in conflict with the "new style" syntax.


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


==Reserved words==
==Reserved words==
Here are the keywords that have special meaning in BSL.
Here are the keywords that have special meaning in BSL. If you attempt to use these words as names for variables or functions, you will confuse the BSL parser and your script will fail.


===Declaration===
===Declaration===
Line 139: Line 139:
===Conditional===
===Conditional===
====if====
====if====
The only conditional statement is "if", with optional follow-up statements "else if" and "else", discussed below.
The only conditional statement in BSL is "if", with optional follow-up statements "else if" and "else", discussed below.


  if (a operator b)
  if (a operator b)
Line 186: Line 186:
  }
  }


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:
Bungie West was aware of this bug, and would work around it in Oni's level scripting by making a global variable and then calling a function to modify that variable. This works because function calls will not fire out of scope:


  var bool one_equals_two;
  var bool one_equals_two;
Line 217: Line 217:
  }
  }


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 you want to express the opposite of some condition, you can use the "!" operator (pronounced "not", and also unused by Bungie West). The following two statements have the same meaning:


  if (!one_equals_two)
  if (!one_equals_two)
Line 291: Line 291:
  }
  }


The statements under "else" will never run. The "return" will actually kick in and the function will exit, even though the surrounding control statement did not evaluate to true.
Yes, "return" is also subject to the scope bug discussed in the [[#Conditional|Conditional]] section above. The statements under "else" will never run. The "return" will actually kick in and the function will exit, even though the surrounding control statement did not evaluate to true.


However, "return" is still useful for returning data to a calling function; see the section on [[#Returning|returning function values]].
However, "return" is still useful at the end of a function for returning data to the function that called that one; see the section on [[#Returning|returning function values]].


====sleep====
====sleep====
You can also delay a script using "sleep", passing it a number in ticks:
You can also delay execution of a script using "sleep", passing it a number in sixtieths of a second:


  sleep(60); # pause execution of BSL at this point for one second
  sleep(60); # pause execution of BSL at this point for one second
Line 303: Line 303:


===Loop===
===Loop===
It is generally desirable in any program to be able to run the same code over and over, such as repeatedly spawning enemies. This is one of BSL's biggest limitations, as it has no conventional loop statement like C's "for" or "while", but there are two indirect methods for looping: (1) use "fork" on a function (see the section on [[#Looping|looping functions]]), and (2) "schedule-repeat-every" (see [[#Concurrency|Concurrency]]).
It is generally desirable in any program to be able to run the same code over and over, such as repeatedly spawning enemies. This is one of BSL's biggest limitations, as it has no conventional loop statement like C's "for" or "while", but there are two indirect methods for looping: (1) use "fork" on a function (see the section on [[#Looping|looping functions]]), and (2) "schedule-at"/"schedule-repeat-every" (see [[#Concurrency|Concurrency]]).


===Multi-threading===
===Multi-threading===
Line 327: Line 327:
| =
| =
| Equals
| Equals
| Sets the left-hand entity to the right-hand value.  
| Sets the variable on the left-hand side to the right-hand value.
|}
|}


Line 333: Line 333:


===Arithmetical===
===Arithmetical===
These operations do not change the numbers that the operators act upon; they merely provide a resulting number for your use.
{| class="wikitable"   
{| class="wikitable"   
! Symbol
! Symbol
Line 340: Line 342:
| +
| +
| Add
| Add
| Sums two numbers.
| Provides the sum of two numbers.
|-
|-
| -
| -
| Subtract
| Subtract
| Deducts one number from another.
| Gives the result of subtracting the second number from the first.
|}
|}


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.
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.


===Relational===
===Relational===
These statements return true/false values for use in "if" statements.
{| class="wikitable"   
{| class="wikitable"   
! Symbol
! Symbol
Line 365: Line 369:
| >
| >
| Greater than
| Greater than
| Tests if a number is greater than another.
| Tests if first number is greater than second one.
|-
|-
| <
| <
| Less than
| Less than
| Tests if a number is smaller than another.
| Tests if first number is smaller than second one.
|-
|-
| >=
| >=
Line 380: Line 384:
|}
|}


Note: ''eq'' and ''ne'' work for strings too.
Note: Although most of these operators are intended for use with numbers, ''eq'' and ''ne'' work for strings too.


===Logical===
===Logical===
Line 390: Line 394:
| !
| !
| Not
| Not
| Logical Not. Gives the inverse of a bool. The same as saying "some_bool ne true".
| Logical Not. Reverses the result of a test. "!some_bool" is the same as saying "some_bool eq false" or "some_bool ne true".
|-
|-
| and
| and
Line 413: Line 417:
  }
  }


Upon declaration, variables are automatically initialized to zero if no value is assigned explicitly, as in the above example with "x".
Upon declaration, variables are automatically initialized to zero if no value is assigned explicitly, as in the above example with "x", or to a blank string in the case of a string variable.


===void===
===void===
Line 427: Line 431:
  }
  }


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/false will set it to 1/true. For instance, assigning a float to a bool will make the bool "true" or "1" unless the float value is "0.0", which will become "false".


===int===
===int===
Line 475: Line 479:


===string===
===string===
A sequence of textual characters. BSL does not provide any functions for manipulating strings (concatenation, trimming, etc.).
A sequence of textual characters. BSL does not provide any explicit means for manipulating strings (concatenation, trimming, etc.), though see below for a simple trimming hack.


  var string example = "hello";
  var string example = "hello";
Line 492: Line 496:


==Functions==
==Functions==
A function is a block of code that can be accessed repeatedly whenever you want to perform a certain task. As in mathematics, functions can be passed input and can return output, though unlike in mathematics, they do not need to do either of those things in order to be useful, as BSL functions can affect external data by modifying global variables and by calling built-in functions which affect the game environment.
A function is a block of code that can be accessed repeatedly whenever you want to perform a certain task. As in mathematics, functions can be passed input and can return output, though unlike in mathematics, they do not need to do either of those things in order to be useful, as BSL functions can affect external data by modifying global variables and by calling built-in functions which affect the game environment (see [[#Built-in functions and variables|Built-in functions and variables]] section).


===Defining===
===Defining===
When beginning to define a function, you must begin the line with "func"...
When beginning to define a function, you must begin the line with "func"...


  func int some_function(int count, string enemy_name)
  func int prepare_fight(int count, string enemy_name)
  {
  {
     ...
     ...
Line 505: Line 509:


====void====
====void====
Functions do not need to accept or return data. When they don't, you use "void" to indicate this. Note that both "void"s can be omitted and will simply be assumed implicitly.
Functions do not need to accept or return data. When they don't, you use "void" to indicate this. Note that both "void"s can be omitted and will be assumed implicitly.


  func void func_start(string ai_name) # Returns nothing to calling function, but accepts a string
  func void func_start(string ai_name) # Returns nothing to calling function, but accepts a string
Line 527: Line 531:
  some_function(3, "Jojo");
  some_function(3, "Jojo");


In this case, we are passing a constant value, three, into some_function, where it will be known as "count", and we are passing a constant string in as "enemy_name", but we could also have passed in variables by name:
In this case, we are passing a constant value of three into prepare_fight(), defined above, where it will be known as "count", and we are passing a constant string in as "enemy_name", but we could also have passed in variables by name:


  var int num_heroes = 3;
  var int num_heroes = 3;
  var string enemy = "Jojo";
  var string enemy = "Jojo";
  some_function(num_heroes, enemy);
  some_function(num_heroes, enemy);
The values of these variables will still be referred to as "count" and "enemy_name" inside prepare_fight().


===Recursive calling===
===Recursive calling===
Line 537: Line 543:


===Returning===
===Returning===
As mentioned under [[#Flow interrupt|Flow interrupt]], "return" exits the function at that point. Because of the bug documented in that section, you cannot exit early from a function under some condition. Since "return" can thus only be placed at the end of a function, there is no point in using it at all unless you are going to pass back a value by placing a variable name after "return":
As mentioned under [[#Flow interrupt|Flow interrupt]], "return" exits the function at that point. Because of the bug documented in that section, you cannot exit early from a function under some logical condition. Since "return" can thus only be placed at the end of a function, there is no point in using it at all unless you are going to pass back a value by placing a variable name after "return":


  func int add_ten(int input)
  func int add_ten(int input)
Line 583: Line 589:
  }
  }


Note that calling the same function again before it finishes running the first time can have undesired effects. Using "sleep" before each call can prevent this overlapping execution. A short function like the example above should not need a "sleep" statement, as the remainder of the function after the "fork" call will complete in the same tick. But the more complex the function, the more ticks you will need to allow for it to complete, placing "sleep(1);", "sleep(3);", etc. before the "fork" call.
Note that calling the same function again before it finishes running the first time can have undesired effects. Using "sleep" before each call can prevent this overlapping execution. A short function like the example above should not need a "sleep" statement, as the remainder of the function after the "fork" call will complete in the same tick. But the more complex the function, the more ticks you will need to allow for it to complete, requiring "sleep(1);", "sleep(3);", etc. before the "fork" call.


===Concurrency===
===Concurrency===
Line 598: Line 604:
     enemies;
     enemies;
  }
  }
 
  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)
  {
  {
Line 613: Line 619:
Also be aware that accidentally calling a nonexistent function using "fork" will crash Oni; when "fork" is not used, BSL will not recognize that the unknown function name represents a function, and thus will not attempt to call it and end up crashing.
Also be aware that accidentally calling a nonexistent function using "fork" will crash Oni; when "fork" is not used, BSL will not recognize that the unknown function name represents a function, and thus will not attempt to call it and end up crashing.


Below are two types of uses for the "schedule" keyword. By scheduling a function call that operates on global variables and doesn't run unless a condition is true, you can simulate a "for" loop with these keyword sets.
Below are two types of uses for the "schedule" keyword. By scheduling a function call that operates on global variables and doesn't run unless a condition is true, you could simulate a slow-acting loop with these keyword sets.


====schedule ... at ...====
====schedule ... at ...====