NAMELIB NAME GENERATOR: THE MANUAL

    NameLib is a simple library for constructing made up names for
    characters, places, and other entities in procedurally generated
    games. Makefiles are provided for OpenWatcom C and for the Gnu C
    Compiler.

    Features of the library include:
    - Different sounding names based on different languages.
    - Control over name length and composition.
    - Scanning of text files to build language data.

    Limitations of the library:
    - Names are limited to 8-bit character sets.
    - Scanned text files are limited to 7-bit ASCII.
    - No real awareness of letter frequency or pairings (like QU).

    So far, NameLib has been used in Star Cadre: Combat Class and Star
    Cadre: Team Command, a pair of science fiction squad combat games,
    and in The Chambers Beneath, a fantasy roguelike RPG. Tactical
    combat and role-playing games are the primary use case for this
    library.

Licence

    This library, its associated programs and utilities, and its
    documentation have been released into the public domain by its
    author Damian Gareth Walker.

How It Works

    Memorable names should be split into pronouncable sequences of
    consonants and vowels. Furthermore, the consonant sequences at the
    start, middle, and ends of names vary; not all consonant sequences
    that sound good at the end of a word would work as well at the
    beginning.

    NameLib separates letter sequences into five groups: initial
    consonants, early vowels, later consonants, later vowels, and
    final consonants. It then constructs names from these groups in
    such a way that they should be as pronouncable as real-world
    names.

    NameLib has a utility for building up these groups of letter
    sequences: a program that reads an ASCII text file, analysis the
    consonants and vowels within, and sorts them into the five groups
    according to where they were found in the word. The words
    generated from this data will vaguely approximate the language
    used in the source text file.

Binary Package Contents

    The NameLib binary package for DOS contains the following
    directory structure and files:

    BIN\ contains binaries and assets.
	DEMO.EXE is the demonstration program.
	BUILDNAM.EXE is the name data builder.
	NAMES.NAM is an example name data file.
    DOC\ contains the documentation.
	NAMELIB.TXT is this document.
    INC\ contains the include files.
	NAMES.H is the only include file.
    LIB\ contains the library files.
	NAMES.LIB is the sole library.

Source Package Contents

    The NameLib source package contains the following directory
    structure and files:

    BIN\ is the directory for binaries.
    DOC\ contains the documentation.
        NAMELIB.TXT is this document.
    INC\ contains the include files.
	NAMES.H is the only include file.
    LIB\ is the directory for the library file.
    OBJ\ is the directory for compiled object files.
    SRC\ is the source code directory.
        DEMO.C is the code for the demonstration program.
	NAMES.C is the code for the library.
    MAKEFILE.WCC is the makefile for OpenWatcom C.

Building a Project with NameLib

    To use NameLib's functions in your project, you need to do the
    following two things. Firstly, you need to include the "names.h"
    header in your own project's source:

	#include "names.h"

    You can copy this header into your project's header directory, but
    a better idea is to add NameLib's include folder to your include
    path on compilation, like this:

	C:\PROJECT\> wcc project.c -I=\namelib\inc

    This assumes that NameLib is installed in a directory called
    \namelib. When you link your object file into an executable, you
    need to link it also with the NAMES.LIB file, like this:

	C:\PROJECT\> wcl project.obj \namelib\lib\names.lib

Rebuilding NameLib

    You might want to rebuild NameLib from its sources, particularly
    if you've made a customised version of it. Two makefiles are
    provided to simplify this process. In DOS, assuming you unpacked
    the source files into the \namelib directory, you can build the
    project like this:

	C:\NAMELIB\> wmake -f makefile.wcc

    This builds the library for the large menu model. If you want to
    build for other memory models, you need to edit the makefile,
    replacing occurrences of '-ml' with the compiler switch for
    another memory model, e.g. '-ms' for small.

Summary of Functions, Methods and Attributes

    typedef struct names Names;
    Names *new_Names (void);
    struct names {
	int minlength;
	int maxlength;
	int miniterations[5];
	int maxiterations[5];
	char **letters[5];
	int lettercounts[5];
	void (*destroy) (Names *names);
	int (*read) (Names *names, FILE *input);
	int (*write) (Names *names, FILE *output);
	int (*add) (Names *names, int group, char *letters);
	char *(*generate) (Names *names, char *name);
    }

minlength

    The minimum length of a generated name. The default for this is
    three letters. Enforcement is primitive: if a name falls short of
    this many characters, it is discarded and another is generated. It
    is possible to hang the library by giving it unreasonable
    parameters for this and other attributes.

maxlength

    The maximum length of a generated name. The default for this is
    twelve letters. Enforcement is primitive: if a name exceeds this
    many characters, it is discarded and another is generated. It is
    possible to hang the library by giving it unreasonable parameters
    for this and other attributes.

miniterations

    The minimum number of iterations for each letter group. Setting
    this to 1 for a group ensures that it will occur in a word. For
    instance, setting miniterations[0] to 1 ensures that there will be
    an initial consonant in the generated name. For each element the
    value can be:

	[0] 0 to allow or 1 to enforce an initial consonant sequence,
	[1] meaningless, as there is always a vowel sequence,
	[2] 0 or more for middle consonant sequences,
	[3] unused as middle consonants are always followed by vowels,
	[4] 0 to allow or 1 to enforce a final consonant sequence.

