Arcane University:Cross Province Travel

The Beyond Skyrim Wiki — Hosted by UESP
Revision as of 14:23, 11 November 2021 by Thingy Person (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

This article explains strategies for adding connections to world spaces that are in different independent masters. In Beyond Skyrim, a project's esm also introduces the world space that project is based in, so other projects can not directly edit that world space without having the other project as a master. Therefore, there can't be a simple door linking one province to another.

Beyond Skyrim uses two frameworks based in BSAssets to solve the problem without requiring extra patches. One framework enables players to activate a door and get moved to the other side via script by way of keywords. The other framework allows NPCs to travel across by routing them through an inaccessible world space, also contained in BSAssets. This inaccessible world space is called BSKTravelWorld and it contains groups of connected cells which represent border crossings, and one large collection which connects all of the ports. For each border crossing, a project esm will edit a cell in this world space to link up its door and navmesh the cell it's in; BSAssets navmeshes the intervening cells, meaning a navmesh path will simply exist if both projects are loaded.

Layout of BSKTravelWorld

Condensed guide (Elscrux)

Border Crossing

  • Add a location for your border crossing and assign it to all the relevant cells
  • Add the keyword (BSKTravel[YourProvince]Border[OtherProvince][BorderName]) that should already be set up it BS Assets to your location
  • Add the location to your travel location form list ([YourProvince]TravelLocations)
  • At the border crossing, add three xMarkerHeading and these three location ref types:
    • BSKPlayerTravelMarker
    • BSKPlayerFollowerTravelMarker
    • BSKPlayerMountTravelMarker
  • One of those options
    • Attach the BSKTravelActivator script to your door/activator
      • Autofill the BSKTravelQuest property and assign this keyword (BSKTravel[OtherProvince]Border[YourProvince][BorderName]) to DestinationKeyword
    • Place a BSKCrossProvinceTravelTrigger trigger, fill its script properties and add a collision plane
      • Create a new message with this text: “You've reached the [YourProvince] border. Would you like to cross it?” and the buttons “Travel to [OtherProvince]” with index 0 and “Stay in [YourProvince]” with index 1
  • Add BSKCrossProvinceTravelDoor (won’t show up in game) at the border gate and link it to another door in BSKTravelWorld in cell: BS[YourProvince][BorderName] and navmesh and finalize the cell

Ship

  • This is part of your province travel quest.
  • Create dialogue for travelling
  • Add and fill a script property called BSKTravelQuest to the dialogue lines
  • Write this line in the end script fragment:

(BSKTravelQuest as BSKTravel).TravelTo(DestinationKeyword)

  • Add a GetGlobalValue [BSKTravelAllowed_OtherProvince] = 1 condition to the dialogue
  • Add a location for your port and assign it to all the relevant cells
  • Add the keyword (BSKTravel[YourProvince]Port[PortName]) that should already be set up it BS Assets to your location
  • Add the location to your travel location form list ([YourProvince]TravelLocations)
  • At the port, add three xMarkerHeading and these three location ref types:
    • BSKPlayerTravelMarker
    • BSKPlayerFollowerTravelMarker
    • BSKPlayerMountTravelMarker
  • Add BSKCrossProvinceTravelDoor (won’t show up in game) at the port at a ship door (where they could have bought a passage in) and link it to another door in BSKTravelWorld in your province’s port cell and navmesh and finalize the cell


Full guide regarding player travel (Kesta)

Adding new destinations

To add a new destination:

Load BSAssets.esm alone in the CreationKit. Create a new plugin, and in here, add a new keyword that will represent your destination. The current convention for such Keywords is the following:

STEP 1: BSAssets.esm

For Border gates: BSKTravelAAABorderBBBxxxxxx Where: AAA is "your" province tag, such as CYR for Cyrodiil. BBB is the other province tagwhere the player is coming from. xxxxx is the name of the area.

For instance, the keyword representing the Border gate that will allow players to travel into Cyrodiil, from Morrowind, which is named "canopy road", would be: BSKTravelCYRBorderBSMCanopyRoad, which is to be created by the Cyrodiil team. And it would need it's counterpart, the destination to allow player to travel into Morrowind from Cyrodiil, created by the MW team: BSKTravelBSMBorderCYRCanopyRoad


For Ports: BSKTravelAAAPortxxxxx Where: AAA is "your" province tag, such as CYR for Cyrodiil. xxxxx is the name of the area.

For instance, the keyword representing the Anvil port in Cyrodiil, that would allow players to travel by boat into Cyrodiil, would be BSKTravelCYRPortAnvil. To be created by the Cyrodiil team.


Setting up those keywords, with the proper convention, is the only thing that need to be done in BSAssets in order to setup a new destination. Once this is complete, save the plugin and send it for merge on the BSAssets repository. You then need to wait for a merge on BSAssets. Feel free to ping 1ShoedPunk, Claire, Kesta, or anyone you know who have the capability and permissions to do so to speed up the process. Note that if you know before hands all of your border gates and ports, it would be a good thing to make a single plugin containing all of the keywords and send it once and for all.


STEP 2: YourProvince.esm

Then the "real" work start: Load your province .esm alongside the BSAssets.esm as for any content addition. Create a proper location for the destination. Most will already should already have one setup anyway if your implementation is advanced enough. If not, create it. (Location is what is used to hold data used in the radiant system, as well as controlling the reach of the AI behavior, so they're needed for anything that's not just pretty landscape).

Add the keyword previously created in BSAssets to your location in the "Location Keywords" list, and assign it the value 1.0

Add this location to the formlist of travel locations for your province (these formlist live in BSAssets). The formlist can be found under <ProvinceTag>TravelLocations. For instance, all CYR locations will be added to CYRTravelLocations. Fair warning: From a CK perspective, only the list of the province you're currently working on will have entries! The formlists are empty in BSAssets, and meant to stay empty there.

Assign the location to the relevant cell(s), at least the one hat will contains the destination markers. (In the cell menu, right click and select Edit, then go to the Location tab and assign the location).

Drag and drop 3 xmarkers: 1 for the player, 1 for the follower, and 1 for the mount. Ideally, give them meaningful EditorID. Assign LocRefType to each xMarker (right click on the object -> edit, and go to LocRefType)

For the player marker, assign BSKTravelPlayerMarker For the follower marker, assign BSKTravelFollowerMarker For the mount marker, assign BSKTravelPlayerMountMarker

Save your plugin, and send it for merge to your province: Once it's done, this destination will now be ready to be used by other provinces to send the player there.

Adding triggers to new destinations

This bit really is an implementer thing, so will contains a few less details. To add a travel trigger:

You will need to check with the province you're trying to setup a trigger to that they indeed performed the previous step for said location. Once this is confirmed:

Create a new plugin. If you're setting a borde gate: Attach a script to the activator that will act as the border (an unlinked door is probably the ideal, check with LD what they want to do). If t's supposed to be "the player just walk through here and travel", this will be a bit trickier as you'll need a trigger box, but it won't desplay the usual <To Destination> on the player's HUD without extra SKSE/SkyUI trickery.

If you're setting a dialogue-based travel like for boat travel, just create your lines.

Whatever script / fragment that's supposed to send the player somewhere only need the following: A "BSKTravelQuest" Property pointing to the BSKTravelQuest quest (which is part of the framework and live in BSAssets). Ideally, make it a BSTravel property (this is the script that will be used), instead of just Quest for nicer looking script. A "DestinationKeyword" Property pointing to the Keyword of the destination the script should send you to (the one mentioned in the first part of the documentation, that exist in BSAssets).

Then this single line will initiate the travel if the destination is available: BSKTravelQuest.TravelTo(DestinationKeyword)

If for stylistic reason, you only want to assign base types to properties (i.e. BSKTravelQuest will be a Quest Property rather than a BSTravel Property), simply cast before the call: (BSKTravelQuest as BSKTravelQuest).TravelTo(DestinationKeyword)

And this is it This would typically take place in the OnActivate event of a door activator for cross-province, or in the dialogue fragment of topic info in response to "Take me to XXX" in your sailor's dialogues. The framework will take care of the rest, including displaying a message that it's impossible to travel there if no suitable destination have been found. (Meaning the STEP 2 of "Setting up a new destination" haven't been done/merged yet on the target province, or the province is not available in this game session, so not installd, or plugin not enabled).


For consistency reason, you will obviously want to prevent the player to even have access to the possibility of triggering the interaction if the destination is not available:

For triggers / activators, in their script, add an OnCellAttach event, with code that will check that the target province is installed, through the use of the BSKInstalled_<Province> Globals. These globals are "constants" (i.e. not baked in player's savegame), and set to 0 in BSAssets. They are ooverriden by each province .esm and set to 1. Meaning they're value is 0 if a province is not installed, and 1 if it is. If target province is not installed, simply call Self.Disable()on the activator. If it is, call Self.Enable()

For dialogues, add a condition checking against the same variable, so that the topic won't be available topic won't be available to the player if the target province is not installed.