⚔ THE CLANS
⚙ THE CLANS — DEVELOPER NOTES
v0.97b2 · Source Code Reference
[0.1]

Overview

The Clans is a multi-user fantasy RPG door game for BBS systems. It features clan management, empire warfare, turn-based combat, and a unique Inter-BBS (IBBS) multiplayer system that links multiple BBSes into a shared game world. The game was written in C (originally Turbo C++ for DOS), and has since been modernized to compile with GCC and C17, targeting FreeBSD, Linux, and Windows.

Each BBS running the game acts as a "village" in a shared league. Players build clans, join empires, and fight for control of the village. Through the IBBS system, clans can travel to -- and attack -- other villages over a Fidonet-style packet network.

This document is the developer and contributor reference. For sysop installation and game setup, see release/clans.txt. For quest and NPC development, see devkit/clandev.txt.

[0.2]

License and History

The Clans was written by Allen Ussher from 1997 to 2002. The game originally ran on DOS-based BBS systems, compiled with Turbo C++ and using the OpenDoors door library by Brian Pirie. It was released as open source under the GPL v2 in 2002.

Modernization work includes:

  • Compiler: GCC with C17 standard, replacing Turbo C++
  • Platforms: FreeBSD, Linux, and Windows (was DOS only)
  • Serialization: Explicit pack/unpack macros replacing raw struct I/O
  • Platform abstraction: unix_wrappers.c / win_wrappers.c / platform.c
  • Build system: GNUmakefile (Unix) and Visual Studio solution (Windows)

The OpenDoors library is still used for BBS door integration and is linked statically as libODoors.a. For the GPL v2 license text, see docs/gpl.txt.

[1.1]

Prerequisites

Unix (FreeBSD, Linux)

  • GCC with C17 support
  • GNU Make (invoke as gmake on BSD systems)
  • libODoors.a -- the OpenDoors library, compiled for your target platform and placed where the linker can find it

On FreeBSD, GCC and GNU Make can be installed via ports or pkg. The OpenDoors library must be compiled from source. Once libODoors.a is available, the build system will link it automatically.

Windows

  • Visual Studio 2022 (version 17) or later
  • The solution file src/clans.sln contains a project for every binary. Open it in Visual Studio and build the desired projects or the entire solution. libODoors.a must also be compiled and available for the linker. Alternatively, MinGW-w64 with GCC and GNU Make can be used with the GNUmakefile path described below.
[1.2]

Building from Source

All source files are in src/. Data source files are in data/.

Unix / MinGW -- GNUmakefile

CommandDescription
gmake -j8Build game binaries and data (PAK)
gmake -j8 gameBuild game binaries only
gmake -j8 devkitBuild devkit tools only
gmake cleanRemove object files and local copies
gmake deepcleanFull clean, including bin/ directories
gmake DEBUG=1 gameDebug build with full warnings and -g

Build output goes to bin/<os>.<arch>.<flavour>/ -- for example:

bin/freebsd.amd64.opt/      Release build on FreeBSD/amd64
bin/freebsd.amd64.debug/    Debug build

The game target also copies binaries and clans.pak to the project root directory, which is where the test/ instance expects to find them. The release build uses -Os -flto. The debug build enables -Wall -Wextra -Wconversion -pedantic -g -O0.

Windows -- Visual Studio 2022

Open src/clans.sln. The solution contains one project per binary. Build individual projects or the full solution. Output goes to the standard VS output directories (Debug/ or Release/ under each project). Data files must still be built using the devkit tools.

[1.3]

Running and Testing

There are no automated tests. The test/ directory contains a pre-configured game instance for manual verification:

cd test/
./clans       Run the main game
./reset       Run the maintenance reset
./config      Run the configuration utility
./pcedit      Edit player characters

After gmake game, binaries and clans.pak are copied to the project root alongside test/. The shell scripts in test/ expect the binaries in the parent directory (../clans, etc.).

