-
Notifications
You must be signed in to change notification settings - Fork 1
Scripting
<> Needs update, specifically on how script functions get exported, on how scripts related to sectors, levels or dialog and on what scripting is used for. <>
Windstille uses http://www.squirrel-lang.org/ for scripting. The full source code of it is included within the external/SQUIRREL2/ subdirectory of Windstille's source and it is automatically built into the game. The <>ScriptManager<> is for most part responsible for actually running scripts.
C++ functions are exposed to scripts via the interface in the scripting/ subdirectory. Miniswig, found in the tools/miniswig subdirectory, is a tool that creates wrapper functions to allow the interface functions to be called from squirrel. If you have bison and flex installed then miniswig will be built automatically and any changes you make to the scripting interface will automatically be added to the wrapper functions when you execute scons.
The plan is that scripts can then be associated with these .wst sectors, or indeed with individual bad guys or objects within these sectors. Multiple scripts can be run simultanously. Some examples of things scripts might be used for:
- to alter the starting locations of bad guys, NPCs or the player character, or stop any one of these from spawing at all
- to alter which trigger points are active, or to alter their locations, or what they do
- to control the AI behaviour of bad guys or NPCs
- to control dialog with NPCs
- to control when and where new information is provided to the player
== Environments
There are currently two environments in which scripts can be evaluated. These different environments ensure that scripts do not polute the global namespace and that they are cleanly seperated.
=== Global Environment
The global environment contains all the native Squirrel functions, exported C++ functions as well as things defined in global scripts. It is currently used only for data/scripts/windstille.nut. It also contains the objects table which contains all the objects in the current sector.
=== Child Environment
Each script that is started from a .wst file is a child to the global environment, meaning all functions not defined in the child environment will be looked up in the global environment. Newly defined functions and variables will stay local to the single script.
== Basic Script
A basic script should contains currently two functions, init() and run(), new functions might be added later. The functions need not to be defined, so leaving them out when they are not needed is ok. However no code should be placed outside of these function, since it will screw things up when the script is run multiple times.
Global scripts are directly evaluted in the global environment and thus don't need either of those.
function init()
{
// The init() section is run exactly once when the script is loaded
}
function run()
{
// The run() section is called on each run of the script
}
== Script Suspend/Wakeup
Scripts can be suspended an resumed at a later point, a very simple use case would be:
jane.walkto("bar");
jane.say("Jane, nice bar");
jane.walkto("door");
The walkto() instructions gives the character object the task to reach the given location, installs a wake-up signal and then suspends the script. When the character reaches its target it fires a wake-up signal which in turn then wakes up the script and continues with the say() instruction.
== Trigger Functions
Some game objects such as <>Trigger<> allow users to define callbacks. The callback is the name of a function that is defined in the sectors init-script and it will be evaluated in the VM created by init-script. Note that trigger scripts must always return and never suspend the VM. Functionality for spawing new background scripts might be added later.
== Types of Scripting
- interaction/event scripts: scripts that are triggered when the user looks/uses/talks to an object or character
- background scripts: scripts that control background animation or events in the scene
- character/ai scripts: scripts that define the AI of computer controled characters and also there reaction to events (ducking when a grenade explodes near by)
Trouble: scripts are not limited to a single entity, thus multiple scripts might want to access the same entity at the same time.
== Export Objects to Squirrel
Currently objects get exported into the table objects in Squirrel, so if a object has the name (name "Painting1") it is accessible as objects.Painting1. Its not quite clear if that is the right way to do it or if it shouldn't be handled via handles/accessors instead, i.e. painting = get_object("Painting1"). One reason is that it would limit the export objects to only those that are actually needed and another is that it would make the exporting more clear in the code. For normal usage one would likely do it 'accessor-style' anyway, i.e. bob = objects.bob to not have write the whole objects... things.
One disadvantage is that objects would no longer be equal, if one does get_object("bob") twice the resulting Squirrel object is different, while the C++ one is the name. Not sure if that is important enough to matter.
== Background AI Scripts
<> Just an unfinished idea, not sure if and how exactly it can be implemented. <>
The idea of a background AI script is that it runs in the background and lets characters do things. For example a script for the barkeeper that lets him walk from guest to guest and do a little cleanup at the bar might look like this:
// Barkeeper background script
while (true)
{
barkeeper.walkto(guest1);
barkeeper.say("Your beer");
barkeeper.walkto(guest2);
barkeeper.say("Your whiskey");
barkeeper.walkto(guest3);
barkeeper.say("Your milk");
barkeeper.walkto(bar);
barkeeper.play("cleanup-animation");
}
So far so good, with the current scripting engine that should already work fine. However where the thing becomes troublesome is with player interaction. If the player talks to the barkeeper, the dialog script is triggered, again already doable with the current engine. However the barkeeper character is now controlled by two scripts, which obviously isn't going to work. Therefore we need a way to solve this. A quick idea how this might work in a Dialog script is this:
// Stop other scripts from using the barkeeper,
// those scripts that do already use him shall be
// suspended for moment till unlock() is called.
barkeeper.lock()
barkeeper.say("Hello, what can I do for you?")
jane.say("Nothing, thank you")
// Release the barkeeper and let the background
// script continue where it was
barkeeper.unlock()
This might work for such a simple script, but it not quite clear if and how this would work in more complex situations, say when multiple characters are involved in a background script and how exactly the background script can be resumed at the exact right spot. Its also not clear if lock() and unlock() should be explicit calls or if certain functions should have automatic priority over other things.
== Syncronisation
<> Random idea, not sure how to implement or if at all <>
Current scripting handles sequential events just fine, but how to handle multiple parallel ones?
// Have two characters walk to a target, wait for both of them to arrive
bob.walkto(door, DO_NOT_WAIT)
jane.walkto(door, DO_NOT_WAIT)
wait_for([bob, jane])
// Have two characters walk to a target, wait for both of them to arrive
execute_in_parallel {
bob.walkto(door);
jane.walkto(door);
}
// Create use Squirrels newthread() to
function bob_script() {
// ...
}
function jane_script() {
// ...
}
threads = [newthread(bob_script), newthread(jane_script)]
while(1) {
bob_script.wakeup();
jane_script.wakeup();
}
== VM Serialization
Serialization of the VM is needed so that a savegame can continue at exactly the same point where it left. This however doesn't seem to be nativly supported by Squirrel. See this http://squirrel-lang.org/forums/thread/691.aspx or this http://squirrel-lang.org/forums/thread/1213.aspx.
Possible solutions:
- ignore the problem for now and make sure that the Scripting API is clean (most likely for now)
- implement serialization in Squirrel
- don't implemented exact saves and work with predefined savepoints that just restart scripts
- roll our own scripting language
- switch to something else: ** http://www.lua.org in combination with http://lua-users.org/wiki/PlutoLibrary (should support serialization) ** http://www.stackless.com/ (should support serialization) ** http://plib.sourceforge.net/psl/index.html (unknown) ** http://www.somedude.net/gamemonkey/ (unknown) ** http://www.angelcode.com/angelscript/ (unknown)