-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgui_airdef.lua
588 lines (518 loc) · 16.1 KB
/
gui_airdef.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
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
include("keysym.h.lua")
function widget:GetInfo()
return {
name = "Air Defense Range",
desc = "Displays range of enemy air defenses",
author = "lov",
date = "2023",
license = "GNU GPL v2",
layer = 0,
enabled = false
}
end
-- CONFIGURATION
local lowspec = false -- if your computer is low spec
--local keycode = 112 -- o key
local onlyShowWhenAircraftSelected = true
local drawToggle = false
local drawMode = 1
local settingsInit = false
local enabledAsSpec = true
local pi = math.pi
local function rgb(r, b, g, a)
return { r / 255, b / 255, g / 255 }
end
-- local id = 27685
-- local _, vy, _ = Spring.GetUnitWeaponVectors(id, 1)
-- local x, y, z = Spring.GetUnitPosition(id)
-- local gy = Spring.GetGroundHeight(x, z)
-- Spring.Echo("HIEHGT", vy, y, gy, vy - gy)
local color1 = rgb(242, 121, 0) -- missile old: 39, 255, 0
local color2 = rgb(255, 0, 0) --flak old: 255, 0, 0
local color3 = rgb(0, 0, 255) --strongmissile
local alphamax = 100 -- limits the maximum alpha that overlapping circles can reach
local drawalpha = 10
local alphaincrement = 10
local unitList = {
-- ARMADA
armrl = { weapons = { 2 }, color = color1, weaponheight = 64 }, --nettle
armfrt = { weapons = { 2 }, color = color1, weaponheight = 64 }, --floating nettle
armferret = { weapons = { 2 }, color = color1, weaponheight = 16 },
armfrock = { weapons = { 2 }, color = color1, weaponheight = 29 },
armcir = { weapons = { 2 }, color = color3, weaponheight = 46 }, --chainsaw
armflak = { weapons = { 2 }, color = color2, weaponheight = 44 },
armfflak = { weapons = { 2 }, color = color2 }, --floating flak AA
armmercury = { weapons = { 1 }, color = color3, weaponheight = 70 },
armsam = { weapons = { 2 }, color = color1 }, --whistler
armjeth = { weapons = { 2 }, color = color1 }, --bot
armamph = { weapons = { 2 }, color = color1 }, --platypus
armaak = { weapons = { 2 }, color = color1 }, -- t2bot
armlatnk = { weapons = { 2 }, color = color1 }, --jaguar
armyork = { weapons = { 2 }, color = color2 }, --mflak
armpt = { weapons = { 2 }, color = color1 }, --boat
armaas = { weapons = { 2 }, color = color1 }, --t2boat
armah = { weapons = { 2 }, color = color1 }, --hover
-- armamd = { weapons = { 3 } }, --antinuke
-- armscab = { weapons = { 3 } }, --antinuke
corrl = { weapons = { 2 }, color = color1 },
corfrt = { weapons = { 2 }, color = color1 }, --floating rocket laucher
cormadsam = { weapons = { 2 }, color = color1 },
corfrock = { weapons = { 2 }, color = color1 },
corflak = { weapons = { 2 }, color = color2 },
cornaa = { weapons = { 2 }, color = color2 },
corscreamer = { weapons = { 1 }, color = color3, weaponheight = 59 },
corcrash = { weapons = { 2 }, color = color1 }, --bot
coraak = { weapons = { 2 }, color = color1 }, --t2bot
cormist = { weapons = { 2 }, color = color1 }, --lasher
corsent = { weapons = { 2 }, color = color2 }, --flak
corban = { weapons = { 2 }, color = color3 },
corpt = { weapons = { 2 }, color = color1 }, --boat
corarch = { weapons = { 2 }, color = color1 }, --t2boat
corah = { weapons = { 2 }, color = color1 }, --hover
-- corfmd = { weapons = { 3 } }, --an
-- cormabm = { weapons = { 3 } }, --an
}
-- cache only what we use
local weapTab = {} --WeaponDefs
local wdefParams = { 'salvoSize', 'reload', 'coverageRange', 'damages', 'range', 'type', 'projectilespeed',
'heightBoostFactor', 'heightMod', 'heightBoostFactor', 'projectilespeed', 'myGravity' }
for weaponDefID, weaponDef in pairs(WeaponDefs) do
weapTab[weaponDefID] = {}
for i, param in ipairs(wdefParams) do
weapTab[weaponDefID][param] = weaponDef[param]
end
end
wdefParams = nil
local unitRadius = {}
local unitNumWeapons = {}
local canMove = {}
local unitSpeeds = {}
local unitName = {}
local unitWeapons = {}
for unitDefID, unitDef in pairs(UnitDefs) do
unitRadius[unitDefID] = unitDef.radius
local weapons = unitDef.weapons
if #weapons > 0 then
unitNumWeapons[unitDefID] = #weapons
for i = 1, #weapons do
if not unitWeapons[unitDefID] then
unitWeapons[unitDefID] = {}
end
unitWeapons[unitDefID][i] = weapons[i].weaponDef
end
end
unitSpeeds[unitDefID] = unitDef.speed
-- for a, b in unitDef:pairs() do
-- Spring.Echo(a, b)
-- end
canMove[unitDefID] = unitDef.canMove
unitName[unitDefID] = unitDef.name
end
--Button display configuration
--position only relevant if no saved config data found
local buttonConfig = {}
buttonConfig["enabled"] = {
ally = { ground = false, air = false, nuke = false, radar = false },
enemy = { ground = true, air = true, nuke = true, radar = false }
}
local rangeCircleList --glList for drawing range circles
local spGetSpectatingState = Spring.GetSpectatingState
local spec, fullview = spGetSpectatingState()
local myAllyTeam = Spring.GetMyAllyTeamID()
local defences = {}
local lineConfig = {}
lineConfig["lineWidth"] = 1.5 -- calcs dynamic now
lineConfig["alphaValue"] = 0.0 --> dynamic behavior can be found in the function "widget:Update"
lineConfig["circleDivs"] = 80.0
local myPlayerID
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
---
local GL_LINE_LOOP = GL.LINE_LOOP
local glBeginEnd = gl.BeginEnd
local glColor = gl.Color
local glDepthTest = gl.DepthTest
local glLineWidth = gl.LineWidth
local glTranslate = gl.Translate
local glVertex = gl.Vertex
local glCallList = gl.CallList
local glCreateList = gl.CreateList
local glDeleteList = gl.DeleteList
local sqrt = math.sqrt
local abs = math.abs
local upper = string.upper
local floor = math.floor
local PI = math.pi
local cos = math.cos
local sin = math.sin
local spEcho = Spring.Echo
local spGetGameSeconds = Spring.GetGameSeconds
local spGetMyPlayerID = Spring.GetMyPlayerID
local spGetPlayerInfo = Spring.GetPlayerInfo
local spGetPositionLosState = Spring.GetPositionLosState
local spGetUnitDefID = Spring.GetUnitDefID
local spGetUnitPosition = Spring.GetUnitPosition
local spGetGroundHeight = Spring.GetGroundHeight
local spIsGUIHidden = Spring.IsGUIHidden
local spGetLocalTeamID = Spring.GetLocalTeamID
local spIsSphereInView = Spring.IsSphereInView
local chobbyInterface
local mapBaseHeight
local h = {}
for i = 1, 3 do
for i = 1, 3 do
h[#h + 1] = Spring.GetGroundHeight(Game.mapSizeX * i / 4, Game.mapSizeZ * i / 4)
end
end
mapBaseHeight = 0
for _, s in ipairs(h) do
mapBaseHeight = mapBaseHeight + s
end
mapBaseHeight = mapBaseHeight / #h
local gy = math.max(0, mapBaseHeight)
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
function widget:Shutdown()
if rangeCircleList then
gl.DeleteList(rangeCircleList)
end
end
local function initSettings()
if WG.options then
WG.options.addOption({ widgetname = "Air Defense Range", id = "airdeftoggle", group = "custom", category = 2, name = "When should it show", type = "select", options = {"When aricraft selected", "Toggle with keybind", "Always"}, value = drawMode, description = "Toggle between always showing the rings or only when aircraft are selected.",
onload = function(i)
--onlyShowWhenAircraftSelected = true
end,
onchange = function(i, value)
drawMode = value
onlyShowWhenAircraftSelected = drawMode == 1
UpdateCircleList()
end,
})
WG.options.addOption({ widgetname = "Air Defense Range", id = "airdefalpha", group = "custom", category = 2, name = "Alpha", type = "slider", min = 0, max = 99, step = 1, value = drawalpha, description = "Set the alpha of the rings",
onload = function(i)
--onlyShowWhenAircraftSelected = true
end,
onchange = function(i, value)
drawalpha = tonumber(value)
UpdateCircleList()
end,
})
settingsInit = true
end
end
local function init()
local units = Spring.GetAllUnits()
for i = 1, #units do
local unitID = units[i]
UnitDetected(unitID, Spring.IsUnitAllied(unitID))
-- Spring.Echo("height", unitID, Spring.GetUnitDefID(unitID), Spring.GetUnitHeight(unitID), Spring.GetUnitMass(unitID),
-- Spring.GetUnitRadius(unitID), Spring.GetUnitArmored(unitID))
end
end
local function toggleAARanges()
drawToggle = not drawToggle
local text = "off"
if not drawToggle then
text = 'on'
end
Spring.Echo('AA ranges toggled ' .. text)
end
function widget:Initialize()
myPlayerID = spGetLocalTeamID()
widgetHandler:AddAction("toggle_aa_ranges", toggleAARanges, nil, "p")
init()
end
function widget:KeyPress(key, modifier, isRepeat)
if key == keycode then
drawalpha = (drawalpha + alphaincrement) % alphamax
UpdateCircleList()
end
end
function widget:UnitEnteredLos(unitID, allyTeam)
UnitDetected(unitID, false, allyTeam)
end
function widget:UnitEnteredRadar(unitID, allyTeam)
if defences[unitID] then
local i
for i = 1, #defences[unitID].weapons do
defences[unitID].weapons[i].range = defences[unitID].weapons[i].originalrange
end
end
end
local function traceRay(x, y, z, tx, ty, tz)
local stepsize = 3
local dx = tx - x
local dy = ty - y
local dz = tz - z
local distance = math.sqrt(dx * dx + dy * dy + dz * dz)
if not (distance > stepsize) then return tx, ty, tz end
local iterations = distance / stepsize
local nx = dx / distance
local ny = dy / distance
local nz = dz / distance
local height
for i = 0, iterations do
x = x + nx * stepsize
y = y + ny * stepsize
z = z + nz * stepsize
height = Spring.GetGroundHeight(x, z)
if y < height then
return x, height, z
end
end
return tx, ty, tz
end
local function drawCircle(x, y, z, range, weaponheight, donttraceray)
if lowspec then donttraceray = true end
local altitude = 85
local list = { { x, y + weaponheight, z } }
local numSegments = 100
local angleStep = (2 * pi) / numSegments
local gy = Spring.GetGroundHeight(x, z)
for i = 0, numSegments do
local angle = i * angleStep
local rx = sin(angle) * range + x
local rz = cos(angle) * range + z
local dx = x - rx
local dz = z - rz
local len2d = math.sqrt(dx ^ 2 + dz ^ 2)
local splits = 30
local step = len2d / splits
dx = dx / len2d
dz = dz / len2d
local j = 0
local ry = Spring.GetGroundHeight(rx, rz) + altitude
while j < splits - 1 and not donttraceray do
local hx, hy, hz = traceRay(x, gy + weaponheight, z, rx, ry, rz)
if hx == rx and hy == ry and hz == rz then
j = splits + 1 -- exit
else
rx = rx + dx * step
rz = rz + dz * step
ry = Spring.GetGroundHeight(rx, rz) + altitude
j = j + 1
end
end
list[#list + 1] = { rx, ry, rz }
end
return list
end
local function ShouldEnd()
if drawMode == 2 then
return drawToggle
end
if drawMode == 3 then
return false
end
if fullview and not enabledAsSpec then
return true
end
if drawalpha == 0 then
return true
end
local selectedUnits = Spring.GetSelectedUnits()
local aircraftSelected = false
for _, uID in ipairs(selectedUnits) do
if UnitDefs[Spring.GetUnitDefID(uID)].canFly then
aircraftSelected = true
end
end
if (not aircraftSelected) and onlyShowWhenAircraftSelected then
return true
end
return false
end
function UnitDetected(unitID, allyTeam, teamId)
if allyTeam then return end
local unitDefID = spGetUnitDefID(unitID)
local x, y, z = spGetUnitPosition(unitID)
local uName = unitName[unitDefID]
if unitList[uName] == nil then return end
local foundWeapons = {}
for i = 1, unitNumWeapons[unitDefID] do
if unitList[uName]["weapons"][i] then
local weaponDef = weapTab[unitWeapons[unitDefID][i]]
local range = weaponDef.range --get normal weapon range
local type = unitList[uName]["weapons"][i]
local dam = weaponDef.damages
local dps, damage
local color = unitList[uName].color
color[4] = .2
dps = 0
damage = dam[Game.armorTypes.vtol]
if damage then
dps = damage * weaponDef.salvoSize / weaponDef.reload
end
-- color1 = GetColorsByTypeAndDps(dps, type, (allyTeam == false))
local weaponheight = unitList[uName].weaponheight or 63
local verts = drawCircle(x, y, z, range, weaponheight, type == 1)
foundWeapons[#foundWeapons + 1] = {
type = type,
range = range,
originalrange = range,
color1 = color,
unitID = unitID,
weaponnum = i,
weaponheight = weaponheight,
verts = verts,
x = x,
y = y,
z = z
}
end
end
defences[unitID] = {
allyState = (allyTeam == false),
pos = { x, y, z },
unitId = unitID,
mobile = canMove[unitDefID],
weapons = foundWeapons,
unitSpeed = unitSpeeds[unitDefID]
}
UpdateCircleList()
end
function ResetGl()
glColor({ 1.0, 1.0, 1.0, 1.0 })
glLineWidth(1.0)
end
function widget:PlayerChanged()
if myAllyTeam ~= Spring.GetMyAllyTeamID() or fullview ~= select(2, spGetSpectatingState()) then
myAllyTeam = Spring.GetMyAllyTeamID()
spec, fullview = spGetSpectatingState()
init()
end
end
local lastupdate = 0
local updateinterval = .6
function widget:Update()
if ShouldEnd() then
return
end
local time = spGetGameSeconds()
if time - lastupdate > updateinterval then
lastupdate = time
local didupdate = false
for k, def in pairs(defences) do
if def.mobile then
local ux, uy, uz = Spring.GetUnitPosition(def["unitId"])
for i = 1, #def.weapons do
local weapon = def.weapons[i]
local upd = false
if not uy then
weapon.range = weapon.range - def.unitSpeed * updateinterval
upd = true
else
if weapon.x ~= ux or weapon.y ~= uy or weapon.z ~= uz then
upd = true
end
weapon.x = ux
weapon.y = uy
weapon.z = uz
end
if upd then
didupdate = true
if weapon.range > 0 then
weapon.verts = drawCircle(weapon.x, weapon.y, weapon.z, weapon.range, weapon.weaponheight)
else
defences[k] = nil
end
end
end
end
local x, y, z = def["pos"][1], def["pos"][2], def["pos"][3]
local a, b, c = spGetPositionLosState(x, y, z)
local losState = b
if losState then
if not spGetUnitDefID(def["unitId"]) then
defences[k] = nil
didupdate = true
end
end
end
if didupdate then
UpdateCircleList()
end
end
end
local function BuildVertexList(verts)
for i, vert in pairs(verts) do
glVertex(vert)
end
end
function DrawRanges()
glDepthTest(false)
glTranslate(0, 0, 0) -- else it gets rendered below map sometimes
local color
local range
for test, def in pairs(defences) do
gl.PushMatrix()
for i, weapon in pairs(def["weapons"]) do
local execDraw = spIsSphereInView(def["pos"][1], def["pos"][2], def["pos"][3], weapon["range"])
if execDraw then
color = weapon["color1"]
range = weapon["range"]
gl.Blending("alpha_add")
if drawalpha > alphamax then
drawalpha = alphamax
end
glColor(color[1], color[2], color[3], drawalpha / 255)
gl.PolygonMode(GL.FRONT_AND_BACK, GL.FILL)
glBeginEnd(GL.TRIANGLE_FAN, BuildVertexList, weapon.verts)
end
end
gl.PopMatrix()
end
glDepthTest(true)
end
function UpdateCircleList()
--delete old list
if rangeCircleList then
glDeleteList(rangeCircleList)
end
rangeCircleList = glCreateList(function()
--create new list
DrawRanges()
ResetGl()
end)
end
function widget:RecvLuaMsg(msg, playerID)
if msg:sub(1, 18) == 'LobbyOverlayActive' then
chobbyInterface = (msg:sub(1, 19) == 'LobbyOverlayActive1')
end
end
function widget:DrawWorld()
if ShouldEnd() then
return
end
if chobbyInterface then return end
if not spIsGUIHidden() and (not WG['topbar'] or not WG['topbar'].showingQuit()) then
if rangeCircleList then
glCallList(rangeCircleList)
else
UpdateCircleList()
end
end
end
function widget:GetConfigData()
return {
alpha = drawalpha,
drawMode = drawMode,
}
end
function widget:SetConfigData(data)
if data.alpha ~= nil then
drawalpha = data.alpha
drawMode = data.drawMode
end
initSettings()
end
local firstFrame
function widget:GameFrame(n)
if not firstFrame then
firstFrame = n
end
if (n>firstFrame) and (not settingsInit) then
initSettings()
end
end