The devkit tool qtest allows testing a single quest event script outside of the full game -- useful when developing new quests.

[2.1]

Module Summary

All 61 source files live in src/. They are organized as follows:

CategoryFiles
Core game engineclans.c, game.c, empire.c, user.c, fight.c, village.c
Inter-BBS networkingibbs.c, myibbs.c, packet.h, interbbs.h
Gameplay systemsalliance.c, alliancem.c, quests.c, spells.c, items.c, npc.c, class.c, trades.c, pawn.c, voting.c, news.c, mail.c, maint.c, help.c, scores.c, event.c, menus.c, menus2.c
Data serializationserialize.c, deserialize.c, myopen.c
Platform abstractionunix_wrappers.c, win_wrappers.c, platform.c, door.c
Content and stringslanguage.c, mstrings.h
Utilitiesinput.c, video.c, random.c, parsing.c, misc.c, tools.c, readcfg.c, clansini.c, semfile.c, system.c, console.c, reg.c
Game utilitiesconfig.c, reset.c, pcedit.c
DevKit toolsecomp.c, mcomp.c, mclass.c, mitems.c, mspells.c, makenpc.c, makepak.c, langcomp.c, chew.c, gum.c, qtest.c, install.c

Compiled Binaries

TargetBinaries
DOOR_BINARIESclans, config, pcedit, reset
DEVKIT_BINARIESchew, ecomp, langcomp, makenpc, makepak, mcomp, mclass, mitems, mspells, qtest
[2.2]

Core Game Engine

FileDescription
clans.cEntry point. Command-line parsing, pre-game menu, and daily maintenance gate. main() lives here.
game.cGame-state management. Loads and saves GAME.DAT, which records the active game instance: start date, IBBS flag, next clan ID, and per-game settings.
empire.c(~100KB) Empire and domain management: buildings, army raising, tribute, and inter-BBS packet dispatch. This is the largest and most complex file in the codebase. Also processes incoming IBBS packets during maintenance.
user.c(~68KB) Player and clan data management: file I/O, user list maintenance, clan creation and deletion, and travel state.
fight.c(~50KB) Combat engine: damage calculations, the battle loop, and post-fight effects including experience gain, loot, and casualties.
village.cVillage state management. Loads and saves VILLAGE.DAT; manages tax rates, market prices, colour schemes, and village flags.
[2.3]

Inter-BBS Networking

FileDescription
ibbs.c(~101KB) The IBBS subsystem. Reads and writes Fidonet-style packet files, processes all incoming packet types from remote BBSes, and drives the IBBS portion of daily maintenance. See section 4 for packet details; see docs/ibbs-notes.txt for the full protocol.
myibbs.cLow-level Fidonet netmail message creation. Based on OpenDoors sample code by Brian Pirie. Constructs outbound netmail messages with kludge lines and INTL/FMPT/TOPT headers, with data files as attachments. Do not modify this file unless you have a clear understanding of the Fidonet message format.
packet.hPacket-type constants (PT_*). See section 4.1.
interbbs.hAdditional inter-BBS protocol definitions.
[2.4]

Gameplay Systems

FileDescription
alliance.cAlliance management ADT: create, join, and disband player alliances.
alliancem.cAlliance menus and player-facing interaction.
quests.cQuest system. Loads QUESTS.INI, executes compiled .e event scripts, and tracks per-clan completion state.
spells.cSpell system: casting, effects, and learning.
items.cItem system: inventory, buying, and equipping.
npc.cNPCs in town (streets, church, market, etc.). Loads NPC data from clans.pak. These are not the monsters fought in the mines.
class.cPlayer classes and races (PClass struct).
trades.cTrade and commerce between clans.
pawn.cPawn shop: selling and buying used items.
voting.cVillage leader voting mechanics.
news.cVillage news bulletin system.
mail.cInternal mail between clans.
maint.cDaily maintenance: stat recalculation, IBBS sync, online flag cleanup, score tabulation.
help.cInteractive help system; displays .hlp files.
scores.cScore calculation and leaderboard generation.
event.cMine event system. Runs compiled .e scripts for encounters within the mines.
menus.cMain menu rendering and navigation.
menus2.cAdditional menus (split from menus.c when that file grew too large).
[2.5]

