1--------------------------------------------------------------------------------
2--------------------------------------------------------------------------------
3--
4--  file:    gui_highlight_unit.lua
5--  brief:   highlights the unit/feature under the cursor
6--  author:  Dave Rodgers
7--
8--  Copyright (C) 2007.
9--  Licensed under the terms of the GNU GPL, v2 or later.
10--
11--------------------------------------------------------------------------------
12--------------------------------------------------------------------------------
13
14function widget:GetInfo()
15  local grey   = "\255\192\192\192"
16  local yellow = "\255\255\255\128"
17  return {
18    name      = "HighlightUnit",
19    desc      = "Highlights the unit or feature under the cursor\n"..
20                grey.."Hold "..yellow.."META"..grey..
21                " to show the unit or feature name",
22    author    = "trepan",
23    date      = "Apr 16, 2007",
24    license   = "GNU GPL, v2 or later",
25    layer     = 5,
26    enabled   = true  --  loaded by default?
27  }
28end
29
30--------------------------------------------------------------------------------
31--------------------------------------------------------------------------------
32
33-- Automatically generated local definitions
34
35local GL_BACK                   = GL.BACK
36local GL_EYE_LINEAR             = GL.EYE_LINEAR
37local GL_EYE_PLANE              = GL.EYE_PLANE
38local GL_FILL                   = GL.FILL
39local GL_FRONT                  = GL.FRONT
40local GL_FRONT_AND_BACK         = GL.FRONT_AND_BACK
41local GL_INVERT                 = GL.INVERT
42local GL_LINE                   = GL.LINE
43local GL_ONE                    = GL.ONE
44local GL_ONE_MINUS_SRC_ALPHA    = GL.ONE_MINUS_SRC_ALPHA
45local GL_POINT                  = GL.POINT
46local GL_QUAD_STRIP             = GL.QUAD_STRIP
47local GL_SRC_ALPHA              = GL.SRC_ALPHA
48local GL_T                      = GL.T
49local GL_TEXTURE_GEN_MODE       = GL.TEXTURE_GEN_MODE
50local GL_TRIANGLE_FAN           = GL.TRIANGLE_FAN
51local glBeginEnd                = gl.BeginEnd
52local glBlending                = gl.Blending
53local glCallList                = gl.CallList
54local glColor                   = gl.Color
55local glCreateList              = gl.CreateList
56local glCulling                 = gl.Culling
57local glDeleteList              = gl.DeleteList
58local glDeleteTexture           = gl.DeleteTexture
59local glDepthTest               = gl.DepthTest
60local glFeature                 = gl.Feature
61local glGetTextWidth            = gl.GetTextWidth
62local glLineWidth               = gl.LineWidth
63local glLogicOp                 = gl.LogicOp
64local glPointSize               = gl.PointSize
65local glPolygonMode             = gl.PolygonMode
66local glPolygonOffset           = gl.PolygonOffset
67local glPopMatrix               = gl.PopMatrix
68local glPushMatrix              = gl.PushMatrix
69local glScale                   = gl.Scale
70local glSmoothing               = gl.Smoothing
71local glTexCoord                = gl.TexCoord
72local glTexGen                  = gl.TexGen
73local glText                    = gl.Text
74local glTexture                 = gl.Texture
75local glTranslate               = gl.Translate
76local glUnit                    = gl.Unit
77local glVertex                  = gl.Vertex
78local spDrawUnitCommands        = Spring.DrawUnitCommands
79local spGetFeatureAllyTeam      = Spring.GetFeatureAllyTeam
80local spGetFeatureDefID         = Spring.GetFeatureDefID
81local spGetFeaturePosition      = Spring.GetFeaturePosition
82local spGetFeatureRadius        = Spring.GetFeatureRadius
83local spGetFeatureTeam          = Spring.GetFeatureTeam
84local spGetModKeyState          = Spring.GetModKeyState
85local spGetMouseState           = Spring.GetMouseState
86local spGetMyAllyTeamID         = Spring.GetMyAllyTeamID
87local spGetMyPlayerID           = Spring.GetMyPlayerID
88local spGetMyTeamID             = Spring.GetMyTeamID
89local spGetPlayerControlledUnit = Spring.GetPlayerControlledUnit
90local spGetPlayerInfo           = Spring.GetPlayerInfo
91local spGetTeamColor            = Spring.GetTeamColor
92local spGetTeamInfo             = Spring.GetTeamInfo
93local spGetUnitAllyTeam         = Spring.GetUnitAllyTeam
94local spGetUnitDefID            = Spring.GetUnitDefID
95local spGetUnitIsCloaked        = Spring.GetUnitIsCloaked
96local spGetUnitTeam             = Spring.GetUnitTeam
97local spIsCheatingEnabled       = Spring.IsCheatingEnabled
98local spTraceScreenRay          = Spring.TraceScreenRay
99
100
101--------------------------------------------------------------------------------
102--------------------------------------------------------------------------------
103
104include("colors.h.lua")
105include("fonts.lua")
106
107local font = 'FreeMonoBold'
108local fontSize = 16
109local fontName = ':n:'..LUAUI_DIRNAME..'Fonts/'..font..'_'..fontSize
110
111
112local showName = (1 > 0)
113
114local customTex = LUAUI_DIRNAME .. 'Images/highlight_strip.png'
115local texName = LUAUI_DIRNAME .. 'Images/highlight_strip.png'
116--local texName = 'bitmaps/laserfalloff.tga'
117
118local cylDivs = 64
119local cylList = 0
120
121local outlineWidth = 3
122
123local vsx, vsy = widgetHandler:GetViewSizes()
124function widget:ViewResize(viewSizeX, viewSizeY)
125  vsx = viewSizeX
126  vsy = viewSizeY
127end
128
129local smoothPolys = (glSmoothing ~= nil) and false
130
131
132--------------------------------------------------------------------------------
133--------------------------------------------------------------------------------
134
135function widget:Initialize()
136  cylList = glCreateList(DrawCylinder, cylDivs)
137end
138
139
140function widget:Shutdown()
141  glDeleteList(cylList)
142  glDeleteTexture(customTex)
143end
144
145
146--------------------------------------------------------------------------------
147--------------------------------------------------------------------------------
148
149function DrawCylinder(divs)
150  local cos = math.cos
151  local sin = math.sin
152  local divRads = (2.0 * math.pi) / divs
153  -- top
154  glBeginEnd(GL_TRIANGLE_FAN, function()
155    for i = 1, divs do
156      local a = i * divRads
157      glVertex(sin(a), 1.0, cos(a))
158    end
159  end)
160  -- bottom
161  glBeginEnd(GL_TRIANGLE_FAN, function()
162    for i = 1, divs do
163      local a = -i * divRads
164      glVertex(sin(a), -1.0, cos(a))
165    end
166  end)
167  -- sides
168  glBeginEnd(GL_QUAD_STRIP, function()
169    for i = 0, divs do
170      local a = i * divRads
171      glVertex(sin(a),  1.0, cos(a))
172      glVertex(sin(a), -1.0, cos(a))
173    end
174  end)
175end
176
177
178--------------------------------------------------------------------------------
179--------------------------------------------------------------------------------
180
181local function HilightModel(drawFunc, drawData, outline)
182  glDepthTest(true)
183  glPolygonOffset(-2, -2)
184  glBlending(GL_SRC_ALPHA, GL_ONE)
185
186  if (smoothPolys) then
187    glSmoothing(nil, nil, true)
188  end
189
190  local scale = 20
191  local shift = (2 * widgetHandler:GetHourTimer()) % scale
192  glTexCoord(0, 0)
193  glTexGen(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR)
194  glTexGen(GL_T, GL_EYE_PLANE, 0, (1 / scale), 0, shift)
195  glTexture(texName)
196
197  drawFunc(drawData)
198
199  glTexture(false)
200  glTexGen(GL_T, false)
201
202  -- more edge highlighting
203  if (outline) then
204    glLineWidth(outlineWidth)
205    glPointSize(outlineWidth)
206    glPolygonOffset(10, 100)
207    glPolygonMode(GL_FRONT_AND_BACK, GL_POINT)
208    drawFunc(drawData)
209    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
210    drawFunc(drawData)
211    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
212    glPointSize(1)
213    glLineWidth(1)
214  end
215
216  if (smoothPolys) then
217    glSmoothing(nil, nil, false)
218  end
219
220  glBlending(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
221  glPolygonOffset(false)
222  glDepthTest(false)
223end
224
225
226--------------------------------------------------------------------------------
227
228local function SetUnitColor(unitID, alpha)
229  local teamID = spGetUnitTeam(unitID)
230  if (teamID == nil) then
231    glColor(1.0, 0.0, 0.0, alpha) -- red
232  elseif (teamID == spGetMyTeamID()) then
233    glColor(0.0, 1.0, 1.0, alpha) -- cyan
234  elseif (spGetUnitAllyTeam(unitID) == spGetMyAllyTeamID()) then
235    glColor(0.0, 1.0, 0.0, alpha) -- green
236  else
237    glColor(1.0, 0.0, 0.0, alpha) -- red
238  end
239end
240
241
242local function SetFeatureColor(featureID, alpha)
243  glColor(1.0, 0.0, 1.0, alpha) -- purple
244  do return end  -- FIXME -- wait for feature team/allyteam resolution
245
246  local allyTeamID = spGetFeatureAllyTeam(featureID)
247  if ((allyTeamID == nil) or (allyTeamID < 0)) then
248    glColor(1.0, 1.0, 1.0, alpha) -- white
249  elseif (allyTeamID == spGetMyAllyTeamID()) then
250    glColor(0.0, 1.0, 1.0, alpha) -- cyan
251  else
252    glColor(1.0, 0.0, 0.0, alpha) -- red
253  end
254end
255
256
257local function UnitDrawFunc(unitID)
258  glUnit(unitID, true)
259end
260
261
262local function FeatureDrawFunc(featureID)
263  glFeature(featureID, true)
264end
265
266
267local function HilightUnit(unitID)
268  local outline = (spGetUnitIsCloaked(unitID) ~= true)
269  SetUnitColor(unitID, outline and 0.5 or 0.25)
270  HilightModel(UnitDrawFunc, unitID, outline)
271end
272
273
274local function HilightFeatureModel(featureID)
275  SetFeatureColor(featureID, 0.5)
276  HilightModel(FeatureDrawFunc, featureID, true)
277end
278
279
280local function HilightFeature(featureID)
281  local fDefID = spGetFeatureDefID(featureID)
282  local fd = FeatureDefs[fDefID]
283  if (fd == nil) then return end
284
285  if (fd.drawType == 0) then
286    HilightFeatureModel(featureID)
287    return
288  end
289
290  local radius = spGetFeatureRadius(featureID)
291  if (radius == nil) then
292    return
293  end
294
295  local px, py, pz = spGetFeaturePosition(featureID)
296  if (px == nil) then return end
297
298  local yScale = 4
299  glPushMatrix()
300  glTranslate(px, py, pz)
301  glScale(radius, yScale * radius, radius)
302  -- FIXME: needs an 'inside' check
303
304  glDepthTest(true)
305  glLogicOp(GL_INVERT)
306
307  glCulling(GL_FRONT)
308  glCallList(cylList)
309
310  glCulling(GL_BACK)
311  glCallList(cylList)
312
313  glLogicOp(false)
314  glCulling(false)
315  glDepthTest(false)
316
317  glPopMatrix()
318end
319
320
321--------------------------------------------------------------------------------
322--------------------------------------------------------------------------------
323
324local GetPlayerControlledUnit = spGetPlayerControlledUnit
325local GetMyPlayerID           = spGetMyPlayerID
326local TraceScreenRay          = spTraceScreenRay
327local GetMouseState           = spGetMouseState
328local GetUnitDefID            = spGetUnitDefID
329local GetFeatureDefID         = spGetFeatureDefID
330
331
332--------------------------------------------------------------------------------
333--------------------------------------------------------------------------------
334
335local type, data  --  for the TraceScreenRay() call
336
337
338function widget:Update()
339  local mx, my = GetMouseState()
340  type, data = TraceScreenRay(mx, my)
341end
342
343
344function widget:DrawWorld()
345  if (type == 'feature') then
346    HilightFeature(data)
347  elseif (type == 'unit') then
348    local unitID = GetPlayerControlledUnit(GetMyPlayerID())
349    if (data ~= unitID) then
350      HilightUnit(data)
351      -- also draw the unit's command queue
352      local a,c,m,s = spGetModKeyState()
353      if (m) then
354        spDrawUnitCommands(data)
355      end
356    end
357  end
358end
359
360
361widget.DrawWorldReflection = widget.DrawWorld
362
363
364widget.DrawWorldRefraction = widget.DrawWorld
365
366
367--------------------------------------------------------------------------------
368--------------------------------------------------------------------------------
369
370local teamNames = {}
371
372
373local function GetTeamName(teamID)
374  local name = teamNames[teamID]
375  if (name) then
376    return name
377  end
378
379  local teamNum, teamLeader = spGetTeamInfo(teamID)
380  if (teamLeader == nil) then
381    return ''
382  end
383
384  name = spGetPlayerInfo(teamLeader)
385  teamNames[teamID] = name
386  return name
387end
388
389
390--------------------------------------------------------------------------------
391--------------------------------------------------------------------------------
392
393local teamColorStrs = {}
394
395
396local function GetTeamColorStr(teamID)
397  local colorSet = teamColorStrs[teamID]
398  if (colorSet) then
399    return colorSet[1], colorSet[2]
400  end
401
402  local outlineChar = ''
403  local r,g,b = spGetTeamColor(teamID)
404  if (r and g and b) then
405    local function ColorChar(x)
406      local c = math.floor(x * 255)
407      c = ((c <= 1) and 1) or ((c >= 255) and 255) or c
408      return string.char(c)
409    end
410    local colorStr
411    colorStr = '\255'
412    colorStr = colorStr .. ColorChar(r)
413    colorStr = colorStr .. ColorChar(g)
414    colorStr = colorStr .. ColorChar(b)
415    local i = (r * 0.299) + (g * 0.587) + (b * 0.114)
416    outlineChar = ((i > 0.25) and 'o') or 'O'
417    teamColorStrs[teamID] = { colorStr, outlineChar }
418    return colorStr, outlineChar
419  end
420end
421
422
423--------------------------------------------------------------------------------
424--------------------------------------------------------------------------------
425
426function widget:DrawScreen()
427  local a,c,m,s = spGetModKeyState()
428  if (not m) then
429    return
430  end
431
432  local mx, my = GetMouseState()
433  local type, data = TraceScreenRay(mx, my)
434
435  local typeStr = ''
436  local teamID = nil
437
438  local cheat  = spIsCheatingEnabled()
439
440  if (type == 'unit') then
441    local udid = GetUnitDefID(data)
442    if (udid == nil) then return end
443    local ud = UnitDefs[udid]
444    if (ud == nil) then return end
445    typeStr = YellowStr .. ud.humanName -- .. ' ' .. CyanStr .. ud.tooltip
446    if (cheat) then
447      typeStr = typeStr
448                .. ' \255\255\128\255(' .. ud.name
449                .. ') \255\255\255\255#' .. data
450    end
451    teamID = spGetUnitTeam(data)
452  elseif (type == 'feature') then
453    local fdid = GetFeatureDefID(data)
454    if (fdid == nil) then return end
455    local fd = FeatureDefs[fdid]
456    if (fd == nil) then return end
457    typeStr = '\255\255\128\255' .. fd.tooltip
458    if (cheat) then
459      typeStr = typeStr
460                .. ' \255\255\255\1(' .. fd.name
461                .. ') \255\255\255\255#' .. data
462    end
463    teamID = spGetFeatureTeam(data)
464  end
465
466  local pName = nil
467  local colorStr, outlineChar = nil, nil
468  if (teamID) then
469    pName = GetTeamName(teamID)
470    if (pName) then
471      colorStr, outlineChar = GetTeamColorStr(teamID)
472      if ((colorStr == nil) or (outlineChar == nil)) then
473        pName = nil
474      end
475    end
476  end
477
478  local fh = fontHandler
479  if (fh.UseFont(fontName)) then
480
481    local f = fh.GetFontSize() * 0.5
482    local gx = 12 -- gap x
483    local gy = 8  -- gap y
484
485    local lt = fh.GetTextWidth(typeStr)
486    local lp = pName and fh.GetTextWidth(pName) or 0
487    local lm = (lt > lp) and lt or lp  --  max len
488
489    pName = pName and (colorStr .. pName)
490
491    if ((mx + lm + gx) < vsx) then
492      fh.Draw(typeStr, mx + gx, my + gy)
493      if (pName) then
494        fh.Draw(pName, mx + gx, my - gy - f)
495      end
496    else
497      fh.DrawRight(typeStr, mx - gx, my + gy)
498      if (pName) then
499        fh.DrawRight(pName, mx - gx, my - gy - f)
500      end
501    end
502  else
503    local f = 14
504    local gx = 16
505    local gy = 8
506
507    local lt = f * glGetTextWidth(typeStr)
508    local lp = pName and (f * glGetTextWidth(pName)) or 0
509    local lm = (lt > lp) and lt or lp  --  max len
510
511    pName = pName and (colorStr .. pName)
512
513    if ((mx + lm + gx) < vsx) then
514      glText(typeStr, mx + gx, my + gy, f, 'o')
515      if (pName) then
516        glText(pName, mx + gx, my - gy - f, f, outlineChar)
517      end
518    else
519      glText(typeStr, mx - gx, my + gy, f, 'or')
520      if (pName) then
521        glText(pName, mx - gx, my - gy - f, f, outlineChar .. 'r')
522      end
523    end
524  end
525end
526
527
528--------------------------------------------------------------------------------
529--------------------------------------------------------------------------------
530