Libraries

From ZDoom Wiki
Jump to: navigation, search

Libraries are most useful for large scale projects which span multiple levels (hub or standard form) in which you need certain scripts (such as mission objectives, player alterations such as speed, health or ammo, or certain other things) to execute for each map. Libraries will not only save you space in your scripts (which is useful if you use a script editor with limited length, such as Wadauthor has) but you also will not have to copy over certain scripts every time, and when you want to modify it you will not have to change it for every single map (which can become tedious and lead to errors). Making a library is relatively simple, though it can probably be considered one of the more advanced features ACS has to offer.

First off you need your library file. It can be named anything, though giving it the file extension .acs is probably best to avoid confusion. This is just a standard text file (like normal map scripts) that contains any scripts and functions you will use often in the maps. At the very top of it should be two lines:

#library "libname"
#include "zcommon.acs"

Where 'libname' is the name of your library (limited to eight characters, for reasons you will soon see). This is then compiled the same as any scripts lump, with ACC (if you use it from the command line, the syntax is 'acc inputfile.acs -o output.o'). Next you must insert the newly created object (.o) file into your wad between two A_ markers (A_START and A_END), and the name of the lump will be the same as "libname" given above (this is why you should limit the name to eight characters). So if you have #library "somelib" then the lump name between A_START and A_END should be SOMELIB. Modern resource editors such as SLADE 3 can compile an ACS source lump and place the compiled output in the namespace marker automatically if they know where ACC is. Lastly, a LOADACS lump, in which the name of the library is listed, is needed to actually load the library.

That is about all you need to know with how to actually get a library working in your wad, so I will go into a few more things. First off, a library can contain both scripts and functions. Scripts, as always, can be ENTER, OPEN, LIGHTNING, DEATH, RESPAWN, or regular scripts with zero to four arguments. As is the case with scripts in the BEHAVIOR lump, ENTER and OPEN scripts in your library will execute once you enter a new map, so make sure you actually want those scripts to execute on every map.

Also note that if you assign any scripts in the library the script numbers in the library will only work if the same numbers have not been used in the maps. So if you define script 100 (int speed, int tag) in your library it will only work, if you do not have script 100 in any of the maps (so your best bet is to use high numbers that you would normally not use in map scripts). Alternatively, the script conflict could be avoided by using named scripts.

If you define constants in your library, and you want these constants to be accessible by the importing maps, you need to use #libdefine instead of #define.

When you have your library set up you then must (obviously) be able to use the functions and scripts within it. Doing so is simpler than you might imagine. Just simply add to the top of each map script file:

#import "libname.acs"
#include "zcommon.acs"

It is important to use #import and not #include, because using #include will compile libname.acs (your text format library) into the BEHAVIOR lump which quite defeats the purpose of a library. Note that you can use full (or partial) paths, so #import "/doom/levels/mymaps/foomap/lumps/lib/foolib.acs" is perfectly legal (though any user wishing to recompile the library and behavior lumps himself (for whatever reason) will have a somewhat hard time, that is really the least of your worries).

At any rate, after you have done that, any functions and scripts in your library can be readily called from within your map scripts. Note that included script files in the imported library will not be accessible in your map scripts when you #import a library (hence why you need to #include "zcommon.acs" again).

So now that I have covered everything on how to make and use ACS libraries, I will provide a short and (hopefully) simple example, which is a simple level objective library. I have commented it more than I normally do, and it assumes you have some fundamental knowledge of ACS (such as loops, if clauses, HudMessage, #define, functions, arrays and variables), so if you do not, do not go trying to wrack your brain figuring it out (also if you do not know about those things, you probably should not be using libraries just yet anyway).

//foolib.acs, the script (library) text file
#library "foolib"
#include "zcommon.acs"

#define MAX_OBJECTIVES 4 //max number of objectives per map

//this array holds the objectives, this is altered each time you enter a new map
world str 0:objectives[];

//this function is called by script 999 (See below)
function void printobjs(void)
{
	HudMessage(s:"Objectives\n\n"; 
		   HUDMSG_PLAIN | HUDMSG_LOG, 0, CR_RED, 0.0, 0.0, 5.0 );
	
	//print out each objective
	for(int x = 0; x < MAX_OBJECTIVES; x++)
	{
		HudMessage(s:objectives[x]; 
			   HUDMSG_PLAIN | HUDMSG_LOG, 0, CR_WHITE, 0.0, (x * 0.1) + 0.1, 5.0);
	}
}

//This script will execute each time you enter a new map, showing the 
//mission objectives for that particular map.  If you want the player 
//to be able to see their objectives after they disappear, you'll need 
//to set up a puke 999 alias in a KEYCONF lump.
script 999 ENTER
{
	printobjs();
	delay(35 * 5); //five second delay
	//note that delays cannot be put in functions, which is why it appears here
}

//this is a scripts lump for map01
#import "foolib.acs"
#include "zcommon.acs"

script 100 OPEN
{
	objectives[0] = "Find the shotgun";
	objectives[1] = "Get the blue key";
	objectives[2] = "Destroy the computer";
	objectives[3] = "Escape from the complex";
}

//Then you simply have whatever scripts you need for the map under here.
//If you like, when you complete an objective you can alter the color of 
//the completed objective so the player knows they completed it.  For 
//instance the hudmessage that prints the objectives prints them in 
//white, so the following script will execute when you complete 
//the first objective (objectives[0])

script 1 (void)
{
	objectives[0] = "\cFFind the shotgun";
	//the \cF appended to the front of the string changes the color of 
	//the message to yellow (or gold)
}

//this is a scripts lump for map02
#import "foolib.acs"
#include "zcommon.acs"

script 100 OPEN
{
	objectives[0] = "Kill some imps";
	objectives[1] = "Eat a cheeseburger";
	objectives[2] = "Find a BFG";
	objectives[3] = "";
	//if you don't want an objective to show up, make it an empty string
}

Note that I am storing each objective in the map scripts, you could store them in the library, but then you would need to have the exit map scripts note which map you were entering (not hard to do) and then select the appropriate array in the library when printing the objectives. This is the most simple and easy to understand implementation (I hope) so as to avoid any confusion as much as possible.