Data Serialization

FileDescription
serialize.cSerializes structs to byte buffers using pack_* macros. All data written to disk passes through here.
deserialize.cDeserializes byte buffers back into structs using unpack_* macros. Mirrors serialize.c.
myopen.cFile I/O wrappers. Applies XOR encryption when reading and writing, manages buffer allocation, and wraps fopen/fread/fwrite.
myopen.hDeclares all BUF_SIZE_* constants. These MUST match the actual serialized byte size of each struct. Update here when adding struct fields.

See section 3.2 for serialization rules and the full BUF_SIZE list.

[2.6]

Platform Abstraction

FileDescription
unix_wrappers.cUnix-specific: _fsopen() with fcntl locking, delay() via nanosleep(), FindFileList() via glob().
win_wrappers.cWindows-specific: Win32 file enumeration, error dialog helpers, FreeFileList().
platform.cPlatform detection and initialization.
door.cBBS door interface (OpenDoors integration): string output, semaphore files, chat, status bar, file display, local-game loading.

Conditional compilation uses #ifdef UNIXY for Unix-specific code and #ifdef WIN for Windows. Platform detection is handled by mk/Platform.gmake, which sets UNIXY or WIN and selects the appropriate wrapper file.

[2.7]

Content and Strings

FileDescription
language.cLoads the compiled language file (strings.xl) into memory. Provides string lookup by index.
mstrings.hCRITICAL. Every user-visible string in the game is referenced through a macro defined here. The macros expand to language file lookups by index. Never call od_printf() or similar functions with a string literal for user-visible text. Add new strings to data/strings.txt instead.

See section 3.4 for the full strings workflow.

[2.8]

Utility Modules

FileDescription
input.cUser input handling and key mapping.
video.cTerminal output: ANSI colour codes, screen layout, text input from the user. Hardware-specific; see section 6.1 before modifying.
random.cRandom number generation (my_random()).
parsing.cString parsing utilities.
misc.cMiscellaneous helpers (date calculations, etc.).
tools.cGeneral-purpose helper functions.
readcfg.cINI/config file reader, used by clansini.c.
clansini.cLoads and parses CLANS.INI: NPC file list, race and class overrides, language file path, etc.
semfile.cSemaphore file management (ONLINE.FLG).
system.cLow-level system calls for the door environment.
console.cError output and console text cursor helpers.
reg.cLegacy registration stub (no longer enforced; registration was removed in the open-source release).
[2.9]

Tools and Utilities

Game Utilities

FileDescription
config.cCONFIG -- interactive setup for node and BBS configuration. Run once before the game.
reset.cRESET -- initializes or resets all game data files. Must be run after CONFIG on first install.
pcedit.cPCEDIT -- sysop tool for editing player character data directly.
install.cINSTALL -- installation helper (separate binary, not part of DOOR_BINARIES or DEVKIT_BINARIES).

DevKit Tools

Built by gmake devkit:

FileDescription
langcomp.cCompiles strings.txt into strings.xl.
ecomp.cCompiles .evt event scripts into binary .e files.
mcomp.cCompiles monster data text files.
mclass.cCompiles class and race data.
mitems.cCompiles item data.
mspells.cCompiles spell data.
makenpc.cCompiles NPC definitions.
makepak.cBundles compiled data files into clans.pak.
chew.cCreates .GUM compressed archives for distribution.
gum.cGUM archive format implementation (used by chew).
qtest.cQuest script tester: runs a single .e file outside of the full game environment.

