diff --git a/lua/autorun/server/sv_init.lua b/lua/autorun/server/sv_init.lua index 69fa389..b80b37f 100644 --- a/lua/autorun/server/sv_init.lua +++ b/lua/autorun/server/sv_init.lua @@ -6,4 +6,6 @@ AddCSLuaFile("cfc_disconnect_interface/client/cl_ponger.lua") AddCSLuaFile("cfc_disconnect_interface/client/cl_api.lua") AddCSLuaFile("cfc_disconnect_interface/client/cl_interface.lua") +resource.AddSingleFile("materials/icons/cross.png") + include("cfc_disconnect_interface/server/sv_pinger.lua") \ No newline at end of file diff --git a/lua/cfc_disconnect_interface/client/cl_interface.lua b/lua/cfc_disconnect_interface/client/cl_interface.lua index 914b47f..5eaa2fc 100644 --- a/lua/cfc_disconnect_interface/client/cl_interface.lua +++ b/lua/cfc_disconnect_interface/client/cl_interface.lua @@ -13,7 +13,7 @@ surface.CreateFont( "CFC_Normal", surface.CreateFont( "CFC_Special", { font = "coolvetica", - size = 26, + size = 30, weight = 500 } ) @@ -33,11 +33,16 @@ local GAME_WIDTH = 1256 local interfaceDerma = false -local TIME_TO_RESTART = 10 +local TIME_TO_RESTART = 180 local timeDown = 0 local apiState local previouslyShown = false +-- Helper function +function getFrom(i, ...) + return ({...})[i] +end + -- Colors primaryCol = Color( 36, 41, 67 ) secondaryCol = Color( 42, 47, 74 ) @@ -84,7 +89,7 @@ end -- Creates and populates a title bar for the frame local function addTitleBar(frame) local frameW, frameH = frame:GetSize() - local titleBarHeight = 32 + local titleBarHeight = 70 -- The bar itself local titleBar = vgui.Create( "DPanel", frame ) @@ -96,11 +101,12 @@ local function addTitleBar(frame) end -- Close button, could be removed, but I personally think it should stay, allows you to save e2/sf files - local closeBtnPadding = (titleBarHeight - 16) / 2 + local closeBtnSize = 32 + local closeBtnPadding = (titleBarHeight - closeBtnSize) / 2 local closeBtn = vgui.Create( "DImageButton", titleBar ) - closeBtn:SetSize( 16, 16 ) - closeBtn:SetPos( frameW - 16 - closeBtnPadding, closeBtnPadding) - closeBtn:SetImage( "icon16/cross.png" ) + closeBtn:SetSize( closeBtnSize, closeBtnSize ) + closeBtn:SetPos( frameW - closeBtnSize - closeBtnPadding, closeBtnPadding) + closeBtn:SetImage( "icons/cross.png" ) function closeBtn:DoClick() frame:Close() end @@ -109,10 +115,16 @@ local function addTitleBar(frame) local titleLabelPadding = (titleBarHeight - 26) / 2 local titleLabel = vgui.Create( "DLabel", titleBar ) titleLabel:SetFont( "CFC_Special" ) - titleLabel:SetText( "Oops! Looks like the server crashed..." ) - titleLabel:SizeToContents() - titleLabel:SetPos( 0, titleLabelPadding + 2 ) - titleLabel:CenterHorizontal() + + local function setTitle(title) + titleLabel:SetText( title ) + titleLabel:SizeToContents() + titleLabel:SetPos( 0, titleLabelPadding + 2 ) + titleLabel:CenterHorizontal() + end + + setTitle("Oops! Looks like you disconnected...") + titleBar.setTitle = setTitle return titleBar end @@ -121,18 +133,21 @@ end -- xFraction is 0-1 for how far across the button should be -- Colours are self explan local function makeButton(frame, text, xFraction, doClick, outlineCol, fillCol, hoverOutlineCol, hoverFillCol) - -- Defaults for colours - outlineCol = outlineCol or Color( 255, 255, 255 ) - fillCol = fillCol or primaryCol - hoverOutlineCol = hoverOutlineCol or Color(155,241,255) - hoverFillCol = hoverFillCol or primaryCol + local frameW, frameH = frame:GetSize() local btn = vgui.Create( "DButton", frame ) + + -- Defaults for colours + btn.outlineCol = outlineCol or Color( 255, 255, 255 ) + btn.fillCol = fillCol or primaryCol + btn.hoverOutlineCol = hoverOutlineCol or Color( 255, 255, 255 ) + btn.hoverFillCol = hoverFillCol or primaryCol + btn:SetText( text ) btn:SetTextColor( Color( 255, 255, 255 ) ) btn:SetFont( "CFC_Button" ) - btn:SetSize( frameW * 0.4, frameH * 0.6 ) + btn:SetSize( frameW * 0.3, frameH ) btn:CenterHorizontal( xFraction ) btn:CenterVertical() btn.DoClick = doClick @@ -141,7 +156,7 @@ local function makeButton(frame, text, xFraction, doClick, outlineCol, fillCol, btn.fadeState = 0 btn.prevTime = CurTime() - local btnAnimSpeed = 0.05 * 60 + local btnAnimSpeed = 0.1 * 60 function btn:Think() -- Make anim same speed for all framerates @@ -160,47 +175,99 @@ local function makeButton(frame, text, xFraction, doClick, outlineCol, fillCol, function btn:Paint(w, h) local lineCol local bgCol + local borderWeight if self:GetDisabled() then lineCol = Color( 74, 74, 74 ) - bgCol = fillCol + bgCol = self.fillCol + borderWeight = btnBorderWeight self:SetCursor( "no" ) else - lineCol = lerpColor(self.fadeState, outlineCol, hoverOutlineCol) - bgCol = lerpColor(self.fadeState, fillCol, hoverFillCol) + lineCol = lerpColor(self.fadeState, self.outlineCol, self.hoverOutlineCol) + bgCol = lerpColor(self.fadeState, self.fillCol, self.hoverFillCol) + borderWeight = 1.5 * btnBorderWeight + 1.5 * self.fadeState * btnBorderWeight self:SetCursor( "hand" ) end self:SetTextColor( lineCol ) - surface.SetDrawColor( lineCol ) - surface.DrawRect( 0, 0, w, h ) - surface.SetDrawColor( bgCol ) - surface.DrawRect( btnBorderWeight, btnBorderWeight, - w - (btnBorderWeight*2), h - (btnBorderWeight*2) ) + draw.RoundedBox(h/2, 0, 0, w, h, lineCol) + + local nw, nh = w - (borderWeight*2), h - (borderWeight*2) + draw.RoundedBox(nh/2, borderWeight, borderWeight, nw, nh, bgCol) + end return btn end +local function showMessage(msg) + if interfaceDerma and (interfaceDerma.messageLabel:GetText() ~= msg or not interfaceDerma.messageLabel:IsVisible()) then + if interfaceDerma.messageLabel:IsVisible() then + interfaceDerma.messageLabel:AlphaTo(0, 0.25) + timer.Simple(0.25, function() + interfaceDerma.messageLabel:setTextAndAlign(msg) + interfaceDerma.messageLabel:AlphaTo(255, 0.25) + end) + else + interfaceDerma.messageLabel:setTextAndAlign(msg) + interfaceDerma.messageLabel:Show() + interfaceDerma.messageLabel:AlphaTo(255, 0.5) + end + end +end + +local function hideMessage(msg) + if interfaceDerma and interfaceDerma.messageLabel:IsVisible() then + interfaceDerma.messageLabel:AlphaTo(0, 0.25) + timer.Simple(0.25, function() + interfaceDerma.messageLabel:Hide() + end) + end +end + -- Create bar panel and add buttons local function addButtonsBar(frame) local frameW, frameH = frame:GetSize() - local buttonBarHeight = 64 + local buttonBarHeight = 90 + local buttonBarOffset = 90 local barPanel = vgui.Create( "DPanel", frame ) barPanel:SetSize( frameW, buttonBarHeight ) - barPanel:SetPos( 0, frameH - buttonBarHeight ) - function barPanel:Paint(w, h) - surface.SetDrawColor( accentCol ) - surface.DrawLine( 16, 0, w - 16, 0 ) - end + barPanel:SetPos( 0, frameH - buttonBarHeight - buttonBarOffset ) + barPanel.Paint = nil -- Put buttons onto the panel as members for easy access - barPanel.reconBtn = makeButton(barPanel, "RECONNECT", 0.25, rejoin, - Color( 74, 251, 191 ), nil, Color( 74, 251, 191 ), Color( 64, 141, 131 )) + barPanel.reconBtn = makeButton(barPanel, "RECONNECT", 0.25, function() + if not barPanel.disconMode then + rejoin() + else + leave() + end + end) + --Color( 74, 251, 191 ), nil, Color( 74, 251, 191 ), Color( 64, 141, 131 )) -- Reconnect button will usually start as disabled barPanel.reconBtn:SetDisabled( true ) - barPanel.disconBtn = makeButton(barPanel, "DISCONNECT", 0.75, leave) + barPanel.disconBtn = makeButton( barPanel, "DISCONNECT", 0.75, function(self) + if not barPanel.disconMode then + showMessage("Are you sure? Hang in there, the server will restart soon...") + barPanel.disconMode = true + barPanel.disconPrevDisabled = barPanel.reconBtn:GetDisabled() + barPanel.reconBtn:SetDisabled(false) + self:SetText("NO") + self.fadeState = 0 + self.hoverOutlineCol = Color( 255, 0, 0 ) + barPanel.reconBtn.hoverOutlineCol = Color( 0, 255, 0 ) + barPanel.reconBtn:SetText("YES") + else + hideMessage() + barPanel.disconMode = false + self:SetText("DISCONNECT") + self.hoverOutlineCol = Color( 255, 255, 255 ) + barPanel.reconBtn:SetText("RECONNECT") + barPanel.reconBtn.hoverOutlineCol = Color( 255, 255, 255 ) + barPanel.reconBtn:SetDisabled(barPanel.disconPrevDisabled) + end + end) return barPanel end @@ -209,26 +276,30 @@ end local function makeLabel(frame, text, top, col, xFraction) col = col or Color( 255, 255, 255 ) local label = vgui.Create( "DLabel", frame ) - label:SetText( text ) label:SetFont( "CFC_Special" ) - label:SizeToContents() - label:SetPos( 0, top ) - label:SetTextColor( col ) - label:CenterHorizontal( xFraction ) + function label:setTextAndAlign( str ) + self:SetText( str ) + self:SizeToContents() + self:SetPos( 0, top ) + self:SetTextColor( col ) + self:CenterHorizontal( xFraction ) + end + label:setTextAndAlign(text) return label end -- Text for internet down on body local function populateBodyInternetDown(body) - local label1 = makeLabel(body, "Looks like your internet has gone down!", 20) - local label2 = makeLabel(body, "Stick around for when it comes back", 64) + local label1 = makeLabel(body, "Please check you're still connected to the internet.", 20) + local label2 = makeLabel(body, "In the meantime,", 80) + return "Oops, looks like you disconnected" end -- Text for server down on body local function populateBodyServerDown(body) local frameW, frameH = body:GetSize() - local restartTimeStr = "The server normally takes about " .. secondsAsTime(TIME_TO_RESTART) .. " to restart!" + local restartTimeStr = "The server normally takes about " .. secondsAsTime(TIME_TO_RESTART) .. " to restart." local restartTimeLabel = makeLabel(body, restartTimeStr, 0) local curTimePreLabel = makeLabel(body, "It has been down for", 32) -- When the server comes back up, "It has been down for" => "It was down for" @@ -242,50 +313,51 @@ local function populateBodyServerDown(body) end end - -- Label for when timeDown > averageTimeDown - -- Currently on the right, change the 0.8 at the end to move it horizontally - local tooLongLabel = makeLabel(body, "Uh oh, seems it's taking a little longer than usual!", 70, Color( 251, 191, 83 ), 0.8) - tooLongLabel:SetAlpha(0) - tooLongLabel:Hide() - -- Text for downTime, update its value in Think -- If server comes back up, make it green and stop updating it - -- If timeDown > averageTimeDown, make it red and show the tooLongLabel + -- If timeDown > averageTimeDown, make it red and show the messageLabel local curTimeLabel = makeLabel(body, secondsAsTime(math.floor(timeDown)), 70, Color( 251, 191, 83 )) function curTimeLabel:Think() if apiState ~= crashApi.SERVER_UP then self:SetText(secondsAsTime(math.floor(timeDown))) if timeDown > TIME_TO_RESTART then self:SetTextColor(Color(255, 0, 0)) - if not tooLongLabel:IsVisible() then - tooLongLabel:Show() - tooLongLabel:AlphaTo(255, 1) + if not messageLabel:IsVisible() then + showMessage("Uh oh, seems it's taking a little longer than usual...") end end else self:SetTextColor(Color(0, 255, 0)) end end + + return "Oops, looks like the server crashed..." end -- Fill the body with elements, body created elsewhere as it's size relies on size of titleBar and buttonsBar local function populateBody(body) body.Paint = nil + local title + local frameW, frameH = body:GetSize() + + -- Warning message label + interfaceDerma.messageLabel = makeLabel(body, "", frameH - 45, Color( 255, 255, 0 ), 0.5) + interfaceDerma.messageLabel:SetAlpha(0) + interfaceDerma.messageLabel:Hide() -- Fill top text based on crashApi state if apiState == crashApi.NO_INTERNET then - populateBodyInternetDown(body) + title = populateBodyInternetDown(body) else -- Server down or up via api, and down via net - populateBodyServerDown(body) + title = populateBodyServerDown(body) end - local frameW, frameH = 0.8 * ScrW(), 0.8 * ScrH() local playGameLabel = makeLabel(body, "Why not play a game while you wait? (Press space)", 108) -- Game wrapper, in case we ever want to make a game that runs in lua local gamePanel = vgui.Create( "DPanel", body ) gamePanel:SetSize( frameW - 20, frameH - 134 - 15 ) - gamePanel:SetPos( -6, 134 + 10 ) + gamePanel:SetPos( 10, 134 ) gamePanel.Paint = nil -- HTML element rending game at GAME_URL, constantly grabs focus @@ -296,6 +368,8 @@ local function populateBody(body) function gameHtml:Think() if not gameHtml:HasFocus() then gameHtml:RequestFocus() end end + + return title end -- Entry point for creating the interface @@ -328,9 +402,10 @@ local function createInterface() -- Create body that fills the unused space local body = vgui.Create( "DPanel", frame ) - body:SetSize(frameW - 32, frameH - 32 - titlePanel:GetTall() - btnsPanel:GetTall()) + body:SetSize(frameW - 32, getFrom(2, btnsPanel:GetPos()) - 32 - titlePanel:GetTall()) body:SetPos(16, titlePanel:GetTall() + 16) - populateBody(body) + local title = populateBody(body) + titlePanel.setTitle( title ) -- If server fully recovers without crashing, close menu -- If server reboots, enabled the reconnect button @@ -340,7 +415,6 @@ local function createInterface() elseif apiState == crashApi.SERVER_UP then if btnsPanel.reconBtn:GetDisabled() == true then btnsPanel.reconBtn:SetDisabled( false ) -- Server back up - -- Maybe show a "The server is back up, click here to reconnect?" end end end @@ -350,6 +424,8 @@ local function createInterface() end end +concommand.Add("cfc_interface", createInterface) + hook.Add("cfc_di_crashTick", "cfc_di_interfaceUpdate", function(isCrashing, _timeDown, _apiState) timeDown = _timeDown or 0 apiState = _apiState @@ -362,7 +438,7 @@ hook.Add("cfc_di_crashTick", "cfc_di_interfaceUpdate", function(isCrashing, _tim if not isCrashing then previouslyShown = false if interfaceDerma then - interfaceDerma:Close() + --interfaceDerma:Close() end end end) diff --git a/materials/icons/cross.png b/materials/icons/cross.png new file mode 100644 index 0000000..2b466b0 Binary files /dev/null and b/materials/icons/cross.png differ