Hpl3:Community:scrapbooks:scripting:advanced - api and infrastructure

From Frictional Wiki
Jump to navigation Jump to search

Advanced - Scripting API and Script-Side Infrastructure

This is a somewhat quick rundown of some of the more advanced script features and low-level script-side code organization within HPL3/SOMA.

<note> Advanced
Please note that in most cases you can script your mod without using these advanced features. A great majority of users will find that the usual helper functions provide more than enough flexibility. Features covered here are of use primarily to those who drastically want to change some aspect of the game, or create something completely different, but cannot achieve this by the usual means, as Jens has noted in a forum thread. Stick with the helper functions if you can.
If you're curious about how some of the code works, or have a need for some of these advanced features, read on.</note>

(Note: these are scraps of info collected by studying .hps files. Feel free to add new or correct existing information.)

The core scripting API exposed by the game is listed in the hps_api.hps file. Users may need to start the game once to generate this file if it's not present.
The file itself is ?not actually used by the game. It's purpose is (1) to provide a quick reference to the core scripting API to the developers, and (2) to provide code completion for setups using CodeLite.
NOTE: this file does not list all functions/interfaces available to the scripters - a lot of this code is spread out through various .hps files, that were built around the core API.
Script files now support #include-s.
Scripting is now object-oriented.

Generally speaking, for many of the classes defined in the scripts, there's a high level interface class (prefix "i") from which they inherit, and that has the sole purpose to expose core game objects.
This is generally done by exposing a single "base object" (usually named mBaseObj) that can be obtained in derived classes via the GetBase() method.

E.g. Game maps are now scripted by writing an implementation of a cScrMap class that derives from iScrMap, which in turn contains (and exposes) a member variable mBaseObj of type cLuxMap. Note that cLuxMap is part of the core API and can be found in hps_api.hps.

[cScrMap] —–inherits—–> [iScrMap]<>—–contains(as mBaseObj)—–>[cLuxMap]

The game most likely injects the appropriate mBaseObj when it constructs the map script. It seems to do so via the SetupBaseInterface(cLuxMap @aMapObject) function.
Then, within cScrMap you can do
GetBase().SomeBaseMethod(params_if_any);

However, helper scipts expose a lot of the same functionality as free functions. It can be just a simple wrapper, but these helper/module functions often do extra work.
E.g. cLuxMap (exposed in iScrMap) has a function called SetDisplayNameEntry(“name”).

In helper_map.hps, this function that does the same thing is defined:

void Map_SetDisplayNameEntry(const tString&in asNameEntry)
{
	cLuxMap@ pMap = cLux_GetCurrentMap();
	pMap.SetDisplayNameEntry(asNameEntry);
}

Note two things. First, in this case it just calls the version defined in cLuxMap. Second, it obtains the map object by calling cLux_GetCurrentMap() - which is a pretty convenient function!
cLuxMap@ cLux_GetCurrentMap() is a free function exposed by the engine (core API), and can be found in hps_api.hps.

The basic setup for map scripts is similar to what it was before - except that the functions are now within a class.

void OnStart()
void OnEnter()
void OnLeave()
// Then you add timers, event handlers (callbacks) etc...
void OnPlayerKilled(numDeaths, source)   // this one's new!
void OnAction(action, isPressed)        // button/input events?  (mostly used for debug?)
void Update(timeStep)                    // game loop update phase? WOW!
void OnGui(timeStep)                     // player GUI or in-game GUI?
//etc...

It's instructive to examine some of the elements of LuxMap's interface (some functions and signature details omitted; comments mine):

class cLuxMap
{
	// These seem pretty usefull
	cWorld@ GetWorld();
	iPhysicsWorld@ GetPhysicsWorld();
	const tString& GetName();
	const tString& GetFileName();

	// we can now implement update-level logic! This is called on every update (roughly: every frame)!
	void Update(float afTimeStep);

	// dynamic entity creation/destruction, and existance querying!
	void CreateEntity(const tString&in asName,  const tString&in asFile,  const cMatrixf&in a_mtxTransform,  const cVector3f&in avScale);
	bool DestroyEntity(iLuxEntity @apEntity);
	bool EntityWasDestroyed();
	bool EntityExists(iLuxEntity @apEntity);

        // get entity as object, other useful entity funcs
	iLuxEntity@ GetEntityByName(const tString&in asName,  ...);
	iLuxEntity@ GetEntityByID(tID alID,  ...);
	tID GetEntityIDByName(const tString&in asName,  ...);

        // check game files to see how this works. probably a utility func to get sequentially named entities or something like that
	bool GetEntityArray(const tString&in asName,  ...,  array&lt;iLuxEntity@&gt; &inout avOutEntities);

        // TAKE NOTE: get's the player
	iLuxEntity@ GetPlayerEntity();

        // Can work with timers, probably the same as in Amnesia
	void AddTimer(const tString&in asName,  float afTime,  const tString&in asFunction);
	void RemoveTimer(const tString&in asName);
	// (bunch of timer-related functions omitted)
	void RestartCurrentTimer(float afNewTime=-1);

        // This should support working with components - which are probably means to "extend" or reuse entity behavior in some way
	void AddEntityComponent(iLuxEntityComponent @apComp);
	void RemoveEntityComponent(iLuxEntityComponent @apComp);
	iLuxEntityComponent@ GetEntityComponent(eLuxEntityComponentType aType,  const tString&in asName);

        // Ooooh! Iterators! Cool. :D
	cLuxEntityComponentIterator@ GetEntityComponentIterator(eLuxEntityComponentType aType);

        void PlacePlayerAtStartPos(const tString&in asName);   // <-- usefull

        // These can be very useful for anyone implementing the Update function
	int GetTimeStamp();
	float GetElapsedTime(int alTimeStamp);

	// Dynamic script execution? Coooool!
	bool ScriptPrepare(const tString&in asMethod);     // <--- is this a method name or method definition (code as a string)?
	bool ScriptPrepareFast(const tString&in asMethod,  int alId);
	bool ScriptExecute();
	bool ScriptMethodExists(const tString&in asMethod);
	bool ScriptMethodExistsFast(const tString&in asMethod,  int alId);

	// I guess these set script function arguments for dynamic scripts
	void SetArgBool(int alArgNum,  bool abVal);
	void SetArgInt(int alArg,  int alX);
	void SetArgFloat(int alArg,  float afX);
	void SetArgString(int alArg,  const tString &in asStr);
	void SetArgVector2f(int alArg,  const cVector2f &in avX);
	void SetArgVector3f(int alArg,  const cVector3f &in avX);
	void SetArgVector2l(int alArg,  const cVector2l &in avX);
	void SetArgVector3l(int alArg,  const cVector3l &in avX);

	// Pretty sure these are return values from executed dynamic scripts
	bool GetReturnBool();
	int GetReturnInt();
	float GetReturnFloat();
	tString GetReturnString();

        // This one is useful
	void CheckLineOfSight(const tString&in asCallbackFunc,  ... direction ...,  bool abCheckOnlyShadowCasters,  bool abCheckOnlyStatic);

	// etc, etc...
}

TODO: components, entities - scripting-related config files, etc.