Realms of Ultima: Overland Map Land and Sea Travel System

by JasonNH

1/18/2009

Version 1.0

1.Features/Issues

Features:

  • Complete set of scripts, blueprints, and conversations for implementing land and sea transitions on the overland map.
  • Ability to require ownership deeds on a ship to ship basis.
  • Detects busy landing locations and re-directs to the next nearest location on shore if available.
  • Automatic placement of map pins on the area map to inform the player of anchored ship locations.
  • Single header file configuration parameters for determining sea travel music track and maximum distance from shore for boarding/unboarding.
  • Easily extendable to be used with docks and other placeable objects.

Known Issues:

  • Controllable ship models are essentially placed effects, making their collision meshes much smaller than they appear. This is solved for “anchored” ships by spawning in a bigger collision sphere around them, but two actively controlled ships would appear to pass through each other.

2.Overview

The purpose of the Realms of Ultima Overland Map Sea and Land Travel System is to provide a flexible environment for exploration and travel between land and sea terrains on an overland map. The system is intended to work in an area where land and sea are separated by a non-walkable border. In order to go from one side to the other, the player will make use of “skiffs” to board their ships from land or return back to shore.

When traveling on land, a skiff will appear as a usable, placeable object. Using the object prompts the player with a question asking if (s)he desires to board any available ships in the area. If accepted, a script will search for ships that are within the maximum boarding distance. If there is more than one, the player can choose the first, second, third, etc. nearest ship to him/her, up to a maximum of five. Once a ship is selected, a script will check for any deed ownership requirements for it. If a deed is required, the player's inventory will be searched. If no matching deed is detected, permission to board the ship will be denied. If a deed is found, or no deed is required, the player will be jumped to the location of the target vessel. The ship will then be added to the player's roster and control will be switched over to it. The skiff placeable that was on shore will disappear because it becomes part of the ship’s inventory. When a player opens the ship’s inventory, (s)he will find a skiff item represented by a ship wheel icon equipped at the right, ring finger location. This will be used to return to shore. At this point, the player may travel by ship as far as the walkmesh allows.


When a player wants to return to shore, (s)he will need to use the skiff once again, which is located in the ships inventory. The skiff is represented by a ship wheel icon equipped at the right, ring finger location. To return to shore, the player simply unequips the skiff, which will prompt the player with the opportunity exit the ship. If accepted, a script will look for the nearest available shoreline. This is configured through the placement of special “shore” waypoints in the area. The module designer must place a shore waypoint at each location that a player is permitted to land. There are no modifications needed to the objects when they are placed, so it is a quick and easy process. When the ship exiting script finds one of these special “shore” waypoints, it checks to see if a skiff is already at that location. If one exists, the landing is considered to be “busy” and the next closest location is sought. If no available shoreline is found, the player is notified by a bark string from the ship. Otherwise, the player is jumped to the nearest, vacant shoreline. The skiff item in the ship’s inventory is deleted and a new skiff placeable object is created at the location on shore where the player was jumped. A map pin is also enabled at this point, enabling the player to recall any and all beached skiffs from the area map.


3.Internal Mechanics

3.1 Area Preparation

Begin by creating an overland map area and paint an unwalkable border beginning at the edge of the walkable land and extending far enough into the water to prevent ships from getting too close to land. In order for your ships to appear at the right height with respect to the water, there should be a difference of about 0.5m between the water and the ground underneath it. This is because the ships are visual effects with a fixed “z” axis offset of 2.0 so that they will appear above the water even when they are sitting on the ground from the engine’s perspective.


Next, place your special shore waypoints that are stored under Blueprints->Waypoints->Realms of Ultima->Ships->Shore WP. Place as many as you need in order to make your land easily accessible without crowding them together too much.


In actuality, there is nothing very special about these waypoints. They are simply map notes that all have the same tag. However, it is important that you do not change the tag value of these waypoints. When the scripts search for the nearest shoreline, it uses the predefined tag associated with these waypoints. If you change the tags, the scripts will not work!

3.2 Ship Placement and Configuration

One your area is prepared, go ahead and place your useable ships in the water. You will find them in the Blueprints->Creatures->Realms of Ultima->Ships folder. There are currently 6 ship models supported.


Notice that each ship has an appearance of “Air Elemental” with a visual effect attached to it. This is for two reasons. The first is that there is only one ship creature in the toolset and we would like to have more options than that. The second is to make it easier for the ship to appear on top of the water since visual effects can have an offset assigned to them. One drawback to using visual effects is that the collision mesh is much smaller than the model shown in game. If a ship is sitting in the water, then another ship controlled by the player could essentially sail right through it, which is not very eye-pleasing. In order prevent this from happening, inactive ships have their own collision meshes turned off and larger collision objects are spawned at their locations. This is implemented in the secondary spawn scripts of the ship blueprints, as well as anytime a player returns to land from the ship. This does introduce one other area design consideration. When creating your unwalkable border between land and sea, you want to make sure that the collision mesh spawned around the ship cannot reach the target waypoint where the PC will be jumped. Otherwise, the PC could end up inside the collision box of the ship and be stuck inside that invisible, impenetrable fortress!


