Technical / Grid System (proposal)

Introduction

First thing first, I want to thank Jaga Te'lesin and U'lias Clearmoon because I based this grid system on their systems.

Indeed this grid system is loosely based on ALL-IN-ONE SEAMLESS AREA TRANSITIONER (for Neverwinter Nights) by Jaga Te'lesin (jaga-nwn@earthlink.net) and the modifications made for ALFA by U'lias Clearmoon (Shawn Marcil)

This grid system allows:

  • the definition of several (eventually overlapping) grids ;
  • seamless transitionning between grid areas and between grids ;
  • some grid areas can be replaced by generic areas called virtual areas ;
  • offers a way to find a location matching global ALFA coordinates (useful for portals and teleportation spells).

Grid areas tagging

Grid areas should be tagged as follows: SAT_NNNXXXYYYZZZ

SAT_ is a prefix in order to quickly identify a grid area from another area.

NNN is the number of the grid starting at 000 and ending at 999
The grid number acts as a priority for overlapping grids. The smaller grids should have small grid numbers.

XXX, YYY and ZZZ are coordinates.
They can be of the form nXX (for -XX when X < -9), n0X (for -0X when 9 <= X < 0), p0X (for +0X when 0 <= X <= 9) and pXX (when X > 9).

This means that you can have 99 negative and 100 positive areas in each dimension.

Here is an example of grid areas positions and tags:

Area positions:

NorthWest Area North Area NorthEast Area
West Area Center Area East Area
SouthWest Area South Area SouthEast Area

Matching tags (for grid 0):

SAT_000n01p01p00 SAT_000p00p01p00 SAT_000p01p01p00
SAT_000n01p00p00 SAT_000p00p00p00 SAT_000p01p00p00
SAT_000n01n01p00 SAT_000p00n01p00 SAT_000p01n01p00
              ^
 Y axis being |
 X axis being ->

Onload script

If this is not clear please look at the bottom of this page for an example.

In your onload script you should define grids as follows:

  1. you should call ALFA_CreateGridNumber() in order to get a new grid number ;
  2. you should call ALFA_SetGridOriginCoordinates(nGridNumber, fX, fY, fZ) to set the grid origin using the ALFA global coordinates (the units are miles) ;
  3. you should call ALFA_SetGridScaling(nGridNumber, fScaleX, fScaleY, fScaleZ) to set the scale of each grid areas (ie the size of one area in ALFA global coordinates - units are miles as well) ;
  4. you should call ALFA_SetGridAreaSize(nGridNumber, fSizeX, fSizeY) to set the size of the NWN areas using NWN coordinates (160 x 160 for a 16 x 16 areas) This might change with NWN2
  5. you should then call ALFA_SetNegativeGridRow(nGridNumber, nZ, nY, sAreaString) and ALFA_SetPositiveGridRow(nGridNumber, nZ, nY, sAreaString)

Those two last functions work as follows:

  • each call defines a half (negative or positive) line (on the X axis) of one grid ;
  • the line is defined by letters representing areas arranged in a character string ;
  • the letters can be:
    • 1 for a defined area ;
    • X for a forbidden/void area (you can also use that if there is an overlapping grid with a lower grid number using this portion of your server) ;
    • another letter defining a virtual area (see below).

Virtual areas

Virtual areas have a type identified by a letter (eg F for forest) and some generic areas. You use the following function to attach generic areas to a letter: ALFA_AddVirtualArea(sLetter, sAreaTag); sAreaTag is a standard tag (not a SAT_... area tag).

Note: there can be less generic areas than the real number of virtual areas in your grid but if all are used (ie there are PCs in all the areas) and someone tries to go in another virtual area of the same type, they won't AT and will get an error.

Area Transition Triggers

In each grid area (SAT_ tagged areas and virtual areas) you should place 4 triggers surrounding the area (one to the north, one to the south, one to the west and one to the east). Those triggers should not be more than 5 units large and can be as long as you want.

