Code Style

The nature of 3Kingdoms is that over time wizards come and go. As such, we have generally adopted a coding style that is easy for new coders and seasoned coders alike to read and interact with. It does not follow the conventions that are seen in professional coding jobs or specific employers/contractors/governments/etc try to utilize, but we find its easy to train, easy to read, and easy to maintain. Maintenance is the critical part as, speaking empirically based on the data, odds are someone else will maintain your code eventually.

What follows is directly from the codestyle man file that wizards have access to:

Purpose

This is a style guide to help acclimate you to the most common code style used on 3Kingdoms codebase. These might not be the styles you prefer to use if you have coding experience, but in order to make code easily readable by future wizards, many of whom often have limited coding knowledge, this is the preferred styles to be utilized by as many wizards as possible. There are also some best practices included in this listing that are more practical things as opposed to style things.

As such, I ask that you try your best to follow the styles outlined here-in regardless of your own personal preference because this is, at its core, a group maintained project and the continued mixing of styles makes it more and more difficult to maintain with each generation.

So, without further ado:

Styles: Editor Settings

Make sure your editor uses spaces, not tabs.

Indent levels should be two spaces per level.

Make sure your editor uses linefeed as its end-of-line character. If it uses a carriage return, you will see a lot of ‘^M’ characters in your file.

If your editor has a ruler, set it to 80 characters and keep all lines under that length whenever possible.

Styles: Folder Structure

Inside your directory, you can set up your file layout however you like.

However, the following is recommended:

./defs.h                     -A linked header file (explained below).
./.bin/                      -Your command files.
./areas/
        defs.h               -A linked header file (explained below).
        areaname/
                 defs.h      -A linked header file (explained below).
                 docs/       -Text files (proposal, notes, qinfo, etc).
                 missions/   -Only if you have mission(s) in the area.
                 mon/        -Holds all your monster files.
                 obj/        -Holds all your objects (including weapons/armor).
                 rooms/      -Holds all your room files.
./doc/                       -Documentation on your files in general.
./include/                   -Your global include files.
        defs.h               -The root linked header file (explained below).
./logs/                      -Any log files your code generates.
./private/                   -A private folder that no one else *should* view.

Please avoid the use of capital letters or symbols in folder or file names.

Styles: File Structure

All .c files should have the same basic structure:

/*
  filename.c
  Yourname YYMMDD
  Description of what this file is.
*/
#pragma strong_types  //So type checking is enforced.
#include "defs.h"     //So it uses the linked header file.
                      //Note: If your defs.h has *actual* functions defined
                      //      inside, it *cannot* be included *before*
                      //      you inherit anything. In which case this
                      //      #include must be moved *after* the inherit
                      //      section below.

<#define statements>  //Any file specific #define or precompiler directives.

inherit <source>;     //Inherit any files needed, if applicable.

<global variables>    //Define any global level variables here. If the object
                      //uses save_object() make sure they are 'nosave' if you
                      //do not want them to be saved.

<function prototypes> //Prototypes for any functions to be defined further
                      //down in the file, but need to be declared so they
                      //can be used before declaration.

//From this point on the functions needed may change but the most common ones
//for rooms, armor, weapons, monsters, etc are generally these, in this order:

void create()
{
  ::create();
  <Your create code>
  return;
}

void init()
{
  ::init();
  <Your init code>
  return;
}

void reset()
{
  <Your reset code>
  return;
}

<Any/all other required functions>

Do not upload any files to the server with a space in the filename! It will break things and have to be fixed via FTP.

Styles: Precompiler Directives

All code should have #pragma strong_types as the first line (after any comment blocks) in each file.

If you #define a filepath or string, and then use another #define to add to that previous #define, it should be enclosed in ().

For example:

#define MY_PATH  "/players/adalius/"
#define MY_AREAS (MY_PATH+"areas/")
#define MY_ROOM  (MY_AREAS+"room/")

If you do not do this, and you then try to use call_other() or the ‘->’ operator, the function may fail due to order of operations (-> comes before +).

It is recommended that you make a central defs.h file in your ./include/ directory. This should house all your common definitions and macros that can be shared amongst all your code. It’s easiest to do this before you write any code but can also be done afterwards. In your home folder you would then make another defs.h file that simply has:

#include "./include/defs.h"

Then in each and every subdir containing executable code, you would make additional defs.h with the first line being:

