Langband technical intro

Langband, though based on Angband and feels pretty alike when playing it, is very different when you look at the code. It is written in a different language, structured differently and assuming any code-resemblance with standard Angband may prove problematic. Writing another "variant" with a drastic difference with the Angband code-base, which many good variants have been based on, might seem like a crazy project. It might very well be a crazy project, but sometimes one has to be crazy to further the development.

On the positive side, Langband possess some qualities over the standard Angband code-base:

Langband advantages

  • It is written in a different, and more powerful language: Common Lisp. Common Lisp is a high-level programmable programming language with an advanced object system and strong functional roots. This language removes the need for a separate scripting-language which the Angband-developers have wanted to add to Angband for a long time. Unlike classical scripting, Common Lisp code is compiled to native code and the "scripting" can exploit the full code of Langband.
  • Angband is a large program of more than 100 000 lines of code, and with more than a hundred global variables. The code, though well-commented and exceptionally structured for C-code, has most game-play and possibly customisable code hard-coded and any change is tough work and, more often than not: complex work. Langband has a different strategy; a small customisable engine for the basic parts, which a "variant" uses and adds details to. This means that Langband is an engine which variants can use/be plugged into. The size of the Langband code, engine and variant, is much smaller than the corresponding Angband code.
  • Langband uses the object system of Common Lisp liberally, and Angband has in many ways been turned into an object-oriented program. This object-orientation is central in making the engine extensible and customisable. Only a selected few areas of the code have been made into non-extensible constructs to optimise speed in places where this is needed.
  • [Add more later.]

But we might want to jump to the technical side of Langband, how things are structured.

Engine <-> Variant <-> Level