Each devkit tool is documented in devkit/*.txt. See especially devkit/clandev.txt for a comprehensive content-creation guide.

[3.1]

Data Structures (structs.h)

Almost all game data structures are defined in src/structs.h. The 42 structs are organized by function:

Configuration and Runtime System

StructDescription
struct IniFileFile handles for all loaded data files.
struct NodeDataBBS node config: number, dropfile dir, FOSSIL address, IRQ.
struct configGlobal sysop config: BBS name, IBBS settings, logging flags.
struct systemRuntime state: dates, working directory, node number, local-game flags.

Core Game State

StructDescription
struct game_dataActive game instance: start date, IBBS flag, next clan ID, game settings. Persisted in GAME.DAT.
struct gameWrapper for game_data with init flag.
struct village_dataVillage state: name, taxes, markets, colour scheme, flags. In VILLAGE.DAT.
struct villageWrapper for village_data with init flag.
struct empireEmpire/domain: vault gold, land, buildings, alliance, spy data.
struct ResetDataData written by RESET, read at startup.

Player and Combat

StructDescription
struct pcPlayer character: name, HP/SP, 6 attributes, equipped items, race, class, XP, level, training points, spells.
struct clanClan: ID, username, clan name/symbol, quests completed, NPC members, items, empire link.
struct ArmyMilitary unit: footmen, axemen, knights, followers, combat rating.
struct StrategyCombat strategy: attack/defend length and intensity, loot level.
struct SpellsInEffectActive spell on a combatant: spell ID and remaining energy.
struct AttackResultBattle outcome: success flag, damage, casualties, gold and land captured.
struct PClassPlayer class or race: name, attribute modifiers, starting HP/gold, spells.

Items, Spells, and Content

StructDescription
struct item_dataItem: name, type, attribute effects, market cost, village type restriction.
struct SpellSpell: name, type flags, damage/heal text strings, SP cost.
struct BuildingTypeEmpire building: name, attack hit zones, land and energy use, construction cost.
struct LanguageLoaded language file: string offsets and the full text pool.

Social, NPCs, and Alliances

StructDescription
struct AlliancePlayer alliance: ID, name, creator, member list, shared empire, items.
struct NPCInfoNPC: name, topic list, loyalty score, wander type, village type restriction.
struct NPCNdxNPC runtime: name, clan membership, current location.
struct TopicConversation topic: visibility flags, display name, script label.

Trade and Messages

StructDescription
struct TradeListTrade offer: gold, followers, troops.
struct TradeDataTrade request: offer/ask lists, clans.
struct Msg_TxtMessage body: text buffer, line offsets.
struct MessageMessage header: to/from clan IDs and names, type, alliance, BBSID.

Inter-BBS Packets and Nodes

StructDescription
struct PacketIBBS packet header: type, GameID, date, source/destination BBSID, payload length.
struct AttackPacketAttack payload: armies, target empire, source/dest BBSIDs.
struct SpyAttemptPacketSpy mission: spy stats, target BBSID.
struct SpyResultPacketSpy result: success, empire data.
struct ibbs_node_infoNode: BBS name, village, Fidonet address, routing, mailer type.
struct ibbs_node_resetReset tracking per remote node.
struct ibbs_node_reconRecon tracking per remote node.
struct ibbs_node_attackAttack packet index per remote node.
struct ibbsTop-level IBBS state: this BBS's ID, node array, flags.

Other

StructDescription
struct UserInfoUser record in userlist.dat: ClanID, name, deleted flag.
struct UserScoreLeaderboard entry: ClanID, symbol, name, score points.
struct LeavingDataClan travel state: active, destination, troop counts.
struct DoorDoor session state: initialized flag, pause-screen setting, colour mode.
[3.2]

Serialization System

Game data files are written and read using explicit serialization rather than raw struct dumps. This ensures a stable on-disk format that is independent of compiler layout and padding decisions.

The serialize.c / deserialize.c pair provides pack_* and unpack_* macros for each supported type:

Pack / UnpackSizeDescription
pack_char / unpack_char1 byteCharacter
pack_int8 / unpack_int81 byteSigned integer
pack_int16_t / unpack_int16_t2 bytesSigned, little-endian
pack_int32_t / unpack_int32_t4 bytesSigned, little-endian
pack_bool / unpack_bool1 byteBoolean
pack_int16_tArrn × 2 bytesArray of int16_t values

Both serialize and deserialize functions return SIZE_MAX on buffer overflow or underflow.

XOR encryption is applied by myopen.c at the file I/O layer. Each data file type has its own constant (see section 3.3).

Buffer Sizes (BUF_SIZE_* in myopen.h)

Buffer sizes for every serialized struct are declared as BUF_SIZE_* constants in myopen.h. The current values are:

ConstantSizeConstantSize
BUF_SIZE_Alliance2121BUF_SIZE_Army27
BUF_SIZE_AttackPacket192BUF_SIZE_AttackResult263
BUF_SIZE_clan2249BUF_SIZE_empire141
BUF_SIZE_game_data105BUF_SIZE_item_data62
BUF_SIZE_Language4032BUF_SIZE_LeavingData27
BUF_SIZE_Message210BUF_SIZE_MessageHeader190
BUF_SIZE_Msg_Txt84BUF_SIZE_NPCInfo1266
BUF_SIZE_NPCNdx29BUF_SIZE_Packet38
BUF_SIZE_pc140BUF_SIZE_PClass69
BUF_SIZE_Spell39BUF_SIZE_SpellsInEffect4
BUF_SIZE_SpyAttemptPacket88BUF_SIZE_SpyResultPacket186
BUF_SIZE_Strategy5BUF_SIZE_Topic98
BUF_SIZE_TradeData86BUF_SIZE_TradeList24
BUF_SIZE_UserInfo65BUF_SIZE_UserScore61
BUF_SIZE_village_data304
IMPORTANT: When adding a field to any serialized struct, you must:
  1. Add pack_*/unpack_* calls to serialize.c and deserialize.c.
  2. Update the corresponding BUF_SIZE_* constant in myopen.h.
  3. The CRC field must always be the last field serialized.