Once your ship is placed, you can configure a few different variables associated with it. One of them is mandatory, the other two are optional, and the other should be left alone unless you're modifying the system.


string sShipTag: This variable is used to distinguish ships from each other. THIS VARIABLE IS VERY IMPORTANT! Similar to the shore waypoints, every ship must have the same Tag value under its Basic Properties (c_rou_om_ship) for the scripts to work. It is this local variable “sShipTag” that provides differentiation within the system. It is used for deed checking and roster naming, so it must be unique for every ship that can be boarded. If you do not specify a value, a unique one will automatically be generated from the ship's spawn script. If you do specify a value, make sure it is unique across the entire module.

int nDeedRequired: Set to TRUE (1) or FALSE (0) depending on whether the PC must obtain a deed to operate the ship.

string sDeedDesc: This field allows you to enter the description that your deed item will have when a deed for this ship is given to the player via script. If left blank, the generic description, “This is a deed to one of your ships” will be supplied.

string SpawnScript:This is the user defined spawn script called by the OEI nw_c2_default9 spawn script. It takes care of creating a unique sShipTag value if none was given, and creates the collision sphere over the ship.

3.3 Skiff Placeables

Skiffs are used to go from the shoreline to the ship. You will find them under the Blueprints->Placeables->Realms of Ultima->Ships folder.


These objects may be placed according to the developers liking in the area. No modifications are required once they are placed down. When a PC uses the placeable, it will begin its conversation file “c_rou_skiff”.

3.3 Skiff Inventory Item

When a player boards a ship from a skiff, an inventory item is equipped at the right ring finger location. The object is located under the Blueprints->Items->Realms of Ultima->Ships folder as “Skiff”


There is nothing very special about the item. It performs its functions using tag based scripting, being associated with the unequip action through the script i_rou_it_skiff_ue. The unequip script disables dialogue orientation to prevent the ship from turning and fires the “c_rou_it_skiff” conversation with itself.

3.4 Conversations

There are two conversations included in the travel system. One for boarding ships, and another for returning to shore:

c_rou_skiff: This conversation is called from the placeable skiff's OnUsed event. It makes use of two scripts. The gc_rou_boardable_ships() check returns TRUE or FALSE depending on whether there is a ship in the area that the player can board. The criteria are distance and deed ownership for any ship that requires one. If there are multiple ships in the area, the player is given up to five different ships to choose from. The other script is ga_rou_skiff_to_ship(), which transitions the player from the skiff placeable to the given ship. See the individual script descriptions below for more details.


c_rou_it_skiff: This conversation is called when a skiff item is unequiped from a ship's inventory. It is very simple and makes use of two action scripts. The ga_rou_skiff_to_land() script transitions the player back to shore if the requirements are met. The ga_rou_abort_skiff_to_land() script re-equips the skiff item in the appropriate inventory slot and closes the inventory GUI screen for the player.


3.5 Scripts

The system includes the following scripts:

Script Name: ginc_rou_ships:

Description: This is the general include file for supporting ship transition functions and contains the majority of the source code for the system. Most, if not all of the other supporting scripts include this file so if you make changes to it, you need to recompile all the other scripts.

Configuration Variables: The following variables were defined for providing an easy way to change aspects of the system that are likely to be module dependent.

Name: ROU_OM_MAX_UNBOARDING_DISTANCE

Type: float

Value: 25.0

Description:This defines the maximum distance in meters that a boat can be from shore when a player desires to return to land.

Name: ROU_SHIP_MUSIC_TRACK_DAY

Type: int

Value: 169

Description:This is the day track that will be played when a player is at sea. The current value is Ambient Beach Theme from Storm of Zehir.

Name: ROU_SHIP_MUSIC_TRACK_NIGHT

Type: int

Value: 201

Description:This is the nighttime track that will be played when a player is at sea. The current value is Herald of Zehir Theme from Storm of Zehir.

Internal Variables: The following variables are used internally by the scripting system. They are here primarily to help toolset users avoid creating a conflict by using the same names. Modify at your own risk!

Name: ROU_SHIP_DEED_BASE_TAG

Type: string

Value: “rou_it_sd_”

Description:This is used to form the base tag value of ship deed items. Ship deeds are created with object tag values consisting of this root concatenated with the “sDeedDesc” local variable of the associated ship.

Name: ROU_SHIP_OWNER

Type: string

Value: “rou_ship_owner”

Description:This is the variable name used to store the PC as a local object on a ship indicating him/her as the current owner.

Name: ROU_OM_SHIP_TAG

Type: string

Value: “c_rou_om_ship”

Description:This is the fixed Creature tag value expected for every ship.

Name: ROU_OM_WP_SHORE_TAG

Type: string

Value: “rou_wp_shore”

Description:This is the fixed Waypoint tag value expected for every shore waypoint.

Name: ROU_SKIFF_MAP_NOTE

Type: string

Value: “rou_skiff_mapnote”

Description:This is no longer used.

Name: ROU_SHIP_COL_BALL

Type: string

Value: “rou_ship_col_ball”

Description:This is the resource name of the spherical collision mesh created on a ship when it is anchored at sea.

Name: ROU_DAYTIME_MUSIC

Type: string

Value: “rou_daytime_music”

Description:This is the variable name used to store the original daytime music track before changing it at sea.

Name: ROU_NIGHTTIME_MUSIC

Type: string

Value: “rou_nighttime_music”

Description:This is the variable name used to store the original nighttime music track before changing it at sea.

Name: ROU_NUM_NONAME_SHIPS

Type: string

Value: “rou_om_ships_noname”

Description:This is the variable name used to store the number of ships that have spawned without an “sShipTag” value defined. It is used by the spawn script to create a unique “sShipTag” value by concatenating the value of this variable with the base tag “rou_om_nonameship_”.

Name:ROU_EV_SHIP_JOIN_PC

Type: int

Value: 10000

Description:This is the value used to create a user defined event that tells a ship to join the PC as a roster member. The PC value is stored under the ROU_SHIP_OWNER variable name described above.

Functions:

Name: ROU_GiveShipDeed

Prototype: object ROU_GiveShipDeed(object oPC, string sDeedResName, string sShipTag)

Description: This function creates an item on a player that grants the ability

to board and operate a specific ship. First it scans the list of ship objects in the module looking for the one with a matching “sShipTag” variable. If found, it creates a ship deed with the description provided in the local variable “sDeedDesc” for that ship, or a default one if none is given. The deed has a tag value of ROU_SHIP_DEED_BASE_TAG plus the value of “sShipTag” for the associated ship. This deed is placed in the player's inventory.

Arguments: oPC: Player receiving the ship deed.

SdeedResName: Resource Name for the ship deed item blueprint.

SshipTag: The value of the "sShipTag" string variable for the

associated ship. NOTE: This is NOT the tag of the object, it is a local variable stored on the object.

Returns: Object handle to the newly created deed item.

Name: ROU_CheckShipDeed

Prototype:int ROU_CheckShipDeed(object oPC, string sShipTag, object oShip = OBJECT_INVALID)

Description:This function checks to see if the PC has the rightful use of a ship. If a ship object is not already specified, the ship corresponding with the “sShipTag” variable will be located. Then the “nDeedRequired” variable of that ship will be checked, and if it is set to FALSE, then the check will pass. Otherwise the script will search the player's inventory for a matching deed, which must have a tag value of ROU_SHIP_DEED_BASE_TAG plus the value of “sShipTag” for the associated ship. The return result will indicate the success of that search.

Arguments:oPC: Object handle to player being checked for the proper deed.

sShipTag: The “sShipTag” local variable of the ship needing the deed.

oShip: Object handle of the ship the player is trying to board if known.

Returns:TRUE – No deed is required or the PC has the deed required.

FALSE - The PC does not have the deed required.

Name:ROU_OM_IsShipBoardable

Prototype:int ROU_OM_IsShipBoardable(object oShip, object oPC)

Description:This function checks to see if a ship meets all the requirements to be boarded. It must be within 10 meters of the distance ROU_OM_MAX_UNBOARDING_DISTANCE, and the player must pass a ship deed check. The reason for allowing the extra 10 meters is to accommodate collision bumps and other minor inconsistencies. The most important objective is to prevent the frustrating circumstance of not being able to get to one of your ships, yet prevent outrageous jumps from land to a ship way off in the distance somewhere.

Arguments:oShip: Object handle of the ship the player is trying to board.

oPC: Object handle to player being checked.

Returns:TRUE - The PC may board the ship.

FALSE - The PC cannot board this ship.

Name:ROU_OM_SkiffToShip

Prototype:void ROU_OM_SkiffToShip(object oShip, object oPC)

Prerequisites:This function should be called by a skiff PLACEABLE object..

Description:This function will jump the PCs to the nearest overland map boat, add the vessel to the roster, and switch control to it. First the script attempts to create a skiff item object in the ships inventory and equip it at the right ring inventory slot. It will then fade the screen to black for the player, destroy the collision sphere placed around the anchored ship, and store the PC in the ships ROU_SHIP_OWNER local object variable. The map pin associated with this skiff is disabled and the ambient music tracks are configured according to the ROU_DAYTIME_MUSIC and ROU_NIGHTTIME_MUSIC variables. The PC is assigned two actions in its action queue: jump to the location of the ship and then send the ship a ROU_EV_SHIP_JOIN_PC user event signaling that it is time to join the PC's roster. This event is delayed by 0.25 seconds. It is not exactly known why this is the case, but probably due to a race condition between the time it takes for the ROU_SHIP_OWNER variable to be set and when the event is executed. Finally, the original skiff placeable is destroyed with a 0.5 second delay.