[Please note that all parts of the code is written in CAPS-LOCK here to be visible in the text. You're very strongly encouraged to write in only small letters any code to avoid breaking compatibility.]

A variant is an encapsulation of all the features which make the game fun to play: fun levels, objects, monsters, classes, races, flavouring of objects and fun rooms. All these things can be customised for a variant, and the engine has nothing of this hardcoded. Other parts of the engine may also be customised; equipment handling, shops, etc.

The variant object is available to the engine and it's own code in the global dynamical variable *VARIANT*, just as the player object is available in the *PLAYER* variable.

Also available globally is the *LEVEL* object, which points to the current level explored. The engine provides a random level (RANDOM-LEVEL) type which is the basic random level which is probable to be common among variants, but a variant may tailour this and add new level-types. A level-type defines it's own dungeon-generation, which monsters and objects may be found and other characteristica. The town-level in Vanilla angband is such a special, or themed, level. Langband is structured to make it easy to add your own levels and choose for your variant what kind of levels are to be created when.

A variant may also define it's own room-types, which may be vaults, shop-rooms, churches, elven courts, dragon's den, etc. This code has not been fully expanded yet, but it should have the same customisability as a level. A level-type may also define which rooms may be generated for that level.

Another useful global dynamical variable is the *DUNGEON* object, which is the technical representation of the current level. It may also be found by using the accessor (LEVEL.DUNGEON *LEVEL*).

Variant object

SlotStructure
attk-descsEQ hash-table, symbol -> description
skill-translationsassociation list, (from . to)
house-ownersEQL hash-table
house-typesEQL hash-table
flavour-typeslist of structs (FLAVOUR-TYPE)
filtersEQ hash-table
objectsEQ hash-table (see monsters)
monstersEQ hash-table, symbols -> struct game-obj-table
xp-tablearray with positive integers
max-charlevelpositive fixnum
max-depthpositive fixnum
Other slots:
	    ROOM-BUILDERS         #<EQUAL hash table, 2 entries {486D43DD}>
	    FLOOR-FEATURES        #<EQL hash table, 64 entries {486D441D}>
	    LEVEL-BUILDERS        #<EQUAL hash table, 2 entries {486D445D}>
	    TURN-EVENTS           #<EQUAL hash table, 0 entries {486D449D}>
	    TURN                  138
	    CLASSES               #<EQUAL hash table, 6 entries {486D44DD}>
	    RACES                 #<EQUAL hash table, 10 entries {486D451D}>
	    CONFIG-PATH           "./variants/vanilla/config"
	    SYS-FILE              "./variants/vanilla/langband-vanilla.system"
	    NAME                  "Vanilla"
	    ID                    LANGBAND-VANILLA
	    TWILIGHT-TIME         6000
	    DAWN-TIME             0
	  
Filter-table:
	    {:OBJECTS} -> {((LEVEL
	    . #<Function "DEFMETHOD ACTIVATE-OBJECT :BEFORE (VANILLA-VARIANT)"
	    {4836FC69}>))}
	    {:MONSTERS} -> {((TOWN-LEVEL
	    . #<Function "DEFMETHOD ACTIVATE-OBJECT :BEFORE (VANILLA-VARIANT)"
	    {4836F959}>)
	    (RANDOM-LEVEL
	    . #<Function "DEFMETHOD ACTIVATE-OBJECT :BEFORE (VANILLA-VARIANT)"
	    {4836FAE1}>))}
	  

Langband statistics [CVS 17th july 2001]

  • 49 classes + 11 structs
  • 50 generic functions (with 254 methods)
  • 436 functions
  • 38 macros
  • 45 dynamic variables
  • 208 constants

Langband statistics [root, simple grep, CVS 7th december 2001]

  • 42 classes + 10 structs
  • 99 generic functions (with 242 methods)
  • 374 functions
  • 29 macros
  • 34 dynamic variables
  • 216 constants

Langband statistics [root, simple grep, CVS 17th february 2002]

  • 51 classes + 11 structs
  • 157 generic functions (with 292 methods)
  • 413 functions
  • 33 macros
  • 37 dynamic variables
  • 195 constants

Langband statistics [root, simple grep, CVS 5th january 2003]

  • 63 classes + 19 structs
  • 205 generic functions (with 357 methods)
  • 530 functions
  • 48 macros
  • 51 dynamic variables
  • 194 constants

Langband style

Currently very little is documented or in place, some coding guidelines exist however to make the code easier to understand:

  • All code should be lowercase whenever possible despite uppercase names in documents. The uppercase is used to make the code stand out in the text, but code doesn't need to stand out in code.
  • All constants (DEFCONSTANT) are put in-between +-signs, ie +pi+
  • All global dynamic variables (use DEFVAR, not DEFPARAMETER) are put in-between *-signs, ie *player*
  • All predicates have a ?-suffix, ie player-dead? and should have no side-effects
  • All functions which induce side-effects on their arguments should have a !-suffix, ie equip-player!
  • All functions which manipulate global variables should have a &-suffix, ie init-environment&
  • Functions that are meant to be internal to their file and not be used outside or exported should use a % prefix. Ane example is %birth-input-char in birth.lisp which is only input to birth-functionality and is uninteresting to others.
  • All game-related symbolic constants should be put between < and >, ie '<RANGER>
  • All functions and macros with a DEFINE- prefix implicitly have global side-effects, ie define-monster. They will also return the object they have defined. Do not use the & suffix.
  • The prefix MAKE- are restricted to non-sideeffect constructors of objects, ie wrapped MAKE-INSTANCEs. An object is returned from this kind of function.
  • Functions whose purpose is to "add" something to "global" tables, maybe from a DEFINE- function/macro, should use the ESTABLISH- prefix, and return the object in question. A & suffix is expected. These should not be called by variant code though, variants should use DEFINE-. However, if it seems reasonable with a SETF-match to a GET- function instead of an ESTABLISH-, use SETF.
  • Macros which create an environment to execute code in whould use a WITH- prefix, and if it needs a function to implement itself it should use a INVOKE-ON- prefix. An example is: WITH-DUNGEON and it's implementation in INVOKE-ON-DUNGEON. These macros are allowed to create side-effects on their arguments.
  • Variant-code should use their own special prefix for internal functions, variables, etc. As an example, all vanilla functions and variables use the VAN- prefix. Loading variant-code is also allowed to induce side-effects, ie registering the variant in appropriate places.
  • Code which is expected to be common to all variants should be put in lib/common and have a COMMON- prefix. Loading lib/common might also have side-effects.
  • Identifiers should preferrably be lowercase strings.
  • Functions which need interactive input might want to have a QUERY- prefix.
  • Factory-methods, with no side-effects (except maybe caching) and which require no interactivity, should use the PRODUCE- prefix. Factory-methods should when possible depend on at least the variant, to be able to control it.

Langband symbols

This is an attempt to list certain symbols that one should be careful about manipulating, as they're used by the engine internally.

VARIANTS
Has info on the possible variants available. Any slot may be used..
COMMON
Has information about things common to all variants, mainly the initialisation. Used: PRE-INIT and POST-INIT.

Langband events

[Please rewrite]

This is an attempt to list known events that are triggered and the order they're triggered in. Listed as:
:EVENT-NAME [CLASSNAME,:SETTING-KEY] - (arg1, arg2, ...)

Birth

Dungeon object is NIL. :on-pre-equip is called before adding of racial/class equipment, while :on-post-equip is called after racial/class equipment. All items existing in inventory/equip-slots after post-equip will be identified.

  • :on-pre-equip [birth-settings, :birth] - (dungeon player)
  • :on-post-equip [birth-settings, :birth] - (dungeon player)

Objects

Player and dungeon arguments are NIL. There might exist valid values at *player* or *dungeon* though.

  • :on-create [object-kind, NIL] - (player dungeon active-object)

save/load issues

To ensure that save/load works as it should, all ids for objects should be strings. The ids should be all lowercase-characters (a-z) or hyphen. An id should also be unique for the object it identifies. An object with id uses the id-slot in the object.

Typically a lisp-program would use symbols for this, but it gives some case-issues and package-issues, and strings are more portable without imposing too big performance-hit.

For some code it is useful to be identified by a symbol, e.g classes, races, ... . This symbol should be put in the symbol-slot of these objects.

Compatibility with ordinary Angband code-base

Most of the code is derived from Vanilla Angband 2.9.0 and many of the function names are taken directly from the Vanilla-code but some are changed and more will be changed as things progresses and Langband finds its own system to do things in. You should not expect Angband-ways of doing things to work.

You should be aware that some of the terminology has also been changed, e.g where angband talks about level for objects and monsters, Langband talks about depth, reserving level for a 'power-level' use, not dungeon-level it is found.

Important simple changes is that all Angband underscores are now hyphens, and that the code relies very little on global variables (ie dungeon tables, player object, etc). Other changes may be found in the style guidelines.

Some functions which may be important to remember when going from C/Angband to Lisp/Langband:

	    Angband:         Langband:
	    rand_int         random
	    randint          randint
	    /                int-/
	  

Class-hierarchy beneath class: ACTIVATABLE