Forgetting step 2 will cause silent data corruption or truncation.
[3.3]

XOR Encryption Constants

Data files are XOR-encrypted byte-by-byte with a fixed per-type key defined in src/defines.h. This prevents casual editing of binary data files but is not a security mechanism. A determined attacker with access to the source code can decode any file trivially.

ConstantValueUsed For
XOR_VILLAGE9village.dat
XOR_GAME9game.dat
XOR_USER9userlist.dat, per-clan files
XOR_PC9Player character files
XOR_MSG69Message files
XOR_ITEMS23Item files
XOR_ALLIES0xA3Alliance files
XOR_TRADE0x2BTrade data files
XOR_IBBS0x8AIBBS state files
XOR_PACKET0x47Outgoing packet files
XOR_IPS0x94IPS data
XOR_TRAVEL0x3BLeavingData / travel state files
XOR_ULIST0xCEUser list broadcast packets
XOR_DISBAND0x79Disband / removal packets
[3.4]

Language and String System

User-visible strings are not hardcoded in C source. They live in data/strings.txt and are compiled into the binary language file data/strings.xl by the langcomp devkit tool.

Workflow for Adding or Changing a String

  1. Add or edit the entry in data/strings.txt.
  2. Run: langcomp strings.txt strings.xl
  3. Run: makepak pak.lst clans.pak (or copy strings.xl manually to the game directory for quick testing).

In C code, strings are referenced through macros in src/mstrings.h:

od_printf( STR_WELCOME );
od_printf( STR_CLAN_DISBANDED );

Each macro expands to a lookup in the loaded Language struct by index. The macros are the authoritative list of all user-visible strings. Do not add string literals to source code for any text that a player will see.

[4.1]

Packet Types

