Ultima VII Usecode

UCC
The following is a discussion about UCC, Exult's Usecode Compiler, was taken from Exult's source code. The original file can be found on the Exult project site.

Introduction
Ultima7's plot and conversations are controlled by a script encoded in 'usecode', a file in the game's 'static' directory. UCC is our attempt at providing a language and compiler so that we and others can write our own game plots.

This document is my first at documenting UCC. Please be aware that the UCC language is still being developed, so some language constructs may change to make script-writing easier.

The UCC language (which I'll sometimes refer to as 'usecode') is generally patterned after 'C', but with many exceptions:
 * No type-checking. Values can be integers, strings, and vectors.
 * Some constructs ('for' loops, 'case' statements) are not implemented. (This may change.)
 * Game-specific features have been added, such as 'converse.'
 * Functions need to be assigned unique numbers (though this may be relaxed in the future).

Command line options
write output to
 * -o

add to the include path
 * -I

compile for SI. Usecode for SI needs to be compiled with this option.
 * -s

Top-Level
At the top-level, UCC supports pre-processor directives, functions, function prototypes, and integer constant declarations.

Pre-processing
UCC only supports the "#include " for pulling in anothersource file. In the future, support for macros ("#define") may beadded. And although we have not done so yet, we should also provide header files that declare various Exult constants.

Functions
Functions are similar to those in C, but without type declarations. Plus, one must assign a unique number which may be used to reference that function in ExultStudio.

Example: Add2 0x793 (a, b) { return a + b; }

Ranges of function numbers have specific meanings in Exult:

Function Prototypes
These are declarations of other functions that can be called. In particular, these can refer to functions in U7 usecode. Example:

extern Ask_yesno 0x90a; // Rets. true if 'Yes'.

Constant Declarations
One can declare integer constants globally, as follows: const int AMY = 0x168;	  // Amy's NPC #. const int AVATAR = -356;

Statements
Mainly want to document conversations at the moment:

Conversations
Since conversations are one of the game's strong points, I've tried to make their creation as easy as possible. The structure is:

converse () {   case [(remove)]:

'Say' may also be used without specifying an npc. In this case, the text will appear next to the face of the last npc that spoke.

Each 'say' call will require a click from the user to proceed. More than one string can be specified in 'say', in which case they are concatenated together. A '~' in a string will cause text output to pause until the user clicks. Example:

Add/Remove
These allow you to enable/disable possible choices that the user may make. This only makes sense for choices that appear in 'case' entries in the conversation. Example:

converse (["Name", "Bye"])	// 2 choices enabled. {       case "Bye": break; case "xyzzy" (remove): say("Please don't make fun of my name."); add("Bye"); case "Name" (remove): say("My name is xyzzy"); add("xyzzy");		// Now this is enabled. remove("Bye"); }

Hide
While "npc->say" will bring up a portrait of an NPC during a conversation, "npc->hide" will remove that portrait.

USECODE File
Documentation for the STATIC/USECODE file. This information can also be found here.Italic text

PsudoBNF overview:
 * LEFTVALUE = RIGHTVALUE is read as "LEFTVALUE consists of RIGHTVALUE".
 * [TOKEN]* is read as "zero or more of TOKEN(s)".
 * TOKEN = lots of text. "lots of text" is the details of what TOKEN is ususally data size, and vague description of relevance.

USECODE_FILE = [FUNCTION]*

FUNCTION = FUNCTION_HEADER DATASEGMENT CODESEGMENT

FUNCTION_HEADER = FUNCID FUNCSIZE

DATASEGMENT = DATASEG_SIZE DATASEG_DATA

CODESEGMENT = NUM_ARGS NUM_LOCALS EXTERNS USECODE_BLOCK

EXTERNS = NUM_EXTERNS EXTERN_LIST

FUNCID = (uint16) The "id" of the function. (Also referenced as "function number".)

FUNCSIZE = (uint16) The total size of the function (code + data). Am unsure if this also includes the "header".

DATASEG_SIZE = (uint16) The total size of the data segment (in number of bytes).

DATASEG_DATA = Consists of DATASEG_SIZE bytes of string data of the format "string\0" where \0 is the NULL byte.

NUM_ARGC = (uint16) The number of arguments to the function on the stack.

NUM_LOCALS = (uint16) The number of local "variables" used in the function.

NUM_EXTERNS = (uint16) The number of external function numbers in the external function list.

EXTERN_LIST = NUM_EXTERNS number of external numbers, each is (uint16) in size.

USECODE_BLOCK = [USECODE_CALL]+

USECODE_CALL = Consists of (ubyte8) opcode and a variable number of trailing bytes, depending upon which opcode. See: usecode/ucxt/Docs/opcodes.txt for more information.