Basic info:
The file that controls the right click menus is ui\addons\ego_interactmenu\menu_interactmenu.lua
To mod the file so that it actually works, you have to:
- Creating a mod folder in your %installdir%\extensions folder, adding content.xml so it can load.
- Create the directory structure "ui\addons\ego_interactmenu\" in your mod folder
- Copy the menu_interactmenu.lua into the ego_interactmenu of your mod
- Edit menu_interactmenu.lua with the desired changes
- Make a copy of the edited menu_interactmenu.lua and rename it menu_interactmenu.xpl
- Create a subst_01.cat file from the ui folder. Removing the loose files from the mod folder itself, so that you are left with just subst_01.cat, subst_01.dat, and content.xml in your mod folder (plus what ever other files your mod has, like aiscripts)
The code:
The meat of the code I want to change seems to be in three main areas:
First is a big long ifelse list (what would be a switch statement in other languages)
function menu.insertLuaAction(actiontype, istobedisplayed)
Each statement is something like:
Code: Select all
elseif actiontype == "collect" then
if #menu.selectedplayerships > 0 and menu.possibleorders["Collect"] and menu.componentSlot.component and C.IsComponentClass(menu.componentSlot.component, "drop") then
menu.insertInteractionContent("selected_orders", { type = actiontype, text = ReadText(1001, 7867), script = function () return menu.buttonCollect(false) end, hidetarget = true } )
end
Then we have the conditions for if the order is listed. In this case, if a player ship is slected before we right click, if menu.possibleorders["Collect"], if (what we clicked on exist, i.e. is not nil), and if what we right-clicked on isClass "drop".
After that we add the entry to the menu with menu.insertInteractionContent
"selected_orders"
is basically a category.
type = actiontype
is passed from caller
text = ReadText(1001, 7867)
is the label of the order
script = function () return menu.buttonCollect(false) end
is what happens when we click on the order.
So two things to look at from here, first is "menu.possibleorders["Collect"]"
It is initialized with:
Code: Select all
menu.possibleorders = {
["Attack"] = false,
["Board"] = false,
["Collect"] = false,
["CollectDropsInRadius"] = false,
["DeployObjectAtPosition"] = false,
["DockAndWait"] = false,
["Explore"] = false,
["ExploreUpdate"] = false,
["Flee"] = false,
["Follow"] = false,
["MiningPlayer"] = false,
["MoveWait"] = false,
["Player_DockToTrade"] = false,
["ProtectStation"] = false,
["Repair"] = false,
["TradeRoutine"] = false,
}
Code: Select all
<order id="Collect" name="{1041, 481}" description="{1041, 482}" category="trade">
Code: Select all
for orderid, value in pairs(menu.possibleorders) do
if not value then
if C.IsOrderSelectableFor(orderid, ship) then
menu.possibleorders[orderid] = true
end
end
end
Now onto menu.buttonCollect(false)
Code: Select all
function menu.buttonCollect()
for _, ship in ipairs(menu.selectedplayerships) do
menu.orderCollect(ship, menu.componentSlot.component, menu.offsetcomponent, menu.offset, clear)
end
menu.onCloseElement("close")
end
Code: Select all
function menu.orderCollect(component, drop, sector, offset, clear)
if not C.IsOrderSelectableFor("Collect", component) then
return
end
if clear then
C.RemoveAllOrders(component)
end
local orderidx = C.CreateOrder(component, "Collect", false)
SetOrderParam(component, orderidx, 1, 0, ConvertStringToLuaID(tostring(drop)) )
C.EnableOrder(component, orderidx)
return orderidx
end
I believe C.CreateOrder(component, "Collect", false) is the same as <create_order object="" id="" immediate=""/>
SetOrderParam is simular to <edit_order_param object="" order="" param="(as a index of all params)" (looks like the index of value when the param takes a list) value=""/>
Theoretically we should now be able to add our own order to the right click menu. However, here is where I run into trouble. The first thing we looked at, menu.insertLuaAction(actiontype, istobedisplayed), is called here:
Code: Select all
local n = C.GetNumCompSlotPlayerActions(menu.componentSlot)
local buf = ffi.new("UIAction[?]", n)
n = C.GetCompSlotPlayerActions(buf, n, menu.componentSlot)
for i = 0, n - 1 do
local entry = {}
entry.id = buf[i].id
entry.text = ffi.string(buf[i].text)
entry.active = buf[i].ispossible
local actiontype = ffi.string(buf[i].type)
if (not menu.shown) and (actiontype == "containertrade") then
entry.script = function () return menu.buttonTrade(false) end
elseif (not menu.shown) and (actiontype == "info") then
entry.script = menu.buttonInfo
elseif (not menu.shown) and (actiontype == "comm") then
entry.script = menu.buttonComm
else
entry.script = function () return menu.buttonPerformPlayerAction(entry.id, actiontype) end
end
local basetype, luatype = string.match(actiontype, "(.+);(.+)")
if isknown or (actiontype == "info") or (luatype == "guidance") then
if basetype == "lua" then
menu.insertLuaAction(luatype, buf[i].istobedisplayed)
Code: Select all
menu.insertInteractionContent("selected_orders", { type = actiontype, text = ReadText(1001, 7867), script = function () return menu.buttonCollect(false) end, hidetarget = true } )
I hope this information is helpful for those looking into modding the UI, and if you have any information to add, please do so.
Part 2: Usurpation
Well if the only way we can add right click orders is to pirates some actiontypes then we might as well break out the jolly roger and get to it.
I added some debug output and went through clicking on a bunch of stuff to see what actiontypes get called when. It turns out that "guidance" gets passed in almost every case. The only exception being when you click on gates. Thus I chose this to area to modify adding my own order "CollectDeployables" to the entry, while leaving the original code alone.
Code: Select all
elseif actiontype == "guidance" then
--Modded entry
if menu.possibleorders["CollectDeployables"]
and #menu.selectedplayerships > 0
and menu.componentSlot.component
and isplayerownedtarget and (
(
C.IsComponentClass(menu.componentSlot.component, "satellite")
or C.IsComponentClass(menu.componentSlot.component, "resourceprobe")
or C.IsComponentClass(menu.componentSlot.component, "navbeacon")
)
or GetComponentData(ConvertStringTo64Bit(tostring(menu.componentSlot.component)), "shiptype") == "lasertower") then
menu.insertInteractionContent("selected_orders", { type = actiontype, text = ReadText(1042, 400), script = function () return menu.buttonCollectDeployables(false) end })
end
--end modded changes
if menu.mode ~= "shipconsole" then
if C.IsComponentClass(menu.componentSlot.component, "sector") then
menu.insertInteractionContent(menu.showPlayerInteractions and "player_interaction" or "interaction", { type = actiontype, text = IsSameComponent(GetActiveGuidanceMissionComponent(), convertedComponent) and ReadText(1001, 3243) or ReadText(1001, 3242), script = function () return menu.buttonGuidance(true) end })
else
menu.insertInteractionContent(menu.showPlayerInteractions and "player_interaction" or "interaction", { type = actiontype, text = IsSameComponent(GetActiveGuidanceMissionComponent(), convertedComponent) and ReadText(1001, 3243) or ReadText(1001, 3256), script = function () return menu.buttonGuidance(false) end })
end
end
Code: Select all
function menu.buttonCollectDeployables(clear)
for _, ship in ipairs(menu.selectedplayerships) do
menu.orderCollectDeployables(ship, menu.componentSlot.component, clear, ventureplatform)
end
menu.onCloseElement("close")
end
function menu.orderCollectDeployables(component, target, clear, ventureplatform)
if not C.IsOrderSelectableFor("CollectDeployables", component) then
return
end
if clear then
C.RemoveAllOrders(component)
end
local orderidx = C.CreateOrder(component, "CollectDeployables", false)
--Param 1: target deployable
SetOrderParam(component, orderidx, 1, nil, ConvertStringToLuaID(tostring(target)))
C.EnableOrder(component, orderidx)
return orderidx
end
This leaves me with a fully functioning right click command:
[ external image ]
However, this method is not a good solution since only one mod can make these changes. Ideally we want to create some hooks into the vanilla code to inject the above changes into the core game from some modded files. I don't know how to do this at this time. I do feel it should be possible though. We have the ui.xml file that can be edited with a diff patch to add more files to its list. Theses files are loaded when a save is loaded so you can, at the very least, move the functions to other files (I think, I've not tested this theory). There is also the OpenMenu() function that should allow you to call things from those new files. Dofile() and require() are also options.
What we need, however, is a way to hook into the vanilla files to edit menu.possibleorders (menu being a local variable) and inject code into menu.insertLuaAction(actiontype, istobedisplayed). Something like a global table all files in the ui.xml file could access, with things like Table("OrderID", Condition, Extra_Code, menu.insertInteractionContent arguments)
Then have something like:
Code: Select all
elseif actiontype == "guidance" then
for i=0 to Table.count
if menu.possibleorders[Table[i].OrderID] and Table[i].Condition then
Table[i].Extra_Code
menu.insertInteractionContent(Table[i].arguments)
end
end
...vanilla code