Inter-BBS packets are identified by the PT_* constants in packet.h.

ConstantValueDescription
PT_CLANMOVE0Clan migrating to a remote BBS. Carries up to 6 player character records.
PT_DATAOK1Acknowledgment of a received PT_CLANMOVE.
PT_RESET2Game reset with new game_data payload.
PT_GOTRESET3Acknowledgment of PT_RESET.
PT_RECON4Reconnaissance request.
PT_GOTRECON5Reconnaissance response.
PT_NEWUSER6New user registration. Always sent to the League Controller (BBS ID 1) first.
PT_DELUSER7Delete user (system-generated, e.g. duplicate purge by the League Controller).
PT_ADDUSER8Add user. Broadcast FROM the LC to all nodes after PT_NEWUSER deduplication.
PT_SUBUSER9Subtract user (player-initiated removal).
PT_COMEBACK10Clan returning from another BBS.
PT_MSJ11Message posted across BBSes.
PT_ATTACK12Inter-BBS military attack.
PT_ATTACKRESULT13Attack result from the defending BBS.
PT_SPY14Spy mission attempt.
PT_SPYRESULT15Spy result from the target BBS.
PT_SCOREDATA16Score data from one BBS.
PT_SCORELIST17Score list broadcast.
PT_NEWNDX18New node index (WORLD.NDX update).
PT_ULIST19Full user list broadcast from the LC. Overwrites records for remote users only; never clobbers the home node's own users.
[4.2]

Packet Filenames

Outgoing packet files follow the naming convention:

clFFFTTTDDI
FieldDescription
FFF3-digit zero-padded source BBS ID
TTT3-digit zero-padded destination BBS ID
DD2-character league ID
ISequence index (starts at 'a', increments per batch)

Example: cl001002AAa is the first outbound packet from BBS 001 to BBS 002 in league "AA".

Packets are written to the outbound/ directory and attached to outgoing Fidonet netmail messages by myibbs.c.

BACKUP.DAT tracks all in-flight packets. During maintenance, stale PT_CLANMOVE entries cause the clan to be re-added locally; stale PT_ATTACK entries return troops to the attacking side.

[4.3]

League Structure and Roles

Each BBS in the league is identified by a numeric BBSID. BBS ID 1 is the League Controller (LC), which is the authoritative node.

New User Registration Flow

  1. User registers on BBS X. BBS X sends PT_NEWUSER to the LC.
  2. LC checks for duplicate names across all known users.
  3. LC broadcasts PT_ADDUSER to every node in the league.
  4. All nodes add the user to their local userlist on receipt.

Periodically, the LC sends a full PT_ULIST to all nodes to re-synchronize the user list. PT_ULIST updates only records for users whose home BBS is not the receiving node; local users are never overwritten.

Configuration Files

FileDescription
WORLD.NDXNode list: BBSID, BBS name, village name, Fidonet address, and routing. See release/worldndx.smp.
ROUTE.CFGRouting and forwarding rules. See release/route.smp.

The full IBBS protocol, including packet flow diagrams and edge case handling, is in docs/ibbs-notes.txt.

[4.4]

Further Reading

docs/ibbs-notes.txt is the authoritative IBBS protocol reference. It documents every packet type in detail, the BACKUP.DAT recovery mechanism, and the full sequence for common operations like clan migration, inter-BBS attacks, and league resets.

[5.1]

Data Files Overview

The game reads all content from clans.pak, a custom archive file similar in concept to a Quake PAK file. The PAK is assembled from compiled binary data by the makepak devkit tool.

Files Inside clans.pak

FileContents
strings.xlCompiled language strings
monsters.monMonster definitions
items.itmItem definitions
classes.clsPlayer class definitions
races.clsRace definitions
spells.splSpell definitions
npcquote.qNPC dialogue
clans.npcNPC definitions
eventmon.monEvent encounter monster definitions
*.eCompiled event scripts
*.hlpIn-game help files
*.ascANSI/ASCII screen art

