Advertisement
Guest User

Untitled

a guest
Jun 2nd, 2021
258
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.58 KB | None | 0 0
  1. include('keysym.h.lua')
  2.  
  3. function widget:GetInfo()
  4. return {
  5. name = "Graphic Unit Selector Redux2",
  6. desc = "Selects units when a user presses a certain key.",
  7. author = "Shaman, mod by Helwor, NCG. Almost total rewrite ivand. Almost total rewrite esainane.",
  8. date = "May, 2017",
  9. license = "None",
  10. layer = 15,
  11. enabled = false,
  12. }
  13. end
  14.  
  15. local Echo = Spring.Echo
  16. local TableEcho = Spring.Utilities.TableEcho
  17.  
  18. local glBillboard = gl.Billboard
  19. local glColor = gl.Color
  20. local glDrawGroundCircle = gl.DrawGroundCircle
  21. local glLineStipple = gl.LineStipple
  22. local glLineWidth = gl.LineWidth
  23. local glPushMatrix = gl.PushMatrix
  24. local glPopMatrix = gl.PopMatrix
  25. local glText = gl.Text
  26. local glTranslate = gl.Translate
  27.  
  28. local GetModKeyState = Spring.GetModKeyState
  29. local GetMouseState = Spring.GetMouseState
  30. local GetMyTeamID = Spring.GetMyTeamID
  31. local GetSpectatingState = Spring.GetSpectatingState
  32. local GetUnitDefID = Spring.GetUnitDefID
  33. local GetUnitPosition = Spring.GetUnitPosition
  34. local GetUnitsInCylinder = Spring.GetUnitsInCylinder
  35. local SelectUnitArray = Spring.SelectUnitArray
  36. local IsReplay = Spring.IsReplay
  37. local TraceScreenRay = Spring.TraceScreenRay
  38. local ValidUnitID = Spring.ValidUnitID
  39.  
  40. ------------------------------------------------------------------------------------------------------------
  41. --- CONFIG
  42. ------------------------------------------------------------------------------------------------------------
  43.  
  44. local useIndividualHaloColors = true
  45. local requireMouseCommit = false
  46.  
  47. local orderOfClass = {
  48. Constructor = 1,
  49. Raider = 2,
  50. Skirmisher = 3,
  51. Riot = 4,
  52. Assault = 5,
  53. Artillery = 6,
  54. ["Weird Raider"] = 7, -- Also heavy raider
  55. ["Anti Air"] = 8,
  56. Fat = 9,
  57. Artificier = 10,
  58. Utility = 11,
  59. tiny = 12,
  60. BOOM = 13,
  61. Druid = 14,
  62. }
  63.  
  64. local keysByClass = {
  65. Constructor = KEYSYMS.Z,
  66. Raider = KEYSYMS.Q,
  67. Skirmisher = KEYSYMS.F,
  68. Riot = KEYSYMS.R,
  69. Assault = KEYSYMS.T,
  70. Artillery = KEYSYMS.G,
  71. ["Weird Raider"] = KEYSYMS.E,
  72. ["Anti Air"] = KEYSYMS.N,
  73. Fat = KEYSYMS.Y,
  74. Artificier = KEYSYMS.H,
  75. Utility = KEYSYMS.X,
  76. tiny = KEYSYMS.C,
  77. BOOM = KEYSYMS.V,
  78. Druid = KEYSYMS.B
  79. }
  80.  
  81. local classByKeys = {}
  82. for k, v in pairs(keysByClass) do
  83. classByKeys[v] = k
  84. end
  85.  
  86. local colorByClass = {
  87. Constructor = {1,1,1},
  88. Raider = {0,0.83,0.85},
  89. Skirmisher = {0.35,0.87,0.23},
  90. Riot = {0.86,0.28,0.34},
  91. Assault = {0.25,0.41,1},
  92. Artillery = {0,0,1},
  93. -- next row
  94. ["Weird Raider"] = {0.27,0.79,0.88},
  95. ["Anti Air"] = {0.34,0.59,.9},
  96. Fat = {1,.23,.8},
  97. Artificier = {0,0,0},
  98. Utility = {0.75,0.41,1},
  99. tiny = {0,1,1},
  100. BOOM = {1,0,0},
  101. Druid = {0,1,0},
  102.  
  103. --growth = {0.19,0.5,0.08},
  104. --shrink = {1,0.08,0.58},
  105. }
  106. local colorByKey = {}
  107. for k,v in pairs(colorByClass) do
  108. colorByKey[keysByClass[k]] = v
  109. end
  110.  
  111. local keysAux={
  112. plus = KEYSYMS.KP6,
  113. minus = KEYSYMS.KP4,
  114. }
  115.  
  116. local auxByKey = {}
  117. for k, v in pairs(keysAux) do
  118. auxByKey[v] = k
  119. end
  120.  
  121. local rad = 550
  122.  
  123.  
  124. local unitTypes = {
  125. Constructor = {
  126. cloakcon = true,
  127. shieldcon = true,
  128. vehcon = true,
  129. hovercon = true,
  130. gunshipcon = true,
  131. planecon = true,
  132. spidercon = true,
  133. jumpcon = true,
  134. tankcon = true,
  135. amphcon = true,
  136. shipcon = true,
  137. },
  138. Raider = {
  139. cloakraid = true,
  140. shieldraid = true,
  141. hoverraid = true,
  142. amphraid = true,
  143. shiptorpraider = true,
  144. },
  145. Skirmisher = {
  146. cloakskirm = true,
  147. cloaksnipe = true,
  148. shieldskirm = true,
  149. hoverskirm = true,
  150. spiderskirm = true,
  151. jumpskirm = true,
  152. amphfloater = true,
  153. shipskirm = true,
  154. jumpblackhole = true,
  155. shieldfelon = true,
  156. vehcapture = true,
  157. },
  158. Riot = {
  159. cloakriot = true,
  160. shieldriot = true,
  161. shieldfelon = true,
  162. vehriot = true,
  163. hoverriot = true,
  164. hoverdepthcharge = true,
  165. spiderriot = true,
  166. tankriot = true,
  167. amphriot = true,
  168. },
  169. Assault = {
  170. cloakassault = true,
  171. shieldassault = true,
  172. vehassault = true,
  173. hoverassault = true,
  174. spiderassault = true,
  175. jumpassault = true,
  176. tankheavyraid = true,
  177. tankassault = true,
  178. shipassault = true,
  179.  
  180. },
  181. Artillery = {
  182. cloakarty = true,
  183. cloaksnipe = true,
  184. shieldarty = true,
  185. veharty = true,
  186. gunshipskirm = true,
  187. gunshipheavyskirm = true,
  188. jumparty = true,
  189. shiparty = true,
  190. },
  191. ["Weird Raider"] = {
  192. cloakheavyraid = true,
  193. subraider = true,
  194. hoverheavyraid = true,
  195. spideremp = true,
  196. tankraid = true,
  197. amphimpulse = true,
  198. shipriot = true,
  199. vehraid = true,
  200. gunshipraid = true,
  201. jumpraid = true,
  202. },
  203. ["Anti Air"] = {
  204. cloakaa = true,
  205. shieldaa = true,
  206. vehaa = true,
  207. hoveraa = true,
  208. gunshipaa = true,
  209. spideraa = true,
  210. jumpaa = true,
  211. tankaa = true,
  212. amphaa = true,
  213. shipaa = true,
  214. },
  215. Fat = {
  216. jumpsumo = true,
  217. tankheavyassault = true,
  218. amphassault = true,
  219. striderbantha = true,
  220. striderdetriment = true,
  221. striderdante = true,
  222. striderscorpion = true,
  223. },
  224. Artificier = {
  225. --spidercrabe = true,
  226. amphsupport =true,
  227. hoverarty = true,
  228. shipheavyarty = true,
  229. striderarty = true,
  230. tankarty = true,
  231. },
  232. Utility = {
  233. cloakjammer = true,
  234. shieldshield = true,
  235. gunshiptrans = true,
  236. amphtele = true,
  237. vehheavyarty = true,
  238. --spidercrabe = true,
  239. vehsupport = true,
  240. },
  241. tiny = {
  242. spiderscout = true,
  243. shieldscout = true,
  244. gunshipemp = true,
  245. jumpscout = true,
  246. vehscout = true,
  247. },
  248. BOOM = {
  249. cloakbomb = true,
  250. shieldbomb = true,
  251. gunshipbomb = true,
  252. amphbomb = true,
  253. jumpbomb = true,
  254. },
  255. Druid = {
  256. amphlaunch = true,
  257. shipcarrier = true,
  258. subtacmissile = true,
  259. spiderantiheavy = true,
  260. tankheavyarty = true,
  261. spidercrabe = true,
  262. },
  263. }
  264.  
  265. local keyByUnitDefID = {}
  266. for class,entries in pairs(unitTypes) do
  267. local key = keysByClass[class]
  268. for unitDefName,_ in pairs(entries) do
  269. local ud = UnitDefNames[unitDefName]
  270. if not ud then
  271. Echo('Could not find', unitDefName)
  272. end
  273. --Echo(ud, unitDefName, '->', key)
  274. keyByUnitDefID[ud.id] = key
  275. end
  276. end
  277.  
  278. --[[
  279. for unitDefID,unitDef in ipairs(UnitDefs) do
  280. if keyByUnitDefID[unitDefID] == nil and not string.match(UnitDefs[unitDefID].name, "dyn") and not string.match(UnitDefs[unitDefID].name, "comm_") and not string.match(UnitDefs[unitDefID].name, "chicken") and not string.match(UnitDefs[unitDefID].name, "pw_") then
  281. Echo('Warning, no type set for', unitDef.name)
  282. end
  283. end
  284. --]]
  285.  
  286. ------------------------------------------------------------------------------------------------------------
  287. --- END OF CONFIG
  288. ------------------------------------------------------------------------------------------------------------
  289.  
  290. function widget:Initialize()
  291. --Unload if in replay or if mod is not Zero-K
  292. if IsReplay() then
  293. widgetHandler:RemoveWidget(widget)
  294. end
  295. end
  296.  
  297. function widget:GameOver(winningAllyTeams)
  298. --GameOver is irreversable with cheats, thus removing
  299. widgetHandler:RemoveWidget(widget)
  300. end
  301.  
  302. local myTeamID = GetMyTeamID()
  303. local unloaded = false
  304. local function CheckIfSpectator()
  305. if GetSpectatingState() then
  306. myTeamID = nil
  307. else
  308. myTeamID = GetMyTeamID()
  309. end
  310. --spectator state is reversable with cheats
  311. -- Lets see what its like as a spectator
  312. --[[if GetSpectatingState() then
  313. widgetHandler:RemoveCallIn("KeyPress")
  314. widgetHandler:RemoveCallIn("KeyRelease")
  315. widgetHandler:RemoveCallIn("Update")
  316. widgetHandler:RemoveCallIn("DrawWorld")
  317. unloaded = true
  318. else
  319. if unloaded then
  320. widgetHandler:UpdateCallIn("KeyPress")
  321. widgetHandler:UpdateCallIn("KeyRelease")
  322. widgetHandler:UpdateCallIn("Update")
  323. widgetHandler:UpdateCallIn("DrawWorld")
  324. unloaded = false
  325. end
  326. end--]]
  327. end
  328.  
  329. function widget:TeamChanged(teamID)
  330. CheckIfSpectator()
  331. end
  332.  
  333. function widget:PlayerChanged(playerID)
  334. CheckIfSpectator()
  335. end
  336.  
  337. function widget:PlayerAdded(playerID)
  338. CheckIfSpectator()
  339. end
  340.  
  341. function widget:PlayerRemoved(playerID)
  342. CheckIfSpectator()
  343. end
  344.  
  345. function widget:TeamDied(teamID)
  346. CheckIfSpectator()
  347. end
  348.  
  349. function widget:TeamChanged(teamID)
  350. CheckIfSpectator()
  351. end
  352.  
  353. local currentSelection = {}
  354.  
  355. function widget:SelectionChanged(selection)
  356. currentSelection = selection
  357. end
  358.  
  359. local function CompareButtonOrder(l,r)
  360. return orderOfClass[l] < orderOfClass[r]
  361. end
  362.  
  363. -- Key -> true | nil
  364. local selectingKeys = {}
  365. local activeSelectionKeys = 0
  366.  
  367. local selectionStrings
  368. local selectionColors
  369. local function activeKeysChanged()
  370. selectionStrings = {}
  371. selectionColors = {}
  372. for k,_ in pairs(selectingKeys) do
  373. selectionStrings[#selectionStrings + 1] = classByKeys[k]
  374. selectionColors[#selectionColors+1] = colorByKey[k]
  375. end
  376. table.sort(selectionStrings, CompareButtonOrder)
  377. end
  378.  
  379. local isPregame = true
  380. function widget:GameFrame(n)
  381. if n > 2 then
  382. isPregame = false
  383. widgetHandler:RemoveCallIn("GameFrame")
  384. end
  385. end
  386.  
  387. -- {unitID} -> key | nil
  388. local pendingSelection = {}
  389. -- [unitID]
  390. local pendingSelectionArray = {}
  391.  
  392. local acc = 0
  393. function widget:KeyPress(key, mods, isRepeat)
  394. if isPregame then return end -- pregame effectively has your starting unit "selected"
  395. if selectingKeys[key] then return end -- No dupes
  396. if mods.alt then return end -- we want to be able to quickly select factories with no selection
  397. if mods.ctrl then return end -- also many global commands
  398. if auxByKey[key] then
  399. if key == keysAux.plus then
  400. rad = math.min(3000, rad+100)
  401. end
  402.  
  403. if key == keysAux.minus then
  404. rad = math.max(100, rad-100)
  405. end
  406. elseif not classByKeys[key]then
  407. -- Not a key we're interested in
  408. return
  409. elseif activeSelectionKeys == 0 then
  410. if #currentSelection ~= 0 then
  411. -- If we aren't already selecting types, and we already have an existing selection, do nothing - we'd more likely want these keys to issue orders to them.
  412. return
  413. end
  414. -- New selection! How exciting.
  415. acc = 0
  416. end
  417. selectingKeys[key] = true
  418. activeSelectionKeys = activeSelectionKeys + 1
  419. activeKeysChanged()
  420. return true
  421. end
  422.  
  423. local function DoCommitSelection()
  424. local _, _, _, shiftKey = GetModKeyState()
  425. if #pendingSelectionArray then
  426. SelectUnitArray(pendingSelectionArray, shiftKey)
  427. end
  428. table.clear(pendingSelection)
  429. table.clear(pendingSelectionArray)
  430. end
  431.  
  432. function widget:KeyRelease(key) -- Called whenever user stops pressing a key.
  433. if not selectingKeys[key] then return end -- Not a key we're using
  434. selectingKeys[key] = nil
  435. activeSelectionKeys = activeSelectionKeys - 1
  436. activeKeysChanged()
  437. if activeSelectionKeys > 0 then return true end -- Still selecting more types with other keys
  438. -- We've released all type selection buttons. Commit the pending selection.
  439. if not requireMouseCommit then
  440. DoCommitSelection()
  441. end
  442. return true
  443. end
  444.  
  445. local mouseAdding = false
  446.  
  447. function widget:MousePress(x,y,btn)
  448. if btn == 1 and activeSelectionKeys > 0 then
  449. -- Start adding
  450. mouseAdding = true
  451. elseif btn == 3 and mouseAdding then
  452. -- Cancel adding
  453. mouseAdding = false
  454. table.clear(pendingSelection)
  455. table.clear(pendingSelectionArray)
  456. else
  457. -- Don't interfere with dragging or other potential hotkeys
  458. return false
  459. end
  460. return true
  461. end
  462.  
  463. function widget:MouseRelease(x,y,btn)
  464. if btn ~= 1 or not mouseAdding then return end
  465. mouseAdding = false
  466. DoCommitSelection()
  467. end
  468.  
  469. local function GetCameraHeight()
  470. local cs = Spring.GetCameraState()
  471. local gy = Spring.GetGroundHeight(cs.px, cs.pz)
  472. local testHeight = cs.py - gy
  473. if cs.name == "ov" then
  474. testHeight = WG.AllUnitIcon.iconheight * 2
  475. elseif cs.name == "ta" then
  476. testHeight = cs.height - gy
  477. end
  478. return testHeight
  479. end
  480.  
  481. local radAdj = 1
  482. local x, y, z
  483. local unitsPos = {}
  484. function widget:Update(dt)
  485. if not (activeSelectionKeys or (requireMouseCommit and mouseAdding)) then return end
  486. acc = acc + dt
  487. local mouseX, mouseY = GetMouseState()
  488. local desc, mpos = TraceScreenRay(mouseX, mouseY, true)
  489. radAdj = GetCameraHeight() / 6000
  490. -- Is the mouse over the map?
  491. if desc ~= nil then
  492. -- Save its position
  493. x, y, z = unpack(mpos)
  494.  
  495. -- If we're currently holding down left mouse, or if we don't require mouse clicks to add units, add all units in the area
  496. if activeSelectionKeys and (mouseAdding or not requireMouseCommit) then
  497. for _, uID in ipairs(GetUnitsInCylinder(x, z, rad * radAdj, myTeamID)) do
  498. -- Make sure not to add units multiple times
  499. if not pendingSelection[uID] then
  500. local key = keyByUnitDefID[GetUnitDefID(uID)]
  501. -- If this is the sort of unit we're looking for, add it
  502. if key and selectingKeys[key] then
  503. pendingSelection[uID] = key
  504. pendingSelectionArray[#pendingSelectionArray+1] = uID
  505. end
  506. end
  507. end
  508. end
  509. else
  510. x = nil
  511. end
  512. -- We can continue to update unit halo information even when the mouse is off the world
  513. table.clear(unitsPos)
  514. for _,uID in ipairs(pendingSelectionArray) do
  515. if ValidUnitID(uID) then
  516. local pos = {GetUnitPosition(uID)}
  517. unitsPos[uID] = pos
  518. end
  519. end
  520. end
  521.  
  522. local lineHeight = 80
  523. local textSize = 66
  524. local textX = 85
  525.  
  526. function widget:DrawWorld() -- this is used for openGL stuff.
  527. if (activeSelectionKeys == 0 and not (requireMouseCommit and mouseAdding)) or not x then return end
  528. local color = activeSelectionKeys == 0 and {.46,.41,.41} or selectionColors[1 + ((math.floor(acc * 5)) % #selectionColors)]
  529. glPushMatrix() --This is the start of an openGL function.
  530. glLineStipple(true)
  531. glLineWidth(2.0)
  532. glTranslate(x, y, z)
  533. glColor(color[1], color[2], color[3], 1)
  534. glDrawGroundCircle(0, 0, 0, rad * radAdj, 25) -- draws a simple circle.
  535. glColor(1,1,1,1)
  536. glBillboard()
  537. local description = "AH"
  538. if mouseAdding or not requireMouseCommit then
  539. description = "OH"
  540. end
  541. local textY = (#selectionStrings * lineHeight * .5 + 25)
  542. glText(description, textX, textY, textSize, "v") -- Displays text. First value is the string, second is a modifier for x (in this case it's x-25), third is a modifier for y, fourth is the size, then last is a modifier for the text itself. "v" means vertical align.
  543. for i=1,#selectionStrings do
  544. local text = selectionStrings[i]
  545. color = colorByClass[text]
  546. glColor(color[1], color[2], color[3], 1)
  547. textY = textY - lineHeight
  548. glText(text, textX, textY, textSize, "v")
  549. end
  550. glColor(1, 1, 1, 1) -- we have to reset what we did here.
  551. glLineWidth(1.0)
  552. glLineStipple(false)
  553. glPopMatrix() -- end of function. Have to use this with after a push!
  554.  
  555. glPushMatrix()
  556. glLineWidth(3.0)
  557. for uID, key in pairs(pendingSelection) do
  558. local pos = unitsPos[uID]
  559. if pos ~= nil then
  560. if useIndividualHaloColors then
  561. color = colorByKey[key]
  562. end
  563. glColor(color[1], color[2], color[3], 0.4)
  564. local px,py,pz = unpack(pos)
  565. glDrawGroundCircle(px, py, pz, 60, 60)
  566. end
  567. end
  568. glColor(1, 1, 1, 1)
  569. glPopMatrix()
  570. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement