The eScript and Distro Style Guide

eScript Style and Distro submission Guide
This is a suggested guide for those writing eScript programs and a required guide for those on the Distro team or for those wishing to submit eScript programs or packages to the Distro. I want to thank Austin for creating the original style guide. I'm pretty sure it was his handy work. If not, then my thanks to whomever created it. I changed a few things but most of this guide was taken from the original.

In this guide I will almost always use the word program when referring to an eScript script.

Naming Conventions:
-------------------

FileNames:
All single word or single word and partial word file names should be lower case.

Code: Select all

initialize.src
getreg.src
With filenames containing multiple words, the letter of the first word is lowercase, while the first letter of each additional word is capitalized. This naming convention is known as camelCase. All file extensions are lowercase.

Code: Select all

    Examples:
        npcBackpack.inc
        myIncludeFile.inc
        showAll.src

    Exceptions:
        Commands are all lowercase.
        Core files, such as itemdesc.cfg, serverspecopt.cfg,npcdesc.cfg, etc.,  are all lowercase.
The case of the file name is important on non-Windows systems such as Linux. On Linux systems npcBackpack.inc and NPCBackpack.inc are interpreted as different files.

Note: I will concede that abbreviations in a file name can be uppercase eg. NPCBackpack.inc. Just make certain that your references to those files in scripts match the same case as the file name.

Variable Declaration:
Regular variables:
Note on variable declarations: Because eScript is a loosely typed language, with regular variables you do not need to assign a value to a variable, or specify its type, when you declare it. The code is legal in the following example:

Code: Select all

    var something;
    something := 1234;
    something := "A string literal";
Changing the type of value stored in a variable is not good coding practice but in eScript, it is allowed. It also can be convenient and that's why you might see it done in some scripts. Do not do it in any code submitted to the Distro! It can create issues and it is harder to debug.

Please give your variables names that are relevant to the values they might hold. For example, if your variable might contain the amount of gold found in a character's backpack name it something like char_gold or mobile_gold. Longer variable names are allowed. Do not be afraid to use long names, if necessary, to be descriptive. Remember, you, or someone else, may need to review the code at some point in the future. Descriptive variable names will help in understanding what your program is doing. Variables used for iterations in for loops can be single characters eg. i, j, k unless they are used later in your code and need to be descriptive. Variables are not case sensitive. The variable names “Time” and “time” will be interpreted by eCompile as the same variable.

Code: Select all

    Examples:
        var stuff;
        var my_integer;
        var my_string;
        var layer_array;
	var counter i;
Constants:
Constants should be in all caps (all capitalized) and words are separated using the underscore character '_'.

Code: Select all

        CONST MY_CONSTANT := 10.0;
        CONST NUMBER_OF_GUARDS := 100;
Note: Core constants, such as MOVEOBJECT_FORCELOCATION, do not always separate, or delimit, each word with an underscore character.
Note: Also, an oddity with eScript is that constants are case sensitive.
In the following example, eCompile will interpret the two constants as unique:

Code: Select all

        const TIME := 1600;
        const Time := 2200;
Special Variables:
There are three, variable types that must have their type declared in the var declaration, arrays, structs, and dicts (dictionaries).
Arrays:

Code: Select all

// Preferred.
	var a := {};
        var a := array{1, 2, 3};
Structs:

Code: Select all

// Struct setup
        var my_var := struct; // This is preferred for readability. Assign members and values separately.
        my_var.+member := something;
        my_var.+member2;
// Or...
        var my_var := struct{"member":=something, "member2"};
Dictionaries:

Code: Select all

// Dictionary Setup
	var my_var := dict; // This is preferred for readability. Assign members and values separately.
        var my_var := dictionary{"key"->value, "key2"->value};
Functions:
All functions should use the PascalCase convention, which as you can see has the first
letter of each word capitalized.

