Script Module Guidelines

Started by strazer, Mon 24/04/2006 12:35:54

Previous topic - Next topic

strazer

I've noticed that the length of our discussions and the documents themselves seems to have made people reluctant to release their modules in fear of violating these guidelines.

And I agree the perceived amount of rules seems quite high at first. That's why I've written a short, easy-to-read summary of the guidelines that will hopefully encourage more people to release their modules.

NOTE: This is a summary, you can find the full discussion and detailed documents here.


SCRIPT MODULE GUIDELINES


Please note that we agreed on these guidelines to bring all modules in line and to ensure that they work smoothly together. You are encouraged to follow them, but these are merely suggestions and we'd rather you publish your module as-is than not at all.
If you just want to make sure that your module doesn't interfere with others, pay special attention to the suggestions in bold.


PROGRAMMING CONVENTIONS

Module
- Unique name: Check this Tech Archive and the AGS Wiki
- Name at least 3 characters long (for auto-completion), as short but as descriptive as possible
- Name in camel notation: MyModule

Definitions/Macros
- Name in uppercase: #define MYMACRO 500
- If in module header, prefix module name: #define MyModule_MYMACRO 500

Variables
- If declared in function (dynamic): Lowercase name
- If declared in module script (static): Camel notation name
- If declared in module script & imported into header (global): Module prefix & camel notation name
Code: ags

// module script

int MyModule_MyVariable; // global
export MyModule_MyVariable; // global

int MyVariable; // static

function do_stuff() {
  int my_variable; // dynamic
}

Code: ags

// module header

import int MyModule_MyVariable; // global


Functions
- If only used in module script: Camel notation name
- If imported into script header (global): Module prefix + camel notation name
- Grouping of functions by using static member functions is recommended:
Code: ags

// module header

struct MyModule { // IMPORTANT: The bracket needs to be placed directly after the struct name
  import static function MyStaticFunc();
  //...more functions here
};

Code: ags

// module script

static function MyModule::MyStaticFunc() {
  // do stuff
}

Code: ags

  // some script

  MyModule.MyStaticFunc();
  // typing "MyModule." will pop up all available functions


GUIs *3
- GUI name: Uppercase module name+number: MYMODULE1 (Truncate module name if necessary)
- GUI control name: Prefix name of its GUI: MYMODULE1_Jump

Enums
- Enum name in camel notation, prefixed with ModuleName
- Value names in camel notation, prefixed with e+ModuleName *4
Code: ags

enum MyModule_MyEnum {
  eMyModule_Value1,
  eMyModule_Value2,
  eMyModule_ValueN
};


Structs
- If defined in module script: Lowercase name
Code: ags

// module script

struct mystruct {
  int MyInt;
  char MyChar;   
  short MyShort;
  float MyFloat;    
  String MyString; // AGS v2.71+
};

- If defined in module header: ModuleName + camel notation name
Code: ags

// module header

struct MyModule_MyStruct {
  int MyInt;
  char MyChar;   
  short MyShort;
  float MyFloat;    
  String MyString; // AGS v2.71+
};

- For instances of structs the variable name rules apply
- Struct members:
Code: ags

// module header

struct MyModule {

  // Public member variables
  int MyVar;
  writeprotected int MyReadOnlyVar;

  // Public member functions
  import function MyFunc();

  // Public static member functions
  import static function MyStaticFunc();
       
  // Private member variables (only accessible by other members)
  protected int my_var;

  // Private member functions (only accessible by other members)
  import protected function my_func();
}


Module version info
- Put definitions in module header for dependent modules to check for:
Code: ags

// module header

#define MyModule_VERSION 200 // current version
#define MyModule_VERSION_200 // provides v2.00 functionality
#define MyModule_VERSION_100 // provides v1.00 functionality



DOCUMENTATION

- Plain text or html file for maximum compatibility
- Same name as module
- Should contain:
  - Module name
  - Module version
  - Authors
  - Description of what module does and what it can be used for
  - Dependencies on other modules / plugins / AGS versions
  - List of all public functions and variables and how/where they are used
  - Revision history / Changelog with date
  - License: Free for non-commercial AND commercial use? Credit required or optional?
    We suggest the MIT license:
Quote
/ Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.


PUBLISHING

- Download should include at least the .scm module file & documentation
- A demo game is optional
- Zip format preferred for maximum compatibility
- Post the announcement thread in the main Technical Forum
- Thread subject line: "MODULE: MyModule v1.00 - Optional short description"
- State which version of AGS it was written for
- Moderators will move the thread to the Technical Archive at the appropriate time
- Finally, add your module to the list of script modules in the AGS Wiki



*3 This is my idea, I don't think we reached a consensus yet? What do you think?
*4 e moved to front to avoid value names popping up when typing the module name