maxiterations

    The maximum number of iterations for each letter group. Setting
    this to 0 for a group prevents it from ever occurring in a
    word. For instance, setting maxiterations[4] to 0 ensures that
    there will not be a final consonant in the generated name. For
    each element the value can be:

	[0] 0 to prevent or 1 to allow an initial consonant sequence,
	[1] meaningless, as there is always a vowel sequence,
	[2] 0 or more for middle consonant sequences,
	[3] unused as middle consonants are always followed by vowels,
	[4] 0 to prevent or 1 to allow a final consonant sequence.

letters

    This is the data for the letter sequences themselves. There are
    five arrays of pointers, one for each letter sequence group. Each
    array contains a number of pointers to strings, which are the
    null-terminated letter sequences. If generating names, you need
    not refer to this directly; you'll only need to worry about it if
    you want to construct your own letter data.

lettercounts

    The number of letter sequences in each of the five letter sequence
    groups. If generating names, you need not refer to this directly;
    you'll only need to worry about it if you want to construct your
    own letter data.

new_Names ()

    Declaration:
    Names *new_Names (void);

    Example:
    /* create a new name generator */
    Names *names;
    names = new_Names ();
    /* ... initialise and use the name generator ... */
    names->destroy (names);

    This creates a name generator. It will not have any name data
    assigned to it yet, so it will not be able to generate any names
    until you initialise it. You do that by loading name data with
    read () or creating it yourself. Multiple name generators can
    exist at once; for instance, you might want to draw upon different
    languages for names of protagonists, antagonists, and places.

destroy ()

    Declaration:
    void (*destroy) (Names *names);

    Example:
    /* show the name generator life cycle */
    Names *names;
    names = new_Names ();
    /* ... initialise and use the name generator ... */
    names->destroy (names);

    This destroys a name generator when it is no longer needed. An
    object-oriented syntax is used, as destroy () is a method of the
    Names object.

read ()

    Declaration:
    int (*read) (Names *names, FILE *input);

    Example:
    /* read name generator data */
    Names *names;
    FILE *fp;
    char header[8];
    names = new_Names ();
    fp = fopen ("example.nam", "rb");
    fread (header, 8, 1, fp); /* buildnam writes a header */
    names->read (names, fp);
    fclose (fp);

    Reads in name generator data from an already open file. A file
    pointer is taken rather than a filename, so that data can be
    loaded from an asset or game save file that might contain other
    related data. The method returns 1 if successful or 0 if not.

write ()

    Declaration:
    int (*write) (Names *names, FILE *output);

    Example:
    /* write name generator data */
    Names *names;
    FILE *fp;
    char *header = "NAM100N"; /* for buildnam compatibility */
    names = new_Names ();
    fp = fopen ("example.nam", "wb");
    fread (header, 8, 1, fp);
    names->read (names, fp);
    fclose (fp);

    Writes name generator data to an already open file. A file pointer
    is taken rather than a filename, so that data can be saved in an
    asset file that might contain other related data. The method
    returns 1 if successful or 0 if not.

    Typical use for this is to load a file created by the buildnam
    utility and save it in a game's own asset file, but you might also
    use it if you create your own name data by means other than
    scanning an ASCII text file. The name data for robot IDs in Star
    Cadre: Team Command was created this way.

add ()

    Declaration:
    int (*add) (Names *names, int group, char *letters);

    Example:
    /* Create data for random robot "names" */
    char letter, sequence[3];
    int group, number;
    for (group = 0; group <= 4; group += 2)
        for (letter = 'A'; letter <= 'Z'; ++letter) {
	    sprintf (sequence, "%c", letter);
	    names->add (names, group, sequence);
	}
    for (group = 1; group <= 3; group += 2)
        for (number = 1; number <= 20; ++number) {
	    sprintf (sequence, "%d", number);
	    names->add (names, group, sequence);
	}
    /* ... generate some "names" ... */

    Adds a letter sequence to one of the letter sequence groups. The
    snippet of code above shows the "consonant" groups being filled with
    letters A to Z, and the "vowel" groups being filled with numbers,
    for a simple system of robot code designations. Generated "names"
    might include R2D2, C3P0, K12X, etc.

generate ()

    Declaration:
    char *(*generate) (Names *names, char *name);

    Example:
    /* ... create and initialise the generator ... */
    char name_st[13],
        *name_dyn;
    names->generate (names, name_st);
    name_dyn = names->generate (names, NULL);
    /* ... do things with the names ... */
    free (name_dyn);

    Generates a name, assuming the name generator has been initialised
    with data. The code sample shows how names can be stored both in
    static variables, and can create dynamic variables for names of
    unrestricted length. This function returns NULL if a name could
    not be generated.

History of NameLib

    Some time in around 1990, when play-by-mail games were popular,
    the people running one of those games had an in-house magazine in
    which they published details of how they generated the names used
    in its game. That method was the one used here.

    This name generator has gone through several iterations over the
    decades, and has been put to various uses. After versions in
    BASIC, Psion OPL and even PHP, a C version was created in 2015 for
    a game that has never been released. This was adapted for Star
    Cadre: Combat Class, then spun off into its own library in 2021
    for use in other games.
