How to add an action to units ?

Gedemon

Modder
Super Moderator
Joined
Oct 4, 2004
Messages
12,787
Location
France
I'm completely lost in UnitPanel.lua :blush:

Well, maybe I shouldn't have taken civUP as an example, as there seems to be more than one mod merged in it's files...

Also Alpaca have made a custom mission handler, but I'm not sure if it's still compatible with the latest patch, and I've failed to find a simple example of use (or even a thread about it...)

So anyone could point me to a simple mod adding one action to units ?
 
What kind of action?

If it's an existing action (build farm, etc), check out the Roman Legion as that can build roads.

If you want to build a new type of improvement, you'll need to add both the Improvement and the Build table entries for it - see "Units - Mounted Units" which permits early basic units to build Corrals on Horse resources and then become Mounted

If it's a new action like Conduct Trade Mission then ... perhaps someone else knows :)
 
Yes a new action :D

For example let's call it "Air Recon", I'd like to know how to add the action/icon to the unit panel to trigger the lua function.
 
Actions (in terms of GameInfoActions) don't have Lua associated with them (at least not directly) as they are handled in the game core (via Game.CanHandleAction and Game.HandleAction). The outcome of an action may trigger an event (ImprovementCreated/Destroyed etc) on which you can hang Lua, if (and it's a very big if) an appropriate event exists.

So you're probably going to have to detect the correct type of unit when UnitPanel is processing (assuming you don't want ground units to perform "Air Recon") and then manually create an instance of the UnitAction control and add it to the PrimaryStack or SecondaryStack via the correct InstanceManager.

Welcome to the hell that is UI programming in Civ 5 :( (And on a scale of 1 to 10 for b awful coding, UnitPanel.lua is about an 11 ;) so you haven't picked an easy one to start with!)

But I'm pretty sure someone wrote some tutorials on this subject :)
 
Assuming this is the kind of thing you want to achieve (the popup could be any function call)



Take a copy of UnitPanel.lua into your mod and set VFS=true for it (note this file is very different between Vanilla and G&K so you will now be making your mod specific to one version or the other)

Edit UnitPanel.lua in your mod, and locate the line

Code:
Controls.PrimaryStack:CalculateSize();

and insert the following code immediately BEFORE it

Spoiler :

You can either work out how this works or just accept that it does ;)
Code:
--
-- HACKED MOD STARTS
--
local function SetToolTip(sTitle, sToolTip)
  local ttTable = {}
  TTManager:GetTypeControlTable( "TypeUnitAction", ttTable )
  
  ttTable.UnitActionHelp:SetText(string.format("[NEWLINE]%s", sToolTip))
  ttTable.UnitActionText:SetText(string.format("[COLOR_POSITIVE_TEXT]%s[ENDCOLOR]", Locale.ConvertTextKey(sTitle)))
  ttTable.UnitActionHotKey:SetText("")
  
  ttTable.UnitActionMouseover:DoAutoSize()
  local mouseoverSize = ttTable.UnitActionMouseover:GetSize();
  if (mouseoverSize.x < 350) then
    ttTable.UnitActionMouseover:SetSizeVal(350, mouseoverSize.y)
  end
end
  
for _, action in pairs(addinActions) do
  if (action.Condition == nil or
      (type(action.Condition) == "function" and action.Condition(action, unit)) or
    (type(action.Condition) ~= "function" and action.Condition)) then
    local instance  
    if ((tonumber(action.OrderPriority) or 200) > 100) then
      instance = g_PrimaryIM:GetInstance()
      numPrimaryActions = numPrimaryActions + 1
    else
      instance = g_SecondaryIM:GetInstance()
      numSecondaryActions = numSecondaryActions + 1
    end
  
  if ((type(action.Disabled) == "function" and action.Disabled(action, unit)) or
      (type(action.Disabled) ~= "function" and action.Disabled)) then
      instance.UnitActionButton:SetAlpha(0.4)
      instance.UnitActionButton:SetDisabled(true)
    else
      instance.UnitActionButton:SetAlpha(1.0)
      instance.UnitActionButton:SetDisabled(false)
    end
  
    IconHookup(action.PortraitIndex, actionIconSize, action.IconAtlas, instance.UnitActionIcon)
    local sToolTip
    if (type(action.ToolTip) == "function") then
      sToolTip = action.ToolTip(action, unit)
    else
      sToolTip = action.ToolTip
    end
    instance.UnitActionButton:SetToolTipCallback(function() SetToolTip(action.Title, sToolTip) end)
    instance.UnitActionButton:SetToolTipString(Locale.ConvertTextKey(sToolTip))

    if (type(action.Action) == "function") then
      instance.UnitActionButton:RegisterCallback(Mouse.eLClick, function() action.Action(action, unit, Mouse.eLClick) end)
      instance.UnitActionButton:RegisterCallback(Mouse.eRClick, function() action.Action(action, unit, Mouse.eRClick) end)
    else
      instance.UnitActionButton:RegisterCallback(Mouse.eLClick, function() end)
      instance.UnitActionButton:RegisterCallback(Mouse.eRClick, function() end)
    end
  end
