There are two kinds of mission - those that complete instantaneously (promotions, culture bomb, etc) and those that take a number of turns (improvements, routes, etc). While the new Lua custom mission events can handle both types, they have only ever been tested with instantaneous missions.
(Edit: Durational custom missions are described here.)
To create a new instantaneous custom mission we need to define the mission (via XML/SQL) and add the code to handle it (via Lua)
The custom mission definition is an entry in the Missions table and associated text strings. Taking the "Add Beliefs" custom mission from my Morindim civilization as an example
Code:<GameData> <Missions> <Row> <!-- Unique mission type --> <Type>MISSION_MORINDIM_BELIEF</Type> <!-- See below --> <Description>TXT_KEY_MISSION_MORINDIM_BELIEF</Description> <Help>TXT_KEY_MISSION_MORINDIM_BELIEF_HELP</Help> <DisabledHelp>TXT_KEY_MISSION_MORINDIM_BELIEF_HELP_DISABLED</DisabledHelp> <!-- Icon to display in the Unit Panel UI - need 45 and 64 variants --> <IconAtlas>MORINDIM_ACTIONS_ATLAS</IconAtlas> <IconIndex>1</IconIndex> <!-- Where the mission appears in the Unit Panel UI - <=100 in the secondary stack, >100 in the main stack, >200 above all standard actions --> <OrderPriority>201</OrderPriority> <!-- Is this mission available, should always be 1 for custom missions --> <Visible>1</Visible> <!-- No idea! Copied straight from an existing mission --> <EntityEventType>ENTITY_EVENT_GREAT_EVENT</EntityEventType> <Time>25</Time> </Row> </Missions> <Language_en_US> <Row Tag="TXT_KEY_MISSION_MORINDIM_BELIEF"> <!-- Mission name - not actually used by the UI --> <Text>Add Beliefs</Text> </Row> <Row Tag="TXT_KEY_MISSION_MORINDIM_BELIEF_HELP"> <!-- Tooltip text to display when the unit can perform the mission right now, right here --> <Text>This order will consume the Clan Magician and add new beliefs to the Morindim's pantheon.</Text> </Row> <Row Tag="TXT_KEY_MISSION_MORINDIM_BELIEF_HELP_DISABLED"> <!-- Tooltip text to display when the unit can not perform the mission right now, right here, but could perform the mission under different circumstances --> <Text>The Clan Magician must be within their own territory to add beliefs.</Text> </Row> </Language_en_US> </GameData>
For an instantaneous mission we need to add code to handle three of the custom mission events - CustomMissionPossible, CustomMissionStart and CustomMissionCompleted
The required Lua code is pretty much "is this my custom mission", "can the unit ever perform the custom mission", "can the unit perform the custom mission now", and "action the mission".
Continuing the Morindim "Add Beliefs" example, some setup stuff
Code:-- Utility functions - each function (used in the handlers below) does what it says on the tin! include("MorindimUtils") -- The custom mission we're interested in local iMissionBelief = GameInfoTypes.MISSION_MORINDIM_BELIEF -- Return value constants needed by CustomMissionStart, do not change these, just use them local CUSTOM_MISSION_NO_ACTION = 0 local CUSTOM_MISSION_ACTION = 1 local CUSTOM_MISSION_DONE = 2 local CUSTOM_MISSION_ACTION_AND_DONE = 3
Every time the Unit Panel is displayed, it loops all possible actions (missions, builds, promotions, etc) and determines a) if the unit could perform that action and b) if it can perform it right now, right here. The modded DLL sends the Lua GameEvents.CustomMissionPossible() event to ascertain if each custom mission meets these criteria, so we need to hook that event
Code:function OnCustomMissionPossible(iPlayer, iUnit, iMission, iData1, iData2, _, _, iPlotX, iPlotY, bTestVisible) -- iPlayer - the player this event is for -- iUnit - the unit of that player this event is for -- iMission - the custom mission id -- iData1, iData2 - additional mission data that can be sent via Lua (usually not used) -- _, _ - two parameters that are always 0 and -1 so can be ignored -- iPlotX, iPlotY - where the mission is to occur -- bTestVisible - true = could the unit perform the action, false = can the unit perform the action now -- Is this the Morindim player (told you the utility functions do what they say on the tin!) and the mission we're interested in? if ([COLOR="DarkOrchid"]IsMorindimPlayer(iPlayer) and iMission == iMissionBelief[/COLOR]) then local pPlayer = Players[iPlayer] local pMagicianUnit = pPlayer:GetUnitByID(iUnit) -- Can this particular unit type for this player perform this mission? In our case only Clan Magicians can if ([COLOR="SeaGreen"]IsClanMagician(pMagicianUnit)[/COLOR]) then -- So we know the unit could perform the mission, but does it meet all the circumstantial conditions, ie, is the unit within our territory? if ([COLOR="Orange"]pMagicianUnit:GetPlot():GetOwner() ~= iPlayer[/COLOR]) then -- Circumstantial conditions NOT met (not in our territory), return the value of bTestVisible return bTestVisible end -- All conditions met, so we can perform the action return true end end -- The unit can't perform the mission - wrong player, wrong unit type, whatever - return false return false end GameEvents.CustomMissionPossible.Add(OnCustomMissionPossible)
When the player clicks the mission button in the Unit Panel, the DLL sends the GameEvents.CustomMissionStart event, so we need to hook that event to actually perform the custom mission
Code:function OnCustomMissionStart(iPlayer, iUnit, iMission, iData1, iData2, iFlags, iTurn) -- iPlayer - the player this event is for -- iUnit - the unit of that player this event is for -- iMission - the custom mission id -- iData1, iData2, iFlags - additional mission data that can be sent via Lua (usually not used) -- iTurn - the turn the mission started (not used for instantaneous missions as it will always be the current turn) -- Is this the Morindim player and the mission we're interested in? if ([COLOR="darkorchid"]IsMorindimPlayer(iPlayer) and iMission == iMissionBelief[/COLOR]) then -- We don't need to check that the unit can actually perform the mission now, as we couldn't have clicked the mission button if it can't! -- Action the mission - in this example we just fire a LuaEvent and let some other code worry about bringing up the Choose Beliefs UI and removing the unit [COLOR="magenta"]LuaEvents.MorindimChooseBeliefs(iPlayer, iUnit, 2)[/COLOR] -- Instantaneous missions MUST ALWAYS return the CUSTOM_MISSION_NO_ACTION outcome after executing the mission code return CUSTOM_MISSION_NO_ACTION end -- The mission wasn't for us, return the default outcome return CUSTOM_MISSION_NO_ACTION end GameEvents.CustomMissionStart.Add(OnCustomMissionStart)
Finally we need to handle the GameEvents.CustomMissionCompleted event sent by the DLL after the mission has completed.
Code:function OnCustomMissionCompleted(iPlayer, iUnit, iMission, iData1, iData2, iFlags, iTurn) -- iPlayer - the player this event is for -- iUnit - the unit of that player this event is for -- iMission - the custom mission id -- iData1, iData2, iFlags - additional mission data that can be sent via Lua (usually not used) -- iTurn - the turn the mission started (not used for instantaneous missions as it will always be the current turn) -- Is this the Morindim player and the mission we're interested in? if ([COLOR="darkorchid"]IsMorindimPlayer(iPlayer) and iMission == iMissionBelief[/COLOR]) then -- We don't need to check that the unit can actually perform the mission, as we couldn't have clicked the mission button if it can't! -- Instantaneous missions will never do anything here (as we did all the work in CustomMissionStart) -- Return true as this is our mission return true end -- The mission wasn't for us, return false return false end GameEvents.CustomMissionCompleted.Add(OnCustomMissionCompleted)
IMPORTANT NOTE: The AI uses none of this code. If you want the AI to perform your custom missions you will need to add code (usually at the start of the AI's turn) to determine if the AI controlled unit can perform the mission, if it is appropriate to perform the mission, and then to action the outcome of the mission directly.
Hello. I am working on a Mod which allows naval units to pillage coastal land tiles like Civ6. Once player select the pillage button, available pillage targets (adjacent land tiles that have improvement) should be highlighted, so that player can choose the target tile. It should be similar to this. ( https://forums.civfanatics.com/threads/allowing-air-units-to-pillage-tiles.631140/ ) But I don't know how to use your mod to create a custom mission that requires player to select a target. When I look inside the dll codes, I find those encoded range missions (move to, range attack, etc) use iData1 and iData2 to record the position target plot. But it doesn't work for custom missions as I tested.