Changes:
- btnJump -> Jump
- Added note about placing bracket direclty after struct name
- Fixed full discussion link (Thanks SSH)
- Removed reference to post below
- Added link to Candle's upload space
- Removed link to Candle's upload space
- Fixed AGS Wiki links
- Removed link to Neole's upload space
- Updated code sections

Dualnames

I see room_a(), room_b() etc. in the room script. What is that? How does it work?

These are room interaction event functions. The engine calls them when there is some room interaction running. They have the same meaning as global event functions like game_start(), repeatedly_execute(), interface_click() etc.

Room interactions:

Walk off left
occurs when the player character walks off the left edge of the screen.
Walk off right
occurs when the player walks off the right edge of the screen.
Walk off bottom
occurs when the player character walks off the bottom edge of the screen.
Walk off top
occurs when the player character walks off the top edge of the screen.
First time enters room
occurs the first time the player enters the room. This event occurs AFTER the screen has faded in, so it allows you to display a message describing the scene.
Player enters room (before fadein)
occurs just after the room is loaded into memory. This event occurs every time the player enters the screen, and it happens BEFORE the screen has faded in, which allows you to change object graphics and do other things to the screen which the player won't notice.
NOTE: This event is ONLY meant for adjusting things such as object and character placement. Do NOT use this event for any sort of automated intro to the room - use the "Enters Room After Fade In" event for that instead.
Repeatedly execute (for this room)
occurs repeatedly on every interpreter cycle. The normal game speed is 40 cycles per second, so this event occurs about every 25 milliseconds.
Player enters room (after fadein)
occurs every time the player enters the room, AFTER the screen has faded-in. Suitable for displaying text descriptions and so on, that you want the player to see.
Player leaves room
occurs when the player leaves the screen, just before the screen fades out.

How it works:

When you goto the room interaction editor and add a new interaction then choose RunScript option AGS adds appropriate function for the current room so you could place the code in to decide what you want to be happen when the interaction is running. You may notice the comment line inside of each event function telling what interaction the function is for.

Example:

1. Go to interaction editor
2. Choose interaction: Player enters room (after fadein)
3. Set RunScript option for this interaction
4.
   function room_a() {
   // script for room: Player enters room (after fadein)
     player.Animate(...);
     player.Walk(100, 200, eBlock);
     ...
     ...
     etc.
   }

So now each time the player has entered the room (after fadein) AGS will animate the player character and move him to the specified co-ordinates.

A Word of warning:

You should never place or delete these functions manually. Instead goto the interaction editor and remove appropriate interaction (AGS takes care itself on deleting such functions).
Why is it so? Because when you add new interaction AGS adds an event function as well and links it with the added interaction.
That means two things:
1. If you open script editor (room) and just place room_*() functions manually AGS will not be able to call them unless the appropriate interaction has been specified first. This is why you can't just copy the whole script code from one room and paste it into the new one. You should create interactions first.
2. If someone posted a script like this:
function room_a() {
  cGuy.Walk(...);
  ...
  ...
  etc.
}

you shouldn't just grab & paste it into the room. Even if you have room_a() function in yours script. The reason it should be avoided is that the event function name is NOT connected with the interaction equally for all rooms but depends on on the order the interactions have been added.
What I mean is this:

Interaction editor > Add Interaction > Room: Player enters room (after fadein); RunScript;
Result: function room_a() has been added.

Interaction editor > Add Interaction > Room: Player enters room (before fadein); RunScript;
Result: function room_b() has been added.

Interaction editor > Add Interaction > Room: Player leaves screen; RunScript;
Result: function room_c() has been added.

So in the room script we have:

//room script
function room_a() {
// script for room: Player enters room (after fadein)
}

function room_b() {
// script for room: Player enters room (before fadein)
}

function room_c() {
// script for room: Player leaves room
}

...but changing the order interactions would be added in:

Interaction editor > Add Interaction > Room: Player leaves room; RunScript;
Result: function room_a() has been added.

Interaction editor > Add Interaction > Room: Player enters room (after fadein); RunScript;
Result: function room_b() has been added.

Interaction editor > Add Interaction > Room: Player enters room (before fadein); RunScript;
Result: function room_c() has been added.

... we'll get in the room script:

//room script
function room_a() {
// script for room: Player leaves room
}

function room_b() {
// script for room: Player enters room (after fadein)
}

function room_c() {
// script for room: Player enters room (before fadein)
}

See the difference? The functions have other names.

Btw, there are not only room interactions but hotspot, objects etc. so seeing something like function hospot1_a() {} is also possible.

-

Oops, a bit long post. hope it's not confusing and helps you. If you have any related questions just ask. ;)

-Cheers




What exactly are arrays and structs, and how do you define/use them?

As of AGS v2.71 arrays and structs are officially supported, and are also documented in the manual:
  Manual -> Scripting -> Script language keywords -> Arrays / struct

