From The Mana World
(Add an example of how menus should be indented.)
m
 
(32 intermediate revisions by 7 users not shown)
Line 1: Line 1:
{{Status_construction}}
<metakey>Script Language, Code, Source, eAthena Script, AST</metakey> <metadesc>The Mana World Scripting Standards</metadesc>
{{Status_outdated}}
<div style="background-color: #f6ebc1; text-align: center; border: 1px solid #e0b713; margin-bottom: 10px;">
{{Gaps|Needs to be updated with {{ForumThread|13653}} and {{ForumThread|13672}}, which are generally accepted but there might be more discussion}}
'''TMWA's scripting wiki pages:'''
 
[[Legacy:Scripting Basics]] - [[Legacy:Scripting Standards]] - [[Legacy:Scripting Reference]]
 
For questions feel free to contact us on [https://web.libera.chat/?channels=#themanaworld-dev IRC] and/or on our [https://forums.themanaworld.org forum].
</div>
{{Category:Outdated}}
 


This is an attempt to standardize the eAthena coding. Any suggestions are welcome to be posted on the discussion page before editing.
=Code Formatting=
=Code Formatting=
== Indentation ==
== Indentation ==
Line 17: Line 23:
Example:
Example:
  // if-statement and conditional command in the same line, gotos aligned
  // if-statement and conditional command in the same line, gotos aligned
  if (@quest >= 3) goto L_Done;
  if (@quest >= 3) goto L_Done;
  if (@quest == 2) goto L_Progress;
  if (@quest == 2) goto L_Progress;
  if (@quest == 1) goto L_Question;
  if (@quest == 1) goto L_Question;
Line 47: Line 53:
* Code blocks should be separated from the others in a dinstinct way: insert a blank line between code blocks.  
* Code blocks should be separated from the others in a dinstinct way: insert a blank line between code blocks.  
* Individual blocks should be together No blank lines within a block.  
* Individual blocks should be together No blank lines within a block.  
* The opening brackets should be at the end of the parent line, not in a new line; the closing ones should be in a line of their own.
* The opening brackets should be on a new line, not at the end of the parent line; the closing ones should be in a line of their own.
* newline between if (condition) and conditional_command, except when conditional_command is a goto at the beginning of a major script block (usually only at the beginning of a script), in which case if there are multiple if statements on adjacent lines, the gotos shall be aligned
* A 'menu' must not be in the body of an if statement. Instead, goto a new block for the menu, or negate the logic and goto somewhere else if the label should not be shown.
<span style="color:OrangeRed">'''provide an example'''</span>
* The 'next' command goes at the end of a block (before a goto), never at the beginning. Make sure 'next' is never followed by something that leads to a 'close' without intervening text.
 
==Labels and Subfunctions==
* Labels should be named in this way: L_CamelCase
* Subfunctions should be named in this way: S_CamelCase
* Labels should always start with L_, except those that act as a subfunction, which should start with S_
* Newline before every label
* No newline after a label
* There must not be a fallthrough or goto to a subfunction label.
* A subfunction label must not goto a normal label. It may, however, goto another label that is specific to that subfunction, and eventually reach a return.


Remove unnecessary next;
* Many scripts use a label L_Close, which is used to reset temporary player variables to 0, and then ends the scripts with the close; command. If you use a label with that purpose, it should be named L_Close for consistency. (There are older scripts which use different names, e.g. L_end, but new ones should go with L_Close.)
Remove redundant code such as end; after close;
remove break; instead use end; (requires changing the java converter)
Replace monsters.txt with _mobs.txt except in special cases.
A standard for whitespace and comments.


    ?
    limit line length? (break long strings using + concatentation)
==Labels and Subfunctions==
* newline before every label
* no newline after a label
* Labels should always start with L_, except those that act as a subfunction, which should start with S_. There must not be a fallthrough or goto to a subfunction label.
<span style="color:OrangeRed">'''decision pending'''</span>
L_CamelCase: Current practice for labels and I don't think that is worth changing,
L_lower_case:, o11c prefers, especially for things like L_close:
* '''Subfunctions''':<span style="color:OrangeRed">'''to be discussed'''</span>
"[...]except those that act as a subfunction, which should start with S_"
Also, what should labels within a subfunction label use? I was thinking LS_ or SL_; loratay currently uses L_SUB_ ... or perhaps this should be refactored out into a standalone function?
''summary'': S_, LS_, SL_, L_SUB_, Sub_, refactor out into a standalone function.
==Comments==
==Comments==
<span style="color:OrangeRed">'''to be discussed'''</span>
At the beginning of a script file there should be documented
* o11c suggests:
* who is the author
  "^///" lines for documentation
* the purpose of the script
  "^// " lines to be commented out code (because that is what my editor uses automatically to disable code)
* which variables are used and what's their purpose
  "^//[^ /]" for "other".  
* any additional information that can help to understand the script
Documentation should maybe have doxygen-like tags?
Example:
Dislikes boxed comments.
  // Author: devxy
* Actual
  // This file contains the NPC John, who is part of the Example quest.
  // Yellow dog quests
  // Quest states are saved in Quest_Example.
// author: Saturn
  // State 1: started the quest
// see bluesageConfig for detailed quest description
  // State 2: did the next step
  // NPC Arthur will not make the code for you...
  [...]
  // uses global vars ... f
 
// calls  global function  funkbeat () located at ... f
Within the script there should be comments if there are parts which might be difficult to understand.


In some of the older scripts there are boxed comments used like this:
  //#################################################################################
  //#################################################################################
  //# bla blabla - By Smith, reviewed by john                                      #
  //# NPC John - By Smith                                                           #
  //# GPL v.2                                                                      #
  //#                                                                               #
  //#  ...                                                                          #
  //#  ...                                                                          #
  //#################################################################################
  //#################################################################################
Don't use them in new scripts.
== NPC Dialogs ==
In the following example our NPC dialog formatting guidelines are explained.
    mes "[Example NPC Olaf]";
    mes "\"When I start speaking my name is put in square brackets, and my spoken text here is in quotation marks.\"";
    next;
    mes "\"The next between my previous line and this one interrupted my speaking, so I started with new quotation marks.";
    mes "But didn't close them in the same line, because I continue to speak here.";
    mes "A new message command can be used to have a line break in the dialog without interrupting the speaking.";
    mes "At the end I close the quotation marks again.\"";
    next;
    mes "Olaf stops speaking and scratches his head. This is a descriptive text, so it isn't put into quotation marks.";
    next;
    mes "[Example NPC Olaf]";
    mes "\"Now that I start speaking again, I need my name in brackets again before my actual text starts, because something else happened in between.";
    mes "It isn't that difficult, right?\"";
    menu
        "I agree.", L_Agree,
        "(Leave.)", L_Close;
L_Agree:
    mes "[Example NPC Olaf]";
    mes "\"It's the same after there was a menu with the player saying or doing something, again I put my name into sqaure brackets before starting to speak.\"";
    next;
    mes "\"In the menu you can see that something the player says isn't put into quotation marks, but just as plain text.";
    mes "And an action the player takes is put into usual brackets.\"";
    goto L_Close;
L_Close:
    close;
=Variables=
=Variables=
==Document Variables==
==Document Variables==
Line 115: Line 146:
*initialized all at once
*initialized all at once
*initialized by appending elements to the end, for use in a menu
*initialized by appending elements to the end, for use in a menu
Nothing needs to be done, merely document them. I want this information at hand when I design the new scripting language to minimize conversion pains.
Nothing needs to be done, merely document them. I want this information at hand when I design the new scripting language to minimize conversion pains.
I wouldn't be surprised if there were no troublesome arrays at all. If there are only a couple it might be worth refactoring the code to remove them, but that might be a  high-level decision which is beyond the scope of this proposal.
I wouldn't be surprised if there were no troublesome arrays at all. If there are only a couple it might be worth refactoring the code to remove them, but that might be a  high-level decision which is beyond the scope of this proposal.


Set dynamic and local @variables to 0 before close;
Set dynamic and local @variables to 0 before close;
==Naming Variables==
==Naming Variables==


Line 141: Line 173:


=== Warp Definitions ===
=== Warp Definitions ===
<span style="color:OrangeRed">'''Shouldn't be done manually.'''</span>
Warps are what move players between maps. They can also be used to move players around a single map, if needed. Warps are defined like this:
Warps are what move players between maps. They can also be used to move players around a single map, if needed. Warps are defined like this:


Line 166: Line 199:
|}
|}


