Scripting
This feature requires FreeSpace Open |
Scripting allows a modder to run custom code in the engine that can read and modify the game state to produce new features.
Scripts are either specified via Scripting.tbl or as standalone lua files.
Contents
General Info
- Scripting hooks are not executed on a separate thread (at least as of this writing). They are executed at given points in the code, and all other execution will not take place until a scripting hook has finished. Therefore, large and complicated scripts will most likely cause noticeable lag or slowdown when executed.
Useful references
- FS2 Open Lua Scripting
- Scripting API
- Scripting functions
- Command-Line Reference for creating scripting.html
- Lua-Users Wiki
- Reference manual for Lua 5.1
Standalone Lua scripts
Since writing Lua scripts using the table can lead to some issues while developing scripts (such as wrong syntax highlighting or wrong line numbers in error messages) the engine also supports loading Lua scripts directly and executing them when the initial game data has been loaded. Since that alone does not allow to register hooks, there is a scripting API that allows to do that from Lua code. See engine.addHook in scripting.html.
Prerequisites
Frames
One of the most important things to understand when reading this guide is the concept of "frames". (Experienced modders can probably skip this part) In gameplay, motion is achieved by making incremental changes every fraction of a second. A ship will be moved slightly, and then drawn to the screen. This happens 30-120 times a second; anything lower, and the game will become choppy and difficult to play.
There is also a difference in the moving of objects in-game, and the moving of objects onscreen. All ships are first "moved", in memory. This probably consists of physics calculations, that change the ship's position based on if their engines are turned on, or if they run into another object. After this, the game then renders the now-moved ships to what is referred to as a "backbuffer" - a place in memory that takes the place of the computer screen. Once rendering has finished, the backbuffer is instantly drawn to the screen. This prevents objects from flickering as they are drawn. You do not have to worry about flipping the backbuffer in scripting; FS2_Open will take care of that for you.
For scripting, it is important to understand that each block of code will therefore be executed several times a second, but only once per frame. However, the exact number of times per second will depend on how much work the computer has to do (And therefore how much time it takes) for each frame. To determine how much time you should consider to have passed, you must use the ba.getFrametime() function; which will return the approximate time the frame will take, in seconds. In addition, if you "move" a ship after it has already been rendered, the change will not become apparent until the next frame.
This does mean that if you want to make a block of text move across the screen, at a rate of 5 pixels per second, all you have to do is multiply the speed by the frametime:
#Global Hooks $Global: [ --If the g_WheeXPosition variable does not exist, --it means we are on the first frame. --If that is the case, set it to zero. if not g_WheeXPosition then g_WheeXPosition = 0 end --Move the text g_WheeXPosition = g_WheeXPosition + (5 * ba.getFrametime()) --Draw "WHEE!!!" gr.drawString("WHEE!!!", g_WheeXPosition, 10) ] #End
Hooks
Scripting "hooks" form the basis of the scripting system. A "hook" simply refers to a point in the code where code to execute scripting has been added. The hook may then be added to a table, or added to a SEXP. In general usage, "hook" refers to an entry in scripting.tbl.
All hooks take the form of an identifer (such as $Global:), followed by brackets to determine what type of scripting is being executed. A lack of brackets also indicates a type of scripting execution. All current bracket configurations are listed below. Note that "$Hook:" is used merely as a placeholder, and should be replaced with the actual hook name ($Global:, $HUD:, $On Frame:, etc).
$Hook: scripting
Specifies one line of LUA scripting. In addition, the return value of that line will be passed to the interpreter and used (if applicable). Specifying a variable or value on this line will result in it being returned, as well.$Hook: [scripting]
A single set of brackets specifies a block of LUA scripting. Does not return a value unless explicitly specified. (Explicit specification is not possible at this time; but may be implemented in the future if/when more future hooks take a return value.)$Hook: [[scriptfilename.lua]]
Double brackets specify a file. This file is read out of the data/scripts directory, and must include the file extension. You may specify a compiled lua file as a target as well; this may result in crossplatform issues for a mod, however.
+Override
Some hooks additionally take a +Override: field. This field uses the same bracket configurations as above. If a hook includes a +Override field, this hook always determines whether the default FS2_Open behavior that the hook is associated with will function, or will be disabled. This should be used in cases where scripting will replace the original FS2_Open behavior. For example:
$HUD: [ gr.setColor(255, 255, 255, 255) gr.drawString("HUD Disabled", 50, 50) ] +Override: true
Additionally, override hooks are (by convention) executed before FS2_Open behavior. The HUD hook override, for example, is executed before any part of the HUD is actually drawn.