World Map Tutorial - Part 2  

In Part 1 of this tutorial, the basic setup for custom world maps was covered. In this tutorial, we will unravel the event flow when using world maps and the functionality that can be embedded in each of those event handlers.

First, a short illustration on the event flow tied to world map usage:

Event Flow related to World Map usage

It is good practice to define a primary world map in the EVENT_TYPE_MODULE_START event just to avoid any gotchas later. This just makes sure there is a world map displayed if the player initiates an unforeseen transition ;)
object oWorldMap = GetObjectByTag("<world map tag>");
WR_SetWorldMapPrimary(oWorldMap);

If you want, you can also set a secondary world map using:
WR_SetWorldMapSecondary(object oMapID);

What is the difference? A secondary map enables the second icon in the World Map allowing you to see both world maps. To give an example in the OC, it would be like Denerim and the Wide Open World. When in Denerim, doing a transition would take you to the Denerim map. Clicking the secondary map button would take you to the Wide Open World.

EVENT_TYPE_TRANSITION_TO_WORLD_MAP is the core event where you evaluate plot flags or put in checks to determine which world map to show and actually show the map.
SetWorldMapGuiStatus(WM_GUI_STATUS_USE);
OpenPrimaryWorldMap();

Two important checks (if you have the associated features in your module) that must be done here are:
If the player is exiting out of a random encounter, you have to make sure that the player continues on his way rather than selecting a new destination (this is just for the animation part)
if (GetEventString(ev, 1) == RANDOM_ENCOUNTER_TRANSITION_ID)
{
// exiting random - encounter - finish travel animation
WorldMapCompleteRandomEncounter();
break;
}

If the player is exiting out of a party camp and you want the Party Picker to be shown, you do it here
if(GetEventString(ev, 1) == CAMP_EXIT_TRANSITION_ID)
{
SetPartyPickerGUIStatus(PP_GUI_STATUS_USE);
ShowPartyPickerGUI();
break;
}

The constants used above are defined in the world_maps_h which is a core include and hence, can be included directly in your script - unless you want to change some of the functionality.

Once you show the World Map, the player can select a Map Pin to travel to. This triggers the EVENT_TYPE_BEGIN_TRAVEL where all the grunt work related to area-related plots, random encounters, camp travel is done. Finally, the WorldMapStartTravelling function is called to start the travel animation (important when random encounters are specified; otherwise they will not work)

A few important checks that are usually done here would be -
  • Check if waypoint overrides are set for the map pin (this is actually passed as an event parameter - GetEventString(ev, 2)
  • Handle random encounters - this is basically handled via plots unless you want really random encounters. In any case, this is scripted independently
  • Store the target map pin's area and waypoint tags - the area tag is passed as GetEventString(ev, 1) and the waypoint tag is determined from the waypoints 2da unless a waypoint override is present. This is important if you have random encounters (see EVENT_TYPE_WORLDMAP_PRETRANSITION event below)
  • SetLocalString(GetModule(), WM_STORED_AREA, sTarget); SetLocalString(GetModule(), WM_STORED_WP, sWP);
Camp related checks
  • Store the current location from which the world map was accessed so that when transitioning back, you can jump the party straight back
  • If source area is camp and, then basically don't do the animation trail. Jump the player back to the previous area (tied directly to the point above)
  • If the target area is camp, again, don't play the animation but do a direct transition
  • If source and target are both camps, empty the party and place the followers in their positions
EVENT_TYPE_WORLDMAP_PRETRANSITION is a pretty basic event that is called at the same time as the animation trail starts. All you do here is do the actual transition to the target area - except when the target area is the party camp, in which case it is done in EVENT_TYPE_BEGIN_TRAVEL itself. No fancy stuff here.
The one important thing to remember here is - the player will not be able to click on a map pin when exiting from a random encounter. Hence, you cannot get a target area from the event parameters. This is the reason why you store the target area and waypoint in the module variables in the EVENT_TYPE_BEGIN_TRAVEL event.
string sArea = GetLocalString(GetModule(), WM_STORED_AREA);
string sWP = GetLocalString(GetModule(), WM_STORED_WP);
UT_DoAreaTransition(sArea, sWP);

EVENT_TYPE_WORLDMAP_POSTTRANSITION is an optional event in the sense that it need not be handled for the world map to work correctly. However, this is a great area to do all those 'whats-happening-in-the-evil-genius'-lair' type of cutscenes. Remember the cutscenes with Loghain in the OC or the ones with The Valsharess in NWN-HotU? These can be done in this event.

EVENT_TYPE_WORLDMAP_CLOSED is also not a compulsory event unless you have a party camp in your module or multiple primary/secondary map combinations. The important (and only?) event parameter here is the first integer parameter:
GetEventInteger(ev,0) 
  • 0 when the world map action is cancelled by the player
  • 1 when it is closed after a travel is completed
Obviously, the above events can be analyzed and dissected even more but I don't plan to do that. The above information is enough to get world map transitions working in your module.

Now, let's get to the interesting part - customizations/changes from what's done in the OC.
  1. The waypoints 2DA is not an integral part of the world map functionality. It is deduced via scripting through the WM_GetWorldMapTargetWaypoint function in world_maps_h. All it does is go through the 2DA line by line to compare the area tags and then, get the waypoint tag. You can, instead write your own 2DA to put in waypoint tags based on plot conditions or specify a list of random waypoints from which one is picked to jump to (a diablo-esque dungeon with multiple entry points?) or if you have just one entry point in your areas, skip this 2da altogether, keep the area and waypoint tags same and use GetEventString(ev, 1) from EVENT_TYPE_BEGIN_TRAVEL for both.
  2. The first 2 integers in EVENT_TYPE_BEGIN_TRAVEL are used in the OC to determine random encounters based on terrain. That's not a hard and fast rule since random encounters are scripted and there are no core functions tied to them. What does this mean? Random encounters have to be entirely scripted by the module maker and as such, can be entirely different systems. In that case, you can use these 2 integers coupled with the waypoint changes above to get a more extensive waypoint handling system.
  3. Let's say you want to show something to the player - a written scroll with hidden clues (National Treasure, anyone?) or a picture clue. You could create a map with that scroll/picture and show that to the player with OpenPrimaryWorldMap (after setting it to the Primary map, of course) - basically, it can be used to show any image. 
I actually had a couple of other ideas but haven't explored the system yet to see if they would actually work. You think you know everything and then, the game throws a googly at you by hard-coding something within the engine or the GUI (the blight animation in this case).
I hope this tutorial proves useful to you. I will be posting the tutorial on the social wiki soon so if you find something that needs further explanation or if you uncover anything else related to the map-making process, please add it to the page. Feedback is also welcome here, if you need further clarification on any of the points above.

One last thing - the process to integrate custom world maps into the OC/Awakening is the same process as described in Part 1 of the tutorial. The only difference would be that you will have to intercept the above events prior to them being called by the official game and prevent them from being handled if you so desire. Since these are all module events, it is pretty straightforward to override them in your custom module script.

    1 comments

    • AmstradHero  

      October 3, 2010 at 4:29 AM

      One thing that should be noted is that if you're writing an add-in to the main campaign and not a standalone mod, it is likely that you WILL need to edit the waypoint 2DA. Denerim, for example, needs to have a waypoint specified, otherwise it will dump the player outside of the area when they go to it, effectively breaking the player's game.

    Post a Comment