Width and height are described in detail here: [[EAthena Scripting Standards/Warp Details|Warp Details]].
Width and height are described in detail here: [[Dev:TmwAthena Scripting Standards/Warp Details|Warp Details]].


=== Monster Definitions ===
=== Monster Definitions ===
<span style="color:OrangeRed">'''Shouldn't be done manually.'''</span>
Monsters are defined like this:
Monsters are defined like this:


Line 196: Line 230:
|eventcode  ||the script event to fire upon death
|eventcode  ||the script event to fire upon death
|}
|}
A detailed description of position and area can be found here: [[EAthena Scripting Standards/Mob Details|Mob Details]].
A detailed description of position and area can be found here: [[Dev:TmwAthena Scripting Standards/Mob Details|Mob Details]].


=== NPC Definitions ===
=== NPC Definitions ===
Line 250: Line 284:
//
//


002-4.gat,93,37,0|script|Treasure|111,{
002-4.gat,93,37,0|script|Treasure|111,
 
{
     if (TMW_Quest >= 38) goto L_Finished;
     if (TMW_Quest >= 38) goto L_Finished;


Line 259: Line 293:
     menu
     menu
         "Yes.", L_Yes,
         "Yes.", L_Yes,
         "No.", -;
         "No.", L_Close;
 
L_Close:
     close;
     close;


L_Yes:
L_Yes:
     if(countitem("TreasureKey") < 3) goto L_Not_Enough;
     if(countitem("TreasureKey") < 3)
        goto L_Not_Enough;
     getinventorylist;
     getinventorylist;
     if (@inventorylist_count == 100 && countitem("TreasureKey") > 3) goto L_TooMany;
     if (@inventorylist_count == 100 && countitem("TreasureKey") > 3)
        goto L_TooMany;
     mes "You opened the chest and found a short bow!";
     mes "You opened the chest and found a short bow!";
     delitem "TreasureKey", 3;
     delitem "TreasureKey", 3;
Line 294: Line 332:


Note that anything said by an NPC should be put in double quotes ("). You can do that like this: "'''\"'''Hello!'''\"''' she said."
Note that anything said by an NPC should be put in double quotes ("). You can do that like this: "'''\"'''Hello!'''\"''' she said."
=References=
 
* [[EAthena Scripting Basics]]
[[Category:Development]]
* [[EAthena Scripting Reference]]
* [http://forums.themanaworld.org/viewtopic.php?f=13&t=13653 script cleanup/scripting standards proposal]

Latest revision as of 19:25, 23 January 2022

<metakey>Script Language, Code, Source, eAthena Script, AST</metakey> <metadesc>The Mana World Scripting Standards</metadesc>

TMWA's scripting wiki pages:

Legacy:Scripting Basics - Legacy:Scripting Standards - Legacy:Scripting Reference

For questions feel free to contact us on IRC and/or on our forum.

These are articles that require updating, moving to archive or deletion (see also Template:Delete).


Code Formatting

Indentation

  • Code is indented with four spaces.
  • Code in the same block should have the same indentation.
  • Labels have no indentation.
  • Menu options are on their own lines and are indented further.

Example:

menu
    "Yes.", L_Yes,
    "No.", L_No;
  • When using an if-statement, there should be a line break after the condition and the conditional command should be indented further. An exception to this is a series of if-statements at the beginning of a major code block which use gotos to jump to the correct position in the script. In this case the goto should be in the same line as the if-statement.

Example:

// if-statement and conditional command in the same line, gotos aligned
if (@quest >= 3) goto L_Done;
if (@quest == 2) goto L_Progress;
if (@quest == 1) goto L_Question;

// conditional command in the next line and indented further
if (BaseLevel < 40)
    set @cost, 100;

Whitespaces

In many places whitespaces can increase readability. Examples are

  • When concatenating text and variables:
mes "\"Hello " + strcharinfo(0) + "!\"";

instead of

mes "\"Hello "+strcharinfo(0)+"!\"";
  • When assigning values to variables:
set @var, 3;

instead of

set @var,3;
  • In menus:
menu
    "Yes.", L_Yes,
    "No.", L_No;

instead of

menu
    "Yes.",L_Yes,
    "No.",L_No;

Code Blocks

  • Code blocks should be separated from the others in a dinstinct way: insert a blank line between code blocks.
  • Individual blocks should be together No blank lines within a block.
  • The opening brackets should be on a new line, not at the end of the parent line; the closing ones should be in a line of their own.
  • A 'menu' must not be in the body of an if statement. Instead, goto a new block for the menu, or negate the logic and goto somewhere else if the label should not be shown.
  • The 'next' command goes at the end of a block (before a goto), never at the beginning. Make sure 'next' is never followed by something that leads to a 'close' without intervening text.

Labels and Subfunctions

  • Labels should be named in this way: L_CamelCase
  • Subfunctions should be named in this way: S_CamelCase
  • Labels should always start with L_, except those that act as a subfunction, which should start with S_
  • Newline before every label
  • No newline after a label
  • There must not be a fallthrough or goto to a subfunction label.
  • A subfunction label must not goto a normal label. It may, however, goto another label that is specific to that subfunction, and eventually reach a return.
  • Many scripts use a label L_Close, which is used to reset temporary player variables to 0, and then ends the scripts with the close; command. If you use a label with that purpose, it should be named L_Close for consistency. (There are older scripts which use different names, e.g. L_end, but new ones should go with L_Close.)

Comments

At the beginning of a script file there should be documented

  • who is the author
  • the purpose of the script
  • which variables are used and what's their purpose
  • any additional information that can help to understand the script

Example:

// Author: devxy
// This file contains the NPC John, who is part of the Example quest.
// Quest states are saved in Quest_Example.
// State 1: started the quest
// State 2: did the next step
[...]

Within the script there should be comments if there are parts which might be difficult to understand.

In some of the older scripts there are boxed comments used like this:

//#################################################################################
//# NPC John - By Smith                                                           #
//#                                                                               #
//#  ...                                                                          #
//#################################################################################

Don't use them in new scripts.

NPC Dialogs

In the following example our NPC dialog formatting guidelines are explained.

    mes "[Example NPC Olaf]";
    mes "\"When I start speaking my name is put in square brackets, and my spoken text here is in quotation marks.\"";
    next;
    mes "\"The next between my previous line and this one interrupted my speaking, so I started with new quotation marks.";
    mes "But didn't close them in the same line, because I continue to speak here.";
    mes "A new message command can be used to have a line break in the dialog without interrupting the speaking.";
    mes "At the end I close the quotation marks again.\"";
    next;
    mes "Olaf stops speaking and scratches his head. This is a descriptive text, so it isn't put into quotation marks.";
    next;
    mes "[Example NPC Olaf]";
    mes "\"Now that I start speaking again, I need my name in brackets again before my actual text starts, because something else happened in between.";
    mes "It isn't that difficult, right?\"";
    menu
        "I agree.", L_Agree,
        "(Leave.)", L_Close;

L_Agree:
    mes "[Example NPC Olaf]";
    mes "\"It's the same after there was a menu with the player saying or doing something, again I put my name into sqaure brackets before starting to speak.\"";
    next;
    mes "\"In the menu you can see that something the player says isn't put into quotation marks, but just as plain text.";
    mes "And an action the player takes is put into usual brackets.\"";
    goto L_Close;

L_Close:
    close;

Variables

Document Variables

in every file, document every variable as one of:

  • # account variable (prefix #) (note that there are also ## variable which didn't work in the stable version of the server)
  • permanent player variable (no prefix)
  • @ temporary player variable (prefix @) - used after the script close;s or end;s.
  • @ dynamic player variable (prefix @) - passed into or out of callsub or callfunc
  • @ lexical (local) player variable (prefix @) - used only within the function (scripthelp.txt documents that ".@" means this but I don't know if that works)
  • @ local constant (prefix @) - not dependent on the player, used only within the function. e.g. lots of @QUEST_FOO_{SHIFT,MASK}
  • $ global permanent variable (prefix $) (I'm not sure if we have any of these right now)
  • $ npc permanent variable (prefix $) same as above but only used by one NPC
  • $ global temporary variable (prefix $@)
  • $@ npc temporary variable (prefix $@) same as above but only used by one NPC, scripthelp.txt mentions prefix "." but I don't know if that works
  • special variable (in db/const.txt with a 1 following)
  • global constant (in const.txt)

If we use a consistent method of documenting these, we can then generate a list of all variables by type.

Variables initialization

Check for troublesome arrays: A troublesome array is defined as an array that is not

  • initialized all at once
  • initialized by appending elements to the end, for use in a menu

Nothing needs to be done, merely document them. I want this information at hand when I design the new scripting language to minimize conversion pains. I wouldn't be surprised if there were no troublesome arrays at all. If there are only a couple it might be worth refactoring the code to remove them, but that might be a high-level decision which is beyond the scope of this proposal.

Set dynamic and local @variables to 0 before close;

Naming Variables

Using as little variables as you need

So there are some quests, which require lots of variables. Think of monster oil quest, Oric and Warum quest etc.

This can often be done by bitmasking: One variable has 32 bits. So a variabe can store 2^32 different numbers: 4294967296

But sometimes you only need numbers from 0 to 15, but more of these variables. but numbers in range 0 to 15 can be stored in 4 bits ( 2^4 = 16 different numbers)

Here is an example how to use bitmasking: Media:Tester.txt

Defining Map Objects

These sections describe how to define map objects.

Warp Definitions

Shouldn't be done manually. Warps are what move players between maps. They can also be used to move players around a single map, if needed. Warps are defined like this:

map1,startX,startY|warp|name|width,height,map2,endX,endY

Key:

map1 the starting map
startX the x-coordinate of the starting warp tile
startY the y-coordinate of the starting warp tile
name the name of the warp, unused but must be defined
width the width of the warp
height the height of the warp
map2 the ending map
endX the x-coordinate of the tile the player will end up on
endY the y-coordinate of the tile the player will end up on

Width and height are described in detail here: Warp Details.

Monster Definitions

Shouldn't be done manually. Monsters are defined like this:

<map name>,<x>,<y>,<width>,<height>|monster|<name>|<mobID>,<count>,<spawn1>,<spawn2>,{<eventcode>}
map name the map the monsters should appear on
x the x-coordinate of the spawn tile
y the y-coordinate of the spawn tile
width the tile width of the spawn area
height the tile height of the spawn area
name the name of the mob, unused but must be defined. DO NOT use the keywords such as "spawn" in this name to avoid conflict with other game systems (magic for example)
mobID the mob identifier of the desired monster (in the monster db)the number to spawn
count the number to spawn
spawn1 the minimum delay between successive spawns (per individual)
spawn2 the minimum delay between death and respawn (per individual)
eventcode the script event to fire upon death

A detailed description of position and area can be found here: Mob Details.

NPC Definitions

<map name>,<x>,<y>,<facing>|script|<Name>|<spriteID>,{<code>}
<map name>,<x>,<y>,<facing>|script|<Name>|<spriteID>,<triggerX>,<triggerY>,{<code>}
map name the map the monsters should appear on
x the x-coordinate of the spawn tile
y the y-coordinate of the spawn tile
facing Direction the NPC faces to. It must be set to 0 in TMW
height the tile height of the spawn area
Name the name of the NPC
spriteID the mob identifier of the desired NPC. Sprite ID
triggerX
triggerY
eventcode the script event to fire upon death
  • Server side :
NPC definitions (script) are usually stored as :
(tmwa-server-data/)world/map/npc/<map name>/<NPC Name>.txt or in the file :
(tmwa-server-data/)world/map/npc/<map name>/npc.txt
  • Client side :
The NPC is associated with a sprite (the image that will appear in the game), and a XML file which tells the client some display parameters.
(tmwa-client-data/)graphics/sprites/npcs/npc-<Name>.png
(tmwa-client-data/)graphics/sprites/npcs/npc-<Name>.xml
The NPC spriteID is linked to graphic files and XML in the file : (tmwa-client-data/)npcs.xml


Script Functions

function|script|<function name>|{<code>}

Actual script functions are stored or imported in the files : (tmwa-server-data/)world/map/npc/functions/<function name>.txt, regardless rhey are called from a Map, NPC, Mob or item.

Script Loading

The main script import file is:(tmwa-server-data/)world/map/npc/scripts.conf
It loads (tmwa-server-data/)world/map/npc/_import.txt which imports all NPC scripts wich are stored under the respective map name folders, usually under NPC name.

Examples

Open a chest and get reward

An example is given by a quest to get a key for a chest (in fact 3 keys):

// A treasure chest. You need three keys to open it.
//
// TMW_Quest (document variable here)
//

002-4.gat,93,37,0|script|Treasure|111,
{
    if (TMW_Quest >= 38) goto L_Finished;

    mes "There is a chest here.";
    mes "Do you want to try to open it?";
    next;
    menu
        "Yes.", L_Yes,
        "No.", L_Close;

L_Close:
    close;

L_Yes:
    if(countitem("TreasureKey") < 3)
        goto L_Not_Enough;
    getinventorylist;
    if (@inventorylist_count == 100 && countitem("TreasureKey") > 3)
        goto L_TooMany;
    mes "You opened the chest and found a short bow!";
    delitem "TreasureKey", 3;
    getitem "ShortBow", 1;
    set TMW_Quest, 38;
    close;

L_Not_Enough:
    mes "It seems that you do not have the right key for this chest yet...";
    close;

L_Finished:
    mes "You have already opened this chest.";
    close;

L_TooMany:
    mes "You do not have enough room to loot this chest. Maybe you should try again later.";
    close;
}

This script starts with describing the NPC (the chest, NPC sprite 111) and its location (map 002-4 X=93, Y=37, in tiles from top left of the map). Then follows what happens on activation: First, the variable "TMW_Quest" is checked to know if the player already accomplished the quest (the variable should be documented), If he has the script continues at label L_Finished, the player is reminded fe alredy accomplished the task and script finishes, if not the scrip continues. A message is shown (mes), then an option dialog (menu) is displayed with two possible answers : "Yes." or "No.". If the player answers "No." the script stops (close;); if he answers "Yes." the script continues at label L_Yes. Now player is checked to know if he has at least 3 keys, if not script branches to label L_Not_Enough tells the player he "has not the right key" and script closes. If he has keys enough, his inventory list is retrieved by the built-in function "getinventorylist" and it's content is checked to know if the player will have room enough to store the reward after the 3 keys use. If not, script goes to label L_TooMany and script close after a warning message, If yes 3 keys are deleted in player's inventory, he gets the reward: a Short Bow and the variable TMW_Quest is set to 38.

For more examples of the current system, check out the current scripts in use by the server. Beware though, this could affect your enjoyment of the game as it does spoil some of the mystery. Here's a link to git so you can view them in your browser:

https://github.com/themanaworld/tmwa-server-data/tree/master/world/map/npc

Note that anything said by an NPC should be put in double quotes ("). You can do that like this: "\"Hello!\" she said."