#include "../defs.h"

These buried files can also include any additional macros or defines that have not previously been #define’d but need to be available for all subdirs from this point on.

This central defs.h in your ./include/ would contain common defines like your filepath to your directory, your areas directory, etc, as well as common macros like #define TP this_player() or #define ETO environment(), etc.

Defines (#define) should be in UPPERCASE to distinguish them from normal variables. Using a #define like myvar is too easily confused with a variable local to the file in question.

Styles: Naming Conventions

If a file is inheritable, it often times (but not always) makes sense to start or end the filename with inh_ or _inh to indicate it’s designed to be an inheritable.

When it comes to room files, the entrance to your area from outside the area, or at least the primary entrance or entrance closest to the realms if theres multiple linked areas, should be named ‘entrance.c’ to make it easily found.

Variables and functions should all be lowercase with VERY limited exception. Generally the only time a name should have uppercase is if it is something that you want to call attention to so it isn’t accidentally called by a wizard. For example:

status IAMAPLAYERKILLER;
status query_IAMAPLAYERKILLER() { return IAMAPLAYERKILLER; }

Spaces should be replaced with underscores.

All variables that are used multiple times/places should have meaningful names. Single use or counters are fine to have simpler names, like ‘i’, ‘t’, ‘x’.

Function names should have a name that at least alludes to what it does. If the function returns a queried value, it should start with query_, if it sets a value, it should start with set_, if it clears a variable, it should start with clear_, if it resets it to a default value, it should start with reset_, if it’s used with add_action() as the target of a player action, it should start with do_. Having a function called “no_mode()” might make sense if you wrote it, but to a wizard not familiar with the code, it could do a hundred different things. Does it return 1 if there is no mode? Does it set the mode to 0? Does it print a message to the player saying “You have no mode.”? It is completely unclear from the name.

That said, function and variable names should also not be ungodly long as they get unwieldy when you need to manually ‘call’ them or when they are so long they break to a new line in the debug log when they throw an error.

Styles: Types

All variables and functions should have proper types. If you followed the precompiler directives above, strong_types should enforce this.

Despite being listed as ‘DEPRECATED’, ‘status’ should still be used as the variable or function type when the value is either true (1) or false (0) because this designates to the reader that the value is not expected to be anything other than those values, which ‘int’ does not clearly indicate.

If your function is not meant to return anything, define it as ‘void’. Do not define it as some other type and return an empty (0) value for all your returns. This will drive someone mad.

Do not use ‘mixed’ data types unless you truly need ‘mixed’ data types as any future coder looking at your code will have to trace it through all the calls to determine what type it is, and the compiler can’t enforce any real type checking since it could be anything. *There are acceptable times for this!

Float is not a precise type. What that means is if you do mathmatical operations on a float, it can lose precision (and thus not be the expected value after multiple operations). For this reason you should not be using float to store large precise values like experience, guild experience, etc. A better option (until we are on 64 bit) is to use ipairs (see ‘man ipairs’).

Styles: Brackets

Brackets should be alone on the line, indented to the appropriate location.

For instance, this is preferred:

void my_function(string arg)
{
  if(arg && arg=="two")
  {
    arg = "three";
    return;
  }
  else
    arg = "four";
  return;
}

As opposed to the alternate undesirable:

void my_function(string arg){
  if(arg && arg=="two"){
    arg = "three";
    return;
  }
  else
    arg = "four";
  return;
}

As you can see it’s much easier to line up brackets in the first example to try and find where one may be missing. This also allows IDEs to collapse the bracketed area and the collapse symbol will appear at the start of an empty line as opposed to the end of a function name where it could be missed.

Styles: Variables

Remove any variable declarations that you do not use. It’s wasteful to declare them if they aren’t used, and it can also be bewildering if the reader sees it declared, but can’t find where it’s assigned anywhere.

It pleases those of us with OCD if the variables are listed by type and name alphabetically, and if pointers are listed after standard variables. For example:

int a, b, f, *d;
object obj;
status x;
string str, *str_array;

Do not define a variable if you’re only going to use it once. Heck, in some cases even twice (possibly even three times, depending on the cost of the call) might not be enough of a reason to define a variable. You have to balance the increased memory allocation to define & carry the variable against the CPU cost of processing the call that you’re assigning to the variable. Storing an array to use it twice might make sense, but storing a players level probably doesn’t.

Styles: Functions

Void functions should still have a return; at the end clearly demarking where the function terminates.

Simple functions such as single line set or query functions should be written fully on a single line when possible:

string query_name() { return name; }

And not written as such:

string query_name()
{
  return name;
}

The function type declaration should be on the same line as the function name.

Like this:

object query_patrol_obj() { return patrol_obj; }

Not like this:

object
query_patrol_obj() { return patrol_obj; }

Styles: Strings

When joining multiple strings and variables together, its easier to read when done with a (s)printf() rather than concatinating them with ‘+’. Plus this allows you to control formatting better.

You can continue open strings onto a new line by use of a ‘' at the end of the line. This can be useful if you want to use your editors ruler when setting up a long string, or if you want to see the formatting in place without the indentation of the current code around it. Note: It must be the last character on the line!

For example:

  set_long("\
This allows me to see exactly where the lines will be given my current ruler \
but its important I remember to put a space before the slash if I want it to \
have a space before the word I start on the next line otherwise it will conc\
atinate them together.");

Any time a string is written to a single player, it should be wrapped to the players COLS setting if the length of the string exceeds 40-50 characters. This can be done with the addition of a few lines of code. Firstly, in your ./include/defs.h you would need the following:

#define TP this_player()
#define WRAP      (TP?(to_int(TP->query_property_arg("COLS"))?\
                      to_int(TP->query_property_arg("COLS"))-5:75):75)