Code: Select all

    Examples:
        function ListOnlineDrow(who)
        function GetLastName(who, num_of_letters)
    Methods:
        object.MethodFunction(arguments)
Exceptions:
If the function/s is part of a special package it can include a small prefix to define its origin i.e. AP_GetSkill( who, skill_name ), which is a function in the attributes package.


Programs:
Please be descriptive with the name of the program. Here I am referring to the name given after the keyword “program” in your code. That name does not need to be the same as the file name but the names should be similar. Please use 'PascalCase' naming conventions for the program name within your code.

Code: Select all

    Examples:
        (FileName is "bardInst.src"), thus the program should be named:
        program BardInstruments(item)

        (FileName is "enumTowns.src), thus the program should be  named:
        program EnumerateTowns(who, uX, uY, lX, lY, realm)
You can use any name after the program keyword of course. If the program is a text command you could append command_ or textcmd_ to the beginning of the name or if it is a spell program, append spell_ to the name. That is not a requirement but it does help for quickly determining what the code is intended to do.

Note: When it comes to your code, eCompile and the eScript language is case neutral, unlike certain other languages. The case of a program name or variable does not affect compilation or execution of your programs. The only exception, which I noted above, are constants.

These guidelines are intended to promote readability of source code. File names are a different matter as they are sent to the operating system and on some systems case is important. Also, remember that with in-game text commands, the command is the file name. The command info, for example, is compiled from the file info.src to an executable file, info.ecl. So whatever you name the file for a text command, that will be the name you type in-game to execute the command, without the .ecl extension.

=== Other Formatting ===

Math:
This example is addressing general rules regarding order of operation than styles. In it, do you want the addition to be done before the multiplication? If so, then enclose the 4 + 3 in parenthesis.

Code: Select all

        var math := 4 + 3 * 10;
        var pemdas := ( ( 4 * 4 ) + ( 8 / ( 3 + 2 ) ) ) - ( 100 + some_var );
This is how an expression should be formatted for the Distro:

Code: Select all

        var pemdas := ( ( 4 * 4 ) + ( 8 / ( 3 + 2 ) ) ) - ( 100 + some_var );
Please note that I do not prefer spaces between parenthesis and values, or parenthesis and another parenthesis. However a case could be made for readability when having the spaces present. So we won't have a hard and fast rule regarding this. However spacing between values and operators is strongly encouraged.
Either of the following will be acceptable:

Code: Select all

        var pemdas := ((4 * 4) + (8 / (3 + 2))) - (100 + some_var);
        var pemdas := ( ( 4 * 4 ) + ( 8 / ( 3 + 2 ) ) ) - ( 100 + some_var );





Strings:
The same rule applies to operators when used with strings. Always place a space between to operator and the string.

Code: Select all

        var string := "Hello there " + "Stephen." + " I am " + math + " years old.";
Function Usage:
Functions and method naming conventions for the Distro are PascalCase. Each word should begin with a capital letter. Even if the function name is a single word or part of the function name is an abbreviation. Methods should never contain the word “Method” in their name. Context will indicate that it is a method.

Code: Select all

        var random_weapon := PickRandWeap();
        var value := SomeFunction(argument_1, argument_2);
        var other_value := object.SomeFunction(argument_1); // This is an example of a call to an object’s method.
Properties:

Code: Select all

        object.somepropery := some_value;
Comments:

Code: Select all

// Do unto others as you would have them do unto you.
// Matthew 7:12

/*
 * This is another option
 * When creating multi line comments.
 *
 */

A word about comments in your code, you can never have too many comments documenting what your code is doing but you can certainly have too few. Remember, even though you wrote the code, you might need to make changes months or years afterward. By that time you may have forgotten a lot about how your program functios. Those comments will save you a lot of time when trying to understand what your code does. If you are contributing to an open source project, such as the Distro, good commenting of code will be invaluable to others who want to understand your program.

Whenever possible you should provide a comment block preceding a function like the example below.

Taken from Austin's damage.inc file:

Code: Select all

/*
 * ApplySpellDamageEX(mobile, amount, type, source, circle)
 *
 * Purpose: Does spell damage to mobile taking into account mobile's AR
 *
 * Parameters
 * mobile: Victim
 * amount: Raw damage
 * type:   Fire, cold, energy, poison or physical
 * source: Source of the damage
 * circle: Circle or level of spell. Basic spells are 1 through 8.
 *
 * Return value: damage amount done
 *
 */
If, While, etc. Stuff:
Please take note of the indentation inside the conditional statements (if and case) and the loops (while, do, repeat, and for etc.). This is important for readability so others can look at your code and understand it. You should always indent 4 spaces inside each if or loop.
The if block examples:

Code: Select all

   
        if ( something )
                Code here;
        endif
The or operator ( || ):

Code: Select all

        if ( this || that ) // Don't use 'or' spelled out.
The and operator ( && ):

Code: Select all

        if ( this && that ) // Don't use 'and' spelled out.
        
        if ( this || ( one && two ) ) // Parenthesis are important for order of operations.
An example of nested if statements:

Code: Select all

        if(something)
                this gets done...
                if(this happens)
                        this other stuff gets done
                else
                        do this stuff
                endif
        endif
The case block:

Code: Select all

        case ( something )
                value:    code;
                break;
                default:  code;
                break;
        endcase
The while block:

Code: Select all

        while ( something )
                code here;
                sleepms(2);
        endwhile
Thedo block:

Code: Select all

        do
                codehere;
                sleepms(2);
        dowhile ( case );
The foreach block:

Code: Select all

   
        foreach iteration in ( iterated )
                codehere;
                sleepms(2);
        endforeach
The for block:

Code: Select all

        for i:=1 to value
                codehere;
                sleepms(2);
        endfor
Other Notes:
* If at all possible do not use spaces for indents, use tabs. Set your tab stop to the equivalent of 4 spaces. If you must use spaces indent 4 spaces.

All Distro scripts that use gumps, can use the gumps package to build them but this is not a hard and fast rule.


Additional Distro submission guidelines:

Follow as closely as possible the eScript guidelines for coding.

General use include files (*.inc) can be placed in /scripts/include. Examples are client.inc, itemtypes.inc etc.

All specialised include files specific to a package shall be included in the package that requires them. Under the package directory, create a sub-directory named include to hold those include files.

Packages:
Going forward, packages should be as self-contained as possible. This means that if at all humanly possible a package should be able to be dropped in without modifying any other packages or scripts outside the package. This is especially important if you are submitting an optional package that shard devs can, should they choose, add to their server. If you need to add or modify some function in an existing Distro include file, copy that include file to your package and modify it there. Please add comments in your copy of the file indicating where the changes were made. This will help keep the Distro as simple as possible.

Packages will follow the "Austin" format:
/pkg/packagename
<main scripts go here> start.src, logon.src, reconnect.src etc.

/pkg/packagename/commands
<any command specific to this package>
Commands need to be placed in sub directories in the /commands directory that pertain to the level of the intended user. In the Distro the folders must be named player, councilor (or coun), seer, gm, admin, developer (or dev), test. You only need to create the sub-directories that will actually contain commands.

/pkg/packagename/config
<cfg files> itemdesc.cfg, icp.cfg, npcdesc.cfg (for any custom NPCs your package may need) or any cfg files specific to the package.

/pkg/packagename/include
<include.inc files specific to this package>

/pkg/packagename/itemname
<method and use scripts required for an item defined in the package> Create additional itemname directories for additional items as required. The use script for an item should be named use.src and the method script should be named methods.src


Also familiarize yourself with the control package in \pkg\utils\control and create an icp.cfg file for your package. Try to be as thorough as possible when adding the info to the icp file.

If you do want to help by contributing to the Distro you can send me a personal message on the POL forums.