end
--
-- HACKED MOD ENDS
--

Now up the top of the UnitPanel.lua file add the following (just before the g_BuildCityControlMap block is a good place)

Code:
--
-- HACKED MOD STARTS
--
local addinActions = {
  { Name = "MyAction",
    Title = "My Action", -- or a TXT_KEY
    OrderPriority = 150, -- default is 200
    IconAtlas = "UNIT_ACTION_ATLAS", -- 45 and 64 variations required
    PortraitIndex = 47,
    ToolTip = "My action tooltip", -- or a TXT_KEY_ or a function
    Condition = function(action, unit) print(string.format("Condition %s called for unit %i for player %i", action.Name, unit:GetID(), unit:GetOwner())); return true end, -- or nil or a boolean, default is true
    Disabled = function(action, unit) print(string.format("Disabled %s called for unit %i for player %i", action.Name, unit:GetID(), unit:GetOwner())); return false end, -- or nil or a boolean, default is false
    Action = function(action, unit, eClick)
      Events.SerialEventGameMessagePopup({Type=ButtonPopupTypes.BUTTONPOPUP_TEXT, Data1=600, Text=string.format("%s %s called for %s %s", ((eClick == Mouse.eLClick) and "Left" or "Right"), action.Name, Players[unit:GetOwner()]:GetCivilizationAdjective(), unit:GetName())})
    end,
  },   
}
--
-- HACKED MOD ENDS
--

Build your mod and you should get the example shown above

You can add one or many "actions" into the addinActions array

Each "action" has the following
  • Name (optional) - a name for you to identify the action by
  • Title (required) - string or TXT_KEY to display at the top of the tooltip
  • OrderPriority (optional) - anything over 100 displays the action button in the main stack, 100 or less in the secondary stack, default is 200
  • IconAtlas (required) - the icon atlas to use (must have both 45 and 64 icon variants)
  • PortraitIndex (required) - the icon index to use
  • ToolTip (required) - a string or TXT_KEY or a function that returns a string or TXT_KEY for the tooltip
  • Condition (optional) - a boolean or a function that returns a boolean that determines if this action is displayed for the current unit, default is true
  • Disabled (optional) - a boolean or a function that returns a boolean that determines if the action is enabled for the current unit, default is false
  • Action (optional) - function that is called when the action button is clicked (if omitted the action icon is displayed but the button is non-functional

Functions for Condition, Disabled and ToolTip are of the form "function(action, unit)" where "action" is the action entry you have created and "unit" is the current unit object

The function for Action is of the form "function(action, unit, click)" where "click" is either Mouse.eLClick or Mouse.eRClick

If this is the kind of thing your after I'll probably create two versions of a "Utils - Modular UnitPanel Action Addins" mod that can load the actions on a per-mod basis is a nice dynamic manner (like the DiploCorner and SumaryBar addins)

HTH

W
 
I did something analogous to whoward69's code for Éa allowing new actions to be added via a new "EaActions" table. These can show either in the Actions UI area or the Build panel, and can have specific functions added for additional UI, action effects, or AI valuation.

The hard part was building AI for this system. Just the generic part has taken most of my coding effort over the last 9 months. But it's a major feature of Éa with 50 or so actions so far, fully usable by the AI (. . . and I'm just starting to add spells).
 
thank you very much whoward69, that's exactly what I needed :goodjob:
 
If this is the kind of thing your after I'll probably create two versions of a "Utils - Modular UnitPanel Action Addins" mod that can load the actions on a per-mod basis is a nice dynamic manner (like the DiploCorner and SumaryBar addins)

Yes please...for G&K :yup:
 
Back
Top Bottom