Oni (folder)/GameDataFolder/IGMD/global: Difference between revisions

From OniGalore
Jump to navigation Jump to search
m (+cat)
(total rewrite, this was too confusing, also wanted to mention overriding)
Line 1: Line 1:
==Oni global script folder==
'''global''' is a sort of secret subfolder of [[IGMD|IGMD]]; a regular installation of Oni doesn't have it at all. But if you create it manually, you'll find it can serve a very special purpose: functions in the '''global''' folder can be called from every one of Oni's levels.
"global" is a "secret subfolder" of [[IGMD|IGMD]] : a regular installation of Oni doesn't have it at all.


But you can create it and use it and it's actually very rewarding. It's a very special folder.
==Code reuse==
One obvious use for this feature is that when writing a script that affects multiple levels, you can place common BSL functions in '''global''' instead of duplicating that code in each level's BSL files. However, you are still going to have to modify each level's BSL to ''call'' this global function.


==What's so special about it?==
Let's say you want Oni to run a function every time you load a level. You have to go in every level's IGMD folder, open the file where <tt>func void main(void)</tt> is declared and modify it as follows :
The global folder is visible from every one of Oni's levels. Rather, the scripts (files with extension BSL) in the global folder are always processed at the same time as the level logic of a particular level, when that particular level is loaded.
func void main(void) {
    some_global_function # <--inserted line
  # ORIGINAL CODE
}
 
Next you would go to the '''global''' folder and create a file by any name with the suffix ".bsl", and the function:
func some_global_function {
    # Code to run at start of every level
}
 
You might be tempted to use <tt>fork some_global_function</tt>, but it's usually better to call the function "inline" (that is, no <tt>fork</tt>) to make sure that it fully executes before the original level logic resumes. You can always <tt>fork</tt> what you want to be <tt>fork</tt>ed inside your global function. Just be aware that anything you add, such as a <tt>sleep</tt> command, will delay the execution of the original logic, unless you do decide to use <tt>fork</tt>.


==Code overriding==
A less obvious "feature" of the '''global''' folder is overriding. A normally unseen rule in BSL is that <u>global functions override local ones</u>. For instance, place this in a global BSL file and play Chapter 4, Airport Assault:


