How to write templates

Templates are very useful not only for beginners, but also for experienced programmers who can use often-needed functions from a library, without having always to invent the wheel twice (or even more). However, templates and libraries can quickly become a huge mess when certain guidelines are not followed - especially when many users contribute to them. Here are some suggestions to keep the template system robust and usable.

Template and library structure

A7/A8 templates consist of a .h file (the customizable template) and optionally a .c file (a library containing functions that are used by the template). The .c library is located in the include folder, and can also be used in non-template scripts. The .h file is originally located in the templates\code folder, and can be automatically copied by the WED Project Manager into the user's work folder for customization.

In C/C++, a .h file normally only contains definitions, while the .c file contains the real variables and functions. This is different here. The .h template often contains set-up functions and customizable variables. And the .c file must be usable as a stand alone library, and thus must also work without the .h file.

Including a template manually

For manually including a template, the user copies the .h file in his work folder and writes for instance

#include "camera.h"

at the beginning of his script. If a template or library file needs other templates or library files, it must include them itself. However, libraries should be 'stand alone' whenever possible. All files that can be included must contain simple 'guards' against including them twice, like this:

///////////////////////////////////////////////
// Camera template 
//////////////////////////////////////////////
#ifndef camera_h
#define camera_h
#include <camera.c> // include the camera library
... // camera.h template content
#endif

If a .h template file requires a .c library, the .h file must include the .c file - not the other way around!

If a template or library file requires certain images or external files, it must include the folder with PRAGMA_PATH.

Functions, variables, skills

The library functions must be short, simple, modular, and easy to understand, so that users can reuse them in their scripts without much hassle. Do not use extreme programming tricks, and do not write functions containing endless code over several pages. If your function is really this complicated, divide it into general subfunctions that are usable separately. If this is not possible, the function probably not suited for a library anyway.

The library functions must work in scripts that don't use templates; therefore, they must not rely on any functions, variables, or defines from a template. Of course, a template can redefine a variable that is already defined in a library. In this case the variable is declared twice. The declaration in the template comes later, and thus overrides the declaration in the library.

For library functions, the same rules as for writing good C/C++ code applies. Information to a function should normally be passed through parameters, not through global variables. Library functions must still work when they are called more than once, or are assigned to more than one entity. Library functions should be as 'general' as possible, which means that they must not be 'hardwired' to certain keys, events, or the like. Anything specific, such as key assignment or use of certain sounds or bitmaps, should be customizable and belongs in the template, not in the library.

If a function has to repeat similar tasks - such as, doing something similar with all 4 wheels of a car - you might be tempted to just multiply parts of your code with copy'n paste. Fight that temptation and use a loop or call a subfunction. Having many similar code sections with subtle differences is worst beginner's style, inflates the script, makes the code hard to modify or fix, and is one of the most frequent sources of bugs.

Avoid defining global variables or pointers in a library script, unless they are absolutely necessary for exchanging data between functions, or subject to customizing. If a variable is only used in a certain function, but must be preserved between calls, use a static variable rather than a global variable. Defining a global variable "temp" in a library will be subject to severe punishment.

When a certain variable is a property of an entity - such as the motor power of a car - an entity skill must be used rather than a global variable (otherwise, all cars in a user's level had the same motor power!). The library can use skill1..skill20 for values that the user sets up in WED, skill41..skill50 for materials and shaders, and skill61..skill100 for entity-specific variables. Usually, skill66 (actor_id) is used for an object identification code. The rest of the skills is reserved for the user. When entity skill1..skill20 are used, they must always be checked if they are zero, i.e. not set in WED, and then default to an average value (which can, in turn, be set up by customizing the template). Avoid unnecessary customization. For instance, the wheel offsets of a car's right and left side are the same, so it is sufficient to set them up for one side. Define meaningful names for all used entity skills at the beginning of the library; never access a skill by number.

Objects

Don't define static file objects in a library (such as SOUND, BMAP etc). Otherwise the files are always loaded at startup time, at a huge time penalty for anyone who includes the .c file! Instead, pass a char* or STRING* for the file name, and load or create the object only at runtime when it's needed.

Avoid defining global panels, texts, screen entities, etc. in a libary. Instead create them locally in the function that needs them. If required, use a _startup function for automatically initializing the objects. Don't used fixed coordinates for screen elements - other users might own a monitor with a different size. When a position depends on the screen size or a bitmap size, use screen_size or bmap_width or bmap_height to calculate the position instead of using fixed coordinates or the customize panel. Give the user x/y offsets for placing screen elements, and use positive values for offsets from the left or upper screen edge, and negative values for offsets from the right or lower screen edge. Look into the speedometer placement of car_props.c to see how it's done.

Naming convention

For avoiding name conflicts with user scripts, all functions should follow a consistent naming scheme (f.i. in a camera.c library, functions, global structs and global variables should normally begin with the prefix "cam_"). If a bunch of variables are related, they should be put together in a struct - just like in C/C++. Then they can have any name as long as the struct itself follows the naming convention.

Within the library, exported functions or actions should be separated from internal helper functions. Brief comments (one or two lines) should explain the purpose and use of any exported function.

Header and Comment tags

Both the .h file and the .c file should come with a descriptive header. The .c file header just contains some info about the file, the author, and the version, as in level.c. The .h file header however must follow the header convention as described under comment tags, and contain the ver, engineReq, date, title, class, type, help, prefix tags. A needs tag is optional. Example:

// ---------------------------------------------------------------------
// STARTHEADER
//
// ver: 1.1
// date: 091011
// engineReq: 7.82
// prefix: wfx_
//
// title: Weapon Effects
// class: WEAPON
// type: SUPPORT
// help: Handle weapon effects (bullet holes, sparks, etc.).
//
// needs: t_particles.h
//
// ENDHEADER
// ---------------------------------------------------------------------

Note that the needs tag must not include the .c library file or any other non-customizable files, which are directly included in the template file. It must however include other .h template files if they are required. WED then copies the templates into the work folder for subsequent customization.

The title tag is the title that appears in the Project Manager. It must not contain any special characters such as &, <, >, etc.

The type tag is either USER for user-customizable templates, or SUPPORT for mere support templates that can not be customized by the user.

All customizable fields in the template file should contain a help and id tag. All actions in the template should contain a desc tag and - if skills 1..20 or flags 1..8 are used - a uses, skill, or flag tag. Examples:

// entry: Hole life (seconds)
// help: Bullet hole decal lifespan
// id: 21
var wfx_bullethole_time = 60;
// Desc: gun that creates a light 'flash' at hitpoint
// skill1: flashcolor_blue 255
// skill2: flashcolor_green 255
// skill3: flashcolor_red 255 // skill4: range 500

action wfx_FlashGun()
{
...

The function of customizable fields can be directly tested in SED (Tools / Customize script). The function of the header must be tested by adding and updating the script with the WED Template Manager. Note that the path to the template scripts can be changed by editing data\options.scr; by default it's template_6\code.

Adding a template to the Project Manager

A8 The WED Project Manager automatically adds, updates, and removes templates from the main script. For making a template known to the template manager, make sure that the .h file is in the templates\code folder, then start template.exe in the Gamestudio folder. Navigate to your new template script and click [Run]. This updates the template.xml file that the code manager uses for finding templates.

Examples

Have a look into the car library car.c and its template car.h (A8 only) , or the camera library camera.c and its template camera.h. These files are examples how template and library files should be structured.


► latest version online