#define WRAPP(XX) ((XX)?(to_int((XX)->query_property_arg("COLS"))?\
                        to_int((XX)->query_property_arg("COLS"))-5:75):75)
#define PWRAP(XX)     printf("%-=*s",WRAP,(XX))
#define SWRAPP(XX,YY) sprintf("%-=*s",WRAPP((YY)),(XX))

You can then use PWRAP("Your string here") to display it, wrapped, to the current this_player() or you can use SWRAPP("Your string here",(object)who) to wrap your string per (object)who’s COLS setting and return the string to send to tell_object() or some other location with.

Decorative strings such as:

*****************{Guild Help Headers}*****************

or

[===============|---------------] Progress Bars

should not be shown to players who have the SCREENREADER property set.

You can test this by doing player->query_property("SCREENREADER") and displaying just the simple text (or percentage in the case of progress bars) to anyone where that function call returns 1.

Remember, if you manually wrap anything in a set_long() by including even a single ‘\n’, you are responsible for wrapping the entire thing. set_long() will only autowrap if it senses NO ‘\n’ in the entire string passed to it.

Standard for all of 3Kingdoms is single spacing between sentences.

Styles: General Programming

Document. Document. Document. Comment your code. It’s almost a statistical certainty that someone else is going to be stuck maintaining it eventually and if you don’t want them talking about how you were dumber than a sack of potatoes because your code doesn’t make any damn sense, then use comments to make it clear to future observers. Hell, it’ll even help YOU if you go back and try to figure out what you did during a rum bender 3 years later.

If you are going to use nested arrays/mappings, please create interface functions to make it easier to use and more-readable, plus you don’t have to remember which sub-index is what if you name the functions properly.

int get_index_mobcount(string index) { return m[index][5]; }

results in…

printf("There are %d mobs.\n",get_index_mobcount(2));

versus the less obvious…

printf("There are %d mobs.\n",m[index][5]);

When using an if or if/else structure and you only have a single line of code after either the if or the else, do not use brackets. Simply do the code as follows:

if(<expr>)
  single_line_of_code;
else
  also_single_line_of_code;

Learn the terenary operator, it can save you lines of code when you just need to do a quick inline if/else. For example you can replace this:

void print_output(string obj)
{
  if(member(({"a","e","i","o","u"}),lower_case(obj[0..0]))!=-1)
    printf("You have an %s.\n",obj);
  else
    printf("You have a %s.\n",obj);

  return;
}

Can be rewritten as:

void print_output(string obj)
{
  printf("You have a%s %s.\n",
    (member(({"a","e","i","o","u"}),lower_case(obj[0..0]))!=-1 ? "n" : ""),
    obj);
  return;
}

Styles: In Closing

The above are all things to consider when writing code here on the MUD, and for the most part are only strong recommendations, but by following them your code will be easier to read, easier to maintain, and prevent causing headaches to others down the road.