==How do I use it?==
func void bomber_boom(string char_index)
Is there really a short way to describe it? Oh well...
  {
-----------
    dmsg "Where's the kaboom?!"
Let's say you want Oni to do the exact same thing every time you load a save point. Like, you've just learned about how the [[BSL:Functions#message|dmsg]] command works, and you want the message "Thank you for playing Oni!" to be displayed after a level loads. Then the trivial solution is to go in every level's folder, open the file where <tt>func void main(void)</tt> is declared and modify it as follows :
    dmsg "There was supposed to be an Earth-shattering kaboom!"
  func void main(void) {
    dmsg "Thank you for playing Oni!"
#  WHATEVER ELSE THERE IS
  }
  }
OK. You've done that for all 14 levels. But now, let's say you've learned about how to add colors to debug messages, and you want the whole message to be red, except for the word Oni which shall be blue.


What then? Open every level's "main file" again and replace "Thank you for playing Oni!" with "[r.Thank you for playing ][b.Oni][r.!]"? 14 times?
That function is what causes the explosion of the ramp in the airport. Now, instead of creating an explosion particle and deleting the ramp, Oni just prints that message. Seemingly one could use this method to "patch" level BSL. However....


No. You use the global folder
'''Big catch''': there may be unpredictable results; for instance, this override causes random problems in the level, though the dmsg will print appropriately when you reach the point where the ramp is supposed to explode.


==<tt>func pre</tt>==
Here's an even more astounding application of BSL overriding: another rule is that <u>functions declared in BSL override built-in (hardcoded) ones</u>. This means that you can create a pseudo-[[wikipedia:Hooking|hook]], like this one:
Go to the global folder and create a file called <tt>pre.bsl</tt>. Open it and give it the following content :
 
  func pre {
# Built-in function takes two ints, so we'll do that here too
    dmsg "[r.Thank you for playing ][b.Oni][r.!]"
  func env_show(int a, int b)
{
    dmsg "Env_show has been disabled."
  }
  }
Then go and open the 14 files where you had <tt>dmsg "Thank you for playing Oni!"</tt>, and replace that line with <tt>pre</tt>, getting :
func void main(void) {
    pre
#  WHATEVER ELSE THERE IS
}
This is an inline call to the <tt>pre</tt> function we have just set up in the global folder. The function <tt>pre</tt> is "globally recognized".


===Why call it <tt>pre</tt>?===
Now Oni's level scripts cannot hide objects in the environment, and as you play a level you will see the effect of this. One obvious change is found in the introductory cutscene in Chapter 2. Ah, that must be the seat the receptionist wanted you to take! ^_^
Because everything in <tt>func main</tt> is level-specific settings and/or logic, so if you want something to happen exactly the same way every time you load a level, the only place to add that something is at the very start of <tt>func main</tt>, that is, ''before'' all the level-specific content. Which is why I've been calling it <tt>pre</tt>. Oh, you can name it <tt>shpadoinkle</tt> if you like... but that choice is a bit harder to justify. [[User:Geyser|geyser]]
 
*Let's build a snowman, we can make him our best friend, we could name him Bob... or we could name him ''Beowulf''! We could make him tall, or we could make him ''not so tall''... Snowman!
'''Big catch''': pseudo-hooks like this are of limited value because there's no way to call the original function once you've overridden its name with your own declaration, so you can't add to a built-in function this way; you can only prevent it from running, and run your own code instead. In theory you might be able to reconstruct a built-in call's effects using BSL. This is attempted by Gumby's cutscene-skipping script, which attempts to replace [[BSL:Functions#cutscene|begin_cutscene and end_cutscene]] with its own implementations so it can add a "skip cutscene" key, with somewhat unpredictable results.


===Why make it inline? (WARNING : EXPERT STUFF)===
Thus, the override "feature" is not recommended for use.
Because in that case, it's as if the content of <tt>func pre</tt> got pasted at the start of <tt>func main</tt> which is perfectly OK for some purposes (like the <tt>dmsg "[r.Thank you for playing ][b.Oni][r.!]"</tt> call in the example above). Three things to consider :
#You can always <tt>fork</tt> what you want to be <tt>fork</tt>ed inside <tt>func pre</tt>. And it will be exactly the same as if you placed the <tt>fork</tt>ed call directly inside <tt>func main</tt>.
#If you put <tt>fork pre</tt> instead of <tt>pre</tt> in <tt>func main</tt>, you can no longer consider that you're "just adding stuff to the beginning of <tt>func main</tt>". You can make an inline call into a forked call, but not the other way round.
#In some cases, you may want to be sure some part of <tt>func pre</tt> is executed ''rigorously before'' <tt>func main</tt>. And that's something you can not guarantee with a <tt>fork</tt>ed call.
Sure you can't put anything you want in an inline-called <tt>func pre</tt>, like a <tt>sleep</tt> statement, or a call to <tt>chr_wait_animtype</tt>.
It's up to you to bear in mind that what you're looking at is in fact just a part of every level's <tt>func main</tt>.


[[Category:Game directory map]]
[[Category:Game directory map]]

Revision as of 19:55, 26 April 2013

global is a sort of secret subfolder of IGMD; a regular installation of Oni doesn't have it at all. But if you create it manually, you'll find it can serve a very special purpose: functions in the global folder can be called from every one of Oni's levels.

Code reuse

One obvious use for this feature is that when writing a script that affects multiple levels, you can place common BSL functions in global instead of duplicating that code in each level's BSL files. However, you are still going to have to modify each level's BSL to call this global function.

Let's say you want Oni to run a function every time you load a level. You have to go in every level's IGMD folder, open the file where func void main(void) is declared and modify it as follows :

func void main(void) {
    some_global_function # <--inserted line
  # ORIGINAL CODE
}

Next you would go to the global folder and create a file by any name with the suffix ".bsl", and the function:

func some_global_function {
    # Code to run at start of every level
}

You might be tempted to use fork some_global_function, but it's usually better to call the function "inline" (that is, no fork) to make sure that it fully executes before the original level logic resumes. You can always fork what you want to be forked inside your global function. Just be aware that anything you add, such as a sleep command, will delay the execution of the original logic, unless you do decide to use fork.

Code overriding

A less obvious "feature" of the global folder is overriding. A normally unseen rule in BSL is that global functions override local ones. For instance, place this in a global BSL file and play Chapter 4, Airport Assault:

func void bomber_boom(string char_index)
{
   dmsg "Where's the kaboom?!"
   dmsg "There was supposed to be an Earth-shattering kaboom!"
}

That function is what causes the explosion of the ramp in the airport. Now, instead of creating an explosion particle and deleting the ramp, Oni just prints that message. Seemingly one could use this method to "patch" level BSL. However....

Big catch: there may be unpredictable results; for instance, this override causes random problems in the level, though the dmsg will print appropriately when you reach the point where the ramp is supposed to explode.

Here's an even more astounding application of BSL overriding: another rule is that functions declared in BSL override built-in (hardcoded) ones. This means that you can create a pseudo-hook, like this one:

# Built-in function takes two ints, so we'll do that here too
func env_show(int a, int b)
{
   dmsg "Env_show has been disabled."
}

Now Oni's level scripts cannot hide objects in the environment, and as you play a level you will see the effect of this. One obvious change is found in the introductory cutscene in Chapter 2. Ah, that must be the seat the receptionist wanted you to take! ^_^

Big catch: pseudo-hooks like this are of limited value because there's no way to call the original function once you've overridden its name with your own declaration, so you can't add to a built-in function this way; you can only prevent it from running, and run your own code instead. In theory you might be able to reconstruct a built-in call's effects using BSL. This is attempted by Gumby's cutscene-skipping script, which attempts to replace begin_cutscene and end_cutscene with its own implementations so it can add a "skip cutscene" key, with somewhat unpredictable results.

Thus, the override "feature" is not recommended for use.