-------------------------------------------------------------------------------------------------------------------------------------------------------------------- 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 endendfunction 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) endendfunction 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() endendfunction 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 endendfunction 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, }, }}