With arrays, you can store several values in one variable, so to speak.
So instead of doing

Code: ags

// main global script

int weapon1_cost; // define variables available from all functions in the global script
int weapon2_cost;
int weapon3_cost;

function game_start {

  weapon1_cost = 60;
  weapon2_cost = 20;
  weapon3_cost = 40;

}

export weapon1_cost; // export variables to make them available in room scripts
export weapon2_cost;
export weapon3_cost;


Code: ags

// main script header

import int weapon1_cost; // import global script variables to make them available in room scripts
import int weapon2_cost;
import int weapon3_cost;


you can do

Code: ags

// main global script

int weapon_cost[3]; // define array with a dimension/size of 3

function game_start {

  weapon_cost[0] = 60;
  weapon_cost[1] = 20;
  weapon_cost[2] = 40;

}

export weapon_cost; // export array to make it available in room scripts


Code: ags

// main script header

import int weapon_cost[3]; // import array to make it available in room scripts


Note that the array's index (the number by which you access it) starts at 0 and ends at arraysize - 1.
In the past, accessing an array outside these bounds could cause weird things to happen. Although AGS has been improved with better array bounds checking since then, you should be careful to avoid any problems.

Now, with structs, you can do this (AGS v2.71 and up, see below for older versions):

Code: ags

// main global script

myWeaponStruct weapon1; // make variables from the struct defined in the script header (see below)
myWeaponStruct weapon2;
myWeaponStruct weapon3;

function game_start {

  weapon1.cost = 60;
  weapon1.power = 30;
  weapon1.description = "Mace";

  weapon2.cost = 20;
  weapon2.power = 10;
  weapon2.description = "Dagger";

  weapon3.cost = 40;
  weapon3.power = 20;
  weapon3.description = "Sword";

}

export weapon1; // export variables to make them available in room scripts
export weapon2;
export weapon3;


Code: ags

// main script header

struct myWeaponStruct {
  int cost;
  int power;
  String description; // Note: "String", not "string"
};

import myWeaponStruct weapon1; // import variables to make them available in room scripts
import myWeaponStruct weapon2;
import myWeaponStruct weapon3;


You can also combine structs and arrays like this:

Code: ags

// main global script

myWeaponStruct weapons[3];

function game_start {

  weapons[0].cost = 60;
  weapons[0].power = 30;
  weapons[0].description = "Mace";

  weapons[1].cost = 20;
  weapons[1].power = 10;
  weapons[1].description = "Dagger";

  weapons[2].cost = 40;
  weapons[2].power = 20;
  weapons[2].description = "Sword";

}

export weapons;


Code: ags

// main script header

struct myWeaponStruct {
  int cost;
  int power;
  String description;
};

import myWeaponStruct weapons[3];





For AGS v2.70 and below:

You can't make arrays from string variables and the new String type doesn't exist in AGS v2.70 and below.
Here's a workaround to do the above in older AGS versions:

Code: ags

// main global script

myWeaponStruct weapon1; // make variables from the struct defined in the script header (see below)
myWeaponStruct weapon2;
myWeaponStruct weapon3;

function game_start {

  weapon1.cost = 60;
  weapon1.power = 30;
  StrCopy(weapon1.description, "Mace");

  weapon2.cost = 20;
  weapon2.power = 10;
  StrCopy(weapon2.description, "Dagger");

  weapon3.cost = 40;
  weapon3.power = 20;
  StrCopy(weapon3.description, "Sword");

}

export weapon1; // export variables to make them available in room scripts
export weapon2;
export weapon3;


Code: ags

// main script header

struct myWeaponStruct {
  int cost;
  int power;
  char description[200]; // instead of "string description;"
};

import myWeaponStruct weapon1; // import variables to make them available in room scripts
import myWeaponStruct weapon2;
import myWeaponStruct weapon3;


You can also combine structs and arrays like this:

Code: ags

// main global script

myWeaponStruct weapons[3];

function game_start {

  weapons[0].cost = 60;
  weapons[0].power = 30;
  StrCopy(weapons[0].description, "Mace");

  weapons[1].cost = 20;
  weapons[1].power = 10;
  StrCopy(weapons[1].description, "Dagger");

  weapons[2].cost = 40;
  weapons[2].power = 20;
  StrCopy(weapons[2].description, "Sword");

}

export weapons;


Code: ags

// main script header

struct myWeaponStruct {
  int cost;
  int power;
  char description[200];
};

import myWeaponStruct weapons[3];


Edit:
- Updated with AGS v2.71's String type
- Changed "item" to "weapon" to avoid confusion with inventory items
Worked on Strangeland, Primordia, Hob's Barrow, The Cat Lady, Mage's Initiation, Until I Have You, Downfall, Hunie Pop, and every game in the Wadjet Eye Games catalogue (porting)

SMF spam blocked by CleanTalk