All pastes #743461 Raw Edit

Unnamed

public lua v1 · immutable
·published 2007-10-20 13:44 UTC
rendered paste body
-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Info--function widget:GetInfo()  return {    name      = "Display Options",    desc      = "In-game options menu",    author    = "quantum",    date      = "Aug 8, 2007",    license   = "GNU GPL, v2 or later",    layer     = 0,    enabled   = false  --  loaded by default?  }end-- See LuaUI/Widgets/Rooms/documentation.txt for documentation. -- FIXME /viewtaflip-------------------------------------------------------------------------------------------------------------------------------------------------------------------- External Access--local rooms = widgetWG.rooms = roomsrooms.Window = {}rooms.Tab = {}-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Speedups--local fontHandler    = fontHandlerlocal gl, GL         = gl, GLlocal GetGameSeconds = Spring.GetGameSecondslocal Window         = rooms.Windowlocal Tab            = rooms.Tablocal Delist         = rooms.Delist-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Constants--ROOM_DIRNAME = "LuaUI/Widgets/Rooms"FONT_DIRNAME = LUAUI_DIRNAME.."/Fonts/"VFSMODE      = VFS.RAW_FIRST-- Color codes: embed in a string to select text color.white   = "\255\255\255\255"black   = "\255\001\001\001"grey    = "\255\128\128\128"red     = "\255\255\001\001"pink    = "\255\255\064\064"green   = "\255\001\255\001"blue    = "\255\001\001\255"cyan    = "\255\001\255\255"yellow  = "\255\255\255\001"magenta = "\255\255\001\255"-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Locals--local realSeconds          = 0local viewSizeX, viewSizeY = widgetHandler:GetViewSizes()local objectsToDrawSet     = {} -- FIXME: replace completely with zStack-- Tables of objects that are to be notified when the specified-- event takes place.local onMousePressQueue    = {}local onMouseReleaseQueue  = {}local onIsAboveQueue       = {}local onMouseMoveSet       = {}local onUnitFinishedSet    = {}local onUnitCreatedSet     = {}local getConfigDataSet     = {}local onUpdateSet          = {}-- Array of display lists to destroy.local cleanUp              = {}local zStack               = {}-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Globals--groups = {}-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Utility--function WordWrap(str, maxLineWidth) -- Width in pixels.                                     -- TODO: Make it respect newlines.  local insert = table.insert  local concat = table.concat    local wordArray = {}  for word in string.gmatch(str, "[^%s]+") do    insert(wordArray, word)  end    local firstWordInPiece = 1  local lastWordInPiece = 1  local lineArray = {}    repeat      local linePiece = concat(wordArray, " ", firstWordInPiece, lastWordInPiece)    local strippedLinePiece = fontHandler.StripColors(linePiece)    local pieceWidth = fontHandler.GetTextWidth(strippedLinePiece)        if (pieceWidth >= maxLineWidth) then                          local line = concat(wordArray, " ", firstWordInPiece, lastWordInPiece-1)      insert(lineArray, line)                           firstWordInPiece = lastWordInPiece    end        lastWordInPiece = lastWordInPiece + 1      until (lastWordInPiece == #wordArray)    -- Insert the last words in the lineArray  local line  if (#wordArray > 1) then    line = concat(wordArray, " ", firstWordInPiece, lastWordInPiece)    local strippedLine = fontHandler.StripColors(line)    local lineWidth = fontHandler.GetTextWidth(strippedLine)    if (lineWidth < maxLineWidth and line ~= "") then      insert(lineArray, line)    else      -- There can be only one extra word, see the repeat/until loop.      line = (concat(wordArray, " ", firstWordInPiece, lastWordInPiece-1))      insert(lineArray, line)      insert(lineArray, wordArray[#wordArray])    end  else    lineArray = wordArray  end    return lineArrayendfunction Copy(original)       -- Warning: circular table references lead to  local copy = {}             -- an infinite loop.  for k, v in pairs(original) do    if (type(v) == "table") then      copy[k] = Copy(v)    else      copy[k] = v    end  end  return copyendfunction ToSI(n) -- Adapted from IceUI. Thanks, Meltrax!    if (not n) then    return "nil"  end    local resultString  local exponent  local suffix  local siSuffixes = {"y", "z", "a", "f", "p", "n", "µ", "m", "", "k", "M",                       "G", "T", "P", "E", "Z", "Y" }  if (math.abs(n) < 10^27   and      math.abs(n) > 10^-27) then    n            = math.abs(n)    exponent     = n == 0 and 0 or math.log(n)/math.log(10)    suffix       = n == 0 and 0 or math.floor(exponent/3)    n            = (n + (0.5*10^(math.floor(exponent)-2))) / 10^(suffix*3)    n            = string.sub(n, 1, tonumber(n) > 100 and 3 or 4)    resultString = n..siSuffixes[suffix+9]  else    resultString = "0"  end    return resultStringendfunction MakeCounter()  local count = 0  return function()    count = count + 1    return count  endendfunction MakeTimer()  local startTime = realSeconds  return function()    return realSeconds - startTime  endendfunction Delist(array, object) -- Finds and removes a value from an array.  local objectPos    for index, key in ipairs(array) do    if (key == object) then      objectPos = index      break    end  end  table.remove(array, objectPos)endfunction MakeLineArray(text) -- Separates text in lines.  local array = {}  for word in string.gmatch(text, "[^\n]+") do    table.insert(array, word)  end  return arrayendfunction RunOnce(func, ...)  local first = true  return function (...)    if (first) then      first = nil      func(...)    end  endendfunction Empty()  return Emptyend-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Obsolete Utility----[[--------------- Might be useful again if gl.Font is used. ------------------local function WordWrapMono(str, columns) -- Accurate with monospace fonts only.  local li  = 1  local len = #str  while (len-li > columns) do    local j    local k    local i = li + columns    while (i > li and string.sub(str, i, i) ~= " ") do      i = i-1    end    j = i    while (j > li and string.sub(str, j, j) == " ") do      j = j-1    end    str = string.sub(str, 1, j).."\n"..string.sub(str, i+1, -1)    k   = 1+j-i    li  = j+k    len = len+k  end  return strendlocal function MakeLineArray(str, width)  local Lines = string.gmatch(WordWrap(str, width), "[^\n]+")  local lineArray = {}  for line in Lines do    table.insert(lineArray, line)  end  return lineArrayendlocal function GetWidth(lineArray)  local width = 0  for _, line in pairs(lineArray) do    local s = fontHandler.StripColorCodes(s)    local l = #s    if (l > width) then      width = l    end  end  return width -- In pixels.endlocal function GetParagraphSize(lineArray, lineSpacing)  -- Line spacing in  local lineSpacing = lineSpacing or defaultLineSpacing  -- in pixels.  local w = GetWidth(lineArray)  local width = fontHandler.GetTextWidth(string.rep("a", w))  local fontHeight = fontHandler.GetFontSize()  local height = #lineArray * (fontHeight + lineSpacing)  return width, height -- In pixels.end]]-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Tab Prototype--function Tab:Create(newTab, parent)  newTab = newTab or {}  setmetatable(newTab, self)  self.__index = self  -- Inherit values from the "Tab" prototype.  newTab.parent = parent    newTab.font = newTab.font or "FreeSansBold_16"  fontHandler.UseFont(FONT_DIRNAME..newTab.font)    newTab:ProcessArguments()  newTab.x1, newTab.y1, newTab.x2, newTab.y2 = newTab:GetCoordinates()  fontHandler.UseFont(FONT_DIRNAME..parent.font)  return newTabendfunction Tab:ProcessArguments()  local parent = self.parent  self.self = self    self.title            = self.title            or white.."This is a tab"  self.offset           = self.offset           or parent.tabOffset  self.leftMargin       = self.leftMargin       or parent.leftMargin  self.rightMargin      = self.rightMargin      or parent.rightMargin  self.topMargin        = self.topMargin        or parent.topMargin  self.bottomMargin     = self.bottomMargin     or parent.bottomMargin  self.noBorder         = self.noBorder         or parent.border  -- Border offsets determine how much the border lines protrude.  self.borderOffset1    = self.borderOffset1    or parent.borderOffset1  self.borderOffset2    = self.borderOffset2    or parent.borderOffset2  self.position         = self.position         or "top"  self.borderColor      = self.borderColor      or self.borderColor  -- Background colors when the mouse cursor is within the tab.  self.isAboveColors    = self.isAboveColors    or Copy(parent.isAboveColors)  self.topLeftColor     = self.topLeftColor     or Copy(parent.topLeftColor)  self.topRightColor    = self.topRightColor    or Copy(parent.topRightColor)  self.bottomLeftColor  = self.bottomLeftColor  or Copy(parent.bottomLeftColor)  self.bottomRightColor = self.bottomRightColor or Copy(parent.bottomRightColor)  self.gradient         = self.bottomLeftColor  or parent.gradient  self.backgroundColor  = self.backgroundColor  or parent.backgroundColor  self.borderColor      = self.borderColor      or parent.borderColor  self.useOnIsAbove     = true -- FIXME  if (not self.gradient) then    self.topRightColor    = Copy(self.backgroundColor)    self.topLeftColor     = Copy(self.backgroundColor)    self.bottomLeftColor  = Copy(self.backgroundColor)    self.bottomRightColor = Copy(self.backgroundColor)  end  self.OnMousePressAction = self.OnMousePressAction or function()                                                         return true                                                       end  if (self.Click) then    self.OnMouseReleaseAction = self.Click  end    if (self.preset) then    self.preset(self)  endendfunction Tab:GetCoordinates()  local parent = self.parent  local x1, y1, y2, x2  local textWidth = fontHandler.GetTextWidth(self.title)  local fontSize  = parent.fontSize  local width     = self.leftMargin + textWidth + self.rightMargin  local height    = self.topMargin + fontSize + self.bottomMargin  local offset    = self.offset  if (self.position == "top") then    local oldTabOffset = parent.tabOffsets.top -- Place the new tab next to    x1 = parent.x1 + offset + oldTabOffset     -- the previous one.    y1 = parent.y2    x2 = x1 + width    y2 = parent.y2 + height    local newTabOffset = x2 - parent.x1    if (oldTabOffset < newTabOffset ) then      parent.tabOffsets.top = newTabOffset    end  elseif (self.position == "right") then    local oldTabOffset = parent.tabOffsets.right    x1 = parent.x2    y1 = parent.y2 - offset - height - oldTabOffset    x2 = parent.x2 + width    y2 = parent.y2 - offset - oldTabOffset    local newTabOffset = parent.y2 - y1    if (oldTabOffset < newTabOffset ) then      parent.tabOffsets.right = newTabOffset    end  elseif (self.position == "left") then    local oldTabOffset = parent.tabOffsets.left    x1 = parent.x1 - width + 1    y1 = parent.y2 - offset - height - oldTabOffset    x2 = parent.x1 + 1    y2 = parent.y2 - offset - oldTabOffset    local newTabOffset = parent.y2 - y1    if (oldTabOffset < newTabOffset ) then      parent.tabOffsets.left = newTabOffset    end  elseif (self.position == "bottom") then    local oldTabOffset = parent.tabOffsets.bottom    x1 = parent.x1 + offset + oldTabOffset    y1 = parent.y1 - height + 1    x2 = x1 + width    y2 = parent.y1    local newTabOffset = x2 - parent.x1    if (oldTabOffset < newTabOffset ) then      parent.tabOffsets.bottom = newTabOffset    end  end  return x1, y1, x2, y2endfunction Tab:UpdateCoordinates()  self.x1, self.y1, self.x2, self.y2 = self:GetCoordinates()endfunction Tab:Draw()  local x1 = self.x1  local y1 = self.y1  local x2 = self.x2  local y2 = self.y2  local a = self.borderOffset1  local b = self.borderOffset2  if (self.backgroundColor[4] ~= 0) then    gl.BeginEnd(GL.QUADS, function()      gl.Color(unpack(self.bottomLeftColor))      gl.Vertex(x1, y1)      gl.Color(unpack(self.topLeftColor))      gl.Vertex(x1, y2)      gl.Color(unpack(self.topRightColor))      gl.Vertex(x2, y2)      gl.Color(unpack(self.bottomRightColor))      gl.Vertex(x2, y1)    end)  end  if (not self.noBorder) then    self:DrawBorder()  endendfunction Tab:DrawTitle()  local x = self.x1 + (self.x2-self.x1)*0.5  local y = self.y1 + self.bottomMargin  fontHandler.UseFont(FONT_DIRNAME..self.font)  fontHandler.DrawCentered(self.title, x, y)  fontHandler.UseFont(FONT_DIRNAME..self.parent.font) -- FIXME TEMPendfunction Tab:DrawBorder()  local x1 = self.x1  local y1 = self.y1  local x2 = self.x2  local y2 = self.y2  local a = self.borderOffset1  local b = self.borderOffset2  gl.BeginEnd(GL.LINES, function()    gl.Color(unpack(self.borderColor))    if (self.position == "top") then      gl.Vertex(x1-b, y1+b)      gl.Vertex(x1-b, y2+a)      gl.Vertex(x1-a, y2+b)      gl.Vertex(x2+a, y2+b)      gl.Vertex(x2+b, y2+a)      gl.Vertex(x2+b, y1+b)    elseif (self.position == "right") then      gl.Vertex(x1+b, y2+b)      gl.Vertex(x2+a, y2+b)      gl.Vertex(x2+b, y2+a)      gl.Vertex(x2+b, y1-a)      gl.Vertex(x2+a, y1-b)      gl.Vertex(x1+b, y1-b)    elseif (self.position == "left") then      gl.Vertex(x1-b, y1-a)      gl.Vertex(x1-b, y2+a)      gl.Vertex(x1-a, y2+b)      gl.Vertex(x2-3*b, y2+b)      gl.Vertex(x2-3*b, y1-b)      gl.Vertex(x1-a, y1-b)    elseif (self.position == "bottom") then      gl.Vertex(x1-b, y1-a)      gl.Vertex(x1-b, y2-b)      gl.Vertex(x2+b, y2-b)      gl.Vertex(x2+b, y1-a)      gl.Vertex(x2+a, y1-b)      gl.Vertex(x1-a, y1-b)    end  end)endfunction Tab:OnMousePress(x, y, button)  local capture  if (not self.parent.closed) then    if (x > self.x1 + self.parent.dx  and        x < self.x2 + self.parent.dx  and        y > self.y1 + self.parent.dy  and        y < self.y2 + self.parent.dy) then      self.pressCaught = true      self.parent:BringToFront()      if (self.OnMousePressAction) then        capture = self.OnMousePressAction(button)      end    end  end  return captureendfunction Tab:UpdateParentDisplayList()  self.parent:UpdateDisplayList()endfunction Tab:OnIsAbove(x, y)  local iac = self.isAboveColors  if (x > self.x1 + self.parent.dx  and      x < self.x2 + self.parent.dx  and      y > self.y1 + self.parent.dy  and      y < self.y2 + self.parent.dy) then    if (not self.isAbove) then      self.isAbove = true      iac.bottomLeft, self.bottomLeftColor        = self.bottomLeftColor, iac.bottomLeft      iac.topLeft, self.topLeftColor        = self.topLeftColor, iac.topLeft      iac.topRight, self.topRightColor        = self.topRightColor, iac.topRight      iac.bottomRight, self.bottomRightColor        = self.bottomRightColor, iac.bottomRight      self:UpdateParentDisplayList()    end  elseif (self.isAbove) then    self.isAbove = nil    self.bottomLeftColor, iac.bottomLeft      = iac.bottomLeft, self.bottomLeftColor    self.topLeftColor, iac.topLeft      = iac.topLeft, self.topLeftColor    self.topRightColor, iac.topRight      = iac.topRight, self.topRightColor    self.bottomRightColor, iac.bottomRight      = iac.bottomRight, self.bottomRightColor    self:UpdateParentDisplayList()  endendfunction Tab:OnMouseRelease(x, y, button)  local capture  if (not self.parent.closed) then    if (x > self.x1 + self.parent.dx  and        x < self.x2 + self.parent.dx  and        y > self.y1 + self.parent.dy  and        y < self.y2 + self.parent.dy) then      if (self.OnMouseReleaseAction and self.pressCaught) then        capture = self.OnMouseReleaseAction(button)      end    end  end  self.pressCaught = nil  return captureend-- Preset Functions ------------------------------------------------------------function Tab:Close()  return function(tab)    tab.title = "Close"    tab.position = "right"    tab.OnMouseReleaseAction = function()      tab.parent:Close()      -- mainButton:Open()    end  endendfunction Tab:Goto(args)  return function(tab)    tab.title = args.title    tab.position = "left"    tab.OnMouseReleaseAction = function()      tab.parent:Close()      local f = loadstring(args.destination..":Open()") -- FIXME: there must be a      setfenv(f, getfenv(1))                      -- better way.      f()    end  endendfunction Tab:Back(args)  return function(tab)    tab.title = "Back"    tab.position = "right"    tab.OnMouseReleaseAction = function()      tab.parent:Close()      local f = loadstring(args.previousWindow..":Open()")      setfenv(f, getfenv(1))      f()    end  endendfunction Tab:SetConfigInt(args)  return function(tab)    tab.title = args.title    tab.position = "left"    tab.OnMouseReleaseAction = function()      Spring.SetConfigInt(args.config, args.int)      tab.parent.lineArray[1] = args.text or        "Current setting is: "..Spring.GetConfigInt(args.config).."."    end  endendfunction Tab:ConfigEnable(args)  return function(tab)    tab.title = "Enable"    tab.position = "left"    tab.OnMousePressAction = function()      Spring.SetConfigInt(args.config, 1)      tab.parent.lineArray[1] = args.text or        "Current setting is: enabled."    end  endendfunction Tab:ConfigDisable(args)  return function(tab)    tab.title = "Disable"    tab.position = "left"    tab.OnMousePressAction = function()      Spring.SetConfigInt(args.config, 0)      tab.parent.lineArray[1] = args.text or        "Current setting is: disabled."    end  endend-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Window Prototype--function Window:Create(newWindow)  newWindow = newWindow or {}  setmetatable(newWindow, self)  self.__index = self -- Inherit fields from the "Window" prototype.  newWindow:ProcessArguments()  -- If the single line string is smaller than the specified text width,  -- reduce the window width to match the string width.  if (not newWindow.noAutoShrink and newWindow.text) then    newWindow.textWidth = newWindow:GetRealTextWidth()  end  if (newWindow.text) then    newWindow.lineArray = WordWrap(newWindow.text, newWindow.textWidth)  else    -- If a line array is fed directly to the window object, make the window    -- as wide as the widest line.    newWindow.textWidth = newWindow:GetMaxLineWidth()  end  local x2, y2 = newWindow:MakeSize()  newWindow.x2 = newWindow.x2 or x2  newWindow.y2 = newWindow.y2 or y2  newWindow.dx = 0  newWindow.dy = 0  newWindow:MakeTabs()  newWindow:ExpandToMakeTabsFit()  if (not newWindow.closed) then    newWindow:Open()  end  return newWindowendfunction Window:ExpandToMakeTabsFit()  local width = self.x2 - self.x1  local redraw  if (self.tabOffsets.top > width) then    self.x2 = math.floor(self.x1 + self.tabOffsets.top + self.tabOffset)    redraw = true  end  local width = self.x2 - self.x1  if (self.tabOffsets.bottom > width) then    self.x2 = math.floor(self.x1 + self.tabOffsets.bottom + self.tabOffset)    redraw = true  end  self.textWidth = self.x2 - self.x1 - self.leftMargin - self.rightMargin  if (redraw) then -- FIXME: Wasteful, lazy hack.    self:MakeTabs()    if (self.text) then      self.lineArray = WordWrap(self.text, self.textWidth)    end    local _    _, self.y2 = self:MakeSize()    self.y2 = math.floor(self.y2)    redraw = false  end  local height = self.y2 - self.y1  if (self.tabOffsets.right > height) then    self.y2 = math.floor(self.y1 + self.tabOffsets.right + self.tabOffset)  end  height = self.y2 - self.y1  if (self.tabOffsets.left > height) then    self.y2 = math.floor(self.y1 + self.tabOffsets.left + self.tabOffset)  end  self:MakeTabs()endfunction Window:MakeTabs()  -- Tab offsets are used to stop tabs from overlapping.  self.tabOffsets = {top = 0, bottom = 0, left = 0, right = 0}  if (self.tabs) then    self.tabData = {}    for tab, tabArgs in ipairs(self.tabs) do      self.tabData[tab] = Tab:Create(tabArgs, self)    end  end  return self.tabOffsetsendfunction Window:MakeSize()  local textHeight   = #self.lineArray * (self.fontSize + self.lineSpacing)  local x2 = self.x1 + self.textWidth + self.leftMargin + self.rightMargin  local y2 = self.y1 + textHeight + self.topMargin + self.bottomMargin  if (self.x2) then    x2 = self.x2  end  if (self.y2) then    y2 = self.y2  end  return x2, y2endfunction Window:GetRealTextWidth()  local width  local oldWidth = self.textWidth  local textNoColorCodes = fontHandler.StripColors(self.text)  local newWidth = fontHandler.GetTextWidth(textNoColorCodes)  if (newWidth < self.textWidth) then    width = newWidth  else    width = oldWidth  end  return widthendfunction Window:GetMaxLineWidth() -- FIXME: math.max  local lineWidth  local maxWidth  maxWidth = 0  for _, line in ipairs(self.lineArray) do    lineWidth = fontHandler.GetTextWidth(line)    if (lineWidth > maxWidth) then      maxWidth = lineWidth    end  end  return maxWidthendfunction Window:ProcessArguments()  self.self = self  self.font            = self.font        or "FreeSansBold_16"  fontHandler.UseFont(FONT_DIRNAME..self.font)    -- Text as an array of strings. Use "self.text" for automatic word wrapping.  self.lineArray       = self.lineArray       or {}  self.x               = self.x               or 400  self.y               = self.y               or 200  self.x1              = self.x1              or self.x  self.y1              = self.y1              or self.y  self.textWidth       = self.textWidth       or 300  self.lineSpacing     = self.lineSpacing     or 3  self.topMargin       = self.topMargin       or self.lineSpacing  self.bottomMargin    = self.bottomMargin    or self.topMargin*2  self.leftMargin      = self.leftMargin      or self.topMargin*2  self.rightMargin     = self.rightMargin     or self.topMargin  -- Border offsets determine how much the border lines protrude.  self.borderOffset1   = self.borderOffset1   or 0.5  self.borderOffset2   = self.borderOffset2   or 0.5  self.tabOffset       = self.tabOffset       or 10    local isAboveColorsDefault = {    bottomLeft  = {  0,   0,   0,   1},    topLeft     = {0.3, 0.3, 0.3,   1},    topRight    = {0.3, 0.3, 0.3,   1},    bottomRight = {0.5, 0.5, 0.5,   1},  }  self.borderColor        = self.borderColor      or {  1,   1,   1,   1}  -- Background color when the mouse cursor is within the window.  self.isAboveColors      = self.isAboveColors    or isAboveColorsDefault  self.bottomLeftColor    = self.bottomLeftColor  or {  0,   0,   0, 0.5}  self.topLeftColor       = self.topLeftColor     or {  0,   0,   0, 0.8}  self.topRightColor      = self.topRightColor    or {  0,   0,   0, 0.5}  self.bottomRightColor   = self.bottomRightColor or {0.4, 0.4, 0.4, 0.5}  self.gradient           = self.IsAboveColors    or true -- FIXME?  self.backgroundColor    = self.backgroundColor  or {  0,   0,   0, 0.5}  if (not self.gradient) then    self.topRightColor    = self.backgroundColor    self.topLeftColor     = self.backgroundColor    self.bottomLeftColor  = self.backgroundColor    self.bottomRightColor = self.backgroundColor  end  self.fontSize = fontHandler:GetFontSize()      if (self.unitPicByName) then    self.unitPicByID = UnitDefNames[self.unitPicByName].id  end  if (self.unitPicByID) then    self.backGroundTextureString = "#"..self.unitPicByID  end  if (self.x1 < 1) then    self.x1 = math.floor(self.x1*viewSizeX)  end  if (self.y1 < 1) then    self.y1 = math.floor(self.y1*viewSizeY)  end  if (self.textWidth < 1) then    self.textWidth = math.floor(self.textWidth*viewSizeX)  end  if (self.x2 and self.x2 < 1) then    self.x2 = math.floor(self.x2*viewSizeX)  end  if (self.y2 and self.y2 < 1) then    self.y2 = math.floor(self.y2*viewSizeY)  end  if (self.group) then    if (groups[self.group]) then      table.insert(groups[self.group], self)    else      groups[self.group] = {self}    end  end  -- self.Counter = MakeCounter()  self.Timer = MakeTimer()    if (self.OnUnitFinished) then    onUnitFinishedSet[self] = true  end    if (self.OnUnitCreated) then    onUnitCreatedSet[self] = true  end    if (self.GetConfigData) then    getConfigDataSet[self] = true  end    if (self.OnUpdate) then    onUpdateSet[self] = true  end    if (self.Click) then     self.OnMouseReleaseAction = self.Click  endendfunction Window:UpdateDisplayList()  if (self.displayList) then    gl.DeleteList(self.displayList)  end  self.displayList = gl.CreateList( function()    self:DrawBackground()    if (not self.noBorder) then      self:DrawBorder()    end    if (self.tabs) then      for _, tab in pairs(self.tabData) do        tab:Draw()      end    end  end)endfunction Window:Animate()  local x1 = self.x1  local x2 = self.x2  local y1 = self.y1  local y2 = self.y2  local t = self.Timer()    if (t < 3) then    if (self.closed) then -- TODO: give more transition choices      local scaleFactor = 1/(1+(0.66/t)^-15)      gl.Translate(0, 0, -1)      gl.Translate(x1+(x2-x1)/2, y1+(y2-y1)/2, 0)      gl.Scale(scaleFactor, scaleFactor^12, 1)      gl.Translate(-x1-(x2-x1)/2, -y1-(y2-y1)/2, 0)    else      local scaleFactor = 1/(1+(1.5*t+0.7)^-15)      gl.Translate(x1+(x2-x1)/2, y1+(y2-y1)/2, 0)      gl.Scale(scaleFactor, scaleFactor^12, 1)      gl.Translate(-x1-(x2-x1)/2, -y1-(y2-y1)/2, 0)    end  elseif (self.closed) then    objectsToDrawSet[self] = nil    Delist(zStack, self)    return true  end  endfunction Window:Draw()    gl.PushMatrix()  if (not self.noAnimation) then    if self:Animate() then      return    end  end  local x = self.x1 + self.leftMargin  local a = self.fontSize + self.lineSpacing  local b = self.y2 - self.topMargin + self.lineSpacing  gl.Translate(self.dx, self.dy, 0)  gl.CallList(self.displayList)  for line, text in ipairs(self.lineArray) do    local y = b - line * (a)    fontHandler.UseFont(FONT_DIRNAME..self.font)    fontHandler.Draw(text, x, y)  end  if (self.tabs) then    for _, tab in pairs(self.tabData) do      tab:DrawTitle()    end  end  gl.PopMatrix()endfunction Window:DeleteList()  gl.DeleteList(self.displayList)endfunction Window:DrawBorder()  local x1 = self.x1  local y1 = self.y1  local x2 = self.x2  local y2 = self.y2  local a = self.borderOffset1  local b = self.borderOffset2  gl.BeginEnd(GL.LINES, function()    gl.Color(unpack(self.borderColor))    gl.Vertex(x1-b, y1-a)    gl.Vertex(x1-b, y2+a)    gl.Vertex(x1-a, y2+b)    gl.Vertex(x2+a, y2+b)    gl.Vertex(x2+b, y2+a)    gl.Vertex(x2+b, y1-a)    gl.Vertex(x2+a, y1-b)    gl.Vertex(x1-a, y1-b)  end)endfunction Window:DrawBackground()  local x1 = self.x1  local y1 = self.y1  local x2 = self.x2  local y2 = self.y2  if (self.backgroundColor[4] ~= 0) then -- If background alpha is not 0...    gl.BeginEnd(GL.QUADS, function()      gl.Color(unpack(self.bottomLeftColor))      gl.Vertex(x1, y1)      gl.Color(unpack(self.topLeftColor))      gl.Vertex(x1, y2)      gl.Color(unpack(self.topRightColor))      gl.Vertex(x2, y2)      gl.Color(unpack(self.bottomRightColor))      gl.Vertex(x2, y1)    end)  end  -- Draw a unitpic as background.  if (self.backGroundTextureString) then    gl.Color(1, 1, 1, 1)    gl.Texture(self.backGroundTextureString)    gl.TexRect(x1, y1, x2, y2)    gl.Texture(false)  end  endfunction Window:OnMousePress(x, y, button)  local capture  if (not self.closed) then    if (x > self.x1 + self.dx  and        x < self.x2 + self.dx  and        y > self.y1 + self.dy  and        y < self.y2 + self.dy) then      self:BringToFront()            if (self.OnMousePressAction) then        self.OnMousePressAction(button)      end      if (not self.noMove) then        onMouseMoveSet[self] = true      end      self.pressCaught = true      capture = true    end  end  return captureendfunction Window:OnIsAbove(x, y)  local siac = self.isAboveColors  if (x > self.x1 + self.dx  and      x < self.x2 + self.dx  and      y > self.y1 + self.dy  and      y < self.y2 + self.dy) then    if (not self.isAbove) then      self.isAbove = true      siac.bottomLeft, self.bottomLeftColor        = self.bottomLeftColor, siac.bottomLeft      siac.topLeft, self.topLeftColor        = self.topLeftColor, siac.topLeft      siac.topRight, self.topRightColor        = self.topRightColor, siac.topRight      siac.bottomRight, self.bottomRightColor        = self.bottomRightColor, siac.bottomRight      self:UpdateDisplayList()    end  elseif (self.isAbove) then    self.isAbove = nil    self.bottomLeftColor, siac.bottomLeft      = siac.bottomLeft, self.bottomLeftColor    self.topLeftColor, siac.topLeft      = siac.topLeft, self.topLeftColor    self.topRightColor, siac.topRight      = siac.topRight, self.topRightColor    self.bottomRightColor, siac.bottomRight      = siac.bottomRight, self.bottomRightColor    self:UpdateDisplayList()  endendfunction Window:OnMouseRelease(x, y, button)  local capture  if (not self.closed) then    if (x > self.x1 + self.dx  and        x < self.x2 + self.dx  and        y > self.y1 + self.dy  and        y < self.y2 + self.dy) then      if (self.OnMouseReleaseAction) then        self.OnMouseReleaseAction(button)      end      capture = true    end    onMouseMoveSet[self] = nil  end  self.pressCaught = nil  return captureendfunction Window:OnMouseMove(x, y, dx, dy, button)  if (self.group) then    for _, object in pairs(groups[self.group]) do    object.dx = object.dx + dx    object.dy = object.dy + dy    end  else    self.dx = self.dx + dx    self.dy = self.dy + dy  endendfunction Window:AvailableForNotifications()  table.insert(onMousePressQueue, 1, self)  if (self.useOnIsAbove) then    table.insert(onIsAboveQueue, 1, self)  end  table.insert(onMouseReleaseQueue, 1, self)  if (self.tabs) then    for _, tab in pairs(self.tabData) do        table.insert(onMousePressQueue, 1, tab)        table.insert(onIsAboveQueue, 1, tab)        table.insert(onMouseReleaseQueue, 1, tab)    end  endendfunction Window:UnavailableForNotifications()  Delist(onMousePressQueue, self)  Delist(onIsAboveQueue, self)  Delist(onMouseReleaseQueue, self)  if (self.tabs) then    for _, tab in pairs(self.tabData) do        Delist(onMousePressQueue, tab)        Delist(onIsAboveQueue, tab)        Delist(onMouseReleaseQueue, tab)    end  endendfunction Window:Open() -- FIXME: ignore if already opened  self:AvailableForNotifications()  self.closed = nil  self.Timer = MakeTimer()  objectsToDrawSet[self] = true  table.insert(zStack, self)  self:UpdateDisplayList()endfunction Window:Close()  self:UnavailableForNotifications()  if (not self.noAnimation) then    self.Timer = MakeTimer()    self.closed = true  else    objectsToDrawSet[self] = nil    Delist(zStack, self)  endendfunction Window:BringToFront()  Delist(zStack, self)  table.insert(zStack, self)end-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Group Functions--function CloseGroup(group, openMainButton)  for _, object in pairs(groups[group]) do    object:Close()  end  if (mainButton.closed and openMainButton ~= false) then    mainButton:Open()  endend    function OpenGroup(group)  for _, object in pairs(groups[group]) do    object:Open()  endend-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Rooms---- local roomList = VFS.DirList(ROOM_DIRNAME, "room_*.lua", VFSMODE)-- for _, file in ipairs(roomList) do  -- local s = VFS.LoadFile(file, VFSMODE)  -- chunk = loadstring(s, file)  -- setfenv(chunk, rooms)  -- local success, e = pcall(chunk(), file)-- end-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Call-ins--function widget:DrawScreen()  for _, object in ipairs(zStack) do    object:Draw()  endendfunction widget:Update(dt) realSeconds = realSeconds + dt for object in pairs(onUpdateSet) do   object.OnUpdate(dt, realSeconds) endendfunction widget:MousePress(x, y, button)  local capture  for _, object in ipairs(onMousePressQueue) do    capture = object:OnMousePress(x, y, button)    if (capture) then      return true    end  endend function widget:MouseRelease(x, y, button)  local capture  for _, object in ipairs(onMouseReleaseQueue) do    capture = object:OnMouseRelease(x, y, button)    if (capture) then      return true    end  endendfunction widget:IsAbove(x, y)  local capture  for _, object in pairs(onIsAboveQueue) do    capture = object:OnIsAbove(x, y)    if (capture) then      return true    end  endendfunction widget:ViewResize(vsx, vsy)  viewSizeX, viewSizeY = vsx, vsyendfunction widget:MouseMove(x, y, dx, dy, button)  for object in pairs(onMouseMoveSet) do    object:OnMouseMove(x, y, dx, dy, button)  endendfunction widget:UnitFinished(unitID, unitDefID, unitTeam)  for object in pairs(onUnitFinishedSet) do    object.OnUnitFinished(unitID, unitDefID, unitTeam)  endendfunction widget:UnitCreated(unitID, unitDefID, unitTeam)  for object in pairs(onUnitCreatedSet) do    object.OnUnitCreated(unitID, unitDefID, unitTeam)  endendfunction widget:GetConfigData() -- Save  local save = {}  for object in pairs(getConfigDataSet) do    local k, v = object.GetConfigData()    save[k] = v  end  return saveendfunction widget:SetConfigData(data)  -- Load  for object in pairs(getConfigDataSet) do    object.SetConfigData(data)  endend-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Notes----[[  todo: easy way to delete objectsMouseWheel(up, value)UnitCreated(unitID, unitDefID, unitTeam)UnitFinished(unitID, unitDefID, unitTeam)UnitDestroyed(unitID, unitDefID, unitTeam)UnitTaken(unitID, unitDefID, unitTeam, newTeam)UnitIdle(unitID, unitDefID, unitTeam)UnitLoaded(unitID, unitDefID, unitTeam, transportID, transportTeam)  print("-- Mouse Press Begin -------------------")  local count = 0  for i, v in pairs(onMousePressSet) do    count = count + 1    if i.title then      print(i, "tab", i.title)    end    if i.lineArray then      print(i, "window", i.lineArray[1])    end  end  print("objects notified: ", count)  print("-- Mouse Press End----------------------")    print("-- Mouse Release Begin -----------------")  local count = 0  for i, v in pairs(onMouseReleaseSet) do    count = count + 1    if i.title then      print("window", i.parent, "tab", i, i.title)    end    if i.lineArray then      print("window", i, "body", i.lineArray[1])    end  end  print("objects notified: ", count)  print("-- Mouse Release End -------------------")    table.foreach(getfenv(1), print)--]]--[[]]---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Room Info---- In-game options menu.-- -- -- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Speed-ups--local GetConfigInt    = Spring.GetConfigIntlocal GetConfigString = Spring.GetConfigStringlocal SetConfigInt    = Spring.SetConfigIntlocal SetConfigString = Spring.SetConfigStringlocal Window          = Windowlocal Tab             = Tab-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Constants---- local testString = red..[[-- Click on the window and drag to move it.]]..cyan..[[ Lets see word wrapping and -- colored text.]]..magenta..[[ If you close this window, type "/luaui reload" to -- open it again.]]..white..[[ Lua is an extension programming language designed to-- support general procedural programming with data description facilities.-- ]]..green..[[ It also offers good support for object-oriented programming, -- functional programming, and data-driven programming.]]..red..[[ Lua is intended -- to be used as a powerful, light-weight scripting language for any program that -- needs one.]]..yellow..[[ Lua is implemented as a library, written in clean C -- (that is, in the common subset of ANSI C and C++).-- ]]-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Main Menu---- local function MakeLogo()  -- local r = math.random(1, 5)   -- return ROOM_DIRNAME.."/Logos/"..r..".png"-- end-- mainMenu = Window:Create{  -- group = "ingameMenu",  -- x1 = 300,  -- x2 = 600,  -- y1 = 300,  -- y2 = 600,  -- closed = true,  -- backGroundTextureString = MakeLogo(),  -- tabs = {    -- {title = white.."Complete Annihilation Main Menu"},    -- {preset = Tab:Close()},    -- {preset = Tab:Goto{title="Display Options", destination="displayMenu",}},    -- {title = "Unit Information",     -- position = "left",     -- OnMouseReleaseAction = function()      -- MakeInfoWindow()      -- mainMenu:Close()     -- end},    -- {preset = Tab:Goto{title="Storyline", destination="storyLine1",}},    -- {preset = Tab:Goto{title="Debug", destination="debugWindow"}},    -- {preset = Tab:Goto{title="Help Options", destination="helpMessageOptions"}},  -- }-- }-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Display Menu and Sub-menus---- Display Menu ----------------------------------------------------------------displayMenu = Window:Create{  group = "ingameMenu",  closed = false,  lineArray = {"Todo:", "- LOD scales", "- Show ally commands",},  tabs = {    {title = white.."Display Options"},    -- {preset = Tab:Close()},    -- {preset = Tab:Back{previousWindow = "mainMenu"}},    {preset =      Tab:Goto{title="GUI Lines Anti-Aliasing",  destination="smoothLines"}},    {preset =      Tab:Goto{title="GUI Points Anti-Aliasing", destination="smoothPoints"}},    {preset =      Tab:Goto{title="Team Nanospray Color",     destination="teamNanospray"}},    {preset =      Tab:Goto{title="Advanced Unit Shading",    destination="advUnitShading"}},    {preset =      Tab:Goto{title="Water Detail",             destination="waterDetail"}},    {preset =      Tab:Goto{title="Shadow Detail",            destination="shadowDetail"}},    {preset =      Tab:Goto{title="Maximum Particles",        destination="maxParticles"}},    {preset =      Tab:Goto{title="Frame Rate Manager",       destination="managerOptions"}},    }}-- GUI Lines Anti-Aliansing Options Dialog -------------------------------------smoothLines = Window:Create{  group = "ingameMenu",  closed = true,  lineArray = {                "Current setting is: "..GetConfigInt("smoothLines")..".",                red.."Requires a restart to take effect."..white,              },  tabs = {    {title = "GUI Lines Anti-Aliansing Options"},    {preset = Tab:Close()},    {preset = Tab:Back{previousWindow = "displayMenu"}},    {preset = Tab:SetConfigInt{title  = "Set to 0 (off)",                               config = "smoothLines",                               int    = 0}},    {preset = Tab:SetConfigInt{title  = "Set to 1 (fastest)",                               config = "smoothLines",                               int    = 1}},    {preset = Tab:SetConfigInt{title  = "Set to 2 (don't care)",                               config = "smoothLines",                               int    = 2}},    {preset = Tab:SetConfigInt{title  = "Set to 3 (nicest)",                               config = "smoothLines",                               int    = 3}},  }}-- GUI Points Anti-Aliansing Options Dialog ------------------------------------smoothPoints = Window:Create{  group = "ingameMenu",  closed = true,  lineArray = {                "Current setting is: "..GetConfigInt("smoothPoints")..".",                red.."Requires a restart to take effect."..white,                "With 0, points appear as squares.",                "With 1 to 3, points appear as discs.",              },  tabs = {    {title = "GUI Points Anti-Aliansing Options"},    {preset = Tab:Close()},    {preset = Tab:Back{previousWindow = "displayMenu"}},    {preset = Tab:SetConfigInt{title  = "Set to 0 (off)",                               config = "smoothPoints",                               int    = 0}},    {preset = Tab:SetConfigInt{title  = "Set to 1 (fastest)",                               config = "smoothPoints",                               int    = 1}},    {preset = Tab:SetConfigInt{title  = "Set to 2 (don't care)",                               config = "smoothPoints",                               int    = 2}},    {preset = Tab:SetConfigInt{title  = "Set to 3 (nicest)",                               config = "smoothPoints",                               int    = 3}},  }}-- Team Nanospray Color Dialog -------------------------------------------------local function teamNanosprayText()  local status  local s = GetConfigInt("TeamNanospray")  if (s < 1) then    status = "disabled"  else    status = "enabled"  end  return "Team nanospray color is "..status.."."endteamNanospray = Window:Create{  group = "ingameMenu",  lineArray = {teamNanosprayText(),                red.."Requires a restart to take effect."..white},  closed = true,  tabs = {    {title = white.."Team Nanospray Color Dialog"},    {preset = Tab:ConfigEnable{config = "teamNanospray"}},    {preset = Tab:ConfigDisable{config = "teamNanospray"}},    {preset = Tab:Close()},    {preset = Tab:Back{previousWindow = "displayMenu"}},  }}-- 3D Trees Dialog ---------------------------------------------------------------[[ -- Redundant: already in settings.exe.local function threeDTreesText()  local status  local s = GetConfigInt("teamNanospray")  if (s < 1) then    status = "disabled"  else    status = "enabled"  end  return "3D trees are "..status.."."endthreeDTrees = Window:Create{  group = "ingameMenu",  lineArray = {threeDTreesText(), red.."Restart required to take effect."},  closed = true,  tabs = {    {title = white.."3D Trees Dialog"},    {preset = Tab:ConfigEnable{config = "3DTrees"}},    {preset = Tab:ConfigDisable{config = "3DTrees"}},    {preset = Tab:Close()},    {preset = Tab:Back{previousWindow = "displayMenu"}},  }}-- High detail sky Dialog ------------------------------------------------------local function advSkyText()  local status  local s = GetConfigInt("advSky")  if (s < 1) then    status = "disabled"  else    status = "enabled"  end  return "High detail sky is "..status.."."endadvSky = Window:Create{  group = "ingameMenu",  lineArray = {advSkyText(), red.."Restart required to take effect."},  closed = true,  tabs = {    {title = white.."High detail sky Dialog"},    {preset = Tab:ConfigEnable{config = "advSky"}},    {preset = Tab:ConfigDisable{config = "advSky"}},    {preset = Tab:Close()},    {preset = Tab:Back{previousWindow = "displayMenu"}},  }}-- Dual Screen Mode ------------------------------------------------------------local function dualScreenModeText()  local status  local s = GetConfigInt("dualScreenMode")  if (s < 1) then    status = "disabled"  else    status = "enabled"  end  return "Dual screen mode is "..status.."."enddualScreenMode = Window:Create{  group = "ingameMenu",  lineArray = {dualScreenModeText(), red.."Restart required to take effect."},  closed = true,  tabs = {    {title = white.."Dual Screen Mode Dialog"},    {preset = Tab:ConfigEnable{config = "dualScreenMode"}},    {preset = Tab:ConfigDisable{config = "dualScreenMode"}},    {preset = Tab:Close()},    {preset = Tab:Back{previousWindow = "displayMenu"}},  }}-- Dual Screen Minimap Side ----------------------------------------------------local function dualScreenSideText()  local status  local s = GetConfigInt("DualScreenMiniMapOnLeft")  if (s < 1) then    status = "right"  else    status = "left"  end  return "Minimap is on the "..status.." in dual screen mode."enddualScreenSide = Window:Create{  group = "ingameMenu",  lineArray = {dualScreenSideText(), red.."Restart required to take effect"},  closed = true,  tabs = {    {preset = Tab:SetConfigInt{title  = white.."Minimap on left",                               config = "DualScreenMiniMapOnLeft",                               int    = 1,                               text   = "Minimap is on the left in "..                                 "dual screen mode."}},    {preset = Tab:SetConfigInt{title  = "Minimap on right",                               config = "DualScreenMiniMapOnLeft",                               int    = 0,                               text   = "Minimap is on the right in "..                                 "dual screen mode."}},    {preset = Tab:Close()},    {preset = Tab:Back{previousWindow = "displayMenu"}},  }}--]]-- Advanced Unit Shading -------------------------------------------------------local function advUnitShadingText()  local status  local s = GetConfigInt("AdvUnitShading")  if (s < 1) then    status = "disabled"  else    status = "enabled"  end  return "Advanced unit shading is "..status.."."endadvUnitShading = Window:Create{  group = "ingameMenu",  closed = true,  lineArray = {advUnitShadingText(),               'It is also known as "shiny units"',               'or "reflective units".'},  tabs = {    {title = "Advanced Unit Shading"},    {preset = Tab:Close()},    {preset = Tab:Back{previousWindow = "displayMenu"}},    {title = "Enable",      position = "left",      OnMouseReleaseAction = function()        Spring.SendCommands{"advshading 1"}        SetConfigInt("advUnitShading", 1)        advUnitShading.lineArray[1] = advUnitShadingText() -- lineArray changes      end                                                  -- take effect    },                                                     -- immediately.     {title = "Disable",      position = "left",      OnMouseReleaseAction = function()        Spring.SendCommands{"advshading 0"}        SetConfigInt("advUnitShading", 0)        advUnitShading.lineArray[1] = advUnitShadingText()      end    },  }}-- Water Detail ----------------------------------------------------------------local function waterText()  local status  local s = GetConfigInt("ReflectiveWater")  if (s < 1) then    status = "basic"  elseif (s < 2) then    status = "reflective"  elseif (s <3) then    status = "dynamic"  else    status = "reflective and refractive"  end  return "The water surface is "..status.."."endwaterDetail = Window:Create{  group = "ingameMenu",  closed = true,  lineArray = {waterText(),               red..'On computers with old video cards, dynamic',               'water can severely impair performance.'},  tabs = {    {title = white.."Water Detail"},    {preset = Tab:Close()},    {preset = Tab:Back{previousWindow = "displayMenu"}},    {title = "Basic Water",      position = "left",      OnMouseReleaseAction = function()        Spring.SendCommands{"water 0"}        SetConfigInt("ReflectiveWater", 0)        waterDetail.lineArray[1] = waterText()      end    },    {title = "Reflective Water",      position = "left",      OnMouseReleaseAction = function()        Spring.SendCommands{"water 1"}        SetConfigInt("ReflectiveWater", 1)        waterDetail.lineArray[1] = waterText()      end    },    {title = "Reflective and Refractive Water",      position = "left",      OnMouseReleaseAction = function()        Spring.SendCommands{"water 3"}        SetConfigInt("ReflectiveWater", 3)        waterDetail.lineArray[1] = waterText()      end    },    {title = "Dynamic Water",      position = "left",      OnMouseReleaseAction = function()        Spring.SendCommands{"water 2"}        SetConfigInt("ReflectiveWater", 2)        waterDetail.lineArray[1] = waterText()      end    },  }}-- Shadow Detail ---------------------------------------------------------------local function shadowDetailText()  local status  local s = GetConfigInt("Shadows")  local t = GetConfigInt("ShadowMapSize")  if (s < 1) then    status = "Shadows are disabled.   "  elseif (t < 2048) then    status = "Shadow detail is low.   "  elseif (t < 4096) then    status = "Shadow detail is medium."  else    status = "Shadow detail is high.  "  end  return statusendshadowDetail = Window:Create{  group = "ingameMenu",  closed = true,  text = shadowDetailText(),  tabs = {    {title = "Shadow Detail"},    {preset = Tab:Close()},    {preset = Tab:Back{previousWindow = "displayMenu"}},    {title = "Disable Shadows",      position = "left",      OnMouseReleaseAction = function()        Spring.SendCommands{"Shadows 0"}        SetConfigInt("Shadows", 0)        shadowDetail.lineArray[1] = shadowDetailText()      end    },    {title = "Low Detail Shadows",      position = "left",      OnMouseReleaseAction = function()        Spring.SendCommands{"shadows 1 1024"}        SetConfigInt("Shadows", 1)        SetConfigInt("ShadowMapSize", 1024)        shadowDetail.lineArray[1] = shadowDetailText()      end    },    {title = "Medium Detail Shadows",      position = "left",      OnMouseReleaseAction = function()        Spring.SendCommands{"shadows 1 2048"}        SetConfigInt("Shadows", 1)        SetConfigInt("ShadowMapSize", 2048)        shadowDetail.lineArray[1] = shadowDetailText()      end    },    {title = "High Detail Shadows",      position = "left",      OnMouseReleaseAction = function()        Spring.SendCommands{"shadows 1 4096"}        SetConfigInt("Shadows", 1)        SetConfigInt("ShadowMapSize", 4096)        shadowDetail.lineArray[1] = shadowDetailText()      end    },  }}-- Maximum Particles -----------------------------------------------------------local function MaxParticlesText()  local n = GetConfigInt("MaxParticles")  return "At most, "..n.." particles are displayed on-screen."endmaxParticles = Window:Create{  group = "ingameMenu",  closed = true,  lineArray = {MaxParticlesText()},  tabs = {    {title = "Maximum Particles"},    {preset = Tab:Close()},    {preset = Tab:Back{previousWindow = "displayMenu"}},    {title = "1000",      position = "left",      OnMouseReleaseAction = function()        Spring.SendCommands{"maxparticles 1000"}        SetConfigInt("MaxParticles", 1000)        maxParticles.lineArray[1] = MaxParticlesText()        maxParticles:UpdateDisplayList()      end    },    {title = "5000",      position = "left",      OnMouseReleaseAction = function()        Spring.SendCommands{"maxparticles 5000"}        SetConfigInt("MaxParticles", 5000)        maxParticles.lineArray[1] = MaxParticlesText()        maxParticles:UpdateDisplayList()      end    },    {title = "10000",      position = "left",      OnMouseReleaseAction = function()        Spring.SendCommands{"maxparticles 10000"}        SetConfigInt("MaxParticles", 10000)        maxParticles.lineArray[1] = MaxParticlesText()        maxParticles:UpdateDisplayList()      end    },    {title = "20000",      position = "left",      OnMouseReleaseAction = function()        Spring.SendCommands{"maxparticles 20000"}        SetConfigInt("MaxParticles", 20000)        maxParticles.lineArray[1] = MaxParticlesText()        maxParticles:UpdateDisplayList()      end    },    {title = "40000",      position = "left",      OnMouseReleaseAction = function()        Spring.SendCommands{"maxparticles 40000"}        SetConfigInt("MaxParticles", 40000)        maxParticles.lineArray[1] = MaxParticlesText()        maxParticles:UpdateDisplayList()      end    },  }}-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Main Menu Link Button---- mainButton = Window:Create{  -- x1 = 700,  -- y1 = 100,  -- textWidth = 300,  -- lineArray = {"Main Menu"},  -- useOnIsAbove = true,  -- OnMousePressAction = function()    -- mainButton.timeFromMousePress = MakeTimer()    -- return true  -- end,  -- OnMouseReleaseAction = function()    -- if (mainButton.timeFromMousePress() < 0.2) then -- Ignores press/realease          -- mainButton:Close()                            -- couples intended to drag       -- mainMenu:Open()                               -- the window, not click.    -- end    -- mainButton.timeFromMousePress = nil  -- end,-- }---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Room Info: FPS Manager------ Tries to keep the framerate around 35 by adjusting the detail level.-- In case of problems, pester quantum until he fixes all the bugs :P-- See LuaUI/Widgets/gui_doors.lua for documentation.---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Speed-ups--local Window        = Windowlocal Tab           = Tablocal SendCommands  = Spring.SendCommandslocal GetFPS        = Spring.GetFPS-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Constants--local tolerance  = 30local defaultLevelTable = {   {"advshading 1",                  "advshading 0"}, -- 1  {"luaui disablewidget BlobShadow", "luaui enablewidget BlobShadow"}, -- 2  {"shadows 1 1024",                "shadows 0"}, -- 3  {"maxparticles 10000",            "maxparticles 5000"}, -- 4  {"water 1",                       "water 0"}, -- 5  {"shadows 1 2048",                "shadows 1 1024"}, -- 6  {"water 3",                       "water 1"}, -- 7  {"shadows 4096",                  "shadows 1 2048"}, -- 8  {"maxparticles 15000",            "maxparticles 10000"}, -- 9  {"maxparticles 20000",            "maxparticles 15000"}, -- 10  {"dynamicsky 1",                  "dynamicsky 0"}, -- 11  {"water 2",                       "water 3"}, -- 12} -------------------------------------------------------------------------------------------------------------------------------------------------------------------- Locals--local counter       = 0local detailLevel   = 1local fpsTable      = {}local raisePoints   = 0local lowerPoints   = 0minFps = 30maxFps = 40      -------------------------------------------------------------------------------------------------------------------------------------------------------------------- FPS Management--function MakeManagerText()  local s  if (enabled) then    s = "enabled."  else    s = "disabled."  end  local lines = {    "The Frame Rate Manager tries to keep the framerate ",    string.format("around %d by adjusting the detail level. ", (minFps+maxFps)/2),    " ",    "It is "..s,  }  return linesendlocal function Remove(n)  table.remove(levelTable, n)endlocal function MakeLevelTable()  levelTable = Copy(defaultLevelTable)  local st = settingsTable  if (st.noWater3 or st.noWater) then    Remove(12)  end  if (st.noDynSky) then    Remove(11)  end  if (st.noMaxParticles) then    Remove(10)    Remove(9)  end  if (st.noShadows) then    Remove(8)  end  if (st.noWater) then    Remove(7)  end  if (st.noShadows) then    Remove(6)  end  if (st.noWater) then    Remove(5)  end  if (st.noMaxParticles) then    Remove(4)  end  if (st.noShadows) then    Remove(3)  end  if (st.noBlobs) then    Remove(2)  end  if (st.noAdvShad) then    Remove(1)  endendlocal function RaiseDetail()  raisePoints = raisePoints + 1  if ((detailLevel <= #levelTable) and      (raisePoints > tolerance)) then    SendCommands({levelTable[detailLevel][1]})    detailLevel = detailLevel + 1	raisePoints = 0  endendlocal function LowerDetail()  lowerPoints = lowerPoints + 1  if ((detailLevel > 1) and      (lowerPoints > tolerance)) then    SendCommands({levelTable[detailLevel - 1][2]})    detailLevel = detailLevel - 1    lowerPoints = 0  endendlocal function OnUpdate(t)  if (enabled) then    counter  = counter + 1    table.insert(fpsTable, GetFPS())    if (#fpsTable > 30) then      table.remove(fpsTable, 1)    end    if ((counter+23) % 30 < 0.1) then      counter = 0      sum = 0      for _, v in ipairs(fpsTable) do        sum = sum + v      end      average = (sum / #fpsTable)      if (average > maxFps) then        RaiseDetail()      else        raisePoints = 0      end      if (average < minFps) then        LowerDetail()      else        lowerPoints = 0      end    end  endendlocal function GetConfigData() -- Save  room_FPSManager = {     enabled = enabled,    settingsTable = settingsTable,    minFps = minFps,    maxFps = maxFps,  }  return "room_FPSManager", room_FPSManagerendlocal function SetConfigData(data) -- Load    if data.room_FPSManager then    enabled = data.room_FPSManager.enabled    settingsTable = data.room_FPSManager.settingsTable or {}    maxFps = data.room_FPSManager.maxFps or 40    minFps = data.room_FPSManager.minFps or 30  else    data.room_FPSManager = {}    enabled = false    settingsTable = {}    maxFps = 40    minFps = 30  end   if (enabled) then    levelTable = Copy(defaultLevelTable)    detailLevel = #levelTable    for i=1, tolerance*15 do      LowerDetail()    end  end  detailLevel = 1  MakeLevelTable()  managerOptions.lineArray =  MakeManagerText()  moreOptions.lineArray = MakeOptionsText()endlocal function MakeOptionsButton(label, setting)  local tab = {    title = label,    position = "left",    OnMouseReleaseAction = function()      if (settingsTable[setting]) then        settingsTable[setting] = nil      else        settingsTable[setting] = true      end      MakeLevelTable()      moreOptions.lineArray = MakeOptionsText()    end,  }  return tabendmanagerOptions = Window:Create{  lineArray = MakeManagerText(),  group = "FPSManager",  closed = true,  OnUpdate = OnUpdate,  GetConfigData = GetConfigData,  SetConfigData = SetConfigData,  tabs = {    {title = "Frame Rate Manager"},    {preset = Tab:Close()},    {preset = Tab:Back{previousWindow = "displayMenu"}},    {title = "Enable",      position = "left",      OnMouseReleaseAction = function()        enabled = true        managerOptions.lineArray =  MakeManagerText()      end,    },    {title = "Disable",      position = "left",      OnMouseReleaseAction = function()        enabled = nil        managerOptions.lineArray =  MakeManagerText()      end    },    {title = "Options",      position = "left",      OnMouseReleaseAction = function()        managerOptions:Close()        moreOptions:Open()      end    },  }, }local function Check(setting, o)  if (settingsTable[setting] and not o) then    return "disabled."  elseif (not o) then    return "enabled."  elseif (settingsTable[setting] and o) then    return "disallowed."  else    return "allowed."  endendfunction MakeOptionsText()  local st = settingsTable  local lines = {  string.format("The target frame rate is %d (%d - %d)",                 (minFps+maxFps)/2, minFps, maxFps),  "Sky management is "..Check("noDynSky"),  "Dynamic water is "..Check("noWater3", true),  "Shadow management is "..Check("noShadows"),  "Water management is "..Check("noWater"),  "Particle management is "..Check("noMaxParticles"),  "Blob aircraft shadows are "..Check("noBlobs", true),  "Unit shading management is "..Check("noAdvShad"),  " ",  "Blob shadows require the BlobShadows widget.",  }  return linesendmoreOptions = Window:Create{  lineArray = {"Blob shadows require the BlobShadows widget."},  group = "FPSManager",  closed = true,  tabs = {    {title = "Frame Rate Manager: Options"},    {preset = Tab:Close()},    {preset = Tab:Back{previousWindow = "managerOptions"}},    MakeOptionsButton("Toggle Sky Management", "noDynSky"),    MakeOptionsButton("Allow Dynamic Water", "noWater3"),    MakeOptionsButton("Toggle Shadow management", "noShadows"),    MakeOptionsButton("Toggle Water Management", "noWater"),    MakeOptionsButton("Toggle Particle Management", "noMaxParticles"),    MakeOptionsButton("Allow Blob Shadows", "noBlobs"),    MakeOptionsButton("Toggle Unit Shading Management", "noAdvShad"),    {title = "Decrease Target FPS",      position = "bottom",      OnMouseReleaseAction = function()        maxFps = maxFps * .8        minFps = minFps * .8         moreOptions.lineArray = MakeOptionsText()         managerOptions.lineArray =  MakeManagerText()      end,    },    {title = "Increase Target FPS",      position = "bottom",      OnMouseReleaseAction = function()        maxFps = maxFps * 1.2        minFps = minFps * 1.2         moreOptions.lineArray = MakeOptionsText()         managerOptions.lineArray =  MakeManagerText()      end,    },  }}