Coding Standards

Oldies but goldies Angband rules.. ported from MAngaband:

  • Don’t use floating point calculations.
  • Don’t break savefile compatibility (if not absolutely necessary).
  • No C++ code. That also means that it’s better not to use ‘//’ comments (though for Tangaria we use // to distinguish T changes from PWMA changes).
  • Put system dependent code between #ifdef xyz … #endif /* xyz */ .
  • No “magic numbers”. Use #defines. The #defines should be in defines.h rather than the source file, unless they’re very local (such as dungeon generation parameters).
  • Spaces around the mathematical, comparison, and assignment operators (+-*/=!===>, …).
  • Spaces between C-identifiers like ifwhilefor, and return and the opening brackets (if (foo)while (bar), …).
  • No spaces between function names and brackets and between brackets and function arguments (function(1, 2) instead of function ( 1, 2 )).
  • If the precedence of operations is ambiguous then put brackets around them. Also try to insert parentheses whenever necessary for readability, eg. around (foo & RF3_SOME_DEFINE). There is usually no ambiguity and no macro resolution, but this is an aesthetic detail.
  • If a function takes no arguments then it should be declared with a void argument list. Example: int foo(void) instead of int foo().
  • Function declaration and definition should look the same.
  • Don’t assume that functions declared without a return type return int. Specify the type explicitly. Example: int foo(void) instead of foo(void).
  • Indentation with tabs. But generally avoid getting lines over 80 characters.

Code

String functions

Our codebase (dating back to 1980s’) is a tangled mess of supporting many broken, non-standard, POSIX vs ANSI, etc, compilers and environments. Because of that you can see evil things like #define strcasecmp stricmp AND #define stricmp strcasecmp and similar.

Don’t guess! Just use those functions:

Operation Don’t use Use
allocate new and copy strdup, _strdup string_make
case-sensitive compare !strcmp, _strcmp streq
case-insensitive compare stricmp, strcasecmp my_stricmp
case-insensitive cmp,len strnicmp, strncasecmp my_strnicmp
starts with   prefix
ends with   suffix
copy src into dst strcpy, strncpy, strlcpy my_strcpy
concat src into dst strcat, strncat, strlcat my_strcat
free string free string_free

 

Primitive types

Integers

Do NOT use platform-dependent C types, like intlonglong int, etc. You are allowed to use int for creating iterator variables, when it will likely won’t matter, e.g.

int i;
for (i = 0; i < MAX_HOUSES; i++)

but in all other cases, please stick to (M)Angband types:

name sign bits
byte unsigned 8 bits
char signed..? 8 bits
u16b unsigned 16 bits
s16b signed 16 bits
u32b unsigned 32 bits
s32b signed 32 bits

Note, that we also have syte (a signed byte) type, which should be used instead of char, in practice that is not ever used, so you can ignore it.

(M)Angband codebase also has huge and micro types, which are defined as “largest possible integer on your platform”. Those should NOT be used, unless absolutely necessary. Do NOT try to transmit those over network.

Strings

When creating strings, cptr type should be used. This resolves to const char*.

Boolean

When you need a boolean value, use bool. It’s probably a char or an int, beneath the surface, but it denotes your intent. Note, that we have TRUE and FALSE defined, use those instead of 0 and 1.

Return values

It’s OK to return int from functions that need to report an error. However, please consider the alternatives: errr is (probably) the same as int, but signifies your intent much better, it assumes 0 means “no error”. bool, on the other hand, means TRUE for success, and FALSE on error. Had you used plain int, ambiguity around 0 would be created, is it an error or no-error condition?

Bitflags

When creating bitflag variables, use u32b. This gives you 32 bits to play with. If you need more, create ANOTHER variable, e.g.

struct some_type {
    u32b flags1;
    u32b flags2;
}

All actual flags MUST be defined with appropriate names, e.g.

#define XXXF1_SOME_FLAG  0x00000001 /* "1" here means "flags1" */
#define XXXF1_OTHER_FLAG 0x00000002
#define XXXF2_YET_ANOTHER 0x00000001 /* "2" here means "flags2" */

Buffer sizes

We display up to 80 characters on generic terminal. That means: indexes 0-79 may contain data and index 80 is used for string terminator. Meaning the buffers have to declared with size 81 (!). When passing length argument to string functions, pass the buffer size length, not maximum string length.

For your convenience, 2 global constants are defined: MAX_CHARS (80+1) and MAX_COLS 80. Therefore,

char buf[MAX_CHARS];
my_strcpy(buf, foo, MAX_CHARS);
buf[MAX_COLS] = '\0'; /* When cutting */
buf[79] = '\0'; /* WRONG!!! */
buf[78] = '\0'; /* WRONG!!! */

Buffer overflows

Never, ever, ever use strcpy and strcat. Always use my_strcpy and my_strcat, which are mangband equivalents to strlcpy and strlcat from BSD. Our ancestors provided them for a reason.

Switches

It’s fine to use switches and it’s fine to omit breaks, but all such cases must be explicitly marked via a comment.

switch (value) {
case 0:
case 1:
    a = 1;
/* fallthrough */
case 2:
    b = 2;
break;
}

Indentation-wise, put that comment on the same line you would’ve wrote “break”.

Line breaks

All .c and .h files must use UNIX (LF) line-breaks.

Indentation

     /*
      * Multi-line comments look like this.
      * C++ // comments are not allowed
      */
\t   int scope(void)
\t   {
\t   \t   int variable; /* C89 declarations on top of the scope */
\t   \t   char buf[80]; /* Declare arrays from constants */
\n
\t   \t   /* Put curly braces on an extra line with the same indentation level as the previous line */
\t   \t   if (something == 0)
\t   \t   {
\t   \t   \t   do_something(buf)
\t   \t   }
\n
\t   \t   /* Put empty lines between logical blocks of code */
\t   \t   do_something_else(variable);
\t   \t   return 0;
\t   }

Coordinates

Whenever you need a 2-dimensional structure, or to pass X and Y coordinates to a function, always use row/Y first, then column/X.

Whenever you need to display those to player, the reverse applies. All human-readable output should be in a “X,Y” format.

 

Gameplay messages

voice of DM

in great Moria tradition, most of the original game-play related error messages are coming from first person. We should enforce this. Good example of using DM voice:

I see no down staircase here.

personal

You see nothing there to close.

in-personal

You seem unable to go down. Try going up.
There is nothing on the floor.

So all in all, we lack standards here 🙂

 

File names

All .c/.h files despite directory of residence must have unique names. Duplicate files shall append prefix to every copy but first, prioritizing by common, server, client order. The prefixes are “c-” for client, “m” for server and no prefix for common.

Sample table:

original file common/ server/ client/
birth.c birth.c    
birth.c   birth.c  
birth.c     birth.c
birth.c birth.c mbirth.c  
birth.c   birth.c c-birth.c
birth.c birth.c   c-birth.c
birth.c birth.c mbirth.c c-birth.c

 

Network

cq_printf and cq_scanf functions accept the following format specifiers:

Integers:

type bits sign fmt
char 8 ??? %c
byte 8 no %b
s16b 16 yes %d
u16b 16 no %ud
s32b 32 yes %l
u32b 32 no %ul

Strings:

fmt max length comment
%s MAX_CHARS-1 null-terminated
%S MSG_LEN-1 null-terminated
%n MAX_CHARS-1 pascal-style with byte prefix
%N MSG_LEN-1 pascal-style with u16b prefix

We do not have floats and we do not ever transmit floats over network.

 

Micro-optimizations

Our ancestors were a bit too obsessive about micro-optimizing the code. In particular, function calls were considered evil (it adds a lot of asm overhead!), with lots of code duplication, and #define FOO(B) were used a lot instead of proper functions.

It might be tempting to follow suite, but in the current year, with processors faster by several orders of magnitude, code readability should come first.

We’re not looking to rewrite everything, just for the sake of it, but when writing new code DO NOT BE AFRAID to introduce new functions!

Also, do not use inline keyword, ever.


Etc dev notes

Leftovers after Necro design:
fire_ball(who, PROJ_MISSILE, 0, 300, 2, false, true); <<< wrong dir
effect_simple(EF_BLAST, who, "300", PROJ_MISSILE, 2, 0, 0, 0, &context->ident); <<< wrong source (only around p)
// (index, source, dmg, subtype, radius, other, y, x, ident)


MIN(a,b)      (a > b) ? b : a
MAX(a,b)     (a < b) ? b : a
ABS(a)          (a < 0) ? -a : a
SGN(a)          (a < 0) ? -1 : a != 0
CMP(a,b)      (a < b) ? -1 : (b < a) ? 1 : 0


Housing

Important note: Y goes from top! It’s not traditional 2D graph, but graph which starts on top left corner of the map!

   

struct house_type
    struct loc grid_1;          Location of house
    struct loc grid_2;
    struct loc door;            Location of door
    struct worldpos wpos;       Position @ world map
    s32b price;                 Cost of buying
    s32b ownerid;               Owner ID
    char ownername[NORMAL_WID]; Owner name
    byte color;                 Door color
    byte state;                 State
    byte free;                  Bought with Deed of Property
State of a house:
0 = unallocated
1 = normal
2 = extended
3 = custom

Which characters allowed to be used in string literals of C program except numbers and letters?

!  "  #  %  &  '  (  )  *  +  ,  -  .  /  :
;  <  =  >  ?  [  \  ]  ^  _  {  |  }  ~

Also space character and control characters representing horizontal tab, vertical tab and form feed.


In C language */% got equal precedence, but evaluate left to right:

x * y / z is the same as (x * y) / z and x / y * z is the same as (x / y) * z


1 / 10 == 0;


chunk_get()


struct dice_s
{
    int b, x, y, m;
    bool ex_b, ex_x, ex_y, ex_m;
    dice_expression_entry_t *expressions;
};

typedef struct dice_s dice_t;

dice_parse_string(effect.dice, dice_string);

To report typo, error or make suggestion: select text and press Ctrl+Enter.