Instantly share code, notes, and snippets.
kmill/gui_chili_integral_menu.lua Secret
Last active
August 29, 2015 14:10
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save kmill/8c79627d25d8036745c1 to your computer and use it in GitHub Desktop.
LuaUI/Widgets/gui_chili_integral_menu.lua with dvorak keybindings
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- TODO: proper tooltips for queue buttons | |
function widget:GetInfo() | |
return { | |
name = "Chili Integral Menu", | |
desc = "v0.369 Integral Command Menu", | |
author = "Licho, KingRaptor, Google Frog", | |
date = "12.10.2010", --21.August.2013 | |
license = "GNU GPL, v2 or later", | |
layer = math.huge-1, | |
enabled = true, | |
handler = true, | |
} | |
end | |
include("keysym.h.lua") | |
--[[ | |
for i,v in pairs(KEYSYMS) do | |
Spring.Echo(i.."\t"..v) | |
end | |
--]] | |
--[[ | |
HOW IT WORKS: | |
Main window (invisible) is parent of a fake window. | |
Tabs are buttons in main window, just above fake window. | |
Currently selected tab is highlighted, when tab is changed all tabs are removed and regenerated. | |
Two parent Panels (children of fake window), a column for normal commands and a row for state commands. | |
<numRows> (or <numStateColumns>) StackPanels are nested in each of the parents, at right angles. | |
When sorting commands, it splits commands into batches of <MAX_COLUMNS> and assigns them to children | |
so if there are 10 commands, it puts 6 in first row and 4 in second row | |
Build orders work a little differently, they have a predefined row in the config. | |
Ditto for states, except it uses MAX_STATE_ROWS | |
If unit tab is selected and third command row is free, build queue of first selected factory found in array returned by SelectionChanged is displayed. | |
The queue shows up to <MAX_COLUMNS> batches of units and their exact sequence. | |
All items resize with main window. | |
NOTE FOR OTHER GAME DEVS: | |
ZK uses WG.GetBuildIconFrame to draw the unit type border around buildpics. | |
If you're not using them (likely), remove all lines containing that function. | |
--]] | |
-- Chili classes | |
local Chili | |
local Button | |
local Label | |
local Colorbars | |
local Checkbox | |
local Window | |
local Panel | |
local StackPanel | |
local TextBox | |
local Image | |
local Progressbar | |
local Control | |
-- Chili instances | |
local screen0 | |
local window --main window (invisible) | |
local fakewindow --visible Panel | |
local menuTabRow --parent row of tabs | |
local menuTabs = {} --buttons | |
local commands_main --parent column of command buttons | |
local sp_commands = {} --buttons | |
local states_main --parent row of state buttons | |
local sp_states = {} --buttons | |
local buildRow --row of build queue buttons | |
local buildRowButtons = {} --contains arrays indexed by number 1 to MAX_COLUMNS, each of which contains three subobjects: button, label and image | |
local buildProgress --Progressbar, child of buildRowButtons[1].image; updates every gameframe | |
local buildRow_dragDrop = {nil,nil,nil} --current buildqueue that is being dragged by user. | |
local buildRow_visible = false | |
local buildQueue = {} --build order table of selectedFac | |
local buildQueueUnsorted = {} --puts all units of same type into single index; thus no sequence | |
local gridLocation = {} | |
------------------------ | |
-- CONFIG | |
------------------------ | |
------------------------ | |
options_path = 'Settings/HUD Panels/Command Panel' | |
options_order = { 'background_opacity', 'disablesmartselect', 'hidetabs', 'unitstabhotkey', 'unitshotkeyrequiremeta', 'unitshotkeyaltaswell', 'tab_factory', 'tab_economy', 'tab_defence', 'tab_special','old_menu_at_shutdown'} | |
options = { | |
background_opacity = { | |
name = "Opacity", | |
type = "number", | |
value = 0.8, min = 0, max = 1, step = 0.01, | |
OnChange = function(self) | |
fakewindow.backgroundColor = {1,1,1,self.value} | |
fakewindow:Invalidate() | |
end, | |
}, | |
disablesmartselect = { | |
name = 'Disable Smart Tab Select', | |
type = 'bool', | |
value = false, | |
}, | |
hidetabs = { | |
name = 'Hide Tab Row', | |
type = 'bool', | |
advanced = true, | |
value = false, | |
}, | |
unitstabhotkey = { | |
name = 'Hotkey for Units tab', | |
type = 'bool', | |
value = true, | |
}, | |
unitshotkeyrequiremeta = { | |
name = 'Units tab hotkeys require Meta', | |
type = 'bool', | |
value = true, | |
}, | |
unitshotkeyaltaswell = { | |
name = 'Units tab can use Alt as Meta', | |
type = 'bool', | |
value = false, | |
}, | |
tab_factory = { | |
name = "Factory Tab", | |
desc = "Switches to factory tab, enables grid hotkeys", | |
type = 'button', | |
}, | |
tab_economy = { | |
name = "Economy Tab", | |
desc = "Switches to economy tab, enables grid hotkeys", | |
type = 'button', | |
}, | |
tab_defence = { | |
name = "Defence Tab", | |
desc = "Switches to defence tab, enables grid hotkeys", | |
type = 'button', | |
}, | |
tab_special = { | |
name = "Special Tab", | |
desc = "Switches to special tab, enables grid hotkeys", | |
type = 'button', | |
}, | |
old_menu_at_shutdown = { | |
name = 'Reenable Spring Menu at Shutdown', | |
desc = "Upon widget shutdown (manual or upon crash) reenable Spring's original command menu.", | |
type = 'bool', | |
advanced = true, | |
value = true, | |
}, | |
} | |
------------------------ | |
--speedups | |
local spGetUnitDefID = Spring.GetUnitDefID | |
local spGetUnitHealth = Spring.GetUnitHealth | |
local spGetFullBuildQueue = Spring.GetFullBuildQueue | |
local spGetUnitIsBuilding = Spring.GetUnitIsBuilding | |
local spGetSelectedUnits = Spring.GetSelectedUnits | |
local spGetUnitWeaponState = Spring.GetUnitWeaponState | |
local spGetGameFrame = Spring.GetGameFrame | |
local spGetUnitRulesParam = Spring.GetUnitRulesParam | |
local push = table.insert | |
local CMD_PAGES = 60 | |
local common_commands, states_commands, factory_commands, econ_commands, defense_commands, special_commands, globalCommands, overrides, custom_cmd_actions = include("Configs/integral_menu_commands.lua") | |
local function CapCase(str) | |
local str = str:lower() | |
str = str:gsub( '_', ' ' ) | |
str = str:sub(1,1):upper() .. str:sub(2) | |
str = str:gsub( ' (.)', | |
function(x) return (' ' .. x):upper(); end | |
) | |
return str | |
end | |
local function AddHotkeyOptions() | |
local options_order_tmp_cmd = {} | |
local options_order_tmp_cmd_instant = {} | |
local options_order_tmp_states = {} | |
for cmdname, number in pairs(custom_cmd_actions) do | |
local cmdnamel = cmdname:lower() | |
local cmdname_disp = CapCase(cmdname) | |
options[cmdnamel] = { | |
name = cmdname_disp, | |
type = 'button', | |
action = cmdnamel, | |
path = 'Game/Command Hotkeys', | |
} | |
if number == 2 then | |
options_order_tmp_states[#options_order_tmp_states+1] = cmdnamel | |
--options[cmdnamel].isUnitStateCommand = true | |
elseif number == 3 then | |
options_order_tmp_cmd_instant[#options_order_tmp_cmd_instant+1] = cmdnamel | |
--options[cmdnamel].isUnitInstantCommand = true | |
else | |
options_order_tmp_cmd[#options_order_tmp_cmd+1] = cmdnamel | |
--options[cmdnamel].isUnitCommand = true | |
end | |
end | |
options.lblcmd = { type='label', name='Targeted Commands', path = 'Game/Command Hotkeys',} | |
options.lblcmdinstant = { type='label', name='Instant Commands', path = 'Game/Command Hotkeys',} | |
options.lblstate = { type='label', name='State Commands', path = 'Game/Command Hotkeys',} | |
table.sort(options_order_tmp_cmd) | |
table.sort(options_order_tmp_cmd_instant) | |
table.sort(options_order_tmp_states) | |
options_order[#options_order+1] = 'lblcmd' | |
for i=1, #options_order_tmp_cmd do | |
options_order[#options_order+1] = options_order_tmp_cmd[i] | |
end | |
options_order[#options_order+1] = 'lblcmdinstant' | |
for i=1, #options_order_tmp_cmd_instant do | |
options_order[#options_order+1] = options_order_tmp_cmd_instant[i] | |
end | |
options_order[#options_order+1] = 'lblstate' | |
for i=1, #options_order_tmp_states do | |
options_order[#options_order+1] = options_order_tmp_states[i] | |
end | |
end | |
AddHotkeyOptions() | |
local MAX_COLUMNS = 6 | |
local MAX_STATE_ROWS = 5 | |
local MIN_HEIGHT = 80 | |
local MIN_WIDTH = 200 | |
local COMMAND_SECTION_WIDTH = 74 -- percent | |
local STATE_SECTION_WIDTH = 24 -- percent | |
local numRows = 3 | |
local numStateColumns = 3 | |
local forceUpdateFrequency = 0.2 -- seconds | |
local selectedFac -- unitID | |
local alreadyRemovedTag = {} | |
local hotkeyMode = false | |
local recentlyInitialized = false | |
local gridKeyMap = { | |
[KEYSYMS.QUOTE] = {1,1}, | |
[KEYSYMS.COMMA] = {1,2}, | |
[KEYSYMS.PERIOD] = {1,3}, | |
[KEYSYMS.P] = {1,4}, | |
[KEYSYMS.Y] = {1,5}, | |
[KEYSYMS.F] = {1,6}, | |
[KEYSYMS.A] = {2,1}, | |
[KEYSYMS.O] = {2,2}, | |
[KEYSYMS.E] = {2,3}, | |
[KEYSYMS.U] = {2,4}, | |
[KEYSYMS.I] = {2,5}, | |
[KEYSYMS.D] = {2,6}, | |
[KEYSYMS.SEMICOLON] = {3,1}, | |
[KEYSYMS.Q] = {3,2}, | |
[KEYSYMS.J] = {3,3}, | |
[KEYSYMS.K] = {3,4}, | |
[KEYSYMS.X] = {3,5}, | |
[KEYSYMS.B] = {3,6}, | |
} | |
local gridMap = { | |
[1] = { | |
[1] = "'", | |
[2] = ",", | |
[3] = ".", | |
[4] = "P", | |
[5] = "Y", | |
[6] = "F", | |
}, | |
[2] = { | |
[1] = "A", | |
[2] = "O", | |
[3] = "E", | |
[4] = "U", | |
[5] = "I", | |
[6] = "D", | |
}, | |
[3] = { | |
[1] = ";", | |
[2] = "Q", | |
[3] = "J", | |
[4] = "K", | |
[5] = "X", | |
[6] = "B", | |
}, | |
} | |
-- DGun button progressbar, for listening to special-weapon reload progress | |
--VFS.Include("LuaRules/Configs/customcmds.h.lua") --already included in "include("Configs/integral_menu_commands.lua")" | |
local reverseCompat = (Game.version:find('91.0') == 1) and 1 or 0 | |
local turnOffReloadableWeaponChecks = false | |
local reloadableButton = { | |
[CMD.MANUALFIRE] = {progress=0,status=nil}, | |
[CMD_ONECLICK_WEAPON] = {progress=0,status=nil}, | |
[CMD_JUMP] = {progress=0,status=nil}, | |
} | |
-- arrays with commands to be displayed | |
local n_common = {} | |
local n_factories = {} | |
local n_econ = {} | |
local n_defense = {} | |
local n_special = {} | |
local n_units = {} | |
local n_states = {} | |
--shortcuts | |
local menuChoices = { | |
[1] = { array = n_common, name = "Order", hotkeyName = "Order" }, | |
[2] = { array = n_factories, name = "Factory", hotkeyName = "Factory", config = factory_commands, actionName = "epic_chili_integral_menu_tab_factory" }, | |
[3] = { array = n_econ, name = "Econ", hotkeyName = "Econ", config = econ_commands, actionName = "epic_chili_integral_menu_tab_economy" }, | |
[4] = { array = n_defense, name = "Defense", hotkeyName = "Defense", config = defense_commands, actionName = "epic_chili_integral_menu_tab_defence" }, | |
[5] = { array = n_special, name = "Special", hotkeyName = "Special", config = special_commands, actionName = "epic_chili_integral_menu_tab_special" }, | |
[6] = { array = n_units, name = "Units", hotkeyName = "Units" }, | |
} | |
local menuChoice = 1 | |
local lastBuildChoice = 2 | |
-- command id indexed field of items - each item is button, label and image | |
local commandButtons = {} | |
local spaceClicked = false | |
----------------------------------- COMMAND COLORS - from cmdcolors.txt - default coloring | |
local cmdColors = {} | |
-- default config | |
local config = { | |
} | |
------------------------ | |
-- FUNCTIONS | |
------------------------ | |
-- this gets invoked when button is clicked | |
local function ClickFunc(button, x, y, mouse) | |
local left, right = mouse == 1, mouse == 3 | |
local alt,ctrl,meta,shift = Spring.GetModKeyState() | |
local mb = (left and 1) or (right and 3) | |
if mb then | |
local index = Spring.GetCmdDescIndex(button.cmdid) | |
if index then | |
Spring.SetActiveCommand(index,mb,left,right,alt,ctrl,meta,shift) | |
end | |
end | |
end | |
------------------------ | |
-- Generates or updates chili button - either image or text or both based - container is parent of button, cmd is command desc structure | |
------------------------ | |
local function MakeButton(container, cmd, insertItem, index) | |
local isState = (cmd.type == CMDTYPE.ICON_MODE and #cmd.params > 1) or states_commands[cmd.id] --is command a state toggle command? | |
local isBuild = (cmd.id < 0) | |
local gridHotkeyed = not isState and menuChoice ~= 1 and menuChoice ~= 6 | |
local text | |
local texture | |
local countText = '' | |
local tooltip = cmd.tooltip | |
local te = overrides[cmd.id] -- command overrides | |
-- text | |
if te and te.text then | |
text = te.text | |
elseif isState then | |
text = cmd.params[cmd.params[1]+2] | |
elseif isBuild then | |
text = '' | |
else | |
text = cmd.name | |
end | |
local hotkey = cmd.action and WG.crude.GetHotkey(cmd.action) or '' | |
if (options.unitstabhotkey.value and menuChoice == 6 and selectedFac and container.i_am_sp_commands) then | |
if options.unitshotkeyrequiremeta.value then | |
local alt,ctrl,meta,shift = Spring.GetModKeyState() | |
if meta or (alt and options.unitshotkeyaltaswell.value) then | |
hotkey = gridMap[container.index][index] or '' | |
end | |
else | |
hotkey = gridMap[container.index][index] or '' | |
end | |
end | |
if gridHotkeyed and hotkeyMode then | |
hotkey = gridMap[container.index][index] or '' | |
end | |
if not isState and hotkey ~= '' then | |
text = '\255\0\255\0' .. hotkey | |
end | |
--count label (for factory build options) | |
if menuChoice == 6 and isBuild and buildQueueUnsorted[-cmd.id] then | |
countText = tostring(buildQueueUnsorted[-cmd.id]) | |
end | |
--texture | |
if te ~= nil and te.texture then | |
if (isState) then | |
texture = te.texture[cmd.params[1]+1] | |
else | |
texture = te.texture | |
end | |
elseif isBuild then | |
texture = '#'..-cmd.id | |
else | |
texture = cmd.texture | |
end | |
-- tooltip | |
if te and te.tooltip then | |
tooltip = te.tooltip | |
else | |
tooltip = cmd.tooltip | |
end | |
if isBuild and selectedFac then | |
local ud = UnitDefs[-cmd.id] | |
tooltip = "Build Unit: " .. ud.humanName .. " - " .. ud.tooltip .. "\n" -- for special options | |
end | |
if hotkey ~= '' then | |
tooltip = tooltip .. ' (\255\0\255\0' .. hotkey .. '\008)' | |
end | |
-- get cached menu item | |
local item = commandButtons[cmd.id] | |
if not item then -- no item, create one | |
if not insertItem then | |
Spring.SendMessage("CommandBar - internal error, unexpectedly adding item!") | |
end | |
-- decide color | |
--[[ local color = {1,1,1,1} | |
if te ~= nil and te.color ~= nil then | |
color = te.color | |
elseif cmd.name ~= nil then | |
local nl = cmd.name:lower() | |
if cmdColors[nl] then | |
color = cmdColors[nl] | |
color[4] = color[4] + 0.2 | |
end | |
end]] | |
local button = Button:New { | |
parent=container; | |
padding = {5, 5, 5, 5}, | |
margin = {0, 0, 0, 0}, | |
caption=""; | |
isDisabled = cmd.disabled; | |
tooltip = tooltip; | |
cmdid = cmd.id; | |
OnClick = {function(self, x, y, mouse) ClickFunc(self, x, y, mouse) end} | |
} | |
if (isState) then | |
button.padding = {4,4,2.5,2} | |
-- button.backgroundColor = {0,0,0,0} | |
end | |
if (isBuild) then | |
button.padding = {1,1,1,1} | |
end | |
local label | |
if (not cmd.onlyTexture and text and text ~= '') or gridHotkeyed or menuChoice == 6 then | |
label = Label:New { | |
width="100%"; | |
height="100%"; | |
autosize=false; | |
align="left"; | |
valign="top"; | |
caption = text; | |
fontSize = 11; | |
fontShadow = true; | |
parent = button; | |
} | |
end | |
if isBuild then | |
local costLabel = Label:New { | |
parent = button, | |
right = 0; | |
y = 0; | |
x = 3; | |
bottom = 3; | |
autosize=true; -- this (autosize=true) allow text to be truncated/cut off if button size is too small (prevent a wall-of-text on build icon if button is too small) | |
align="left"; | |
valign="bottom"; | |
caption = string.format("%d m", UnitDefs[-cmd.id].metalCost); | |
fontSize = 11; | |
fontShadow = true; | |
} | |
end | |
local image | |
if (texture and texture ~= "") then | |
image= Image:New { | |
width="90%"; | |
height= (not isBuild) and nil or "90%"; | |
bottom = (isBuild) and 10 or nil; | |
y="5%"; | |
x="5%"; | |
-- color = color; | |
keepAspect = not isBuild, --true, --isState; | |
file = texture; | |
parent = button; | |
} | |
if isBuild then | |
image.file2 = WG.GetBuildIconFrame(UnitDefs[-cmd.id]) | |
end | |
if isState then | |
height = "100%" | |
y = 0 | |
end | |
else | |
if label~=nil then label.valign="center" end | |
end | |
local countLabel | |
if isBuild then | |
countLabel = Label:New { | |
parent = image, | |
autosize=false; | |
width="100%"; | |
height="100%"; | |
align="right"; | |
valign="bottom"; | |
caption = countText; | |
fontSize = 16; | |
fontShadow = true; | |
} | |
end | |
--if button is disabled, set effect accordingly | |
if button.isDisabled then | |
button.backgroundColor = {0,0,0,1}; | |
image.color = {0.3, 0.3, 0.3, 1} | |
end | |
item = { | |
button = button, | |
image = image, | |
label = label, | |
countLabel = countLabel, | |
} | |
commandButtons[cmd.id] = item | |
else | |
if insertItem then | |
container:AddChild(item.button) | |
end | |
end | |
-- update item if something changed | |
if (cmd.disabled ~= item.button.isDisabled) then | |
if cmd.disabled then | |
item.button.backgroundColor = {0,0,0,1}; | |
item.image.color = {0.3, 0.3, 0.3, 1} | |
else | |
item.button.backgroundColor = {1,1,1,0.7}; | |
item.image.color = {1, 1, 1, 1} | |
end | |
item.button:Invalidate() | |
item.image:Invalidate() | |
item.button.isDisabled = cmd.disabled | |
end | |
if (not cmd.onlyTexture and item.label and text ~= item.label.caption) then | |
item.label:SetCaption(text) | |
end | |
if (item.countLabel and countText ~= item.countLabel.caption) then | |
item.countLabel:SetCaption(countText) | |
end | |
if (item.image and (texture ~= item.image.file or isState) ) then | |
item.image.file = texture | |
item.image:Invalidate() | |
end | |
if (item.button.tooltip and tooltip ~= item.button.tooltip) then | |
item.button.tooltip = tooltip | |
end | |
end | |
--sorts commands into categories | |
local function ProcessCommand(cmd) | |
if not cmd.hidden and cmd.id ~= CMD_PAGES then | |
-- state icons | |
if (cmd.type == CMDTYPE.ICON_MODE and cmd.params ~= nil and #cmd.params > 1) then | |
n_states[#n_states+1] = cmd | |
elseif common_commands[cmd.id] then | |
n_common[#n_common+1] = cmd | |
elseif factory_commands[cmd.id] then | |
n_factories[#n_factories+1] = cmd | |
elseif econ_commands[cmd.id] then | |
n_econ[#n_econ+1] = cmd | |
elseif defense_commands[cmd.id] then | |
n_defense[#n_defense+1] = cmd | |
elseif special_commands[cmd.id] then | |
n_special[#n_special+1] = cmd | |
elseif cmd.id and UnitDefs[-(cmd.id)] then | |
n_units[#n_units+1] = cmd | |
else | |
n_common[#n_common+1] = cmd --shove unclassified stuff in common | |
end | |
end | |
end | |
local function RemoveChildren(container) | |
for i = 1, #container.children do | |
container:RemoveChild(container.children[1]) | |
end | |
end | |
-- compared real chili container with new commands and update accordingly | |
local function UpdateContainer(container, nl, columns) | |
if not columns then columns = MAX_COLUMNS end | |
local cnt = 0 | |
local needUpdate = false | |
local dif = {} | |
for i =1, #container.children do | |
if container.children[i].isEmpty then | |
break | |
end | |
cnt = cnt + 1 | |
dif[container.children[i].cmdid] = true | |
end | |
if cnt ~= #nl then -- different counts, we update fully | |
needUpdate = true | |
else -- check if some items are different | |
for i=1, #nl do | |
dif[nl[i].id] = nil | |
end | |
for _, _ in pairs(dif) do -- different item found, we do full update | |
needUpdate = true | |
break | |
end | |
end | |
if needUpdate then | |
RemoveChildren(container) | |
for i=1, #nl do | |
MakeButton(container, nl[i], true, i) | |
end | |
for i = 1, columns - #container.children do | |
Control:New { | |
isEmpty = true, | |
parent = container | |
} | |
end | |
else | |
for i=1, #nl do | |
MakeButton(container, nl[i], false, i) | |
end | |
end | |
end | |
local function BuildRowButtonFunc(num, cmdid, left, right,addInput,insertMode,customUnitID) | |
local targetFactory = customUnitID or selectedFac | |
buildQueue = spGetFullBuildQueue(targetFactory) | |
num = num or (#buildQueue+1) -- insert build at "num" or at end of queue | |
local alt,ctrl,meta,shift = Spring.GetModKeyState() | |
local pos = 1 | |
local numInput = 1 --number of times to send the order | |
local function BooleanMult(int, bool) | |
if bool then return int | |
else return 0 end | |
end | |
--CMD.OPT_META = 4 | |
--CMD.OPT_RIGHT = 16 | |
--CMD.OPT_SHIFT = 32 | |
--CMD.OPT_CTRL = 64 | |
--CMD.OPT_ALT = 128 | |
--it's not using the options, even though it's receiving them correctly | |
--so we have to do it manually | |
if shift then numInput = numInput * 5 end | |
if ctrl then numInput = numInput * 20 end | |
numInput = numInput + (addInput or 0) -- to insert specific amount without SHIFT or CTRL modifier | |
--local options = BooleanMult(CMD.OPT_SHIFT, shift) + BooleanMult(CMD.OPT_ALT, alt) + BooleanMult(CMD.OPT_CTRL, ctrl) + BooleanMult(CMD.OPT_META, meta) + BooleanMult(CMD.OPT_RIGHT, right) | |
--insertion position is by unit rather than batch, so we need to add up all the units in front of us to get the queue | |
for i=1,num-1 do | |
for _,unitCount in pairs(buildQueue[i]) do | |
pos = pos + unitCount | |
end | |
end | |
-- skip over the commands with an id of 0, left behind by removal | |
local commands = Spring.GetFactoryCommands(targetFactory) | |
local i = 1 | |
while i <= pos do | |
if not commands[i] then --end of queue reached | |
break | |
end | |
if commands[i].id == 0 then | |
pos = pos + 1 | |
end | |
i = i + 1 | |
end | |
pos = pos- (insertMode and 1 or 0) -- to insert before this index (possibly replace active nanoframe) or after this index (default) | |
--Spring.Echo(cmdid) | |
if not right then | |
for i = 1, numInput do | |
Spring.GiveOrderToUnit(targetFactory, CMD.INSERT, {pos, cmdid, 0 }, {"alt", "ctrl"}) | |
end | |
else | |
-- delete from back so that the order is not canceled while under construction | |
local i = 0 | |
while commands[i+pos] and commands[i+pos].id == cmdid and not alreadyRemovedTag[commands[i+pos].tag] do | |
i = i + 1 | |
end | |
i = i - 1 | |
j = 0 | |
while commands[i+pos] and commands[i+pos].id == cmdid and j < numInput do | |
Spring.GiveOrderToUnit(targetFactory, CMD.REMOVE, {commands[i+pos].tag}, {"ctrl"}) | |
alreadyRemovedTag[commands[i+pos].tag] = true | |
j = j + 1 | |
i = i - 1 | |
end | |
end | |
end | |
local function UpdateFactoryBuildQueue() | |
buildQueue = spGetFullBuildQueue(selectedFac) | |
buildQueueUnsorted = {} | |
for i=1, #buildQueue do | |
for udid, count in pairs(buildQueue[i]) do | |
buildQueueUnsorted[udid] = (buildQueueUnsorted[udid] or 0) + count | |
--Spring.Echo(udid .. "\t" .. buildQueueUnsorted[udid]) | |
end | |
end | |
end | |
--uses its own function for more fine control | |
local function ManageBuildRow() | |
--if (menuChoice ~= 6) or (not buildRow_visible) or (not selectedFac) then return end | |
local overrun = false | |
RemoveChildren(buildRow) | |
if buildQueue[MAX_COLUMNS + 1] then | |
overrun = true | |
end | |
for i=1, MAX_COLUMNS do | |
local buttonArray = buildRowButtons[i] | |
if buttonArray.button then | |
RemoveChildren(buttonArray.button) | |
end | |
if buildQueue[i] then --adds button for queued unit | |
local udid, count, caption | |
for id, num in pairs(buildQueue[i]) do | |
udid = id | |
count = num | |
break | |
end | |
buildRowButtons[i].cmdid = -udid | |
if overrun and i == MAX_COLUMNS then | |
caption = tostring(#buildQueue - MAX_COLUMNS + 1) | |
elseif count > 1 then caption = tostring(count) | |
else caption = '' end | |
buttonArray.button = Button:New{ | |
parent = buildRow; | |
x = (i-1)*(100/MAX_COLUMNS).."%", | |
y = 0, | |
width = (100/MAX_COLUMNS).."%", | |
height = "100%", | |
caption = '', --remove "notext" behind button (which is visible if menu is downsized) | |
OnClick = { function (self, x, y, mouse) | |
local left, right = mouse == 1, mouse == 3 | |
BuildRowButtonFunc(i, buildRowButtons[i].cmdid, left, right) | |
buildRow_dragDrop[1] = nil | |
end}, | |
padding = {1,1,1,1}, | |
--keepAspect = true, | |
OnMouseDown = { function(self,x,y,mouse) --for drag_drop feature | |
if mouse == 1 then | |
buildRow_dragDrop[1] = i; | |
buildRow_dragDrop[2] = -buildRowButtons[i].cmdid | |
buildRow_dragDrop[3] = count-1; | |
buildRow_dragDrop[4] = self.width/2 | |
end | |
end}, | |
OnMouseUp = { function(self,x,y,mouse) -- MouseRelease event, for drag_drop feature --note: x & y is coordinate with respect to self | |
local px,py = self:LocalToParent(x,y) --get coordinate with respect to parent | |
if mouse == 1 and (x>self.width or x<0) and px> 0 and px< buildRow.width and py> 0 and py< buildRow.height then | |
local prevIndex = buildRow_dragDrop[1] | |
local currentIndex = math.ceil(px/(buildRow.width/MAX_COLUMNS)) --estimate on which button mouse was released | |
if not buildQueue[currentIndex] then --drag_dropped to the end of queue | |
currentIndex = #buildQueue | |
if currentIndex == prevIndex then --drag_dropped from end of queue to end of queue | |
return | |
end | |
end | |
local countTransfer = buildRow_dragDrop[3] | |
if buildRow_dragDrop[1] > currentIndex then --select remove & adding sequence that reduce complication from network delay | |
BuildRowButtonFunc(prevIndex, buildRowButtons[prevIndex].cmdid, false, true,countTransfer) --remove queue on the right, first | |
BuildRowButtonFunc(currentIndex, buildRowButtons[prevIndex].cmdid, true, false,countTransfer,true) --then, add queue to the left | |
else | |
BuildRowButtonFunc(currentIndex+1, buildRowButtons[prevIndex].cmdid, true, false,countTransfer,true) --add queue to the right, first | |
BuildRowButtonFunc(prevIndex, buildRowButtons[prevIndex].cmdid, false, true,countTransfer) --then, remove queue on the left | |
end | |
buildRow_dragDrop[1] = nil | |
end | |
end}, | |
} | |
if overrun and i == MAX_COLUMNS then | |
buttonArray.button.caption = '...' | |
buttonArray.button.OnClick = nil | |
end | |
buttonArray.button.backgroundColor[4] = 0.3 | |
if not (overrun and i == MAX_COLUMNS) then | |
buttonArray.button.tooltip = "\255\1\255\1Hold Left mouse \255\255\255\255: drag drop to different factory or position in queue\n".. | |
"\255\1\255\1Left/Right click \255\255\255\255: Add to/subtract from queue" | |
buttonArray.image = Image:New { | |
parent = buttonArray.button, | |
width="90%"; | |
height="90%"; | |
x="5%"; | |
y="5%"; | |
file = '#'..udid, | |
file2 = WG.GetBuildIconFrame(UnitDefs[udid]), | |
keepAspect = false, | |
} | |
buttonArray.label = Label:New { | |
parent = buttonArray.image, | |
width="100%"; | |
height="100%"; | |
autosize=false; | |
--x = "70%", | |
--y = "70%", | |
align="right"; | |
valign="bottom"; | |
--caption = caption; | |
fontSize = 16; | |
fontShadow = true; | |
} | |
buttonArray.label:SetCaption(caption) -- do it here as workaround for vanishing text | |
end | |
if i == 1 then | |
buttonArray.image:AddChild(buildProgress) | |
--Spring.Echo("Adding build progress bar") | |
end | |
end | |
end | |
end | |
--these two functions place the items into their rows | |
local function ManageStateIcons() | |
local stateCols = { } | |
for i=1, numStateColumns do | |
stateCols[i] = {} | |
for v=(MAX_STATE_ROWS * (i-1)) + 1, (MAX_STATE_ROWS*i) do | |
stateCols[i][v - MAX_STATE_ROWS*(i-1)] = n_states[v] | |
end | |
end | |
for i=1, numStateColumns do | |
UpdateContainer(sp_states[i], stateCols[i], MAX_STATE_ROWS) | |
end | |
end | |
local function ManageCommandIcons(useRowSort) | |
local sourceArray = menuChoices[menuChoice].array | |
local configArray = menuChoices[menuChoice].config | |
--update factory data | |
if menuChoice == 6 and selectedFac then | |
UpdateFactoryBuildQueue() | |
end | |
local commandRows = { } | |
--most commands don't use row sorting; econ, defense and special do | |
if not useRowSort then | |
for i=1, numRows do | |
commandRows[i] = {} | |
for v=(MAX_COLUMNS * (i-1)) + 1, (MAX_COLUMNS*i) do | |
commandRows[i][v - MAX_COLUMNS*(i-1)] = sourceArray[v] | |
end | |
end | |
else | |
for i=1, numRows do | |
commandRows[i] = {} | |
for v=1,#sourceArray do | |
if configArray[sourceArray[v].id].row == i then | |
commandRows[i][#commandRows[i]+1] = sourceArray[v] | |
end | |
end | |
end | |
end | |
for i=1, numRows do | |
UpdateContainer(sp_commands[i], commandRows[i], MAX_COLUMNS) | |
end | |
--manage factory queue if needed | |
if menuChoice == 6 and selectedFac and #commandRows[numRows] == 0 then | |
if not buildRow_visible then | |
commands_main:AddChild(buildRow) | |
buildRow_visible = true | |
end | |
ManageBuildRow() | |
else | |
commands_main:RemoveChild(buildRow) | |
buildRow_visible = false | |
end | |
end | |
local function Update(buttonpush) | |
local commands = widgetHandler.commands | |
local customCommands = widgetHandler.customCommands | |
--most commands don't use row sorting; econ, defense and special do | |
local useRowSort = (menuChoice == 3 or menuChoice == 4 or menuChoice == 5) | |
if menuChoice == 1 then | |
hotkeyMode = false | |
end | |
--if (#commands + #customCommands == 0) then | |
---screen0:RemoveChild(window); | |
--window_visible = false; | |
-- return | |
--else | |
--if not window_visible then | |
--screen0:AddChild(window); | |
--window_visible = true; | |
--end | |
--end | |
n_common = {} | |
n_factories = {} | |
n_econ = {} | |
n_defense = {} | |
n_special = {} | |
n_units = {} | |
n_states = {} | |
--Spring.Echo(#commands) | |
for i = 1, #commands do ProcessCommand(commands[i]) end | |
for i = 1, #customCommands do ProcessCommand(customCommands[i]) end | |
for i = 1, #globalCommands do ProcessCommand(globalCommands[i]) end | |
menuChoices[1].array = n_common | |
menuChoices[2].array = n_factories | |
menuChoices[3].array = n_econ | |
menuChoices[4].array = n_defense | |
menuChoices[5].array = n_special | |
menuChoices[6].array = n_units | |
--[[ | |
local function Sort(a, b, array) | |
return array[a.id] < array[b.id] | |
end | |
table.sort(n_factories, Sort(a,b, factory_commands)) | |
table.sort(n_econ, Sort(a,b, econ_commands)) | |
table.sort(n_defense, Sort(a,b, defense_commands)) | |
]]-- | |
--sorting isn't strictly needed, it uses the same order as listed in buildoptions | |
table.sort(n_factories, function(a,b) return factory_commands[a.id].order < factory_commands[b.id].order end ) | |
table.sort(n_econ, function(a,b) return econ_commands[a.id].order < econ_commands[b.id].order end) | |
table.sort(n_defense, function(a,b) return defense_commands[a.id].order < defense_commands[b.id].order end) | |
table.sort(n_special, function(a,b) return special_commands[a.id].order < special_commands[b.id].order end) | |
ManageStateIcons() | |
ManageCommandIcons(useRowSort) | |
end | |
local function MakeMenuTab(i, alpha) | |
local button = Button:New{ | |
parent = menuTabRow; | |
y = 0, | |
height = "100%", | |
-- font = { | |
-- shadow = true | |
-- }, | |
caption = hotkeyMode and menuChoices[i].name or menuChoices[i].hotkeyName, | |
OnClick = { | |
function() | |
menuChoice = i | |
if i >= 2 and i <= 5 then lastBuildChoice = i end | |
Update(true) | |
ColorTabs(i) | |
end | |
}, | |
} | |
button.backgroundColor[4] = alpha or 1 | |
return button | |
end | |
--need to recreate the tabs completely because chili is dumb | |
--also needs to be non-local so MakeMenuTab can call it | |
function ColorTabs(arg) | |
arg = arg or menuChoice | |
RemoveChildren(menuTabRow) | |
for i=1,6 do | |
if i == arg then | |
menuTabs[arg] = MakeMenuTab(arg, 1) | |
else | |
menuTabs[i] = MakeMenuTab(i, 0.4) | |
end | |
end | |
end | |
local function SmartTabSelect() | |
Update() | |
if options.hidetabs.value then | |
menuChoice = 1 | |
ColorTabs(1) | |
elseif options.disablesmartselect.value then | |
return | |
elseif #n_units > 0 and #n_econ == 0 then | |
menuChoice = 6 --selected factory, jump to units | |
ColorTabs(6) | |
elseif #n_econ > 0 and menuChoice == 6 then | |
menuChoice = lastBuildChoice --selected non-fac and in units menu, jump to last build menu | |
ColorTabs(lastBuildChoice) | |
elseif #n_factories + #n_econ + #n_defense + #n_units == 0 then | |
menuChoice = 1 --selected non-builder, jump to common | |
ColorTabs(1) | |
end | |
end | |
local function CopyTable(outtable,intable) | |
for i,v in pairs(intable) do | |
if (type(v)=='table') then | |
if (type(outtable[i])~='table') then outtable[i] = {} end | |
CopyTable(outtable[i],v) | |
else | |
outtable[i] = v | |
end | |
end | |
end | |
-- force update every 0.2 seconds | |
--[[ | |
local timer = 0 | |
function widget:Update(dt) | |
timer = timer + dt | |
if timer >= forceUpdateFrequency then | |
Update() | |
timer = 0 | |
end | |
end | |
]]-- | |
-- layout handler - its needed for custom commands to work and to delete normal spring menu | |
local function LayoutHandler(xIcons, yIcons, cmdCount, commands) | |
widgetHandler.commands = commands | |
widgetHandler.commands.n = cmdCount | |
widgetHandler:CommandsChanged() | |
local reParamsCmds = {} | |
local customCmds = {} | |
local cnt = 0 | |
local AddCommand = function(command) | |
local cc = {} | |
CopyTable(cc,command ) | |
cnt = cnt + 1 | |
cc.cmdDescID = cmdCount+cnt | |
if (cc.params) then | |
if (not cc.actions) then --// workaround for params | |
local params = cc.params | |
for i=1,#params+1 do | |
params[i-1] = params[i] | |
end | |
cc.actions = params | |
end | |
reParamsCmds[cc.cmdDescID] = cc.params | |
end | |
--// remove api keys (custom keys are prohibited in the engine handler) | |
cc.pos = nil | |
cc.cmdDescID = nil | |
cc.params = nil | |
customCmds[#customCmds+1] = cc | |
end | |
--// preprocess the Custom Commands | |
for i=1,#widgetHandler.customCommands do | |
AddCommand(widgetHandler.customCommands[i]) | |
end | |
for i=1,#globalCommands do | |
AddCommand(globalCommands[i]) | |
end | |
Update() | |
if (cmdCount <= 0) then | |
return "", xIcons, yIcons, {}, customCmds, {}, {}, {}, {}, reParamsCmds, {} --prevent CommandChanged() from being called twice when deselecting all units (copied from ca_layout.lua) | |
end | |
return "", xIcons, yIcons, {}, customCmds, {}, {}, {}, {}, reParamsCmds, {[1337]=9001} | |
end | |
local function ScrollTabRight() | |
menuChoice = menuChoice + 1 | |
if menuChoice > #menuChoices then menuChoice = 1 end | |
if menuChoice >= 2 and menuChoice <= 5 then lastBuildChoice = menuChoice end | |
Update(true) | |
ColorTabs() | |
end | |
local function ScrollTabLeft() | |
menuChoice = menuChoice - 1 | |
if menuChoice < 1 then menuChoice = #menuChoices end | |
if menuChoice >= 2 and menuChoice <= 5 then lastBuildChoice = menuChoice end | |
Update(true) | |
ColorTabs() | |
end | |
-------------------------------------- | |
-- Hotkey Mode | |
function widget:KeyPress(key, modifier, isRepeat) | |
if (hotkeyMode) and not isRepeat then | |
local thingsDone = false | |
local pos = gridKeyMap[key] | |
if pos and sp_commands[pos[1]] and sp_commands[pos[1]].children[pos[2]] then | |
local cmdid = sp_commands[pos[1]].children[pos[2]] | |
if cmdid then | |
cmdid = cmdid.cmdid | |
if cmdid then | |
local index = Spring.GetCmdDescIndex(cmdid) | |
if index then | |
Spring.SetActiveCommand(index,1,true,false,false,false,false,false) | |
thingsDone = true | |
end | |
end | |
end | |
end | |
hotkeyMode = false | |
menuChoice = 1 -- auto-return to orders to make it clear hotkey time is over | |
Update(true) | |
ColorTabs() | |
return thingsDone | |
elseif menuChoice == 6 and options.unitstabhotkey.value and selectedFac then | |
local pos = gridKeyMap[key] | |
if pos and pos[1] ~= 3 and sp_commands[pos[1]] and sp_commands[pos[1]].children[pos[2]] then | |
local cmdid = sp_commands[pos[1]].children[pos[2]] | |
if cmdid then | |
cmdid = cmdid.cmdid | |
if cmdid then | |
local index = Spring.GetCmdDescIndex(cmdid) | |
if index then | |
local alt,ctrl,meta,shift = Spring.GetModKeyState() | |
if (meta or (alt and options.unitshotkeyaltaswell.value) or not options.unitshotkeyrequiremeta.value) and not ctrl then | |
local opts = 0 | |
if alt then | |
opts = opts + CMD.OPT_ALT | |
end | |
if shift then | |
opts = opts + CMD.OPT_SHIFT | |
end | |
Spring.GiveOrderToUnit(selectedFac, cmdid, {0}, opts) | |
if WG.sounds_gaveOrderToUnit then | |
WG.sounds_gaveOrderToUnit(selectedFac, true) | |
end | |
-- does not work with meta held | |
--Spring.SetActiveCommand(index,1,true,false,alt,false,false,shift) | |
return true | |
end | |
end | |
end | |
end | |
end | |
end | |
if (key == KEYSYMS.SPACE or ((key == KEYSYMS.RALT or key == KEYSYMS.LALT) and options.unitshotkeyaltaswell.value)) and selectedFac and menuChoice == 6 and options.unitshotkeyrequiremeta.value and options.unitstabhotkey.value then | |
Update(true) | |
end | |
end | |
function widget:KeyRelease(key, modifier, isRepeat) | |
if (key == KEYSYMS.SPACE or ((key == KEYSYMS.RALT or key == KEYSYMS.LALT) and options.unitshotkeyaltaswell.value)) and selectedFac and menuChoice == 6 and options.unitshotkeyrequiremeta.value and options.unitstabhotkey.value then | |
Update(true) | |
end | |
end | |
--Spring.Echo(CMD.OPT_META) = 4 | |
--Spring.Echo(CMD.OPT_RIGHT) = 16 | |
--Spring.Echo(CMD.OPT_SHIFT) = 32 | |
--Spring.Echo(CMD.OPT_CTRL) = 64 | |
--Spring.Echo(CMD.OPT_ALT) = 128 | |
local function HotkeyTabFactory() | |
menuChoice = 2 | |
hotkeyMode = true | |
Update(true) | |
ColorTabs() | |
end | |
local function HotkeyTabEconomy() | |
menuChoice = 3 | |
hotkeyMode = true | |
Update(true) | |
ColorTabs() | |
end | |
local function HotkeyTabDefence() | |
menuChoice = 4 | |
hotkeyMode = true | |
Update(true) | |
ColorTabs() | |
end | |
local function HotkeyTabSpecial() | |
menuChoice = 5 | |
hotkeyMode = true | |
Update(true) | |
ColorTabs() | |
end | |
options.tab_factory.OnChange = HotkeyTabFactory | |
options.tab_economy.OnChange = HotkeyTabEconomy | |
options.tab_defence.OnChange = HotkeyTabDefence | |
options.tab_special.OnChange = HotkeyTabSpecial | |
local function AddAction(cmd, func, data, types) | |
return widgetHandler.actionHandler:AddAction(widget, cmd, func, data, types) | |
end | |
local function RemoveAction(cmd, types) | |
return widgetHandler.actionHandler:RemoveAction(widget, cmd, types) | |
end | |
--this function add hotkey text to each tab name | |
local function updateTabName(num, choice) | |
local hotkey = WG.crude.GetHotkey(choice.actionName) | |
if hotkey ~= '' then | |
choice.hotkeyName = choice.name .. '(\255\0\255\0' .. hotkey .. '\008)' | |
choice.name = choice.name .. '(' .. hotkey .. ')' | |
end | |
end | |
--get reload status for selected weapon | |
local function IsWeaponManualFire(unitID, weapNum) | |
local unitDefID = spGetUnitDefID(unitID) | |
local unitDef = UnitDefs[unitDefID] | |
local weaponNoX = (unitDef and unitDef.weapons and unitDef.weapons[weapNum]) | |
if (weaponNoX ~= nil) and WeaponDefs[weaponNoX.weaponDef].manualFire then | |
local reloadTime = WeaponDefs[weaponNoX.weaponDef].reload | |
return true,reloadTime | |
end | |
return nil --Note: this mean unit doesn't have weapon number 'weapNum' | |
end | |
-- INITS | |
function widget:Initialize() | |
widgetHandler:ConfigLayoutHandler(LayoutHandler) | |
Spring.ForceLayoutUpdate() | |
recentlyInitialized = true | |
RemoveAction("nextmenu") | |
RemoveAction("prevmenu") | |
AddAction("nextmenu", ScrollTabRight, nil, "p") | |
AddAction("prevmenu", ScrollTabLeft, nil, "p") | |
--[[local f,it,isFile = nil,nil,false | |
f = io.open('cmdcolors.txt','r') | |
if f then | |
it = f:lines() | |
isFile = true | |
else | |
f = VFS.LoadFile('cmdcolors.txt') | |
it = string.gmatch(f, "%a+.-\n") | |
end | |
local wp = '%s*([^%s]+)' -- word pattern | |
local cp = '^'..wp..wp..wp..wp..wp -- color pattern | |
local sp = '^'..wp..wp -- single value pattern like queuedLineWidth | |
for line in it do | |
local _, _, n, r, g, b, a = string.find(line, cp) | |
r = tonumber(r or 1.0) | |
g = tonumber(g or 1.0) | |
b = tonumber(b or 1.0) | |
a = tonumber(a or 1.0) | |
if n then | |
cmdColors[n]= { r, g,b,a} | |
else | |
_, _, n, r= string.find(line:lower(), sp) | |
if n then | |
cmdColors[n]= r | |
end | |
end | |
end]]-- | |
-- setup Chili | |
Chili = WG.Chili | |
Button = Chili.Button | |
Label = Chili.Label | |
Colorbars = Chili.Colorbars | |
Checkbox = Chili.Checkbox | |
Window = Chili.Window | |
Panel = Chili.Panel | |
StackPanel = Chili.StackPanel | |
TextBox = Chili.TextBox | |
Image = Chili.Image | |
Progressbar = Chili.Progressbar | |
Control = Chili.Control | |
screen0 = Chili.Screen0 | |
-- Set the size for the default settings. | |
local screenWidth, screenHeight = Spring.GetWindowGeometry() | |
local width = math.max(350, math.min(450, screenWidth*screenHeight*0.0004)) | |
local height = math.min(screenHeight/4.5, 200*width/450) | |
--create main Chili elements | |
window = Window:New{ | |
parent = screen0, | |
name = 'integralwindow'; | |
color = {0, 0, 0, 0}, | |
width = width, | |
height = height, | |
x = 0, | |
bottom = 0, | |
dockable = true; | |
draggable = false, | |
resizable = false, | |
tweakDraggable = true, | |
tweakResizable = true, | |
minWidth = MIN_WIDTH, | |
minHeight = MIN_HEIGHT, | |
padding = {0, 0, 0, 0}, | |
--itemMargin = {0, 0, 0, 0}, | |
} | |
fakewindow = Panel:New{ | |
parent = window, | |
x = 0, | |
y = '15%', | |
width = "100%"; | |
height = "86%"; | |
--disableChildrenHitTest = false, | |
--itemMargin = {0, 0, 0, 0}, | |
dockable = false; | |
draggable = false, | |
resizable = false, | |
padding = {0, 0, 0, 0}, | |
backgroundColor = {1, 1, 1, options.background_opacity.value}, | |
-- skinName = "DarkGlass", | |
} | |
menuTabRow = StackPanel:New{ | |
parent = window, | |
resizeItems = true; | |
columns = 6; | |
orientation = "horizontal"; | |
height = "15%"; | |
width = "99%"; | |
x = '1%'; | |
y = 0; | |
padding = {0, 0, 0, 0}, | |
itemMargin = {0, 0, 0, 0}, | |
OnMouseDown={ function(self) | |
local _,_, meta,_ = Spring.GetModKeyState() | |
if not meta then return false end --allow button to continue its function | |
WG.crude.OpenPath(options_path) --// click+ space on integral-menu tab will open a integral options. | |
WG.crude.ShowMenu() --make epic Chili menu appear. | |
return false | |
end }, | |
} | |
for i=1,6 do | |
menuTabs[i] = MakeMenuTab(i, 1) | |
end | |
ColorTabs() | |
commands_main = Panel:New{ | |
parent = fakewindow, | |
backgroundColor = {0, 0, 0, 0}, | |
height = "100%"; | |
width = COMMAND_SECTION_WIDTH.."%"; | |
x = "0%"; | |
y = "0%"; | |
padding = {4, 4, 0, 4}, | |
itemMargin = {0, 0, 0, 0}, | |
noSelfHitTest = true, | |
NCHitTest = function(self,x,y) | |
if not spaceClicked and x> self.x and y>self.y and x<self.x+self.width and y<self.y+self.height then | |
if select(3,Spring.GetMouseState()) and select(3,Spring.GetModKeyState()) then -- meta and mouse down | |
spaceClicked = true | |
WG.crude.OpenPath('Game/Commands') -- click+ space on panel beside command button will open command-hotkey-binder | |
WG.crude.ShowMenu() --make epic Chili menu appear. | |
end | |
end | |
if spaceClicked and not (select(3,Spring.GetMouseState()) and select(3,Spring.GetModKeyState())) then --not meta, not mouse down | |
spaceClicked = false | |
end | |
return false | |
end, | |
} | |
for i=1,numRows do | |
sp_commands[i] = StackPanel:New{ | |
parent = commands_main, | |
resizeItems = true; | |
orientation = "horizontal"; | |
height = math.floor(100/numRows).."%"; | |
width = "100%"; | |
x = "0%"; | |
y = math.floor(100/numRows)*(i-1).."%"; | |
padding = {0, 0, 0, 0}, | |
itemMargin = {0, 0, 0, 0}, | |
index = i, | |
i_am_sp_commands = true, | |
} | |
--Spring.Echo("Command row "..i.." created") | |
end | |
states_main = Panel:New{ | |
parent = fakewindow, | |
backgroundColor = {0, 0, 0, 0}, | |
height = "100%"; | |
width = (STATE_SECTION_WIDTH).."%"; | |
--x = tostring(100-STATE_SECTION_WIDTH).."%"; | |
right = 0; | |
y = "0%"; | |
padding = {0, 4, 4, 4}, | |
itemMargin = {0, 0, 0, 0}, | |
OnMouseDown={ function(self) --// click+ space on any unit-State button will open Unit-AI menu, | |
local _,_, meta,_ = Spring.GetModKeyState() | |
if not meta then return false end --allow button to continue its function | |
WG.crude.OpenPath('Game/Unit AI') | |
WG.crude.ShowMenu() --make epic Chili menu appear. | |
return true --stop the button's function, else unit-state button will look bugged. | |
end }, | |
} | |
for i=1, numStateColumns do | |
sp_states[i] = StackPanel:New { | |
parent = states_main, | |
resizeItems = true; | |
orientation = "vertical"; | |
height = "100%"; | |
width = math.floor(100/numStateColumns).."%"; | |
x = (100 - (math.floor(100/numStateColumns))*i).."%"; | |
y = "0%"; | |
padding = {0, 0, 0, 0}, | |
itemMargin = {0, 0, 0, 0}, | |
} | |
end | |
buildRow = Panel:New{ | |
parent = commands_main, | |
orientation = "horizontal"; | |
height = (math.floor(100/numRows)).."%"; | |
width = "100%"; | |
x = "0%"; | |
y = (math.floor(100/numRows))*(numRows-1).."%"; | |
padding = {0, 0, 0, 0}, | |
itemMargin = {0, 0, 0, 0}, | |
backgroundColor = {0, 0, 0, 0} | |
} | |
buildProgress = Progressbar:New{ | |
value = 0.0, | |
name = 'prog'; | |
max = 1; | |
color = {0.7, 0.7, 0.4, 0.6}, | |
backgroundColor = {1, 1, 1, 0.01}, | |
width = "92%", | |
height = "92%", | |
x = "4%", | |
y = "4%", | |
skin=nil, | |
skinName='default', | |
}, | |
commands_main:RemoveChild(buildRow) | |
for i=1,MAX_COLUMNS do | |
buildRowButtons[i] = {} | |
end | |
end | |
local lastCmd = nil -- last active command | |
local lastColor = nil -- original color of button with last active command | |
local lastFocusColor = nil | |
-- widget:DrawScreen() is needed to highlight active command | |
-- also draw build icon when drag_dropping queue to another factory | |
function widget:DrawScreen() | |
-- highlight active command | |
local _,cmdid,_,cmdname = Spring.GetActiveCommand() | |
if cmdid ~= lastCmd then | |
if cmdid and commandButtons[cmdid] then | |
local but = commandButtons[cmdid].button | |
lastColor = but.backgroundColor | |
lastFocusColor = but.focusColor | |
but.backgroundColor = {0.8, 0, 0, 1} | |
but.focusColor = {0.8, 0, 0, 1} | |
but:Invalidate() | |
end | |
if lastCmd ~= nil and commandButtons[lastCmd] then | |
local but = commandButtons[lastCmd].button | |
but.backgroundColor = lastColor | |
but.focusColor = lastFocusColor | |
but:Invalidate() | |
end | |
lastCmd = cmdid | |
end | |
--draw build icon when drag_drop queue to another factory | |
if buildRow_dragDrop[1] then | |
local mx,my,lmb = Spring.GetMouseState() | |
if not lmb then | |
local pointingAt, gpos = Spring.TraceScreenRay(mx,my) | |
if gpos and pointingAt=='unit' and Spring.GetUnitTeam(gpos)==Spring.GetMyTeamID() then | |
local udid = spGetUnitDefID(gpos) | |
local ud = UnitDefs[udid] | |
for _, options in ipairs(ud.buildOptions) do --check if target unit can build this queue. Copied from unit_central_build_ai.lua | |
if ( options == buildRow_dragDrop[2] ) then | |
local countTransfer = buildRow_dragDrop[3] | |
BuildRowButtonFunc(nil, -buildRow_dragDrop[2], true, false,countTransfer,false,gpos) --add to new factory | |
BuildRowButtonFunc(buildRow_dragDrop[1], -buildRow_dragDrop[2], false, true,countTransfer) --remove from source | |
break -- only one to find. | |
end | |
end | |
end | |
buildRow_dragDrop[1] = nil | |
else | |
local size = buildRow_dragDrop[4] | |
local udid = buildRow_dragDrop[2] | |
gl.Color(1,1,1,0.5) | |
gl.Texture(WG.GetBuildIconFrame(UnitDefs[udid])) --draw build icon on screen. Copied from gui_chili_gesture_menu.lua | |
gl.TexRect(mx-size*0.2, my-size, mx+size*1.8, my+size) | |
gl.Texture("#"..udid) | |
gl.TexRect(mx-size*0.2, my-size, mx+size*1.8, my+size) | |
gl.Texture(false) | |
gl.Color(1,1,1,1) | |
if buildRow_dragDrop[3]>1 then | |
gl.Text(buildRow_dragDrop[3]+1,mx+size*1.5, my-size*0.57,14,"") | |
end | |
end | |
end | |
end | |
-- Make the hotkeys appear on the menu tabs | |
function widget:Update() | |
if recentlyInitialized then | |
for i = 2, 5 do | |
updateTabName(i, menuChoices[i]) | |
end | |
recentlyInitialized = false | |
ColorTabs(1) | |
end | |
end | |
--This function update construction progress bar and weapon reload progress bar | |
function widget:GameFrame(n) | |
--set progress bar | |
if n%6 == 0 then | |
if menuChoice == 6 and selectedFac and buildRowButtons[1] and buildRowButtons[1].image then | |
local progress | |
local unitBuildID = spGetUnitIsBuilding(selectedFac) | |
if unitBuildID then | |
progress = select(5, spGetUnitHealth(unitBuildID)) | |
end | |
buildProgress:SetValue(progress or 0) | |
end | |
end | |
--update/set dgun progress bar | |
if n%7==0 and not turnOffReloadableWeaponChecks then --every 1/4 second & was not sleeping | |
local oneclickweapon = reloadableButton[CMD_ONECLICK_WEAPON] | |
oneclickweapon.status = nil | |
oneclickweapon.progress = 0 | |
local manualfireweapon = reloadableButton[CMD.MANUALFIRE] | |
manualfireweapon.status = nil | |
manualfireweapon.progress = 0 | |
local jump = reloadableButton[CMD_JUMP] | |
jump.status = nil | |
jump.progress = 0 | |
local selectedUnitList = spGetSelectedUnits() | |
local currentFrame = n | |
for i=1, #selectedUnitList do | |
local unitID = selectedUnitList[i] | |
if not manualfireweapon.status then | |
local _,_, weaponReloadFrame, _, _ = spGetUnitWeaponState(unitID, 3-reverseCompat) --Note: weapon no.3 is by ZK convention is usually used for user controlled weapon | |
local isManualFire, reloadTime = IsWeaponManualFire(unitID, 3) | |
if isManualFire then | |
if weaponReloadFrame > currentFrame then | |
manualfireweapon.status = false | |
local remainingTime = (weaponReloadFrame - currentFrame)*1/30 | |
local reloadFraction =1 - remainingTime/reloadTime | |
if manualfireweapon.progress < reloadFraction then | |
manualfireweapon.progress = reloadFraction | |
end | |
else | |
manualfireweapon.status = true | |
end | |
end | |
end | |
if not oneclickweapon.status then | |
local specialReloadFrame = spGetUnitRulesParam(unitID,"specialReloadFrame") | |
local specialReloadStart = spGetUnitRulesParam(unitID,"specialReloadStart") | |
if specialReloadFrame then | |
if specialReloadFrame > currentFrame then | |
oneclickweapon.status = false | |
if specialReloadStart then | |
local reloadTime = specialReloadFrame - specialReloadStart | |
local remainingTime = (specialReloadFrame - currentFrame) | |
local reloadFraction =1 - remainingTime/reloadTime | |
if oneclickweapon.progress < reloadFraction then | |
oneclickweapon.progress = reloadFraction | |
end | |
end | |
else | |
oneclickweapon.status = true | |
end | |
end | |
end | |
if not jump.status then | |
local jumpReload = spGetUnitRulesParam(unitID,"jumpReload") | |
if jumpReload then | |
if jumpReload < 0.95 then | |
jump.status = false | |
if jump.progress < jumpReload then | |
jump.progress = jumpReload | |
end | |
else | |
jump.status = true | |
end | |
end | |
end | |
end | |
for buttonID, status_progress in pairs(reloadableButton) do | |
if commandButtons[buttonID] then | |
if status_progress.status == true then | |
commandButtons[buttonID].image.color = {1, 1, 1, 1} | |
commandButtons[buttonID].image:Invalidate() | |
elseif status_progress.status == false then | |
local progress = status_progress.progress*0.7 | |
local grey = 0.3+ progress | |
commandButtons[buttonID].image.color = {grey, grey, grey, 1} | |
commandButtons[buttonID].image:Invalidate() | |
end | |
end | |
end | |
if jump.status == nil and oneclickweapon.status==nil and manualfireweapon.status==nil then | |
turnOffReloadableWeaponChecks = true --be efficient, turn off check if no reload state detected | |
end | |
end | |
end | |
function widget:UnitCmdDone(unitID, unitDefID, unitTeam, cmdID, cmdTag) | |
if cmdID == CMD.MANUALFIRE or cmdID == CMD_JUMP or cmdID == CMD_ONECLICK_WEAPON then | |
turnOffReloadableWeaponChecks = false --wake up reload state checking, in case some new unit activated a reload state | |
end | |
end | |
function widget:SelectionChanged(newSelection) | |
--reset reloadable weapon button (newly created unit doesn't have reload state, if user select this unit it need to reset to default value first) | |
for buttonID, _ in pairs(reloadableButton) do | |
if commandButtons[buttonID] then | |
commandButtons[buttonID].image.color = {1, 1, 1, 1} | |
commandButtons[buttonID].image:Invalidate() | |
end | |
end | |
turnOffReloadableWeaponChecks = false; | |
--get new selected fac, if any | |
for i=1,#newSelection do | |
local id = newSelection[i] | |
local defID = spGetUnitDefID(id) --is in LOS? | |
if defID and UnitDefs[defID].isFactory then | |
if selectedFac ~= id then | |
alreadyRemovedTag = {} | |
end | |
selectedFac = id | |
SmartTabSelect() | |
return | |
end | |
end | |
selectedFac = nil | |
SmartTabSelect() | |
end | |
function widget:Shutdown() | |
widgetHandler:ConfigLayoutHandler(options.old_menu_at_shutdown.value) --true: activate Default menu, false: activate dummy (empty) menu, nil: disable menu & CommandChanged() callins. See Layout.lua | |
Spring.ForceLayoutUpdate() | |
end | |
options.hidetabs.OnChange = function(self) | |
fakewindow:SetPosRelative(_, self.value and 0 or '15%', _, self.value and '100%' or '86%') | |
fakewindow:SetPosRelative(_, self.value and 0 or '15%', _, self.value and '100%' or '86%') | |
if self.value then | |
window:RemoveChild(menuTabRow) | |
else | |
window:AddChild(menuTabRow) | |
end | |
menuChoice = 1 | |
ColorTabs(1) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment