Snippet #156876

TTL: forever — WordwrapView raw

on 2022/12/12 15:56:20 (UTC) by Anonymous as Lua

  1. -- tables {} are the main element to store information in Lua
  2. -- widget is a table (a box containing pair of key = value) similar to any other table
  3. local version = 0.1
  4. function widget:GetInfo() -- this is a method, pretty much like a function with an hidden parameter (self) refering to the table where the function is stored
  5.     -- this method will return a table whenever it is called, widget:GetInfo()
  6.     return {
  7.         name      = "Area Select",
  8.         desc      = "select units by class around cursor, sample widget for learning, ver " .. version ,
  9.         author    = "Helwor",
  10.         date      = "Dec 2022",
  11.         license   = "free",
  12.         layer     = 0, -- the layer is the position in which this widget will be loaded compared to other widget's layer, layer can be negative
  13.         enabled   = false,  --  loaded by default?
  14.         -- handler   = true, -- to have access to the real widgetHandler, we don't need it in this case
  15.     }
  16. end
  17.  
  18. local debugNames = false -- set this to true to reveal the names of units in the console
  19. local debugKey = false -- set this to true if you want to reveal in console which key code correspond to the key pressed
  20.  
  21.  
  22. -- any widgets got access to some global variable, we used to declare local variable  to access those globals in order to access them faster during runtime
  23. -- we localize the following needed functions
  24.  
  25. local spGetMyTeamID = Spring.GetMyTeamID
  26. local spGetUnitsInCylinder = Spring.GetUnitsInCylinder
  27. local spGetUnitDefID = Spring.GetUnitDefID -- each unit got a unique defID that tell which unit type they are, 
  28. local spGetMouseState = Spring.GetMouseState
  29. local spTraceScreenRay = Spring.TraceScreenRay
  30. local spSelectUnitMap = Spring.SelectUnitMap
  31. local spValidUnitID = Spring.ValidUnitID
  32. local spGetUnitIsDead = Spring.GetUnitIsDead
  33. local spGetUnitPosition = Spring.GetUnitPosition
  34. local min = math.min
  35. local max = math.max
  36. local UnitDefs = UnitDefs
  37.  
  38. local Echo = Spring.Echo -- this is used for debugging, calling Echo('Hello') will write 'Hello' in the infolog and on the console log ingame
  39.  
  40. -- following will be used for drawing
  41. local glLineWidth = gl.LineWidth
  42. local glLineStipple = gl.LineStipple
  43. local glPushMatrix = gl.PushMatrix
  44. local glPopMatrix = gl.PopMatrix
  45. local glDrawGroundCircle = gl.DrawGroundCircle
  46. local glColor = gl.Color
  47. local glText = gl.Text
  48. --
  49.  
  50. -- we create a table as a catalog of names stored for each classe
  51. -- NOTE: those are my own definition which might not be adequat for you, so you might want to change it
  52. -- NOTE: you could create another class called 'scout' to differentiate scout from raider
  53. local unitClasses = {
  54.     raider = {
  55.         --planefighter = true,
  56.  
  57.         shipscout = true,
  58.         shiptorpraider = true,
  59.         spiderscout = true,
  60.         shieldscout = true,
  61.         cloakraid = true,
  62.         shieldraid = true,
  63.         vehraid = true,
  64.         amphraid = true,
  65.         vehscout = true,
  66.         jumpraid = true,
  67.         hoverraid = true,
  68.         subraider = true,
  69.         tankraid = true,
  70.         gunshipraid = true,
  71.         gunshipemp = true,      
  72.         jumpscout = true,
  73.         tankheavyraid = true,
  74.  
  75.     },
  76.     skirm = {
  77.         cloakskirm = true,
  78.         spiderskirm = true,
  79.         jumpskirm = true,
  80.         shieldskirm = true,
  81.         shipskirm = true,
  82.         amphfloater = true,
  83.         vehsupport = true,
  84.         gunshipskirm = true,
  85.         shieldfelon = true,
  86.         hoverskirm = true,
  87.         hoverdepthcharge = true,
  88.     },
  89.     riot = {
  90.         amphimpulse = true, 
  91.         cloakriot = true,
  92.         shieldriot = true,
  93.         spiderriot = true,
  94.         spideremp = true,
  95.         jumpblackhole = true,
  96.         vehriot = true,
  97.         tankriot = true,
  98.         amphriot = true,
  99.         shiptorpraider = true,
  100.         hoverriot = true,
  101.         gunshipassault = true,
  102.         shipriot = true,
  103.         striderdante = true,
  104.     },
  105.     assault = {
  106.         jumpsumo = true,
  107.         cloakassault = true,
  108.         spiderassault = true,
  109.         tankheavyassault = true,
  110.         tankassault = true,
  111.         shipassault = true,
  112.         amphassault = true,
  113.         vehassault = true,
  114.         shieldassault = true,
  115.         jumpassault = true,
  116.         hoverassault = true,
  117.         hoverheavyraid = true,
  118.         shipassault = true,
  119.         --bomberprec = true,
  120.         --bomberheavy = true,
  121.         gunshipkrow = true,
  122.         striderdetriment = true,
  123.     },
  124.     arty = {
  125.         cloakarty = true,
  126.         amphsupport = true,
  127.         striderarty = true,
  128.         shieldarty = true,
  129.         jumparty = true,
  130.         veharty = true,
  131.         tankarty = true,
  132.         spidercrabe = true,
  133.         shiparty = true,
  134.         shipheavyarty = true,
  135.         shipcarrier = true,
  136.         hoverarty = true,
  137.         gunshipheavyskirm = true,
  138.         tankheavyarty = true,
  139.         vehheavyarty = true,
  140.     },
  141.     special1 = {
  142.         cloakheavyraid = true,
  143.         vehcapture = true,    
  144.         spiderantiheavy = true,   
  145.         shieldshield = true,
  146.         cloakjammer = true,
  147.         --planescout = true,
  148.     },
  149.     special2 = {
  150.         gunshiptrans = true,    
  151.         shieldbomb = true,
  152.         cloakbomb = true,
  153.         gunshipbomb = true,
  154.         jumpbomb = true,
  155.         gunshipheavytrans = true,
  156.         subtacmissile = true,
  157.         spiderscout = true,
  158.         amphtele = true,
  159.         --bomberdisarm = true,
  160.         striderantiheavy = true,
  161.         striderscorpion = true,
  162.     },
  163.     special3 = {
  164.         cloaksnipe = true,
  165.         amphlaunch = true,
  166.         --planescout = true,
  167.     },
  168.     aa = {
  169.         gunshipaa = true,
  170.         shieldaa = true,
  171.         cloakaa = true,
  172.         vehaa = true,
  173.         hoveraa = true,
  174.         amphaa = true,
  175.         spideraa = true,
  176.         jumpaa = true,
  177.         tankaa = true,
  178.         shipaa = true,
  179.     },
  180.     con = {
  181.         amphcon = true,
  182.         planecon = true,
  183.         cloakcon = true,
  184.         spidercon = true,
  185.         jumpcon = true,
  186.         tankcon = true,
  187.         hovercon = true,
  188.         shieldcon = true,
  189.         vehcon = true,
  190.         gunshipcon = true,
  191.         shipcon = true,
  192.         planecon = true,
  193.         striderfunnelweb = true,
  194.     },
  195.  
  196. }
  197. -- this catalog is like this for the purpose of the user to edit it easily
  198. -- but in order to do the less work possible during runtime, we find out which defID correspond to which unit type and store them in a table
  199. -- for this we use a for loop that will iterate through the table unitClasses, giving us each pair name of class = class table
  200. -- and we iterate each of those class table to get names in it
  201. -- in that way we know which name belong to which class, and we note their defID in another table called classByDefID
  202. -- defID are unique identifiers for each unit type, they are part of the unit definitions, those defs are stored in globals UnitDefs and UnitDefNames
  203. -- this table will make it pretty fast to check which unit belong to which class during runtime
  204. --, as we will just have to ask Spring to give us the defID of the unit and we will know immediately which class is it thanks to this table
  205. local classByDefID = {}
  206. for className,classTable in pairs(unitClasses) do
  207.     for unitName in pairs(classTable) do
  208.         if UnitDefNames[unitName] then
  209.             local defID = UnitDefNames[unitName].id
  210.             classByDefID[defID]=className
  211.         else
  212.             Echo(unitName .. ' not found')
  213.         end
  214.     end
  215. end
  216. -- once that table is done, we don't need anymore the unitClasses table
  217. unitClasses = nil
  218.  
  219. -- here we define for which keys belong which class
  220. -- name of class here must correspond exactly to name of class in unitClasses
  221. -- NOTE: put your own hotkeys and complete this, not all class are mentionned
  222. -- N_1... represent '1' in main part of US keyboard
  223. local myHotkeys = {
  224.     N_1 = 'raider',
  225.     N_2 = 'skirm',
  226.     N_3 = 'riot',
  227.     N_4 = 'assault',
  228.     N_5 = 'arty',
  229.     N_6 = 'aa'
  230. }
  231.  
  232. -- now we gonna translate those hotkeys into char code through a loop, because when we get called in via KeyPress, we receive char code
  233. -- for this we need to use KEYSYMS table which is a table of translation human->char code and is written in another lua file
  234. -- including this file in our code will give us the table KEYSYMS
  235. include('keysym.lua')
  236.  
  237. -- now we create a table containing paired char code = class name, in the same manner we did for defID of classes earlier
  238. local myHotkeyCodes = {}
  239.  
  240. for key,class in pairs(myHotkeys) do
  241.     local code = KEYSYMS[key]
  242.     if code then
  243.         myHotkeyCodes[code] = class
  244.     else
  245.         Echo(key ..  'is not known in KEYSYMS')
  246.     end
  247. end
  248. -- once done, we can get rid of myHotkeys
  249. myHotkeys = nil
  250.  
  251. -- setting up color for classes
  252. -- I set a bunch of colors I use myself there
  253. local colors = {
  254.      white          = { 1.0,    1,    1, 1.0 },
  255.      black          = { 0.0,    0,    0, 1.0 },
  256.      grey           = { 0.5,  0.5,  0.5, 1.0 },
  257.      red            = { 1.0, 0.25, 0.25, 1.0 },
  258.      darkred        = { 0.8,    0,    0, 1.0 },
  259.      lightred       = {   1,  0.6,  0.6, 1.0 },
  260.      magenta        = { 1.0, 0.25,  0.3, 1.0 },
  261.      rose           = { 1.0,  0.6,  0.6, 1.0 },
  262.      bloodyorange   = { 1.0, 0.45,    0, 1.0 },
  263.      orange         = { 1.0,  0.7,    0, 1.0 },
  264.      darkgreen      = { 0.0,  0.6,    0, 1.0 },
  265.      green          = { 0.0,    1,    0, 1.0 },
  266.      lightgreen     = { 0.5,    1,  0.5, 1.0 },
  267.      lime           = { 0.5,    1,    0, 1.0 },
  268.      blue           = { 0.3, 0.35,    1, 1.0 },
  269.      turquoise      = { 0.3,  0.7,    1, 1.0 },
  270.      lightblue      = { 0.7,  0.7,    1, 1.0 },
  271.      yellow         = { 1.0,    1,  0.3, 1.0 },
  272.      cyan           = { 0.3,    1,    1, 1.0 },
  273.      brown          = { 0.9, 0.75,  0.3, 1.0 },
  274.      purple         = { 0.9,    0,  0.7, 1.0 },
  275.      softviolet     = { 1.0, 0.25,    1, 1.0 },
  276.      violet         = { 1.0,  0.4,    1, 1.0 },
  277. }
  278. -- here you can set different color for each class
  279. local classColor = {
  280.     raider = colors.yellow,
  281.     skirm = colors.cyan,
  282.     riot = colors.red,
  283.     assault = colors.orange,
  284.     arty = colors.green,
  285.     aa = colors.lightblue,
  286.     con = colors.white,
  287.     special1 = colors.brown,
  288.     special2 = colors.rose,
  289.     special3 = colors.turquoise,
  290. }
  291.  
  292.  
  293. ------------------
  294. ----- PROCESSING
  295. ----- we have set every static info, now the dynamic part
  296. -----------------
  297.  
  298. -- variables 
  299. local myTeamID = Spring.GetMyTeamID() -- our team ID can change during the game
  300. local classCalled = false -- the class name called by press of key 
  301. local mySelection = {} -- table storing the units we gonna select
  302. local radius = 650 -- radius of area
  303. local x,y,z = 0,0,0 -- position of mouse in the world
  304. local vsx, vsy -- size of game window
  305.  
  306. ---------- updating teamID
  307. local MyNewTeamID = function()
  308.     myTeamID = Spring.GetMyTeamID()
  309. end
  310. -- all those callin will run the same function that update our team ID
  311. widget.TeamChanged = MyNewTeamID
  312. widget.PlayerChanged = MyNewTeamID
  313. widget.Playeradded = MyNewTeamID
  314. widget.PlayerRemoved = MyNewTeamID
  315. widget.TeamDied = MyNewTeamID
  316. ----------
  317.  
  318.  
  319.  
  320. -- we make two separate function, one for gathering the units under our area of cursor, the other to effectively select the units
  321. local UpdateFutureSelection = function()
  322.     -- collecting units in area
  323.     local mx,my = spGetMouseState() -- we get the coords of the mouse on screen
  324.     local _,pos = spTraceScreenRay(mx,my,true,false,false,false) -- translate the screen position into world position
  325.     if not pos then
  326.         return
  327.     end
  328.     x,y,z = unpack(pos)
  329.     local units = spGetUnitsInCylinder(x,z,radius,myTeamID)
  330.     -- we keep only units that are of the chosen class
  331.     for i,id in ipairs(units) do
  332.         if spValidUnitID(id) and not spGetUnitIsDead(id) then
  333.             if not mySelection[id] then
  334.                 local defID = spGetUnitDefID(id)
  335.                 if classByDefID[defID] == classCalled then
  336.                     mySelection[id] = true
  337.                 end
  338.                 if debugNames then
  339.                     Echo(id,'unit name: '..UnitDefs[defID].name)
  340.                 end
  341.             end
  342.         end
  343.     end
  344. end
  345. local Select = function()
  346.     for id in pairs(mySelection) do
  347.         if not spValidUnitID(id) or spGetUnitIsDead(id) then
  348.             mySelection[id] = nil
  349.         end
  350.     end
  351.     if next(mySelection) then
  352.         spSelectUnitMap(mySelection)
  353.     end
  354. end
  355.  
  356.  
  357. -- the Engine Spring will call us when a key is pressed if we declare the method KeyPress
  358. -- same for release with KeyRelease
  359. -- that way we can do some work if we recognize our hotkey has been pressed
  360. function widget:KeyPress(key,mods,isRepeat)
  361.     if isRepeat then
  362.         return
  363.     end
  364.     if debugKey then
  365.         Echo('key pressed: '.. key)
  366.     end
  367.     classCalled = myHotkeyCodes[key]
  368.     if classCalled then
  369.         mySelection = {}
  370.         UpdateFutureSelection()
  371.         return true -- returning true on this will block the normal action of that key
  372.     end
  373. end
  374.  
  375. function widget:KeyRelease(key,mods)
  376.     if classCalled then
  377.         Select()
  378.         classCalled = false
  379.     end
  380. end
  381.  
  382. -- changing the radius of area with mouse wheel
  383. function widget:MouseWheel(up,value)
  384.     if not classCalled then
  385.         return
  386.     end
  387.     if up then
  388.         radius = min(3000, radius*(1+0.1*value))    
  389.     else
  390.         radius = max(40, radius*(1+0.1*value))      
  391.     end
  392.     return true
  393. end
  394.  
  395.  
  396.  
  397.  
  398. -- Update call-in is called everytime the game is redrawn (afaik), we use this call in to recheck which units fall into our area around the cursor
  399. function widget:Update()
  400.     if not classCalled then
  401.         return
  402.     end
  403.     UpdateFutureSelection()
  404. end
  405.  
  406.  
  407. -- DRAWING
  408. -- in DrawScreen and DrawWorld call-in, we can run some drawing function
  409. function widget:DrawWorld()
  410.     if not classCalled then
  411.         return
  412.     end
  413.     glColor(classColor[classCalled] or colors.white)
  414.     glLineStipple(true)
  415.     glLineWidth(1.5)
  416.     -- draw the circle of area selection
  417.     glPushMatrix()
  418.     glDrawGroundCircle(x, y, z, radius, 40) -- draws a simple circle on the ground.
  419.     glPopMatrix()
  420.     -- draw little circles around each unit about to get selected
  421.     for id in pairs(mySelection) do
  422.         if spValidUnitID(id) and not spGetUnitIsDead(id) then   
  423.             local ux,_,uz,_,uy = spGetUnitPosition(id,true)
  424.             glPushMatrix()
  425.             glDrawGroundCircle(ux, uy, uz, 40, 40)
  426.             glPopMatrix()
  427.         else
  428.             mySelection[id] = nil
  429.         end
  430.     end
  431.     -- after drawing round, we set everything to default
  432.     glLineWidth(1)
  433.     glColor(1,1,1,1)
  434.     glLineStipple(false)
  435. end
  436. function widget:GetViewSizes(x,y)
  437.     vsx,vsy = x,y
  438. end
  439.  
  440. function widget:DrawScreen()
  441.     if not classCalled then
  442.         return
  443.     end
  444.     glColor(classColor[classCalled] or colors.white)
  445.     glText(classCalled, vsx/2 - 100,150, 25)
  446.     glColor(1,1,1,1)
  447. end
  448.  
  449.  
  450.  
  451. -- this call-in is called once at the loading sequence of all widgets
  452. function widget:Initialize()
  453.     vsx,vsy = Spring.GetViewSizes()
  454. end
  455.  
  456.  
  457. -- memorize the radius size over games
  458.  
  459. function widget:SetConfigData(data)
  460.     if not data.area_select_radius then
  461.         return
  462.     end
  463.     -- update 'radius' with saved value
  464.     radius = data.area_select_radius
  465. end
  466.  
  467. function widget:GetConfigData()
  468.     -- save the radius value on widget exit
  469.     return {area_select_radius = radius}
  470. end

Recent Snippets