[tutorial] Generic Missions - (e.g. gm_achievecoverage.xml)

The place to discuss scripting and game modifications for X4: Foundations.

Moderators: Moderators for English X Forum, Scripting / Modding Moderators

Post Reply
kuertee
EGOSOFT
EGOSOFT
Posts: 789
Joined: Sun, 14. Dec 03, 13:05
x4

[tutorial] Generic Missions - (e.g. gm_achievecoverage.xml)

Post by kuertee » Sun, 2. May 21, 04:26

(All this is the result of my r+d for my mod, Reputations and Professions (https://www.nexusmods.com/x4foundations/mods/636) which offers more generic missions through its 8 guilds that are more varied and more galaxy-spanning than the base generic missions.)

The points in this tutorial will be very succinct.

You need a general understanding of X4's Mission Director (MD) scripting.
It describes how the Generic Missions are created.
After this tutorial, you should get a general understanding of how to create new generic mission varieties.
Particularly, this tutorial will create an Achieve 30% Coverage of 100m mission titled "Xenon Early-Warning System" at your location.

(Note: How to use RMLs is different again.
Understanding RMLs is not required here because all your cue needs are signals from the GMs.
But with this tutorial, you should get an understanding of how the missions are structured.
Even Egosoft's faction missions (e.g. Turret Push, Hunt, etc.) use GMs.)

Base files that we need:
  • md/gm_achievecoverage.xml: This describes the parameters of the mission. E.g. Client, Target sector, Target coverage, etc.
  • Your_MD.xml: This is your own Mission Director (md) code that
    (1) Modifies the particular GM for what you need. E.g. Targets are Xenon ships in one mission. The target is a station, in another.
    And (2) Manages what happens when the GM fails, succeeds, is aborted, etc.
The set up
In Your_MD.xml, these are the minimum cues that you need:
  • Main cue: This cue should contain all your cues related to the mission. It will hold the mission var that is created in the GM when it issues a <create_mission />. You can use Main.hasmission in your code to check if the mission is running or not.
  • Callback cue: The GM will send signals to this cue in regards to what happens in the GM. Example signals are: $MISSION_GENERATED, $MISSION_ACCEPTED, $MISSION_SUCCEEDED, $RML_FAILED. Other signals are listed in the gm_achievecoverage.xml.
  • Clean up cue: Signal to this cue yourself with either a manual <event_cue_signalled /> or other conditions like <event_object_undocking_started />. The GM will listen to this cue. The GM will clean itself up when this cue is signaled.
E.g.:
Spoiler
Show

Code: Select all

<cue name="Main">
	<cues>
		<cue name="Callback" instantiate="true">
			<conditions>
				<event_cue_signalled />
			</conditions>
			<actions>
				<set_value name="$missionCue" exact="@event.param.$Cue" />
				<do_if value="@static.$EndFeedbackValue.$type == 'error' or @static.$FeedbackValue.$type == 'error' or @static.$FeedbackValue.$ID == '$MISSION_NO_VARIANT'">
					<!-- mission generation failed -->
					<signal_cue cue="$missionCleanUp" />
				</do_if>
				<do_elseif value="@static.$EndFeedbackValue.$type == 'removed' or @static.$FeedbackValue.$type == 'removed'">
					<!-- mission removed (after generation). e.g. offer timed out -->
					<signal_cue cue="$missionCleanUp" />
				</do_elseif>
				<do_else>
					<do_if value="@static.$EndFeedbackValue">
						<!-- mission success or failure. e.g.: $MISSION_SUCCEEDED -->
						<signal_cue cue="$missionCleanUp" />
					</do_if>
					<do_elseif value="@static.$FeedbackValue">
						<!-- other callbacks -->
					</do_elseif>
				</do_else>
				<cancel_cue cue="this" /><!-- instance complete. cancel not required. i just like adding it. -->
			</actions>
		</cue>
		<cue name="Cleanup">
			<conditions>
				<check_any>
					<event_cue_signalled />
					<event_object_undocking_started object="player.entity" />
				</check_any>
			</conditions>
			<actions>
				<!-- clean-up your mission here. e.g. destroy any object or that you no longer need. -->
				<reset_cue cue="Main_MD" />
				<reset_cue cue="this" /><!-- not required because reseting Main_MD will also reset all its cues. i just like adding it. -->
			</actions>
		</cue>
	</cues>
</cues>
The GM
These values are used in ALL gms:
  • $OfferObject: station the mission is offered.
  • $Faction: faction that offers the mission.
  • $Client: npc
  • $ClientOwner: faction of npc. for ease, same as $Faction.
  • $Difficulty: E.g. level.easy, level.medium, level.hard. Look at the common.xsd for other values here.
  • $MissionLevel: An int from 1 to 10. Modifies the reward value. 10 being double the reward. But the progress from 1 to 10 is exponential.
For all GMs, I use these values:
  • $RemoveOnSectorChange: false
  • $OfferDistance: 40m - this determines when the icon will appear on the station on the map. 0 will make it visible anywhere you are.
  • $BBSTimeoutMin: 3min - these Bulletin Board System timeouts determine how long the offer will last
  • $BBSTimeoutMax: 5min
  • $TextTable: table[] - an empty table that the GM will populate with text that describes the mission.
Each different GM requires different parameters. For gm_achievecoverage.xml, these are its particular parameters:
  • $Page: text page id. This is the page id in the language file (e.g. t\0001.xml) that describes the mission.
  • $TextOffset: text line id. This is the line id that describes the mission. The GM only needs one id because the following lines (whichever much the GM requires) will be used by the GM. Here's an except from gm_achievecoverage.xml:
    Spoiler
    Show

    Code: Select all

            <!--Text page indexes $TextOffset + #
            1 = $TextTable.$missionname
            2 = $TextTable.$description
            3 = $TextTable.$objective
            3 = $TextTable.$objective_inrange
            10 = Advanced Satellite Hint
            11 = $TextTable.$radiushint Radius hint
            -->
  • $CoverageSector: sector
  • $CoverageOffset: position in sector
  • $CoverageRadius: coverage required
  • $CoveragePercent: percentage required.
For the other GMs, just look at its Start Library parameters. Parameters particular to the GM are specified in the GM file and usually listed at the bottom of the list.
E.g. Here are the mission specific parameters for gm_bringitems.xml.
Spoiler
Show

Code: Select all

<!--Mission specific params-->
<param name="SignalLeakVoiceLine" default="null"                comment="The line that should be used for signal leak introductions to this mission" />
<param name="DeliveryNPC"                                       comment="The NPC to which the items should be delivered" />
<param name="DeliveryStation"                                   comment="The station in which to place the NPC or to place the dynamic interior" />
<param name="DeliveryItem"                                      comment="The item to be delivered" />
<param name="DeliveryAmount"      default="1"                   comment="The number of DeliveryItem to be delivered" />
<param name="OwnedAmount"         default="null"                comment="The number of DeliveryItem to be delivered" />
<!-- ************************************************************************************************************************************************************************ -->
<!-- If the mission is to manage placing the delivery NPC, $StartObject must be set.
Interior parameters are optional but if one is set, they must be passed in together -->
<param name="PlaceNPC"            default="false"               comment="Does the mission handle the placement and removal of the delivery NPC? If false, some other system must be in charge of doing so." />
<param name="PlacementTable"      default="table[$cue = namespace, $priority = 50]"  comment="Table containing the owning cue and priority of placing the NPC vs other missions"/>
<param name="DeliveryNPCSlotTags" default="[tag.npc_generic]"   comment="NPC slot tags to help position the DeliveryNPC" />
<!-- Interior parameters: Room, corridor and door definitions could be provided by the caller through get_room_definition -->
<param name="DeliveryRoomMacro" default="null" comment="The dynamic interior room the DeliveryNPC should be placed in, if not placed already" />
<param name="DeliveryCorridorMacro" default="null" comment="The dynamic interior corridor leading to DeliveryRoomMacro" />
<param name="DeliveryDoors" default="null" comment="The possible dynamic interior doors used to connect the room and corridor. When defining a room or corridor macro, this is is the variable saved in the doors parameter" />
<param name="DeliveryInteriorName" default="null" comment="The name of the dynamic interior the DeliveryNPC should used, if not placed already" />
<!-- ************************************************************************************************************************************************************************ -->
My_AchieveCoverage_GM
With all that, we're ready to create the GM by creating our cue using the GM's Start Library.
Here's our new Main_MD code with the cue for creating the actual GM.
Note that you may need to modify the code. I actually didn't run it in the game. Consider it pseudo-code.
Note how Main_MD, Callback and Cleanup are sent as parameters to the GM's Start Library.

Code: Select all

<cue name="Main_MD">
	<cues>
		<cue name="Callback" instantiate="true">
			<conditions>
				<event_cue_signalled />
			</conditions>
			<actions>
				<set_value name="$missionCue" exact="@event.param.$Cue" />
				<do_if value="@static.$EndFeedbackValue.$type == 'error' or @static.$FeedbackValue.$type == 'error' or @static.$FeedbackValue.$ID == '$MISSION_NO_VARIANT'">
					<!-- mission generation failed -->
					<signal_cue cue="$missionCleanUp" />
				</do_if>
				<do_elseif value="@static.$EndFeedbackValue.$type == 'removed' or @static.$FeedbackValue.$type == 'removed'">
					<!-- mission removed (after generation). e.g. offer timed out -->
					<signal_cue cue="$missionCleanUp" />
				</do_elseif>
				<do_else>
					<do_if value="@static.$EndFeedbackValue">
						<!-- mission success or failure. e.g.: $MISSION_SUCCEEDED -->
						<signal_cue cue="$missionCleanUp" />
					</do_if>
					<do_elseif value="@static.$FeedbackValue">
						<!-- other callbacks -->
					</do_elseif>
				</do_else>
				<cancel_cue cue="this" /><!-- instance complete. cancel not required. i just like adding it. -->
			</actions>
		</cue>
		<cue name="Cleanup">
			<conditions>
				<event_cue_signalled />
			</conditions>
			<actions>
				<!-- clean-up your mission here. e.g. destroy any object or that you no longer need. -->
				<reset_cue cue="Main_MD" />
				<reset_cue cue="this" /><!-- not required because reseting Main_MD will also reset all its cues. i just like adding it. -->
			</actions>
		</cue>
		<cue name="My_AchieveCoverage_GM">
			<conditions>
				<event_cue_signalled />
			</conditions>
			<actions>
				<set_value name="$TextTable" exact="[]" />

				<find_station name="$OfferObject" space="player.galaxy">
					<match_relation_to object="player.entity" comparison="gt" relation="enemy" />
					<match_gate_distance object="player.entity" min="0" max="4" />
				</find_station>
				<set_value name="$Faction" exact="faction.argon" />
				<create_cue_actor cue="this" name="$Client">
					<select faction="$Faction" />
					<owner exact="$Faction" />
				</create_cue_actor>
				<set_value name="$Difficulty" exact="level.easy" />
				<set_value name="$MissionLevel" exact="1" />

				<set_value name="$Page" exact="30102" /> 
				<set_value name="$TextOffset" exact="101" /><!-- this will grab the Xenon Early-Warning System description -->
				<set_value name="$CoverageSector" exact="player.sector" />
				<set_value name="$CoverageOffset" exact="player.position" />
				<set_value name="$CoverageRadius" exact="100" />
				<set_value name="$CoveragePercent" exact="30" />
			</actions>
			<cues>
				<cue name="AchieveCoverage_MissionActual" ref="md.GM_AchieveCoverage.Start">
					<param name="RemoveOnSectorChange" value="false" />
					<param name="OfferDistance" value="0" />
					<param name="ReportSignalCue" value="Callback" />
					<param name="BBSTimeoutMin" value="3min" />
					<param name="BBSTimeoutMax" value="5min" />
					<param name="TextTable" value="$TextTable" />
					<param name="ForceCleanup" value="CleanUp" />

					<param name="OfferObject" value="$OfferObject" />
					<param name="Faction" value="$Faction" />
					<param name="Client" value="$Client" />
					<param name="ClientOwner" value="$Client.owner" />
					<param name="MissionCue" value="Main_MD" />
					<param name="Difficulty" value="$Difficulty" />
					<param name="MissionLevel" value="$MissionLevel" />

					<param name="MissionGroup" value="$missionParameterValues.$MissionGroup" />
					<param name="Page" value="$Page" />
					<param name="TextOffset" value="$TextOffset" />
					<param name="CoverageSector" value="$CoverageSector" />
					<param name="CoverageOffset" value="$CoverageOffset" />
					<param name="CoverageRadius" value="$CoverageRadius" />
					<param name="CoveragePercent" value="$CoveragePercent" />
				</cue>
			</cues>
		</cue>
		<cue name="Start_it_up">
			<actions>
				<signal_cue cue="My_AchieveCoverage_GM" />
			</actions>
		</cue>
	</cues>
</cues>
Mods: RPG: Reputations and Professions, Social Standings and Citizenships, Crime has Consequences, Alternatives to Death. Missions/NPCs: Emergent Missions, NPC Reactions, Mod Parts Trader, High-sec Rooms are Locked, Hacking Outcomes, More Generic Missions, Waypoint Fields for Deployment. Others: Auto-cam, Friendly Fire Tweaks, Teleport From Transporter Room, Wear and Tear. QoL: Trade Analytics, Loot Mining, Ship Scanner, Signal Leak Hunter, Station Scanner, Surface Element Targeting, etc.

Post Reply

Return to “X4: Foundations - Scripts and Modding”