The source text files for all of the above live in data/ and are compiled by devkit tools into their binary forms. pak.lst in data/ lists exactly which compiled files are included in the PAK and what internal path each one gets.

[5.2]

Building Data Files

All commands below are run from the data/ directory (or provide paths accordingly). The tools must be on your PATH or invoked with their full path from bin/.

CommandOutput
langcomp strings.txt strings.xlLanguage file
mcomp monsters.txt monsters.monMonsters
mcomp eventmon.txt eventmon.monEvent monsters
mcomp npc-pc.txt npc-pc.monNPC PC stats
mitems items.txt items.itmItems
mspells spells.txt spells.splSpells
mclass classes.txt classes.clsClasses
mclass races.txt races.clsRaces
makenpc npc-pc.mon npcquote.q clans.npcNPC index
ecomp foo.evt foo.eEvent script
makepak pak.lst clans.pakBundle into PAK

After modifying any source data file, you must recompile it and rebuild clans.pak before the changes will appear in the game.

The genall.bat script in data/ was the original batch runner for all of the above steps. Under Unix, run the commands individually or write a shell script to automate them.

Each tool is documented in devkit/*.txt. See devkit/clandev.txt for a comprehensive guide to creating quests, NPCs, and monsters.

[5.3]

Event Scripts

Events are scripts for mine encounters and quests. They are written in a simple compiled scripting language and processed by event.c and quests.c at runtime.

Key Scripting Commands

CommandDescription
Event BlockNameBegin a named block.
EndEnd the block (stop execution).
Text "message"Display text to the player.
Input [Label] [text]Numbered-choice menu; jumps to Label.
Option [Char] [Label]Single-keystroke option.
Fight [monster] [...]Initiate a combat encounter.
# commentLine comment.

Scripts are compiled from .evt (source) to .e (binary) using ecomp. The compiled .e files are what the game actually loads.

Registering Quests in QUESTS.INI

FieldDescription
NameDisplay name shown to the player in the quest list.
FilePath to the compiled .e script.
IndexEntry-point block name in the script.
KnownQuest is visible from the start.

Quest Help Entries (QUESTS.HLP)

Quest descriptions shown in the help system live in QUESTS.HLP. Each entry follows this format:

^Quest Name
Description text.
^END

See devkit/ecomp.txt for the full event command reference, and devkit/event1.evt through event5.evt for worked examples. The QUESTS.INI and QUESTS.HLP in the release/ directory are the reference versions for the built-in quest set.

[5.4]

Help Files

The in-game F1/? help system loads plain ASCII .hlp files bundled in clans.pak. Each file covers one topic area. Current files:

army.hlp bulletin.hlp church.hlp citizen.hlp
clans.hlp combat.hlp empire.hlp items.hlp
menus.hlp newbie.hlp races.hlp ruler.hlp
spells.hlp stats.hlp strategy.hlp village.hlp
war.hlp wizard.hlp

To add a new help topic, create a .hlp file, add it to pak.lst, and register it in help.c. Help files are plain text; ANSI colour codes are supported.

[5.5]

Internal %-codes

rputs() in door.c processes %-codes embedded in any displayed text. The general-purpose codes (%P, %C, %L, %R, %D, %B, %V) and the game-state codes (%F, %M, %1, %2, %X, %Q, %N, %T, %Y) are documented in clans.txt section 1.9.

The following codes are used internally by strings.txt and the combat system. They depend on transient state that is only valid during specific game routines and are not useful in quest packs or sysop-editable files:

CodeDescription
%SSSpell cast source name (Spells_szCastSource)
%SDSpell cast destination name (Spells_szCastDestination)
%SVSpell cast value (Spells_CastValue)
%ZTruncate line -- returns from rputs() immediately

%SS, %SD, and %SV are populated only during spell resolution in fight.c. Outside that context, they contain stale or empty values.

%Z silently discards the rest of the current rputs() call. It has no practical use in content files.

[6.1]

Current Issues

XOR encryption is trivially weak

The per-file XOR keys in defines.h are visible in the source. Anyone with the source can decode and modify any data file. This has always been the case; the original author acknowledged it explicitly. Do not rely on it for security. See section 6.2 for the recommended approach.

video.c is hardware-specific

The terminal output layer carries assumptions from the original DOS/serial-port environment. It works correctly on current platforms but is fragile. Review the full file before making any changes to the terminal output layer.

ibbs.c and empire.c are large and complex

At ~100KB each, these files pre-date the current serialization and platform abstraction layers and contain legacy patterns. Changes here require extra care and thorough manual testing.

Some user-visible strings are still hardcoded

Despite the language system, a number of string literals remain in the C source that should be in strings.txt. Any such string is a localization gap and a maintenance burden.

No automated tests

All verification is manual via the test/ directory. A bug in the IBBS path, for example, can only be caught by running a two-node local league and exercising the specific code path.

[6.2]

Suggested Improvements

These are the original author's recommendations, still valid:

1. Replace the Fidonet transport with TCP sockets

The current IBBS system requires a configured Fidonet mailer (Binkley or compatible) at every node. This is the biggest practical barrier to setting up an IBBS league. A direct TCP packet exchange between nodes would eliminate this dependency.

2. Replace XOR encryption with real packet authentication

Ideally, sign packets with a per-league private key so that nodes can verify packet origin. This would close the cheating vector in competitive leagues.

3. Harden the IBBS state machine

The BACKUP.DAT recovery logic handles the common cases, but desync between nodes is still possible in some edge cases (e.g., a node missing a PT_ADDUSER and later receiving a PT_ULIST that includes that user's data).

Additional modernization ideas:

4. Add an automated test harness

The quest event interpreter (quests.c / event.c) is the most tractable place to start; individual .evt scripts can be tested in isolation with qtest.

5. Move remaining hardcoded strings to data/strings.txt

A grep for od_printf(" in the source will find them.

6. Add CRC validation to all deserialization paths

Currently, some deserialize functions check CRC and some do not. Consistent validation would catch file corruption much earlier and make debugging easier.

[7.1]

Build Targets

TargetDescription
gmake gameBuild game binaries and copy them, along with clans.pak and release config, to the project root directory.
gmake devkitBuild devkit tools and copy them, along with devkit/ documentation, to the project root directory.
gmake installerAssemble a distribution .zip. Builds all binaries and data, stages them in stage/, runs chew, and outputs: Clans-<os>-<arch>-0_97b1.zip
gmake devkit-installerAssemble the devkit .zip: ClansDevKit-<os>-<arch>-0_12.zip
gmake installInstall to PREFIX. Copies game binaries to $(INSTALLDIR) and data to the same.
gmake cleanRemove object files and local binary copies from the project root.
gmake deepcleanFull clean including the bin/ directory.
[7.2]

Release Artifacts

Complete Game Installation

CategoryFiles
Binariesclans, config, pcedit, reset
Dataclans.pak (monsters, items, spells, events, strings, NPCs, help files, and screen art)
Configclans.ini, quests.ini
IBBSroute.cfg, world.ndx (league-specific; see samples in release/worldndx.smp and route.smp)
Windowsrunclans.bat (launcher; edit paths for your setup)
Docsrelease/clans.txt (sysop reference)
AI questrelease/questgen.txt (LLM quest generation guide), release/prompt.md (prompt for the LLM)

DevKit Installation

CategoryFiles
Binarieschew, ecomp, langcomp, makenpc, makepak, mcomp, mclass, mitems, mspells, qtest
Docsdevkit/*.txt (tool reference documentation)
Examplesdevkit/event1.evt through event5.evt, devkit/example.ini

Copyright Notice

"Originally copyright 1997-2002 Allen Ussher. Open source contributors. GPL v2."