Architecture
Overview
The framework is built from two kinds of scripts:
Include files (
ff_i_*) are libraries of functions and constants shared by every script. They contain no entry point and do nothing on their own.ff_i_coreincludes all of them, so a script only needs a single#include "ff_i_core".Unit files (
ff_u_*) are small, self-contained scripts. Each one provides a single feature, for example respawning destroyed placeables, logging chat, or limiting inventory size.
Units
A unit is one script file that implements one feature and reacts to events. It holds everything the feature needs: its constants, variables, functions and event handlers. No other script ever calls a unit directly.
Every unit follows the same pattern:
When the module starts, the framework sends the
ON_REGISTERevent to the unit.On
ON_REGISTER, the unit subscribes to the events it needs withSubscribeToEvent().Each time one of those events fires, the unit is executed again, reads the current event with
NWNX_Events_GetCurrentEvent(), and acts on it.
#include "ff_i_core"
void main()
{
string sEvent = NWNX_Events_GetCurrentEvent();
if (sEvent == ON_REGISTER)
SubscribeToEvent(ON_DEFAULT_ITEM_ACQUIRE, __FILE__);
else if (sEvent == ON_DEFAULT_ITEM_ACQUIRE)
{
// do something
}
}
Script prefixes
Prefix |
Purpose |
|---|---|
|
Framework include files. |
|
Framework units. |
|
Framework user-defined scripts, set directly in an object’s event slots (for example a trash can’s OnClose). |
|
Conversation actions and conditions. |
|
Framework system scripts (module startup, overrides, …). |
|
User units. |
|
User user-defined scripts. |
|
User system scripts (for example |
Adding and removing units
Units are discovered automatically. On module start,
ff_x_modstartex searches the resource manager for every compiled
script matching ff_u_*, ff_d_*, us_u_* and us_d_*, and sends
each one the ON_REGISTER event. Script names containing a double
underscore (__) are skipped.
This means that:
adding a unit only requires adding its file to the module or a hak;
removing a unit only requires removing its file;
a unit can also be turned off without removing it, through its module variable
ENABLE__<UNIT>. Most units are opt-out (set the variable to “N” to disable them); a few are opt-in (set it to “Y” to enable them). Each unit’s page lists its variable and its default.
User units
Builders can extend the framework without touching its files: adding a new
us_u_* script to the module is enough. User units are registered right
after the framework units, and work exactly like them.
Events
Event dispatching
Every event is sent and received through the NWNX event
NWNX_ON_RUN_EVENT_SCRIPT_BEFORE. On module start, the framework
subscribes the single dispatcher script on_event to it. Whenever the
engine is about to run an event script (module, area, creature, door,
placeable, store, trigger, …), on_event signals the matching
ON_DEFAULT_* event to every unit subscribed to it.
Event scripts are whitelisted on demand: SubscribeToEvent() adds
an engine event to the NWNX whitelist only when a unit subscribes to it. An
engine event nobody listens to therefore costs nothing.
On top of the engine events, the framework sends events of its own, such as
ON_REGISTER, ON_MODULE_START,
ON_DELAYED_REGISTER, the real-time minute, hour and day events,
and console commands. See ff_i_events_c for the full list.
Existing event scripts
The framework runs before the engine event script and never skips it. Scripts already set in an object’s event slots keep working as before, so the framework can be added to an existing module without rewriting its scripts.