forked from zxbc/BAR_widgets
-
Notifications
You must be signed in to change notification settings - Fork 0
/
cmd_single_volley_attack_mode.lua
308 lines (269 loc) · 12.4 KB
/
cmd_single_volley_attack_mode.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
function widget:GetInfo()
return {
name = "Single Volley Attack Mode",
desc = "Sets units to single volley attack mode. Alt-Q to toggle. V1.2 added a new separate bindable command 'single_volley_attack_command'",
author = "Errrrrrr",
date = "June 22, 2023",
license = "GNU GPL, v2 or later",
version = "1.2",
layer = -10,
enabled = true,
handler = true
}
end
---------------------------------------------------------------------------------------------
-- Default keybind is "alt-q"
-- Set custom_keybind_mode to true for custom keybind.
-- Bindable action name: single_volley_attack_toggle
--
-- Version 1.2: Added a new bindable command "single_volley_attack_command"
-- The new bindable command simply performs one volley of attack (when it's a ground attack)
-- You can bind the new command to your default attack key (e.g. "a")
-- Icon drawing is disabled now, if you want to use the old toggle mode, set drawIcons to true
---------------------------------------------------------------------------------------------
local custom_keybind_mode = false
local drawIcons = false
local degen_mode = false -- deprecated, no need
local gameFrame = 0
local overWatched = {} -- this table contains all the units that are on single volley mode
local overWatchedCmdCount = {} -- this table contains the number of single volley commands in the command queue of the overwatched units
local overWatchedUpdate = {} -- this table indicates whether the overwatched units need to be updated
local myAllyTeamID
local singleVolleyAttackActive = false
-- speed ups
local spSetActiveCommand = Spring.SetActiveCommand
local spGetActiveCommand = Spring.GetActiveCommand
local spGetSelectedUnits = Spring.GetSelectedUnits
local spGiveOrder = Spring.GiveOrder
local spGetUnitWeaponState = Spring.GetUnitWeaponState
local spGetUnitPosition = Spring.GetUnitPosition
local spGetUnitDefID = Spring.GetUnitDefID
local spGetUnitCommands = Spring.GetUnitCommands
local spGetUnitStates = Spring.GetUnitStates
local spGiveOrderToUnit = Spring.GiveOrderToUnit
local spEcho = Spring.Echo
local CMD_ATTACK = CMD.ATTACK
local CMD_REMOVE = CMD.REMOVE
local CMD_STOP = CMD.STOP
local function tableToString(t)
local result = ""
if type(t) ~= "table" then
result = tostring(t)
elseif t == nil then
result = "nil"
else
for k, v in pairs(t) do
result = result .. "[" .. tostring(k) .. "] = "
if type(v) == "table" then
result = result .. "{"
for k2, v2 in pairs(v) do
result = result .. "[" .. tostring(k2) .. "] = "
if type(v2) == "table" then
result = result .. "{"
for k3, v3 in pairs(v2) do
result = result .. "[" .. tostring(k3) .. "] = " .. tostring(v3) .. ", "
end
result = result .. "}, "
else
result = result .. tostring(v2) .. ", "
end
end
result = result .. "}, "
else
result = result .. tostring(v) .. ", "
end
end
end
return "{" .. result:sub(1, -3) .. "}"
end
local function GiveNotifyingOrder(cmdID, cmdParams, cmdOpts)
if widgetHandler:CommandNotify(cmdID, cmdParams, cmdOpts) then
return
end
spGiveOrder(cmdID, cmdParams, cmdOpts.coded)
end
local function RemoveOverwatchedUnit(unitID)
overWatched[unitID] = nil
overWatchedCmdCount[unitID] = 0
overWatchedUpdate[unitID] = nil
--spEcho("Unit "..tostring(unitID).." removed from single volley overwatch")
end
function widget:Initialize()
myAllyTeamID = Spring.GetMyAllyTeamID()
widgetHandler.actionHandler:AddAction(self,"single_volley_attack_toggle", SingleVolleyAttackToggle, nil, "p")
widgetHandler.actionHandler:AddAction(self,"single_volley_attack_command", SingleVolleyAttackCommand, nil, "p")
end
function SingleVolleyAttackToggle(_,_,_,args)
local selUnits = spGetSelectedUnits()
-- add all units to overWatched table, with their unitID as index and their current weapon 1's reloadFrame as value
for i, unitID in ipairs(selUnits) do
local reloadFrame = spGetUnitWeaponState(unitID, 1, "reloadFrame")
local unitDef = UnitDefs[spGetUnitDefID(unitID)]
if not overWatched[unitID] then
overWatched[unitID] = reloadFrame
overWatchedCmdCount[unitID] = 999999
Spring.Echo("Unit "..tostring(unitID).." (" .. unitDef.name .. ") added to single volley overwatch")
else
overWatched[unitID] = nil
overWatchedCmdCount[unitID] = 0
Spring.Echo("Unit "..tostring(unitID).." (" .. unitDef.name .. ") removed from single volley overwatch")
end
end
end
function SingleVolleyAttackCommand(_,_,_,args)
-- if somehow build options are avaiable, this is a builder and we don't want to do anything
if Spring.GetSelectedUnitsCount() == 1 then
local unitID = Spring.GetSelectedUnits()[1]
local unitDef = UnitDefs[spGetUnitDefID(unitID)]
if unitDef.buildOptions[1] then return end
end
singleVolleyAttackActive = true
spSetActiveCommand("attack", 1)
end
function widget:KeyPress(key, mods, isRepeat)
if custom_keybind_mode then return end
if (key == 113) and (mods.alt) then -- alt + q
SingleVolleyAttackToggle()
end
end
function widget:GameFrame(frame)
gameFrame = frame
end
function widget:Update(dt)
if gameFrame % 3 == 0 then
-- iterate through all of overWatched table, and check if the current weapon 1's reloadFrame is different from the one stored in the table
for unitID, reloadFrame in pairs(overWatched) do
local currentReloadFrame = spGetUnitWeaponState(unitID, 1, "reloadFrame")
if currentReloadFrame ~= reloadFrame then
--spEcho("unit "..tostring(unitID).." has fired a volley at time "..tostring(gameFrame).."!")
overWatched[unitID] = currentReloadFrame
-- remove the current command from queue if it is a single volley attack ground command
local commands = spGetUnitCommands(unitID, -1)
if #commands > 0 then
local cmdID = commands[1].id
local params = commands[1].params
if ((cmdID == CMD_ATTACK) and (params[#params] == 666666)) or (overWatchedCmdCount[unitID] > 1000) then
--spEcho("detected single volley attack command from unit "..tostring(unitID))
overWatchedCmdCount[unitID] = overWatchedCmdCount[unitID] - 1
spGiveOrderToUnit(unitID, CMD_REMOVE, {commands[1].tag, 555555}, 0)
--spEcho("unit "..tostring(unitID).." has "..tostring(overWatchedCmdCount[unitID]).." single volley attack commands left in queue")
-- if the unit's repeat state is set to true, we re-add this command to the end of the command queue
local unitStates = spGetUnitStates(unitID)
if unitStates["repeat"] then
spGiveOrderToUnit(unitID, CMD_ATTACK, params, {"shift"})
end
end
end
-- remove the unit from the overWatched table if it has no more single volley attack commands in queue
if (overWatchedCmdCount[unitID] <= 0) or (#commands == 0) then
RemoveOverwatchedUnit(unitID)
end
end
end
end
if gameFrame % 2 == 1 then
if singleVolleyAttackActive then
local index, cmd_id, cmd_type, cmd_name = spGetActiveCommand()
--spEcho("cmd_id: "..tostring(cmd_id))
if not cmd_id then
singleVolleyAttackActive = false
end
end
end
end
local icon = "LuaUI/Images/groupicons/weaponexplo.png"
-- draw this icon at the top left corner of the unit's model in the world if it is on single volley mode
function widget:DrawWorld()
-- degens don't need graphics
if degen_mode or not drawIcons then return end
for unitID, reloadFrame in pairs(overWatched) do
local x, y, z = spGetUnitPosition(unitID)
gl.PushMatrix()
gl.Translate(x, y, z)
gl.Billboard()
gl.Color(1, 1, 1, 1)
gl.Texture(icon)
gl.TexRect(-40, 10, -20, 30)
gl.PopMatrix()
end
end
function widget:CommandNotify(cmdID, cmdParams, cmdOpts)
if singleVolleyAttackActive then -- if it is a ground attack command
--singleVolleyAttackActive = false
if (cmdID == CMD_ATTACK) and (#cmdParams >= 3) then
-- we add an additional param to the command, indicating this is a single volley attack command
-- what's the worst that could happen?
cmdParams[#cmdParams+1] = 666666
--spEcho("Single volley attack command issued at frame "..tostring(gameFrame).."!")
spGiveOrder(cmdID, cmdParams, cmdOpts) -- simply give the modified command
return true
end
end
end
-- when unit receives a command, check if it is a single volley attack command
function widget:UnitCommand(unitID, unitDefID, unitTeam, cmdID, cmdParams, cmdOpts, cmdTag, playerID, fromSynced, fromLua)
--spEcho("Unit "..tostring(unitID)..", cmdID: "..tostring(cmdID).. ", cmdParams: "..tableToString(cmdParams)..", cmdOpts: "..tableToString(cmdOpts)..", cmdTag: "..tostring(cmdTag).. ", fromSynced: "..tostring(fromSynced)..", fromLua: "..tostring(fromLua))
if (cmdID == CMD_ATTACK) and (cmdParams[#cmdParams] == 666666) and (unitTeam == myAllyTeamID) then
-- add the unit to overWatched table
overWatched[unitID] = spGetUnitWeaponState(unitID, 1, "reloadFrame")
if overWatchedCmdCount[unitID] == nil then
overWatchedCmdCount[unitID] = 1
else
overWatchedCmdCount[unitID] = overWatchedCmdCount[unitID] + 1
end
--spEcho("Unit "..tostring(unitID).."'s overWatchedCmdCount is "..tostring(overWatchedCmdCount[unitID]))
return true
end
-- if it's a stop or remove command, check the unit's overWatchedCmdCount and update it
if ((cmdID == CMD_STOP) or (cmdID == CMD_REMOVE)) and (cmdParams[2] ~= 555555) then
-- get the command queue of the unit, figure out if the removed command is a single volley attack command, decrement the overWatchedCmdCount if it is
local commands = spGetUnitCommands(unitID, -1)
local cmdIndex = 0
if cmdID == CMD_STOP then
RemoveOverwatchedUnit(unitID)
elseif cmdID == CMD_REMOVE then
for i = 1, #commands do
if commands[i].tag == cmdParams[1] then
cmdIndex = i
break
end
end
end
if cmdIndex > 0 then
cmdID = commands[cmdIndex].id
local params = commands[cmdIndex].params
if (cmdID == CMD_ATTACK) and (params[#params] == 666666) then
--spEcho("detected single volley attack command from unit "..tostring(unitID))
if overWatched[unitID] then
overWatchedCmdCount[unitID] = overWatchedCmdCount[unitID] - 1
end
--spEcho("unit "..tostring(unitID).." has "..tostring(overWatchedCmdCount[unitID]).." single volley attack commands left in queue")
end
end
end
end
-- if unit is destroyed, remove it from overWatched table
function widget:UnitDestroyed(unitID, unitDefID, unitTeam)
overWatched[unitID] = nil
end
-- add new units to overWatched table if degen_mode
function widget:UnitCreated(unitID, unitDefID, unitTeam)
if degen_mode and unitTeam == myAllyTeamID then
local reloadFrame = spGetUnitWeaponState(unitID, 1, "reloadFrame")
overWatched[unitID] = reloadFrame
end
end
-- add gifted units also
function widget:UnitGiven(unitID, unitDefID, unitTeam, oldTeam)
if degen_mode and unitTeam == myAllyTeamID then
local reloadFrame = spGetUnitWeaponState(unitID, 1, "reloadFrame")
overWatched[unitID] = reloadFrame
end
end
-- add units captured too, because we're truly degenerate
function widget:UnitCaptured(unitID, unitDefID, unitTeam, oldTeam)
if degen_mode and unitTeam == myAllyTeamID then
local reloadFrame = spGetUnitWeaponState(unitID, 1, "reloadFrame")
overWatched[unitID] = reloadFrame
end
end