How to create and add a World Map

May I thank Montgomery Markland from Rogue Dao Studios for his tutorial which was the main inspiration of the tutorial.

Before we start this tutorial, you will need 2 things (1) A World Map (2) Hot spot icons.

The World map needs to be 647 x 647 in size and a tga file format to work correctly. The hot spot icons are located in your Atari/Neverwinter Nights 2/Campaigns/Official Campaign folder. You can of course use your own which have to be in the tga format and 32 x 32 pixels in size.

First thing we need to do is to create a new campaign (this will be saved as a new folder in your My Documents/Neverwinter Nights 2/Campaigns folder). You will need to put your world map image and you hot spot icons in the same folder.

Now open your module and then click on Plugins and then World Map Editor:

And the following box will pop up:

Now click on the File - New and you will notice that the middle section will go black and you now have the following in the properties section:

Click in the WorldMapImage and then click on the button that appears and then select your map. Your Editor will look similar to:

You will notice that I have added the WorldMapName "Moonshae", which is the parameter that will be passed to the function ShowWorldMap(). Now click on the Add button at the top:

and then select the "hotspot" on your map:

On the right hand side you will see the properties of this "hot spot":

    ActionScript: This script is started when you have selected the hotspot and you press travel.
    ConditionalScript: This script is checking if you can see the hotspot for tests we use only gc_true().
    Image:Specifies the icon that will be displayed as the hotspot.
    ImageMouseOver: Is the image that is displayed when the user hovers their mouse over that point.
    ImageSelected: This is the image of the hotspot when the user selects it.
    Description: This is optional like a comment.
    DisplayName: This is the Name you see on the right side of your map.
    Height: If you use the NWN2 Campaign tga you always have to enter 32.
    Location: The Location is entered by the Click with the Mouse.
    Name: This is the tag name of your Hotspot, you can see it on the left side of your World Map Editor.
    Sound: Sound that is played when the hotspot is selected.
    Width: Same as Height.

Our action script is going to be a custom script called ptp_hotspot_click (Full kudos goes to Montgomery Markland from Rogue Dao Studios for the following script):


// ptp_hotspot_click
//RDS Worldmap System
//Montgomery Markland -- Nov 1, 2006
//A simple worldmap system designed for use in situations when the world map hotspots are all available (Beta Testing Purgatorio)
//on first-look
//Adapted code from OEI kinc_worldmap

#include "ginc_transition"
#include "ginc_debug"

// ** world map locout functions **

// multiple PCs simultaneously clicking travel on the world map causes problems. We use a cooldown time to prevent this.
void SetWorldMapLocked();
int GetWorldMapLocked();


/////////////////////////
// CONSTANT DECLARATIONS
/////////////////////////

// GENERAL //

// these are for locking the world map so only one user can use it at a time
const string WORLD_MAP_LOCKED = "00_bWorldMapLocked";
const string WORLD_MAP_LOCKER = "oWorldMapLocker";
const int STRING_REF_MAP_LOCKED = 183512;
const string WORLD_MAP_LOCKED_DAY = "00_nWorldMapLockDay";
const string WORLD_MAP_LOCKED_HOUR = "00_nWorldMapLockHour";
const string WORLD_MAP_LOCKED_MINUTE = "00_nWorldMapLockMinute";
const string WORLD_MAP_LOCKED_SECOND = "00_nWorldMapLockSecond";
const int WORLD_MAP_LOCK_COOLDOWN = 18; //seconds until we allow another click
const string WORLD_MAP_LOCK_INITIALIZED = "00_bWorldMapLockInit";

void SetWorldMapLocked()
{
SetGlobalInt(WORLD_MAP_LOCKED_DAY, GetCalendarDay());
SetGlobalInt(WORLD_MAP_LOCKED_HOUR, GetTimeHour());
SetGlobalInt(WORLD_MAP_LOCKED_MINUTE, GetTimeMinute());
SetGlobalInt(WORLD_MAP_LOCKED_SECOND, GetTimeSecond());

SetGlobalInt(WORLD_MAP_LOCK_INITIALIZED, TRUE); //so we know the timer is initialized

PrettyMessage("World map locked at time " + IntToString(GetTimeHour()) + ":" + IntToString(GetTimeMinute()) + ":" + IntToString(GetTimeSecond()));
}

int TimeToSeconds(int nHour, int nMinute, int nSecond)
{
//for ease, we just convert the time into seconds -- 60 for 60 seconds in a minute, 3600 for number of seconds in an hour.
return nSecond + nMinute * 60 + nHour * 3600;
}

int GetWorldMapLocked()
{
if(GetIsSinglePlayer())
{
return FALSE;
}

if(!GetGlobalInt(WORLD_MAP_LOCK_INITIALIZED))
{
return FALSE;
}

int nLockedHour = GetGlobalInt(WORLD_MAP_LOCKED_HOUR);
int nLockedMinute = GetGlobalInt(WORLD_MAP_LOCKED_MINUTE);
int nLockedSecond = GetGlobalInt(WORLD_MAP_LOCKED_SECOND);

int nCurrentHour = GetTimeHour();
int nCurrentMinute = GetTimeMinute();
int nCurrentSecond = GetTimeSecond();

int nTotalLockedSeconds = TimeToSeconds(nLockedHour,nLockedMinute,nLockedSecond);
int nTargetTime = nTotalLockedSeconds + WORLD_MAP_LOCK_COOLDOWN; //10 sec cooldown

int nCurrentSeconds = TimeToSeconds(nCurrentHour,nCurrentMinute,nCurrentSecond);

// PrettyDebug("Target time for unlock = " + IntToString(nTargetTime));
// PrettyDebug("Current time in seconds = " + IntToString(nCurrentSeconds));

//same day
if(nTargetTime < nCurrentSeconds)
{
// PrettyDebug(IntToString(nCurrentSeconds - nTotalLockedSeconds) + " seconds passed since the last transition.");
return FALSE;
}

//we've rolled to the next calendar day
int nLockedDay = GetGlobalInt(WORLD_MAP_LOCKED_DAY);
int nCurrentDay = GetCalendarDay();

//!= is the check here because the day change could roll the month
//so going from month 1 day 28 to month 2 day 0 should still register as a day having passed.
if(nCurrentDay != nLockedDay && nCurrentSeconds > WORLD_MAP_LOCK_COOLDOWN)
{
return FALSE;
}

return TRUE;
}

void main(string sHotspot)

{
//Get the player's compass heading from the previous area
string sCompass = GetLocalString(OBJECT_SELF, "temp_wm_compass");
DeleteLocalString(OBJECT_SELF, "temp_wm_compass");
//Combine the player's compass heading with the hotspot string (targets proper waypoint in target area)
string sDestination = sHotspot + sCompass;
//Rather than increasing code complexity, we simple assume the existence of all
//Four compass-heading waypoints in the target area.
//This just means that the builder should paint all four compass heading waypoints
//(sHotspot+_n, sHotspot+_s, sHotspot+_e, sHotspot_+w) in the target area for a particular hotspot
//If there aren't four compass headings into the area, paint multiple waypoints for a specific compass heading
//IE, if you only have 3 compass heading entrances, _n, _s, _e - then paint "_w" in the same place as
//_n or _s. This system gives the builder flexibility to choose defaults and multiple defaults
object oDestination = GetObjectByTag(sDestination);
object oPC = OBJECT_SELF;
//critPathEncounter, nonCritPathEncounter and randomEncounter code to be added later
if(!GetIsSinglePlayer())
{
if(GetWorldMapLocked())
{
SendMessageToPC(oPC, GetStringByStrRef(STRING_REF_MAP_LOCKED));
return;
}
else
{
SetWorldMapLocked();
}
}
SinglePartyTransition(oPC, oDestination);
}

When you select the action script "ptp_hotspot_click" script in your worldmap point there is a sHotspot variable that you need to put the name of the waypoints in. ie XXXX

Thanks to Dehblud for pointing out that this part was missing from the tutorial.

Next we need to paint a World Map Transition, which can be access via the Triggers Blueprint:

Move your cursor over to your area preview and draw a rectangle. Select the trigger and then look at the properties of the trigger. Change the variable sMap to the name of your .wmp (not your image name, but the name of the world map, ie what it is saved as) and leave sOrigin blank.

To do this click in the Variables field and then click on the three dots button and select sMap in the list in the top left hand corner. For the ValueString change it to the name of your map like so:

Tag the trigger anything you want so long as the last two characters correspond to a compass heading: _n, _s, _w, _e


// gtr_world_map_cl
// ptp_world_map_cl (modified from OEI's gtr_world_map_cl
// Transition to the world map

// EPF

// EPF 2/2/06 -- modifying to have a default map for each act if no other map is specified.
// EPF 3/24/06 -- this is now an OnClicked event
// BMA-OEI 6/24/06 -- Added gather party check
// BMA-OEI 8/11/06 -- Autosave before transition
// ChazM 8/21/06 -- updated constant CAMPAIGN_SWITCH_REMOVE_DOMINATED_ON_TRANSITION
// BMA-OEI 8/22/06 -- Replaced auto save w/ AttemptSinglePlayerAutoSave()
// *MLM-RDS 10/31/06 -- Modified for use in Planscape Trilogy: Purgatorio

#include "ginc_debug"
#include "ginc_autosave"
#include "ginc_transition"

void main()
{
object oPC = GetClickingObject();
if ( GetIsPC( oPC ) == FALSE )
{
return;
}

string sMap = GetLocalString(OBJECT_SELF, "sMap");
string sOrigin = GetLocalString(OBJECT_SELF, "sOrigin");

/*if(sMap == "")
{
int nAct = GetGlobalInt("00_nAct");

switch(nAct)
{
case 1:
sMap = "hiveward";
break;
case 2:
sMap = "hiveward";
break;
case 3:
sMap = "hiveward";
break;
default:
sMap = "hiveward";
break;
}
}*/

// BMA-OEI 7/04/06 - Check if using remove dominated campaign flag
if ( GetGlobalInt(CAMPAIGN_SWITCH_REMOVE_DOMINATED_ON_TRANSITION) == TRUE )
{
int nRemoved = 0;
object oFM = GetFirstFactionMember( oPC, FALSE );
while ( GetIsObjectValid(oFM) == TRUE )
{
nRemoved = nRemoved + RemoveEffectsByType( oFM, EFFECT_TYPE_DOMINATED );
oFM = GetNextFactionMember( oPC, FALSE );
}

if ( nRemoved > 0 )
{
// Abort transition if dominated effect was found and removed
return;
}
}

// BMA-OEI 6/24/06
if ( GetGlobalInt( VAR_GLOBAL_GATHER_PARTY ) == 1 )
{
if ( IsPartyGathered( oPC ) == FALSE )
{
ReportPartyGather( oPC );
return;
}
}

//Establish our party's compass heading so they end up in the correct
//Or correct-default entrance to the next area by grabbing the compass heading
//of the clicked world map trigger and setting it as a local string on the clicker
//To utilize this...
//Append the tags of your world map triggers based on their compass heading (*_n, *_s, *_e, *_w)
//Unlike the target waypoints, you don't need all four compass headings matched to a trigger
string sCompass = GetTag(OBJECT_SELF);
SetLocalString(oPC, "temp_wm_compass", GetStringRight(sCompass, 2));
// BMA-OEI 8/22/06 -- Autosave before transition
AttemptSinglePlayerAutoSave();
ShowWorldMap( sMap, oPC, sOrigin );
}

Paint four generic waypoints in each area targetted by a hotspot:

XXXX_n, XXXX_s, XXXX_w, XXXX_e

Position these waypoints at the entrances and exits of your area, use -all four-. The XXXX corresponds to the variable string you input in the field for the hotspot action click script for the hotspot pointing to the area.

Open the campaign editor and add the module with the world map triggers to the new campaign where you saved your world map.