The triggered script can be as follows:

 #include "acr_sat_areatran"

 void main()
 {
    object oCreature = GetEnteringObject();
    object oCurrentArea = GetArea(oCreature);

    if (ACR_SAT_DEBUG) _debugMessage("Seamless AT code starts");

    // try to AT
    int nOk = ALFA_SeamlessAreaTransition(oCreature);

    if (nOk) {
        // check if this is a virtual area
        string sVAreaTag = GetLocalString(oCurrentArea, "ACR_SAT_CURRENT_AREA_TAG");
        if (sVAreaTag != "") {
            if (ACR_SAT_DEBUG) _debugMessage("The area we are leaving is a virtual area for tags: "+ sVAreaTag);
            // check if this was the last PC in the area
            int nPCs = 0;
            object oPC = GetFirstPC();
            while (GetIsObjectValid(oPC))
            {
              if (!GetIsDM(oPC)) {
                nPCs = nPCs+1;
              } else {
                if (ACR_SAT_DEBUG) _debugMessage("Found a DM. I will not count it in the number of PCs.");
              }
              // no need to count them all
              if (nPCs > 1) break;
              oPC = GetNextPC();
            }
            if (ACR_SAT_DEBUG) _debugMessage("Found this number of PCs (2 is 2 or more): "+ IntToString(nPCs));
            // we have to free the area if there is no PC or if
            // there is only one and he is ATing
            if ((nPCs == 0) || (nPCs== 1 && GetFirstPC() == oCreature)) {
                 ALFA_FreeVirtualArea(oCurrentArea);
                 if (ACR_SAT_DEBUG) _debugMessage("Freeing the area for "+sVAreaTag);
            }
        }
    }
 }

A complete example

This complete example is available here in a NWN1 mod based on the basemod: http://bgalfa052.free.fr/Misc/ALFAbasemod3.0_grid.rar

The following figure shows 3 overlapping grids:

Their definition in the OnLoad script is:

  // Seamless AT grid definitions.
  // Seamless AT grid definitions.
  ALFA_AddVirtualArea("P", "varea_p_1");
  ALFA_AddVirtualArea("P", "varea_p_2");
  ALFA_AddVirtualArea("F", "varea_f_1");
  ALFA_AddVirtualArea("F", "varea_f_2");
  ALFA_AddVirtualArea("F", "varea_f_3");

  int nGrid0 = ALFA_CreateGridNumber();

  // Grid 0 (Castle) goes here
  // this should be the coordinates on the global alfa map
  ALFA_SetGridOriginCoordinates(nGrid0, 90.0f, 110.0f, 0.0f);
  // let's say this grid has 3.5 by 3.5 mile areas
  ALFA_SetGridScaling(nGrid0, 3.5, 3.5, 1.0f);
  // this grid has 16 by 16 areas
  ALFA_SetGridAreaSize(nGrid0, 160.0f, 160.0f);
  // the rows:
  ALFA_SetNegativeGridRow(nGrid0, 0,  0, "1");
  ALFA_SetPositiveGridRow(nGrid0, 0,  0,  "1");
  ALFA_SetNegativeGridRow(nGrid0, 0, -1, "1");
  ALFA_SetPositiveGridRow(nGrid0, 0, -1,  "1");

  int nGrid1 = ALFA_CreateGridNumber();

  // Grid 1 (small city) goes here
  // this should be the coordinates on the global alfa map
  ALFA_SetGridOriginCoordinates(nGrid1, 99.0f, 85.0f, 0.0f);
  // let's say this grid has 2 by 1 mile areas
  ALFA_SetGridScaling(nGrid1, 2.0, 1.0, 1.0f);
  // this grid has 16 by 8 areas
  ALFA_SetGridAreaSize(nGrid1, 160.0f, 80.0f);
  // the rows:
  ALFA_SetNegativeGridRow(nGrid1, 0,  0, "");
  ALFA_SetPositiveGridRow(nGrid1, 0,  0,  "1");
  ALFA_SetNegativeGridRow(nGrid1, 0, -1, "");
  ALFA_SetPositiveGridRow(nGrid1, 0, -1,  "1");

  int nGrid2 = ALFA_CreateGridNumber();

  // Grid 2 (Rural/Main grid) goes here
  // this should be the coordinates on the global alfa map
  ALFA_SetGridOriginCoordinates(nGrid2, 100.0f, 100.0f, 0.0f);
  // let's say this grid has 10 by 10 miles areas
  ALFA_SetGridScaling(nGrid2, 10.0, 10.0, 1.0f);
  // this grid has 32 by 32 areas
  ALFA_SetGridAreaSize(nGrid2, 320.0f, 320.0f);
  // the rows:
  ALFA_SetNegativeGridRow(nGrid2, 0,  0, "1");
  ALFA_SetPositiveGridRow(nGrid2, 0,  0,  "FF");
  ALFA_SetNegativeGridRow(nGrid2, 0, -1, "1");
  ALFA_SetPositiveGridRow(nGrid2, 0, -1,  "PF");
  ALFA_SetNegativeGridRow(nGrid2, 0, -2, "1");
  ALFA_SetPositiveGridRow(nGrid2, 0, -2,  "1P");

The complete code

It is available here: http://bgalfa052.free.fr/Misc/acr_sat_areatran.nss.txt