DeathBoardClient = DeathBoardClient or {} local Constants = DeathBoardClient.Constants or {} local Layout = Constants.Layout or {} local Colors = Constants.Colors or {} local ProtocolHandler = DeathBoardClient.Protocol or Constants.Protocol or require("Codex\\DeathBoard\\Managers\\Protocol") DeathBoardClient.RowsPerPage = DeathBoardClient.RowsPerPage or 6 DeathBoardClient.CurrentPage = DeathBoardClient.CurrentPage or 1 DeathBoardClient.TotalPages = DeathBoardClient.TotalPages or 1 DeathBoardClient.Rows = DeathBoardClient.Rows or {} DeathBoardClient.IsLoading = DeathBoardClient.IsLoading or false DeathBoardClient.LastPageRowCount = DeathBoardClient.LastPageRowCount or 0 DeathBoardClient.HUDKill = DeathBoardClient.HUDKill or {} DeathBoardClient.Features = DeathBoardClient.Features or { ServerColumn = false } DeathBoardClient.Cooldown = DeathBoardClient.Cooldown or { Open = 0, Page = 0 } DeathBoardClient.PendingPage = DeathBoardClient.PendingPage or nil local DeathBoardProtocolHandlerReady = false local function EnsureProtocolHandler() if DeathBoardProtocolHandlerReady then return ProtocolHandler end if ProtocolHandler and ProtocolHandler.Setup then ProtocolHandler.Setup(DeathBoardClient.__ProtocolDeps or { Protocol = Constants.Protocol }) end DeathBoardProtocolHandlerReady = true return ProtocolHandler end local function DB2_NowMs() if GetTime then local ok, t = pcall(GetTime) if ok and t then return t end end if os.clock then return math.floor(os.clock() * 1000) end return 0 end local function SafeGLColor4(r, g, b, a) if glColor4f then glColor4f(r, g, b, a) elseif SetColor then local R = math.floor((r or 1) * 255) local G = math.floor((g or 1) * 255) local B = math.floor((b or 1) * 255) local A = math.floor((a or 1) * 255) SetColor(R, G, B, A) end end local function SafeGLColor3(r, g, b) if glColor3f then glColor3f(r, g, b) end end local function DB2_SafeCall(fn, ...) local ok = false if type(fn) == "function" then ok = pcall(fn, ...) end if not ok then DeathBoardClient.__degraded = true end return ok end local function DB2_NormalizeHex64(hex) if type(hex) ~= "string" then return "" end local h = tostring(hex or "") h = string.gsub(h, "[^0-9a-fA-F]", "") if (#h % 2) == 1 then h = "0" .. h end if #h < 64 then h = string.rep("0", 64 - #h) .. h elseif #h > 64 then h = string.sub(h, #h - 64 + 1) end return h end local function DB2_BytesToHex(bytes) if type(bytes) ~= "table" then return "" end local out = {} for i = 1, #bytes do local v = tonumber(bytes[i]) or 0 if v < 0 then v = 0 end if v > 255 then v = 255 end out[i] = string.format("%02X", v) end return DB2_NormalizeHex64(table.concat(out)) end local function DB2_B64UrlDecode(data) data = tostring(data or ""):gsub("-", "+"):gsub("_", "/") local pad = #data % 4 if pad == 2 then data = data .. "==" end if pad == 3 then data = data .. "=" end local b='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/' local t = {} data:gsub(".", function(x) if x == "=" then return end local r = b:find(x, 1, true) if r then table.insert(t, r-1) end end) local out = {} for i=1, #t, 4 do local n = (t[i] or 0) * 262144 + (t[i+1] or 0) * 4096 + (t[i+2] or 0) * 64 + (t[i+3] or 0) local c1 = math.floor(n / 65536) % 256 local c2 = math.floor(n / 256) % 256 local c3 = n % 256 if t[i+1] then table.insert(out, string.char(c1)) end if t[i+2] then table.insert(out, string.char(c2)) end if t[i+3] then table.insert(out, string.char(c3)) end end return table.concat(out) end local function DB2_GetByte(packetName, offset) if not GetBytePacket then return 0 end local ok, value = pcall(GetBytePacket, packetName, offset) if not ok then return 0 end return tonumber(value) or 0 end local function DB2_ReadString(packetName, offset) local len = DB2_GetByte(packetName, offset) offset = offset + 1 if len < 0 then len = 0 end local bytes = {} for i = 1, len do bytes[i] = string.char(DB2_GetByte(packetName, offset)) offset = offset + 1 end return table.concat(bytes), offset end local function DB2_ReadGuildSide(packetName, offset) local name, newOffset = DB2_ReadString(packetName, offset) if name == "" then return "", "", newOffset end local markBytes = {} for i = 1, 32 do markBytes[i] = DB2_GetByte(packetName, newOffset) newOffset = newOffset + 1 end return name, DB2_BytesToHex(markBytes), newOffset end local function DB2_ClientLog(message) local dbg = DeathBoardClient and DeathBoardClient.Config and DeathBoardClient.Config.Debug local enabled = dbg and dbg.Enabled if not enabled then return end local msg = "[DeathBoardClient] " .. tostring(message or "") if Console then Console(1, msg) elseif LogPrint then LogPrint(msg) end end local function HasImage(id) if DeathBoardClient.__panic_no_images or DeathBoardClient.__degraded or DeathBoardClient.__no_images then return false end return type(id) == "number" and id > 0 end local MAX_PAGES = 15 DeathBoardClient.Cache = DeathBoardClient.Cache or { pages = {}, totalPages = 0 } local function DB2_CopyRowCache(r) if not r then return nil end return { KillerName = r.KillerName or "", VictimName = r.VictimName or "", TimeText = r.TimeText or "", MapName = r.MapName or "", KillerClassKey = r.KillerClassKey or "SM", VictimClassKey = r.VictimClassKey or "DL", KillerGuildName = r.KillerGuildName or "", KillerGuildMarkHex = r.KillerGuildMarkHex or "", VictimGuildName = r.VictimGuildName or "", VictimGuildMarkHex = r.VictimGuildMarkHex or "", ServerCode = r.ServerCode or 0 } end local function DB2_ShowCachedPage(page) local c = DeathBoardClient.Cache and DeathBoardClient.Cache.pages and DeathBoardClient.Cache.pages[page] if not c or not c.rows then return false end local out = {} for i=1,#c.rows do out[i] = DB2_CopyRowCache(c.rows[i]) end DeathBoardClient.Rows = out DeathBoardClient.LastPageRowCount = c.count or #out DeathBoardClient.SetPagination(page, DeathBoardClient.Cache.totalPages or DeathBoardClient.TotalPages or 1) DeathBoardClient.IsLoading = false return true end local function DB2_FlushPendingPage(reason) if DeathBoardClient.IsLoading then return false end local pending = tonumber(DeathBoardClient.PendingPage) if not pending or pending < 1 then DeathBoardClient.PendingPage = nil return false end DeathBoardClient.PendingPage = nil DB2_ClientLog(string.format("RequestPage flush pending page=%d reason=%s", pending, tostring(reason or ""))) DeathBoardClient.RequestPage(pending) return true end DeathBoardClient.ServerNameMapLoaded = DeathBoardClient.ServerNameMapLoaded or false local function DB2_TryLoadServerNames() if DeathBoardClient.ServerNameMapLoaded then return end local names = {} local list = DeathBoardClient.Config and DeathBoardClient.Config.Servers if list and type(list) == "table" then for _, srv in pairs(list) do local id = tonumber(srv and srv.ID) local nm = srv and srv.Name if id ~= nil and nm and nm ~= "" then names[id] = tostring(nm) end end end if next(names) == nil then names[0] = "Server 1" names[1] = "Server 2" end DeathBoardClient.ServerNames = names DeathBoardClient.ServerNameMapLoaded = true end DB2_TryLoadServerNames() local function DB2_CopyArray(a) local r = {} for i=1,#a do r[i]=a[i] end return r end local function DB2_ComputeLayout(x, y, width) local tbar = Layout.TitleBar or {} local baseBarW = tbar.Width or 346 local baseInnerW = tbar.InnerWidth or 314 local baseColW = math.floor(baseInnerW / 5) local baseWidths = { baseColW, baseColW, baseColW, baseColW, baseInnerW - baseColW * 4 } local edge = baseBarW - baseInnerW local serverOn = DeathBoardClient.Features and DeathBoardClient.Features.ServerColumn local colWidths, barW, innerW if serverOn then local w6 = DB2_CopyArray(baseWidths) w6[6] = baseWidths[5] colWidths = w6 innerW = baseInnerW + baseWidths[5] barW = innerW + edge else colWidths = baseWidths innerW = baseInnerW barW = baseBarW end local barX = x + math.floor((width - barW) / 2) local innerX = barX + math.floor((barW - innerW) / 2) return { barW=barW, innerW=innerW, barX=barX, innerX=innerX, barH=tbar.Height or 23, colWidths=colWidths } end local function DB2_SafeLayout(x, y, width) local ok, res = pcall(DB2_ComputeLayout, x, y, width) if ok and res and res.colWidths then return res end local tbar = Layout.TitleBar or {} local barW = tbar.Width or 346 local innerW = tbar.InnerWidth or 314 local baseColW = math.floor(innerW / 5) local colWidths = { baseColW, baseColW, baseColW, baseColW, innerW - baseColW * 4 } local barX = x + math.floor((width - barW) / 2) local innerX = barX + math.floor((barW - innerW) / 2) return { barW=barW, innerW=innerW, barX=barX, innerX=innerX, barH=tbar.Height or 23, colWidths=colWidths } end local function DB2_ComputeRowLayout(x, y, width, height) local L = DB2_SafeLayout(x, y, width) local rowLayout = Layout.Row or {} local rowH = rowLayout.Height or 18 local gapTop = (rowLayout.GapTopFromTitleBar or 2) + (rowLayout.TopExtra or 2) local baseGap = rowLayout.GapBetweenRows or 2 local rowsPerPage = DeathBoardClient.RowsPerPage or 6 local pag = Layout.Pagination or {} local pageBtnSize = pag.ButtonSize or 17 local bottomOffset = pag.BottomOffset or 15 local bottomMin = pag.BottomMin or 8 local barYOffset = (Layout.TitleBar and Layout.TitleBar.YOffset) or 46 local barH = (Layout.TitleBar and Layout.TitleBar.Height) or 23 local barY = y + barYOffset local bottomY = y + height local pageY = bottomY - bottomOffset - pageBtnSize local available = pageY - (barY + barH) - gapTop - bottomMin if available < 0 then available = 0 end local totalRowsH = rowsPerPage * rowH local remaining = available - totalRowsH local gapBetween = baseGap if rowsPerPage > 1 and remaining >= 0 then gapBetween = math.max(baseGap, math.floor(remaining / (rowsPerPage - 1))) end local baseRowY = barY + barH + gapTop return { rowH=rowH, gapTop=gapTop, gapBetween=gapBetween, baseRowY=baseRowY } end local ExclusiveWindows = { 1, 2, 3, 4, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 20, 18, 19, 21, 33, 35, 36, 57, 65, 73, 74, 75, 77, 78, 121 } DeathBoardClient.Window = DeathBoardClient.Window or {} DeathBoardClient.ButtonOpenState = DeathBoardClient.ButtonOpenState or 0 DeathBoardClient.ButtonCloseState = DeathBoardClient.ButtonCloseState or 0 DeathBoardClient.ButtonPagePrevState = DeathBoardClient.ButtonPagePrevState or 0 DeathBoardClient.ButtonPageNextState = DeathBoardClient.ButtonPageNextState or 0 local Window = DeathBoardClient.Window Window.IsOpen = Window.IsOpen or false local function IsCharacterLoggedIn() if GetMainScene then local okScene, scene = pcall(GetMainScene) if not okScene or scene ~= 5 then return false end return true end if GetViewMaxHP then local okHp, maxHP = pcall(GetViewMaxHP) if okHp and (maxHP == nil or maxHP <= 0) then return false end end if GetMap then local okMap, m = pcall(GetMap) if okMap and (m == nil or m == -1) then return false end end local name = "" if GetMyName then local okName, n = pcall(GetMyName) if okName and n then name = n end end if name == "" and UserGetName then local okUser, n = pcall(UserGetName) if okUser and n then name = n end end if name == "" and GetPlayerName then local okPlayer, n = pcall(GetPlayerName) if okPlayer and n then name = n end end if name == "" then return false end return true end local function IsSmartWindowOpen() if SmartWindowClient and SmartWindowClient.CheckOpen and SmartWindowClient.CheckOpen() then return true end if UICustomInterface == 121 then return true end return false end local function RenderOpenButton() if CheckWindowOpen and CheckWindowOpen(65) == 1 then DeathBoardClient.ButtonOpenState = 0 return end local cfg = DeathBoardClient.Config and DeathBoardClient.Config.Button or {} local btnW = cfg.Width if btnW == nil then btnW = (Constants.Window and Constants.Window.ButtonOpenW) or 25 end local btnH = cfg.Height if btnH == nil then btnH = (Constants.Window and Constants.Window.ButtonOpenH) or 25 end local btnX = cfg.X if btnX == nil then btnX = (Constants.Window and Constants.Window.ButtonOpenX) or 80 end local btnY = cfg.Y if btnY == nil then btnY = (Constants.Window and Constants.Window.ButtonOpenY) or 20 end local btnImg = Constants.Images and Constants.Images.BTN_OPEN local btnGroup = Constants.UV and Constants.UV.ButtonOpen local btnState = DeathBoardClient.ButtonOpenState or 0 local btnUVs if btnGroup then if btnState == 2 and btnGroup.Pressed then btnUVs = btnGroup.Pressed elseif btnState == 1 and btnGroup.Hover then btnUVs = btnGroup.Hover else btnUVs = btnGroup.Normal or btnGroup.Hover or btnGroup.Pressed end end if RenderImage2 and HasImage(btnImg) and btnUVs then SafeGLColor4(1.0, 1.0, 1.0, 1.0) RenderImage2(btnImg, btnX, btnY, btnW, btnH, btnUVs.U, btnUVs.V, btnUVs.UW, btnUVs.VH, 1, 1, 1.0) elseif RenderImage and HasImage(btnImg) then SafeGLColor3(1.0, 1.0, 1.0) RenderImage(btnImg, btnX, btnY, btnW, btnH) end end local function GetClassStyle(key) if key == "BK" then return {1.0, 0.2, 0.2}, "Blade Knight" elseif key == "SM" then return {0.2, 0.5, 1.0}, "Soul Master" elseif key == "ELF" then return {0.2, 1.0, 0.2}, "Muse Elf" elseif key == "MG" then return {1.0, 0.4, 1.0}, "Magic Gladiator" elseif key == "DL" then return {1.0, 0.8, 0.2}, "Dark Lord" elseif key == "SU" then return {1.0, 0.2, 0.8}, "Summoner" elseif key == "RF" then return {0.5, 0.5, 0.5}, "Rage Fighter" end return {1.0, 1.0, 1.0}, "Unknown" end local GuildMarkPalette = { {0, 0, 0, 0}, {0, 0, 0, 255}, {128, 128, 128, 255}, {255, 255, 255, 255}, {255, 0, 0, 255}, {255, 165, 0, 255}, {255, 255, 0, 255}, {128, 255, 0, 255}, {0, 255, 0, 255}, {0, 255, 128, 255}, {0, 255, 255, 255}, {0, 128, 255, 255}, {0, 0, 255, 255}, {128, 0, 255, 255}, {255, 0, 255, 255}, {255, 0, 128, 255} } local function DecodeGuildMarkHex(hex) if type(hex) ~= "string" then return nil end local h = tostring(hex or "") h = string.gsub(h, "[^0-9a-fA-F]", "") if (#h % 2) == 1 then h = "0" .. h end if #h < 64 then h = string.rep("0", 64 - #h) .. h elseif #h > 64 then h = string.sub(h, #h - 64 + 1) end if #h ~= 64 or not string.match(h, "^[0-9a-fA-F]+$") then return nil end -- Se o hex for todo zero, considerar como "sem guild": não desenhar nada if string.match(h, "^0+$") then return nil end local pixels = {} local pIndex = 1 for i = 1, 64, 2 do local byteHex = string.sub(h, i, i + 1) local byte = tonumber(byteHex, 16) if not byte then return nil end local p1 = math.floor(byte / 16) local p2 = byte % 16 pixels[pIndex] = p1 pixels[pIndex + 1] = p2 pIndex = pIndex + 2 end -- Se todos os pixels forem zero, também tratar como "sem guild" local allZero = true for i=1,64 do if (pixels[i] or 0) ~= 0 then allZero = false break end end if allZero then return nil end return pixels end local function EnsureGuildPixels(rowData, fieldHexName, fieldPixelsName) if not rowData then return nil end local cached = rowData[fieldPixelsName] if cached then return cached end local hex = rowData[fieldHexName] local pixels = DecodeGuildMarkHex(hex) DB2_ClientLog(string.format("EnsureGuildPixels field=%s hexLen=%d pixels=%s", tostring(fieldHexName), string.len(tostring(hex or "")), tostring(pixels ~= nil))) rowData[fieldPixelsName] = pixels return pixels end local function RenderGuildMarkPixels(pixels, x, y, size, alpha) if not pixels or not alpha or alpha <= 0 then return end if not DrawBar or not glColor4f then return end if SetBlend then SetBlend(1) end if DisableTexture then DisableTexture() end -- Desenhar borda de 2px (Preta) glColor4f(0.0, 0.0, 0.0, alpha) DrawBar(x - 2, y - 2, size + 4, size + 4) local cellSize = size / 8 local idx = 1 for py = 0, 7 do local cellY = y + py * cellSize for px = 0, 7 do local colorIndex = pixels[idx] or 0 idx = idx + 1 if colorIndex >= 0 and colorIndex <= 15 then local c = GuildMarkPalette[colorIndex + 1] if c and c[4] > 0 then glColor4f(c[1] / 255.0, c[2] / 255.0, c[3] / 255.0, (c[4] / 255.0) * alpha) local cellX = x + px * cellSize DrawBar(cellX, cellY, cellSize, cellSize) end end end end if EndDrawBar then EndDrawBar() end glColor4f(1.0, 1.0, 1.0, 1.0) if SetBlend then SetBlend(0) end end local function RenderKillHud() local list = DeathBoardClient.HUDKill if not list or #list == 0 then return end local img = Constants.Images and Constants.Images.HUD_KILL_TGA local uv = Constants.UV and Constants.UV.HUDKill local uvClass = Constants.UV and (Constants.UV.HUDClassIcon or Constants.UV.ClassIcon) if not img or not uv or not HasImage(img) then return end local now = (os.clock and os.clock()) or 0 local hudScale = 1.0 do local cfg = DeathBoardClient.Config and DeathBoardClient.Config.HUD local s = cfg and cfg.Scale s = tonumber(s) if s and s > 0 and s <= 2 then hudScale = s end end local baseX = 40 local baseY = 300 local width = math.floor(250 * hudScale) local height = math.floor(49 * hudScale) local spacing = math.max(1, math.floor(4 * hudScale)) local enterDuration = 0.4 local holdDuration = 2.0 local exitDuration = 0.35 local maxItems = 3 local alive = {} local count = #list if count > maxItems then local startIndex = count - maxItems + 1 local trimmed = {} local ti = 1 for i = startIndex, count do trimmed[ti] = list[i] ti = ti + 1 end list = trimmed count = #list end local hadBlend = false if SetBlend and glColor4f then SetBlend(1) hadBlend = true end for i = 1, count do local item = list[i] local startTime = item.StartTime or now local t = now - startTime if t < 0 then t = 0 end local totalDuration = enterDuration + holdDuration + exitDuration if t < totalDuration then local index = #alive + 1 local targetY = baseY + (index - 1) * (height + spacing) local x = math.floor(baseX) local y = math.floor(targetY) local alpha = 1.0 if t < enterDuration then local p = t / enterDuration if p < 0 then p = 0 end if p > 1 then p = 1 end alpha = p local startOffset = math.floor(30 * hudScale) y = math.floor(targetY + (startOffset * (1.0 - p))) elseif t < enterDuration + holdDuration then alpha = 1.0 else local p = (t - enterDuration - holdDuration) / exitDuration if p < 0 then p = 0 end if p > 1 then p = 1 end alpha = 1.0 - p local exitOffset = math.floor(150 * hudScale) x = math.floor(baseX - (exitOffset * p)) end if alpha > 0 then if RenderImage2 then if glColor4f then glColor4f(1.0, 1.0, 1.0, alpha) end RenderImage2(img, x, y, width, height, uv.U, uv.V, uv.UW, uv.VH, 1, 1, 1.0) elseif RenderImage then if glColor4f then glColor4f(1.0, 1.0, 1.0, alpha) elseif glColor3f then glColor3f(alpha, alpha, alpha) end RenderImage(img, x, y, width, height) end if uvClass and RenderImage2 then local sizeClass = math.max(12, math.floor(48 * hudScale)) local halfClass = math.floor(sizeClass / 2) local centerY = y + math.floor((height - sizeClass) / 2) local leftCenterX = x local rightCenterX = x + width local leftX = leftCenterX - halfClass local rightX = rightCenterX - halfClass local killerKey = item.KillerClassKey or "SM" local victimKey = item.VictimClassKey or "DL" local imgKiller = Constants.Images and (Constants.Images["CLASS_" .. tostring(killerKey) .. "_TGA"] or Constants.Images["CLASS_" .. tostring(killerKey)]) local imgVictim = Constants.Images and (Constants.Images["CLASS_" .. tostring(victimKey) .. "_TGA"] or Constants.Images["CLASS_" .. tostring(victimKey)]) local pulse = 0.7 + 0.3 * math.sin(now * 8) -- Pulsa alpha entre 0.4 e 1.0 if imgKiller and HasImage(imgKiller) then -- Efeito Zoom Impact local scale = 1.0 if t < 0.3 then scale = 1.0 + (1.0 - (t / 0.3)) end local drawSize = math.floor(sizeClass * scale) local drawX = leftCenterX - math.floor(drawSize / 2) local drawY = centerY + math.floor((sizeClass - drawSize) / 2) RenderImage2(imgKiller, drawX, drawY, drawSize, drawSize, uvClass.U, uvClass.V, uvClass.UW, uvClass.VH, 1, 1, 1.0) end if imgVictim and HasImage(imgVictim) then local scale = 1.0 if t < 0.3 then scale = 1.0 + (1.0 - (t / 0.3)) end local drawSize = math.floor(sizeClass * scale) local drawX = rightCenterX - math.floor(drawSize / 2) local drawY = centerY + math.floor((sizeClass - drawSize) / 2) RenderImage2(imgVictim, drawX, drawY, drawSize, drawSize, uvClass.U, uvClass.V, uvClass.UW, uvClass.VH, 1, 1, 1.0) end local killer = item.KillerName or "" local victim = item.VictimName or "" local alphaInt = math.floor(255 * alpha) if alphaInt < 0 then alphaInt = 0 end if alphaInt > 255 then alphaInt = 255 end if SetFontType and SetTextColor and SetTextBg and RenderText3 then SetTextBg(0, 0, 0, 0) local textY = y + math.floor(height / 2) local skullCenterX = x + math.floor(width / 2) local skullOffset = math.floor(20 * hudScale) -- Killer local killerAreaStartX = leftX + sizeClass local killerAreaEndX = skullCenterX - skullOffset local killerAreaWidth = killerAreaEndX - killerAreaStartX if killer ~= "" then local centerX_Killer = killerAreaStartX + math.floor(killerAreaWidth / 2) -- Nome agora BRANCO conforme solicitado SetFontType(3) SetTextColor(255, 255, 255, alphaInt) RenderText3(centerX_Killer, textY, killer, killerAreaWidth, 8) end -- Victim local victimAreaStartX = skullCenterX + skullOffset local victimAreaEndX = rightX local victimAreaWidth = victimAreaEndX - victimAreaStartX if victim ~= "" then local centerX_Victim = victimAreaStartX + math.floor(victimAreaWidth / 2) -- Nome agora BRANCO conforme solicitado SetFontType(3) SetTextColor(255, 255, 255, alphaInt) RenderText3(centerX_Victim, textY, victim, victimAreaWidth, 8) end end -- Renderização das Guild Marks local sizeGuild = math.max(8, math.floor(16 * hudScale)) local guildPixelsKiller = EnsureGuildPixels(item, "KillerGuildMarkHex", "KillerGuildPixels") local guildPixelsVictim = EnsureGuildPixels(item, "VictimGuildMarkHex", "VictimGuildPixels") if guildPixelsKiller then local gx = leftCenterX + math.floor(8 * hudScale) local gy = y + height - sizeGuild RenderGuildMarkPixels(guildPixelsKiller, gx, gy, sizeGuild, alpha) end if guildPixelsVictim then local gx = rightCenterX - math.floor(8 * hudScale) - sizeGuild local gy = y + height - sizeGuild RenderGuildMarkPixels(guildPixelsVictim, gx, gy, sizeGuild, alpha) end end end alive[#alive + 1] = item end end DeathBoardClient.HUDKill = alive if glColor4f then glColor4f(1.0, 1.0, 1.0, 1.0) elseif glColor3f then glColor3f(1.0, 1.0, 1.0) end if hadBlend and SetBlend then SetBlend(0) end end local function EnsureExclusiveWindows(smartWindowOpen) if not Window.IsOpen then return end if DeathBoardClient.IsLoading then return end local mustClose = false if CheckWindowOpen then for _, w in ipairs(ExclusiveWindows) do if CheckWindowOpen(w) == 1 then mustClose = true break end end end if smartWindowOpen then mustClose = true end if mustClose then DeathBoardClient.Close() end end local function GetWindowRect() local width = (Constants.Window and Constants.Window.Width) or 380 local height = (Constants.Window and Constants.Window.Height) or 251 height = height + 10 if DeathBoardClient.Features and DeathBoardClient.Features.ServerColumn then local tbar = Layout.TitleBar or {} local baseBarW = tbar.Width or 346 local factor = 6 / 5 local scaledBarW = math.floor(baseBarW * factor) local desiredW = scaledBarW + 20 if desiredW > width then width = desiredW end local hf = 1.06 height = math.floor(height * hf) + 4 end local x = Window.X or 0 local y = Window.Y or 0 return x, y, width, height end function DeathBoardClient.CenterWindow() local _, _, width, height = GetWindowRect() local screenW = (Constants.GetTrueWidth and Constants.GetTrueWidth()) or width local screenH = 480 Window.X = (screenW - width) / 2 Window.Y = (screenH - height) / 2 end local function RenderLoadingOverlay(x, y, width, height) local barW = 136 local barH = 2 local barX = x + math.floor((width - barW) / 2) local barY = y + math.floor(height / 2) local p = DeathBoardClient.LoadingProgress or 0 if p < 90 then p = p + 3 if p > 90 then p = 90 end DeathBoardClient.LoadingProgress = p end if DrawBar then if SetBlend then SetBlend(1) end SafeGLColor4(0.1, 0.1, 0.1, 1.0) DrawBar(barX, barY, barW, barH) SafeGLColor4(1.0, 0.5, 0.0, 1.0) DrawBar(barX, barY, barW * (p / 100), barH) if EndDrawBar then EndDrawBar() end SafeGLColor4(1.0, 1.0, 1.0, 1.0) if SetBlend then SetBlend(0) end end if RenderText3 and SetFontType and SetTextColor and SetTextBg then SetFontType(3) SetTextColor(255, 255, 255, 255) SetTextBg(0, 0, 0, 0) local percentText = tostring(p) .. "%" RenderText3(x + math.floor(width / 2), barY + 8, percentText, 100, 8) end end local function RenderBackground(x, y, width, height) local bgId = Constants.Images and Constants.Images.BACKGROUND local uvBg = Constants.UV and Constants.UV.Background local layoutTitleBar = Layout.TitleBar or {} local barW = layoutTitleBar.Width or 346 local barH = layoutTitleBar.Height or 23 local barYOffset = layoutTitleBar.YOffset or 46 local innerW = layoutTitleBar.InnerWidth or 314 if DeathBoardClient.Features and DeathBoardClient.Features.ServerColumn then local factor = 6 / 5 barW = math.floor(barW * factor) innerW = math.floor(innerW * factor) end local bgW = barW + 40 local bgX = x + math.floor((width - bgW) / 2) if RenderImage2 and HasImage(bgId) and uvBg then SafeGLColor4(1.0, 1.0, 1.0, 1.0) RenderImage2(bgId, bgX, y, bgW, height, uvBg.U, uvBg.V, uvBg.UW, uvBg.VH, 1, 1, 1.0) elseif RenderImage and HasImage(bgId) then SafeGLColor3(1.0, 1.0, 1.0) RenderImage(bgId, bgX, y, bgW, height) end end local function RenderTitleBarAndColumns(x, y, width) local titleBarImg = Constants.Images and Constants.Images.TITLE_BAR local uvTitleBar = Constants.UV and Constants.UV.TitleBar if not titleBarImg or not uvTitleBar then return end local layoutTitleBar = Layout.TitleBar or {} local L = DB2_SafeLayout(x, y, width) local barW = L.barW local barH = layoutTitleBar.Height or 23 local barYOffset = layoutTitleBar.YOffset or 46 local innerW = L.innerW local barX = L.barX local barY = y + barYOffset if RenderImage2 and HasImage(titleBarImg) and uvTitleBar then glColor4f(1.0, 1.0, 1.0, 1.0) RenderImage2(titleBarImg, barX, barY, barW, barH, uvTitleBar.U, uvTitleBar.V, uvTitleBar.UW, uvTitleBar.VH, 1, 1, 1.0) elseif RenderImage and HasImage(titleBarImg) then glColor3f(1.0, 1.0, 1.0) RenderImage(titleBarImg, barX, barY, barW, barH) end local innerX = L.innerX local columnsLayout = Layout.Columns or {} local colWidths = L.colWidths local colCount = #colWidths local colTitles = columnsLayout.Titles or { "KILLER", "VICTIM", "CLASS", "TIME", "MAP" } if DeathBoardClient.Features and DeathBoardClient.Features.ServerColumn then colTitles = { colTitles[1], colTitles[2], colTitles[3], colTitles[4], colTitles[5], "SERVER" } end local cfgText = DeathBoardClient.Config and DeathBoardClient.Config.TextKeys if cfgText and cfgText.Columns then if DeathBoardClient.Features and DeathBoardClient.Features.ServerColumn then colTitles = { cfgText.Columns.Killer or colTitles[1], cfgText.Columns.Victim or colTitles[2], cfgText.Columns.Class or colTitles[3], cfgText.Columns.Time or colTitles[4], cfgText.Columns.Map or colTitles[5], (cfgText.Columns.Server or "SERVER") } else colTitles = { cfgText.Columns.Killer or colTitles[1], cfgText.Columns.Victim or colTitles[2], cfgText.Columns.Class or colTitles[3], cfgText.Columns.Time or colTitles[4], cfgText.Columns.Map or colTitles[5] } end end local textOffsetY = columnsLayout.TitleOffsetY or 8 local textY = barY + textOffsetY local headerColor = Colors.HeaderGold or { 237, 214, 161, 255 } SetFontType(1) SetTextBg(0, 0, 0, 0) SetTextColor(headerColor[1], headerColor[2], headerColor[3], headerColor[4]) if DrawBar and glColor4f then local sepLayout = Layout.Separators or {} local sepH = sepLayout.Height or 10 local sepW = sepLayout.Width or 0.5 local sepAlpha = sepLayout.Alpha or 0.7 local sepY = barY + math.floor((barH - sepH) / 2) if SetBlend then SetBlend(1) end glColor4f(headerColor[1] / 255.0, headerColor[2] / 255.0, headerColor[3] / 255.0, sepAlpha) local currentX = innerX for i = 1, colCount - 1 do local colW = colWidths[i] local sepX = math.floor(currentX + colW) DrawBar(sepX, sepY, sepW, sepH) currentX = currentX + colW end if EndDrawBar then EndDrawBar() end if SetBlend then SetBlend(0) end end local currentX = innerX for i = 1, colCount do local colW = colWidths[i] local centerX = currentX + math.floor(colW / 2) if i == 1 then centerX = centerX + (columnsLayout.TitleXOffsetFirst or -2) elseif i == colCount then centerX = centerX + (columnsLayout.TitleXOffsetLast or 2) end local cx = math.floor(centerX) local ty = math.floor(textY) if RenderText3 then RenderText3(cx, ty, colTitles[i], colW, 8) elseif RenderText2 then RenderText2(cx, ty, colTitles[i], colW, 8) elseif RenderText then RenderText(cx, ty, colTitles[i], colW, 1) end currentX = currentX + colW end end local function RenderRowBackground(x, y, width, rowIndex) local titleBarImg = Constants.Images and Constants.Images.TITLE_BAR local uvTitleBar = Constants.UV and Constants.UV.TitleBar if not titleBarImg or not uvTitleBar then return end local L = DB2_SafeLayout(x, y, width) local layoutTitleBar = Layout.TitleBar or {} local barH = layoutTitleBar.Height or 23 local barYOffset = layoutTitleBar.YOffset or 46 local barW = L.barW local innerW = L.innerW local barX = L.barX local barY = y + barYOffset local innerX = L.innerX local rowLayout = Layout.Row or {} local rl = DeathBoardClient.__rows_layout local rowH = (rl and rl.rowH) or (rowLayout.Height or 18) local rowGapY = (rl and rl.gapTop) or (rowLayout.GapTopFromTitleBar or 2) local rowGapBetween = (rl and rl.gapBetween) or (rowLayout.GapBetweenRows or 2) local rowIndexValue = rowIndex or 1 local baseRowY = (rl and rl.baseRowY) or (barY + barH + rowGapY) local rowY = baseRowY + (rowIndexValue - 1) * (rowH + rowGapBetween) local hoverIndex = DeathBoardClient.HoverRowIndex or 0 local isHover = (hoverIndex == rowIndexValue) local drawRowH = rowH local drawRowY = rowY local columnsLayout = Layout.Columns or {} local colWidths = L.colWidths local colCount = #colWidths local leftExtra = innerX - barX local rightExtra = (barX + barW) - (innerX + innerW) local gap = 1 local cellImg = Constants.Images and Constants.Images.BG_INFO local uvCell = Constants.UV and Constants.UV.BgInfo if not cellImg or not uvCell or not HasImage(cellImg) then return end local currentX = barX for i = 1, colCount do local colW = colWidths[i] local fullW = colW if i == 1 then fullW = colW + leftExtra elseif i == colCount then fullW = colW + rightExtra end local drawX = math.floor(currentX) local drawW if i == colCount then drawW = (barX + barW) - drawX else drawW = math.floor(fullW - gap) end if drawW < 0 then drawW = 0 end if RenderImage2 then local baseAlpha = 1.0 local hoverAlpha = 1.0 local alpha = isHover and hoverAlpha or baseAlpha glColor4f(1.0, 1.0, 1.0, alpha) RenderImage2(cellImg, drawX, drawRowY, drawW, drawRowH, uvCell.U, uvCell.V, uvCell.UW, uvCell.VH, 1, 1, 1.0) elseif RenderImage then glColor3f(1.0, 1.0, 1.0) RenderImage(cellImg, drawX, drawRowY, drawW, drawRowH) end currentX = currentX + drawW if i < colCount then currentX = currentX + gap end end end local function RenderRowVerticalSeparators(x, y, width, rowIndex) if not DrawBar or not glColor4f then return end local layoutTitleBar = Layout.TitleBar or {} local barW = layoutTitleBar.Width or 346 local barH = layoutTitleBar.Height or 23 local barYOffset = layoutTitleBar.YOffset or 46 local innerW = layoutTitleBar.InnerWidth or 314 if DeathBoardClient.Features and DeathBoardClient.Features.ServerColumn then local factor = 6 / 5 barW = math.floor(barW * factor) innerW = math.floor(innerW * factor) end local barX = x + math.floor((width - barW) / 2) local barY = y + barYOffset local innerX = barX + math.floor((barW - innerW) / 2) local rowLayout = Layout.Row or {} local rl = DeathBoardClient.__rows_layout local rowH = (rl and rl.rowH) or (rowLayout.Height or 18) local rowGapY = (rl and rl.gapTop) or (rowLayout.GapTopFromTitleBar or 2) local rowGapBetween = (rl and rl.gapBetween) or (rowLayout.GapBetweenRows or 2) local rowIndexValue = rowIndex or 1 local baseRowY = (rl and rl.baseRowY) or (barY + barH + rowGapY) local rowY = baseRowY + (rowIndexValue - 1) * (rowH + rowGapBetween) local drawRowH = rowH local drawRowY = math.floor(rowY) local columnsLayout = Layout.Columns or {} local colCount = (DeathBoardClient.Features and DeathBoardClient.Features.ServerColumn) and 6 or (columnsLayout.Count or 5) local baseColW = math.floor(innerW / colCount) local colWidths = {} local remaining = innerW for i = 1, colCount - 1 do colWidths[i] = baseColW remaining = remaining - baseColW end colWidths[colCount] = remaining local sepLayout = Layout.Separators or {} local sepW = sepLayout.Width or 0.5 local sepAlpha = sepLayout.Alpha or 0.7 local gap = 1 local sepH = drawRowH - (gap * 2) local sepY = drawRowY + gap local headerColor = Colors.HeaderGold or { 237, 214, 161, 255 } if SetBlend then SetBlend(1) end glColor4f(headerColor[1] / 255.0, headerColor[2] / 255.0, headerColor[3] / 255.0, sepAlpha) local currentX = innerX for i = 1, colCount - 1 do local colW = colWidths[i] local sepX = math.floor(currentX + colW) DrawBar(sepX, sepY, sepW, sepH) currentX = currentX + colW end if EndDrawBar then EndDrawBar() end if SetBlend then SetBlend(0) end end local function RenderRowHorizontalSeparator(x, y, width, rowIndex) if not DrawBar or not glColor4f then return end local layoutTitleBar = Layout.TitleBar or {} local barW = layoutTitleBar.Width or 346 local barH = layoutTitleBar.Height or 23 local barYOffset = layoutTitleBar.YOffset or 46 local innerW = layoutTitleBar.InnerWidth or 314 if DeathBoardClient.Features and DeathBoardClient.Features.ServerColumn then local factor = 6 / 5 barW = math.floor(barW * factor) innerW = math.floor(innerW * factor) end local barX = x + math.floor((width - barW) / 2) local barY = y + barYOffset local innerX = barX + math.floor((barW - innerW) / 2) local rowLayout = Layout.Row or {} local rl = DeathBoardClient.__rows_layout local rowH = (rl and rl.rowH) or (rowLayout.Height or 18) local rowGapY = (rl and rl.gapTop) or (rowLayout.GapTopFromTitleBar or 2) local rowGapBetween = (rl and rl.gapBetween) or (rowLayout.GapBetweenRows or 2) local rowIndexValue = rowIndex or 1 local baseRowY = (rl and rl.baseRowY) or (barY + barH + rowGapY) local rowY = baseRowY + (rowIndexValue - 1) * (rowH + rowGapBetween) local sepLayout = Layout.Separators or {} local thickness = sepLayout.Width or 0.5 local sepAlpha = sepLayout.Alpha or 0.7 local gapCenter = rowY + rowH + (rowGapBetween / 2.0) local sepY = math.floor(gapCenter - (thickness / 2.0)) if SetBlend then SetBlend(1) end local gray = 0.55 glColor4f(gray, gray, gray, sepAlpha) DrawBar(barX, sepY, barW, thickness) if EndDrawBar then EndDrawBar() end if SetBlend then SetBlend(0) end end local function RenderRowClassIcons(x, y, width, rowIndex) local titleBarImg = Constants.Images and Constants.Images.TITLE_BAR local uvTitleBar = Constants.UV and Constants.UV.TitleBar if not titleBarImg or not uvTitleBar then return end local layoutTitleBar = Layout.TitleBar or {} local L = DB2_SafeLayout(x, y, width) local barW = L.barW local barH = layoutTitleBar.Height or 23 local barYOffset = layoutTitleBar.YOffset or 46 local barX = L.barX local barY = y + barYOffset local innerW = L.innerW local innerX = L.innerX local rowLayout = Layout.Row or {} local rl = DeathBoardClient.__rows_layout local rowH = (rl and rl.rowH) or (rowLayout.Height or 18) local rowGapY = (rl and rl.gapTop) or (rowLayout.GapTopFromTitleBar or 2) local rowGapBetween = (rl and rl.gapBetween) or (rowLayout.GapBetweenRows or 2) local rowIndexValue = rowIndex or 1 local baseRowY = (rl and rl.baseRowY) or (barY + barH + rowGapY) local rowY = baseRowY + (rowIndexValue - 1) * (rowH + rowGapBetween) local drawRowH = rowH local drawRowY = rowY local columnsLayout = Layout.Columns or {} local colCount = (DeathBoardClient.Features and DeathBoardClient.Features.ServerColumn) and 6 or (columnsLayout.Count or 5) local baseColW = math.floor(innerW / colCount) local colWidths = {} local remaining = innerW for i = 1, colCount - 1 do colWidths[i] = baseColW remaining = remaining - baseColW end colWidths[colCount] = remaining local leftExtra = innerX - barX local rightExtra = (barX + barW) - (innerX + innerW) local gap = 1 local classIndex = 3 local currentX = barX for i = 1, classIndex - 1 do local colW = colWidths[i] local fullW = colW if i == 1 then fullW = colW + leftExtra elseif i == colCount then fullW = colW + rightExtra end local drawWPrev = math.floor(fullW - gap) currentX = currentX + drawWPrev + gap end local colW = colWidths[classIndex] local fullW = colW if classIndex == 1 then fullW = colW + leftExtra elseif classIndex == colCount then fullW = colW + rightExtra end local drawX = math.floor(currentX) local drawW = math.floor(fullW - gap) local iconsLayout = Layout.Icons or {} local sizeClass = iconsLayout.ClassSize or 14 local sizeKill = iconsLayout.KillSize or 14 local gapIcons = iconsLayout.Gap or 2 local killImg = Constants.Images and Constants.Images.KILL_ICON local uvKill = Constants.UV and Constants.UV.KillIcon local uvClass = Constants.UV and Constants.UV.ClassIcon if not killImg or not uvKill or not uvClass or not HasImage(killImg) then return end local classLeftImg local classRightImg local rows = DeathBoardClient.Rows or {} local rowData = rows[rowIndexValue] if rowData and Constants.Images then local killerKey = rowData.KillerClassKey or "SM" local victimKey = rowData.VictimClassKey or "DL" classLeftImg = Constants.Images["CLASS_" .. tostring(killerKey)] classRightImg = Constants.Images["CLASS_" .. tostring(victimKey)] end if not classLeftImg or not classRightImg then local rowIdx = rowIndex or 1 if rowIdx == 1 then classLeftImg = Constants.Images and Constants.Images.CLASS_SM classRightImg = Constants.Images and Constants.Images.CLASS_DL else classLeftImg = Constants.Images and Constants.Images.CLASS_ELF classRightImg = Constants.Images and Constants.Images.CLASS_RF end end if not classLeftImg or not classRightImg then return end local centerX = drawX + math.floor(drawW / 2) local killX = centerX - math.floor(sizeKill / 2) local killY = drawRowY + math.floor((drawRowH - sizeKill) / 2) local leftX = killX - gapIcons - sizeClass local rightX = killX + sizeKill + gapIcons local classY = drawRowY + math.floor((drawRowH - sizeClass) / 2) if RenderImage2 and HasImage(classLeftImg) and HasImage(classRightImg) then glColor4f(1.0, 1.0, 1.0, 1.0) RenderImage2(classLeftImg, leftX, classY, sizeClass, sizeClass, uvClass.U, uvClass.V, uvClass.UW, uvClass.VH, 1, 1, 1.0) RenderImage2(killImg, killX, killY, sizeKill, sizeKill, uvKill.U, uvKill.V, uvKill.UW, uvKill.VH, 1, 1, 1.0) RenderImage2(classRightImg, rightX, classY, sizeClass, sizeClass, uvClass.U, uvClass.V, uvClass.UW, uvClass.VH, 1, 1, 1.0) elseif RenderImage and HasImage(classLeftImg) and HasImage(classRightImg) then glColor3f(1.0, 1.0, 1.0) RenderImage(classLeftImg, leftX, classY, sizeClass, sizeClass) RenderImage(killImg, killX, killY, sizeKill, sizeKill) RenderImage(classRightImg, rightX, classY, sizeClass, sizeClass) end end local function RenderRowGuildMarks(x, y, width, rowIndex) local layoutTitleBar = Layout.TitleBar or {} local L = DB2_SafeLayout(x, y, width) local barW = L.barW local barH = layoutTitleBar.Height or 23 local barYOffset = layoutTitleBar.YOffset or 46 local barX = L.barX local barY = y + barYOffset local innerW = L.innerW local innerX = L.innerX local rowLayout = Layout.Row or {} local rl = DeathBoardClient.__rows_layout local rowH = (rl and rl.rowH) or (rowLayout.Height or 18) local rowGapY = (rl and rl.gapTop) or (rowLayout.GapTopFromTitleBar or 2) local rowGapBetween = (rl and rl.gapBetween) or (rowLayout.GapBetweenRows or 2) local rowIndexValue = rowIndex or 1 local baseRowY = (rl and rl.baseRowY) or (barY + barH + rowGapY) local rowY = baseRowY + (rowIndexValue - 1) * (rowH + rowGapBetween) local drawRowH = rowH local drawRowY = rowY local columnsLayout = Layout.Columns or {} local colCount = (DeathBoardClient.Features and DeathBoardClient.Features.ServerColumn) and 6 or (columnsLayout.Count or 5) local baseColW = math.floor(innerW / colCount) local colWidths = {} local remaining = innerW for i = 1, colCount - 1 do colWidths[i] = baseColW remaining = remaining - baseColW end colWidths[colCount] = remaining local leftExtra = innerX - barX local rightExtra = (barX + barW) - (innerX + innerW) local gap = 1 local iconsLayout = Layout.Icons or {} local sizeClass = iconsLayout.ClassSize or 14 local sizeGuild = (iconsLayout.GuildSize or (sizeClass - 2)) if sizeGuild < 10 then sizeGuild = 10 end local gapIcons = iconsLayout.Gap or 2 local rows = DeathBoardClient.Rows or {} local rowData = rows[rowIndexValue] if not rowData then return end local killerGuildName = rowData.KillerGuildName or "" local victimGuildName = rowData.VictimGuildName or "" local killerGuildMarkHex = rowData.KillerGuildMarkHex or "" local victimGuildMarkHex = rowData.VictimGuildMarkHex or "" DeathBoardClient.GuildHexCache = DeathBoardClient.GuildHexCache or {} local cache = DeathBoardClient.GuildHexCache local function is_hex64(hex) return type(hex) == "string" and #hex == 64 and string.match(hex, "^[0-9a-fA-F]+$") ~= nil end if cache and type(cache) == "table" then local vn = rowData.VictimName or "" local kn = rowData.KillerName or "" if kn ~= "" and is_hex64(cache[kn]) then killerGuildMarkHex = cache[kn] rowData.KillerGuildMarkHex = killerGuildMarkHex rowData.KillerGuildPixels = nil end if vn ~= "" and is_hex64(cache[vn]) then victimGuildMarkHex = cache[vn] rowData.VictimGuildMarkHex = victimGuildMarkHex rowData.VictimGuildPixels = nil end end local function hasNonBlank(str) return type(str) == "string" and string.find(str, "%S") ~= nil end local function hasValidMark(hex) return DecodeGuildMarkHex(hex) ~= nil end local function hasGuild(name, hex) if hasValidMark(hex) then return true end return false end local hasKillerGuild = hasGuild(killerGuildName, killerGuildMarkHex) local hasVictimGuild = hasGuild(victimGuildName, victimGuildMarkHex) DB2_ClientLog(string.format( "RowGuildMarks row=%d killer=%s kgName=%s kgLen=%d victim=%s vgName=%s vgLen=%d hasK=%s hasV=%s", rowIndexValue, tostring(rowData.KillerName or ""), tostring(killerGuildName or ""), string.len(tostring(killerGuildMarkHex or "")), tostring(rowData.VictimName or ""), tostring(victimGuildName or ""), string.len(tostring(victimGuildMarkHex or "")), tostring(hasKillerGuild), tostring(hasVictimGuild) )) local function GetColumnRect(index) local currentX = barX for i = 1, index - 1 do local colW = colWidths[i] local fullW = colW if i == 1 then fullW = colW + leftExtra elseif i == colCount then fullW = colW + rightExtra end local drawWPrev = math.floor(fullW - gap) currentX = currentX + drawWPrev + gap end local colW = colWidths[index] local fullW = colW if index == 1 then fullW = colW + leftExtra elseif index == colCount then fullW = colW + rightExtra end local drawX = math.floor(currentX) local drawW = math.floor(fullW - gap) return drawX, drawW end local c1x, c1w = GetColumnRect(1) local c2x, c2w = GetColumnRect(2) local guildY = drawRowY + math.floor((drawRowH - sizeGuild) / 2) local colPad = (Layout.Icons and Layout.Icons.GuildPad) or 8 local victimPad = colPad > 2 and (colPad - 2) or colPad local killerX = c1x + colPad local victimX = c2x + victimPad if killerX < c1x + 1 then killerX = c1x + 1 end if killerX + sizeGuild > c1x + c1w - 1 then killerX = (c1x + c1w - 1) - sizeGuild end if victimX < c2x + 1 then victimX = c2x + 1 end if victimX + sizeGuild > c2x + c2w - 1 then victimX = (c2x + c2w - 1) - sizeGuild end local placeholderId = Constants.Images and Constants.Images.GUILD_PLACEHOLDER local cellSize = (sizeGuild / 8) local killerPixels = hasKillerGuild and EnsureGuildPixels(rowData, "KillerGuildMarkHex", "KillerGuildPixels") or nil local victimPixels = hasVictimGuild and EnsureGuildPixels(rowData, "VictimGuildMarkHex", "VictimGuildPixels") or nil local function pixels_has_visible(pixels) if not pixels then return false end local any = false for i=1,64 do local idx = pixels[i] or 0 local c = GuildMarkPalette[(idx or 0) + 1] if c and c[4] and c[4] > 0 then any = true break end end return any end if hasKillerGuild and (not killerPixels or not pixels_has_visible(killerPixels)) then hasKillerGuild = false end if hasVictimGuild and (not victimPixels or not pixels_has_visible(victimPixels)) then hasVictimGuild = false end DB2_ClientLog(string.format( "RowGuildMarks row=%d killerPixels=%s victimPixels=%s visibleK=%s visibleV=%s", rowIndexValue, tostring(killerPixels ~= nil), tostring(victimPixels ~= nil), tostring(hasKillerGuild), tostring(hasVictimGuild) )) if DrawBar and glColor4f then if SetBlend then SetBlend(1) end if DisableTexture then DisableTexture() end if killerPixels and hasKillerGuild then local idx = 1 for py = 0, 7 do local cellY = guildY + py * cellSize for px = 0, 7 do local colorIndex = killerPixels[idx] or 0 idx = idx + 1 if colorIndex and colorIndex >= 0 and colorIndex <= 15 then local c = GuildMarkPalette[colorIndex + 1] if c and c[4] and c[4] > 0 then glColor4f(c[1] / 255.0, c[2] / 255.0, c[3] / 255.0, c[4] / 255.0) local cellX = killerX + px * cellSize DrawBar(cellX, cellY, cellSize, cellSize) end end end end -- Cache hex válido visível local kn = rowData.KillerName or "" if kn ~= "" and is_hex64(rowData.KillerGuildMarkHex) then cache[kn] = rowData.KillerGuildMarkHex end elseif hasKillerGuild then glColor4f(0.2, 0.8, 1.0, 1.0) DrawBar(killerX, guildY, sizeGuild, sizeGuild) end if victimPixels and hasVictimGuild then local idx = 1 for py = 0, 7 do local cellY = guildY + py * cellSize for px = 0, 7 do local colorIndex = victimPixels[idx] or 0 idx = idx + 1 if colorIndex and colorIndex >= 0 and colorIndex <= 15 then local c = GuildMarkPalette[colorIndex + 1] if c and c[4] and c[4] > 0 then glColor4f(c[1] / 255.0, c[2] / 255.0, c[3] / 255.0, c[4] / 255.0) local cellX = victimX + px * cellSize DrawBar(cellX, cellY, cellSize, cellSize) end end end end -- Cache hex válido visível local vn = rowData.VictimName or "" if vn ~= "" and is_hex64(rowData.VictimGuildMarkHex) then cache[vn] = rowData.VictimGuildMarkHex end elseif hasVictimGuild then glColor4f(1.0, 0.6, 0.2, 1.0) DrawBar(victimX, guildY, sizeGuild, sizeGuild) end if EndDrawBar then EndDrawBar() end glColor4f(1.0, 1.0, 1.0, 1.0) if SetBlend then SetBlend(0) end end if placeholderId and HasImage(placeholderId) then DB2_ClientLog(string.format( "RowGuildMarks row=%d placeholder killer=%s victim=%s", rowIndexValue, tostring(not hasKillerGuild), tostring(not hasVictimGuild) )) local uvGuild = Constants.UV and Constants.UV.ClassIcon if RenderImage2 and uvGuild then glColor4f(1.0, 1.0, 1.0, 1.0) if not hasKillerGuild then RenderImage2(placeholderId, killerX, guildY, sizeGuild, sizeGuild, uvGuild.U, uvGuild.V, uvGuild.UW, uvGuild.VH, 1, 1, 1.0) end if not hasVictimGuild then RenderImage2(placeholderId, victimX, guildY, sizeGuild, sizeGuild, uvGuild.U, uvGuild.V, uvGuild.UW, uvGuild.VH, 1, 1, 1.0) end elseif RenderImage then glColor3f(1.0, 1.0, 1.0) if not hasKillerGuild then RenderImage(placeholderId, killerX, guildY, sizeGuild, sizeGuild) end if not hasVictimGuild then RenderImage(placeholderId, victimX, guildY, sizeGuild, sizeGuild) end end end end local function RenderRowTexts(x, y, width, rowIndex) local layoutTitleBar = Layout.TitleBar or {} local L = DB2_SafeLayout(x, y, width) local barW = L.barW local barH = layoutTitleBar.Height or 23 local barYOffset = layoutTitleBar.YOffset or 46 local barX = L.barX local barY = y + barYOffset local innerW = L.innerW local innerX = L.innerX local rowLayout = Layout.Row or {} local rl = DeathBoardClient.__rows_layout local rowH = (rl and rl.rowH) or (rowLayout.Height or 18) local rowGapY = (rl and rl.gapTop) or (rowLayout.GapTopFromTitleBar or 2) local rowGapBetween = (rl and rl.gapBetween) or (rowLayout.GapBetweenRows or 2) local rowIndexValue = rowIndex or 1 local baseRowY = (rl and rl.baseRowY) or (barY + barH + rowGapY) local rowY = baseRowY + (rowIndexValue - 1) * (rowH + rowGapBetween) local drawRowH = rowH local drawRowY = rowY local columnsLayout = Layout.Columns or {} local colCount = (DeathBoardClient.Features and DeathBoardClient.Features.ServerColumn) and 6 or (columnsLayout.Count or 5) local baseColW = math.floor(innerW / colCount) local colWidths = {} local remaining = innerW for i = 1, colCount - 1 do colWidths[i] = baseColW remaining = remaining - baseColW end colWidths[colCount] = remaining local leftExtra = innerX - barX local rightExtra = (barX + barW) - (innerX + innerW) local gap = 1 local texts local rows = DeathBoardClient.Rows or {} local rowData = rows[rowIndexValue] if rowData then if DeathBoardClient.Features and DeathBoardClient.Features.ServerColumn then local function getServerLabel(code) DB2_TryLoadServerNames() local names = DeathBoardClient.ServerNames local key = tonumber(code or -1) if names and key and names[key] then return tostring(names[key] or "") end if code == nil then return "" end return "S" .. tostring(code) end texts = { rowData.KillerName or "", rowData.VictimName or "", "", rowData.TimeText or "", rowData.MapName or "", getServerLabel(rowData.ServerCode) } else texts = { rowData.KillerName or "", rowData.VictimName or "", "", rowData.TimeText or "", rowData.MapName or "" } end else if DeathBoardClient.Features and DeathBoardClient.Features.ServerColumn then texts = { "", "", "", "", "", "" } else texts = { "", "", "", "", "" } end end if SetFontType then SetFontType(3) end if SetTextBg then SetTextBg(0, 0, 0, 0) end local white = {255, 255, 255, 255} local killerColor = (Constants.Colors and Constants.Colors.KillerOrange) or {255, 128, 0, 255} local titleColor = (Constants.Colors and Constants.Colors.TitleGold) or {255, 215, 0, 255} local currentX = barX for i = 1, colCount do local colW = colWidths[i] local fullW = colW if i == 1 then fullW = colW + leftExtra elseif i == colCount then fullW = colW + rightExtra end local drawX = math.floor(currentX) local drawW if i == colCount then drawW = (barX + barW) - drawX else drawW = math.floor(fullW - gap) end local marginLeft = 0 if i == 1 or i == 2 then local sizeGuild = (Layout.Icons and Layout.Icons.ClassSize) or 14 local padKiller = (Layout.Icons and Layout.Icons.GuildPad) or 8 local padVictim = padKiller > 2 and (padKiller - 2) or padKiller local hasKIcon = false local hasVIcon = false if rowData then local killerName = rowData.KillerName or "" local victimName = rowData.VictimName or "" local kgn = rowData.KillerGuildName or "" local vgn = rowData.VictimGuildName or "" local kgm = rowData.KillerGuildMarkHex or "" local vgm = rowData.VictimGuildMarkHex or "" local function hasNonBlank(str) return type(str) == "string" and string.find(str, "%S") ~= nil end local function hasValidMark(hex) return DecodeGuildMarkHex(hex) ~= nil end local function hasGuild(name, hex) if hasValidMark(hex) then return true end return false end hasKIcon = hasGuild(kgn, kgm) or hasNonBlank(killerName) hasVIcon = hasGuild(vgn, vgm) or hasNonBlank(victimName) end if i == 1 and hasKIcon then marginLeft = padKiller + sizeGuild + 2 elseif i == 2 and hasVIcon then marginLeft = padVictim + sizeGuild + 2 end end local drawWAdj = drawW - marginLeft if drawWAdj < 0 then drawWAdj = 0 end local centerX = math.floor(drawX + marginLeft + math.floor(drawWAdj / 2)) local textY = math.floor(drawRowY + math.floor((drawRowH - 10) / 2) + 2) if i ~= 3 then textY = math.floor(textY - 1) end local content = texts[i] or "" if i ~= 3 and content ~= "" and RenderText3 then if SetTextColor then local c if i == 1 then c = killerColor elseif i == 4 then c = titleColor else c = white end SetTextColor(c[1], c[2], c[3], c[4]) end RenderText3(centerX, textY, content, drawWAdj, 8) end currentX = currentX + drawW if i < colCount then currentX = currentX + gap end end end local function RenderWindowTitle(x, y, width) local titleOffsetY = Layout.TitleOffsetY or 17 local L = DB2_SafeLayout(x, y, width) local titleX = L.barX + math.floor(L.barW / 2) local titleY = y + titleOffsetY local titleColor = Colors.TitleGold or { 255, 215, 0, 255 } if SetFontType then SetFontType(1) end if SetTextBg then SetTextBg(0, 0, 0, 0) end if SetTextColor then SetTextColor(titleColor[1], titleColor[2], titleColor[3], titleColor[4]) end local titleText = "DEATH BOARD" local cfgText = DeathBoardClient.Config and DeathBoardClient.Config.TextKeys if cfgText and cfgText.Title then titleText = cfgText.Title end local tx = math.floor(titleX) local ty = math.floor(titleY) if RenderText3 then RenderText3(tx, ty, titleText, L.barW, 8) elseif RenderText2 then RenderText2(tx, ty, titleText, L.barW, 8) elseif RenderText then RenderText(tx, ty, titleText, L.barW, 1) end end local function RenderCloseButton(x, y, width) local layoutClose = Layout.CloseButton or {} local closeW = layoutClose.Width or 16 local closeH = layoutClose.Height or 16 local closeMarginX = layoutClose.MarginX or 11 local closeMarginY = layoutClose.MarginY or 11 local closeX = x + width - closeW - closeMarginX local closeY = y + closeMarginY local closeImg = Constants.Images and Constants.Images.BTN_CLOSE local closeGroup = Constants.UV and Constants.UV.ButtonClose local closeState = DeathBoardClient.ButtonCloseState or 0 local closeUVs if closeGroup then if closeState == 2 and closeGroup.Pressed then closeUVs = closeGroup.Pressed elseif closeState == 1 and closeGroup.Hover then closeUVs = closeGroup.Hover else closeUVs = closeGroup.Normal or closeGroup.Hover or closeGroup.Pressed end end if RenderImage2 and HasImage(closeImg) and closeUVs then SafeGLColor4(1.0, 1.0, 1.0, 1.0) RenderImage2(closeImg, closeX, closeY, closeW, closeH, closeUVs.U, closeUVs.V, closeUVs.UW, closeUVs.VH, 1, 1, 1.0) elseif RenderImage and HasImage(closeImg) then SafeGLColor3(1.0, 1.0, 1.0) RenderImage(closeImg, closeX, closeY, closeW, closeH) end end local function RenderPagination(x, y, width, height) local layoutPagination = Layout.Pagination or {} local pageBtnSize = layoutPagination.ButtonSize or 17 local spacingBtns = layoutPagination.Spacing or 40 local bottomOffset = layoutPagination.BottomOffset or 15 local bottomY = y + height local pageY = bottomY - bottomOffset - pageBtnSize local centerXBtn = x + math.floor(width / 2) local prevX = centerXBtn - math.floor(spacingBtns / 2) - pageBtnSize local nextX = centerXBtn + math.floor(spacingBtns / 2) local pageUV = Constants.UV and Constants.UV.ButtonPage local imgPrev = Constants.Images and Constants.Images.BTN_PAGE_PREV local imgNext = Constants.Images and Constants.Images.BTN_PAGE_NEXT local prevState = DeathBoardClient.ButtonPagePrevState or 0 local nextState = DeathBoardClient.ButtonPageNextState or 0 local currentPage = DeathBoardClient.CurrentPage or 1 local totalPages = DeathBoardClient.TotalPages or 4 if RenderImage2 and pageUV and imgPrev and imgNext then local uvPrev = pageUV.Normal local uvNext = pageUV.Normal if prevState == 1 and pageUV.Hover then uvPrev = pageUV.Hover end if prevState == 2 and pageUV.Pressed then uvPrev = pageUV.Pressed end if nextState == 1 and pageUV.Hover then uvNext = pageUV.Hover end if nextState == 2 and pageUV.Pressed then uvNext = pageUV.Pressed end glColor4f(1.0, 1.0, 1.0, 1.0) RenderImage2(imgPrev, prevX, pageY, pageBtnSize, pageBtnSize, uvPrev.U, uvPrev.V, uvPrev.UW, uvPrev.VH, 1, 1, 1.0) RenderImage2(imgNext, nextX, pageY, pageBtnSize, pageBtnSize, uvNext.U, uvNext.V, uvNext.UW, uvNext.VH, 1, 1, 1.0) elseif RenderImage and imgPrev and imgNext then glColor3f(1.0, 1.0, 1.0) RenderImage(imgPrev, prevX, pageY, pageBtnSize, pageBtnSize) RenderImage(imgNext, nextX, pageY, pageBtnSize, pageBtnSize) end if SetFontType and SetTextBg and SetTextColor and RenderText3 then SetFontType(3) SetTextBg(0, 0, 0, 0) SetTextColor(255, 255, 255, 255) local text = tostring(currentPage) .. " / " .. tostring(totalPages) local textCenterX = math.floor(centerXBtn) local textY = math.floor(pageY + (pageBtnSize / 2)) RenderText3(textCenterX, textY, text, math.max(10, spacingBtns - 2), 8) end end function DeathBoardClient.CheckOpen() return Window.IsOpen == true end function DeathBoardClient.ClearRows() DeathBoardClient.Rows = {} DeathBoardClient.LastPageRowCount = 0 end function DeathBoardClient.SetRow(rowIndex, killerName, victimName, timeText, mapName, killerClassKey, victimClassKey, killerGuildName, killerGuildMarkHex, victimGuildName, victimGuildMarkHex) if not rowIndex or rowIndex < 1 then return end local rows = DeathBoardClient.Rows or {} local row = rows[rowIndex] or {} row.KillerName = killerName or "" row.VictimName = victimName or "" row.TimeText = timeText or "" local resolvedMapName = mapName or "" local cfg = DeathBoardClient.Config and DeathBoardClient.Config.MapNames if cfg and resolvedMapName ~= "" then local id = tonumber(string.match(resolvedMapName, "^Map#(%d+)$") or "") if id and cfg[id] then resolvedMapName = cfg[id] elseif cfg[resolvedMapName] then resolvedMapName = cfg[resolvedMapName] end end row.MapName = resolvedMapName row.KillerClassKey = killerClassKey or "SM" row.VictimClassKey = victimClassKey or "DL" row.KillerGuildName = killerGuildName or "" row.KillerGuildMarkHex = killerGuildMarkHex or "" row.VictimGuildName = victimGuildName or "" row.VictimGuildMarkHex = victimGuildMarkHex or "" rows[rowIndex] = row DeathBoardClient.Rows = rows local function hasNonBlank(str) return type(str) == "string" and string.find(str, "%S") ~= nil end local function hasValidMark(hex) if type(hex) ~= "string" then return false end if string.len(hex) ~= 64 then return false end if not string.match(hex, "^[0-9a-fA-F]+$") then return false end return true end local function hasGuild(name, hex) if not hasNonBlank(name) then return false end return hasValidMark(hex) end end function DeathBoardClient.SetPagination(currentPage, totalPages) local tp = tonumber(totalPages) or 1 tp = math.max(1, math.min(tp, MAX_PAGES)) local cp = tonumber(currentPage) or 1 cp = math.max(1, math.min(cp, tp)) DeathBoardClient.CurrentPage = cp DeathBoardClient.TotalPages = tp end function DeathBoardClient.ShowKillHud(killerName, victimName, killerClassKey, victimClassKey, killerGuildMarkHex, victimGuildMarkHex) local list = DeathBoardClient.HUDKill or {} local now = (os.clock and os.clock()) or 0 local count = #list if count >= 3 then local trimmed = {} local ti = 1 for i = (count - 3 + 2), count do trimmed[ti] = list[i] ti = ti + 1 end list = trimmed end local item = {} item.KillerName = killerName or "" item.VictimName = victimName or "" item.KillerClassKey = killerClassKey or "SM" item.VictimClassKey = victimClassKey or "DL" item.KillerGuildMarkHex = killerGuildMarkHex or "" item.VictimGuildMarkHex = victimGuildMarkHex or "" item.StartTime = now list[#list + 1] = item DeathBoardClient.HUDKill = list end DeathBoardClient.__ProtocolDeps = { Protocol = Constants.Protocol, MaxPages = MAX_PAGES, NowMs = DB2_NowMs, ShowCachedPage = DB2_ShowCachedPage, CopyRowCache = DB2_CopyRowCache, FlushPendingPage = DB2_FlushPendingPage, Log = DB2_ClientLog, BytesToHex = DB2_BytesToHex, NormalizeHex = DB2_NormalizeHex64 } function DeathBoardClient.RequestPage(page) local handler = EnsureProtocolHandler() local ok, result = pcall(handler.RequestPage, page) if ok then return result end DB2_ClientLog("Protocol RequestPage failed: " .. tostring(result)) return nil end function DeathBoardClient.Open() local now = DB2_NowMs() DB2_ClientLog(string.format("Open enter now=%d isOpen=%s cooldown=%d", now, tostring(Window and Window.IsOpen == true), tonumber(DeathBoardClient.Cooldown and DeathBoardClient.Cooldown.Open or 0) or 0)) local enterTs = DeathBoardClient.EnterWorldTs or 0 if enterTs == 0 or (now - enterTs) < 5000 then DB2_ClientLog(string.format("Open aborted enter_world_guard enterTs=%d", enterTs)) return end if not DeathBoardClient.ImagesReady then if DeathBoardClient.LoadImages then DeathBoardClient.LoadImages() end if not DeathBoardClient.ImagesReady then DeathBoardClient.Cooldown.Open = now + 500 DB2_ClientLog("Open aborted images_not_ready") return end end local openDelay = 1000 if now < (DeathBoardClient.Cooldown.Open or 0) then DB2_ClientLog("Open aborted cooldown") return end DeathBoardClient.Cooldown.Open = now + openDelay if DeathBoardClient.CheckOpen and DeathBoardClient.CheckOpen() then DB2_ClientLog("Open aborted already_open") return end if CheckWindowOpen and CheckWindowOpen(33) == 1 then DB2_ClientLog("Open aborted window_33_open") return end if CloseAllCustomWindow then CloseAllCustomWindow() end if SmartWindowClient and SmartWindowClient.CheckOpen and SmartWindowClient.CheckOpen() and SmartWindowClient.Close then SmartWindowClient.Close() end if PvPBetClient and PvPBetClient.Modules and PvPBetClient.Modules.PvPBet then local pvpBet = PvPBetClient.Modules.PvPBet if pvpBet.CheckOpen and pvpBet.CheckOpen() and pvpBet.Close then pvpBet.Close() end elseif PvPBet and PvPBet.CheckOpen and PvPBet.Close and PvPBet.CheckOpen() then PvPBet.Close() end if CloseWindow then for _, w in ipairs(ExclusiveWindows) do CloseWindow(w) end end if Constants.Window and Constants.Window.WindowID then UICustomInterface = Constants.Window.WindowID end Window.IsOpen = true Window.X = 0 Window.Y = 0 DeathBoardClient.PendingPage = nil DeathBoardClient.ClearRows() DeathBoardClient.SetPagination(1, 1) DB2_ClientLog("Open success -> RequestPage(1)") DeathBoardClient.RequestPage(1) end function DeathBoardClient.Close() if Constants.Window and Constants.Window.WindowID then if UICustomInterface == Constants.Window.WindowID then UICustomInterface = 0 end end Window.IsOpen = false DeathBoardClient.PendingPage = nil end function DeathBoardClient.Toggle() DB2_ClientLog(string.format("Toggle called isOpen=%s", tostring(Window.IsOpen == true))) if Window.IsOpen then DeathBoardClient.Close() else DeathBoardClient.Open() end end function DeathBoardClient.Render() if not IsCharacterLoggedIn() then if Window.IsOpen then DeathBoardClient.Close() end DeathBoardClient.ButtonOpenState = 0 DeathBoardClient.EnterWorldTs = 0 return end if not DeathBoardClient.EnterWorldTs or DeathBoardClient.EnterWorldTs == 0 then DeathBoardClient.EnterWorldTs = DB2_NowMs() end local smartWindowOpen = IsSmartWindowOpen() if not smartWindowOpen then RenderOpenButton() else DeathBoardClient.ButtonOpenState = 0 end EnsureExclusiveWindows(smartWindowOpen) RenderKillHud() if not Window.IsOpen then return end if (Window.X or 0) == 0 and (Window.Y or 0) == 0 then DeathBoardClient.CenterWindow() end local x, y, width, height = GetWindowRect() if DeathBoardClient.IsLoading then local startTs = DeathBoardClient.LoadingStartTs or 0 local nowTs = DB2_NowMs() if startTs > 0 and (nowTs - startTs) > 5000 then DeathBoardClient.IsLoading = false DeathBoardClient.LoadingProgress = 100 DeathBoardClient.LastPageRowCount = DeathBoardClient.LastPageRowCount or 0 DB2_FlushPendingPage("timeout") end end DB2_SafeCall(RenderBackground, x, y, width, height) DB2_SafeCall(RenderTitleBarAndColumns, x, y, width) DeathBoardClient.__rows_layout = DB2_ComputeRowLayout(x, y, width, height) if not DeathBoardClient.__rows_layout then end local rowsPerPage = DeathBoardClient.RowsPerPage or 6 local totalRows = DeathBoardClient.LastPageRowCount or 0 if totalRows < 0 then totalRows = 0 end local drawRows = totalRows if drawRows > rowsPerPage then drawRows = rowsPerPage end if DeathBoardClient.IsLoading then RenderLoadingOverlay(x, y, width, height) return end if drawRows == 0 then if RenderText3 and SetFontType and SetTextColor and SetTextBg then SetFontType(1) SetTextColor(150, 150, 150, 255) SetTextBg(0, 0, 0, 0) local msg = "Nenhuma morte registrada ainda." local cfgText = DeathBoardClient.Config and DeathBoardClient.Config.TextKeys if cfgText and cfgText.EmptyMessage then msg = cfgText.EmptyMessage end RenderText3(x + math.floor(width / 2), y + math.floor(height / 2), msg, width, 8) end RenderWindowTitle(x, y, width) RenderCloseButton(x, y, width) RenderPagination(x, y, width, height) return end for rowIndex = 1, drawRows do DB2_SafeCall(RenderRowBackground, x, y, width, rowIndex) DB2_SafeCall(RenderRowVerticalSeparators, x, y, width, rowIndex) DB2_SafeCall(RenderRowClassIcons, x, y, width, rowIndex) DB2_SafeCall(RenderRowTexts, x, y, width, rowIndex) if rowIndex < drawRows then DB2_SafeCall(RenderRowHorizontalSeparator, x, y, width, rowIndex) end end DB2_SafeCall(RenderWindowTitle, x, y, width) DB2_SafeCall(RenderCloseButton, x, y, width) DB2_SafeCall(RenderPagination, x, y, width, height) for rowIndex = 1, drawRows do DB2_SafeCall(RenderRowGuildMarks, x, y, width, rowIndex) end end function DeathBoardClient.UpdateMouse() if not IsCharacterLoggedIn() then DeathBoardClient.ButtonOpenState = 0 return 0 end local smartWindowOpen = (SmartWindowClient and SmartWindowClient.CheckOpen and SmartWindowClient.CheckOpen()) or (UICustomInterface == 121) local cfg = DeathBoardClient.Config and DeathBoardClient.Config.Button or {} local btnW = cfg.Width if btnW == nil then btnW = (Constants.Window and Constants.Window.ButtonOpenW) or 25 end local btnH = cfg.Height if btnH == nil then btnH = (Constants.Window and Constants.Window.ButtonOpenH) or 25 end local btnX = cfg.X if btnX == nil then btnX = (Constants.Window and Constants.Window.ButtonOpenX) or 80 end local btnY = cfg.Y if btnY == nil then btnY = (Constants.Window and Constants.Window.ButtonOpenY) or 20 end local mx = (MousePosX and MousePosX()) or (GetMousePosX and GetMousePosX()) or 0 local my = (MousePosY and MousePosY()) or (GetMousePosY and GetMousePosY()) or 0 local consumed = 0 if not smartWindowOpen then if CheckWindowOpen and CheckWindowOpen(65) == 1 then DeathBoardClient.ButtonOpenState = 0 return 0 end local insideOpen = (mx >= btnX and mx <= (btnX + btnW) and my >= btnY and my <= (btnY + btnH)) if insideOpen then if DisableClickClient then DisableClickClient() end if CheckRepeatKey and CheckRepeatKey(1) == 1 then DeathBoardClient.ButtonOpenState = 2 else DeathBoardClient.ButtonOpenState = 1 end consumed = 1 if CheckReleasedKey and CheckReleasedKey(1) == 1 then DeathBoardClient.Toggle() end else DeathBoardClient.ButtonOpenState = 0 end else DeathBoardClient.ButtonOpenState = 0 end if not Window.IsOpen then return consumed end local x, y, width, height = GetWindowRect() local closeW = 16 local closeH = 16 local closeMarginX = 11 local closeMarginY = 11 local closeX = x + width - closeW - closeMarginX local closeY = y + closeMarginY local insideClose = (mx >= closeX and mx <= closeX + closeW and my >= closeY and my <= closeY + closeH) if insideClose then if DisableClickClient then DisableClickClient() end if CheckRepeatKey and CheckRepeatKey(1) == 1 then DeathBoardClient.ButtonCloseState = 2 else DeathBoardClient.ButtonCloseState = 1 end consumed = 1 if CheckReleasedKey and CheckReleasedKey(1) == 1 then DeathBoardClient.Close() end else DeathBoardClient.ButtonCloseState = 0 end if mx >= x and mx <= x + width and my >= y and my <= y + height then if DisableClickClient then DisableClickClient() end consumed = 1 end local layoutTitleBar = Layout.TitleBar or {} local barW = layoutTitleBar.Width or 346 local barH = layoutTitleBar.Height or 23 local barYOffset = layoutTitleBar.YOffset or 46 local innerW = layoutTitleBar.InnerWidth or 314 local barX = x + math.floor((width - barW) / 2) local barY = y + barYOffset local rl = DB2_ComputeRowLayout(x, y, width, height) local rowH = rl.rowH local rowGapBetween = rl.gapBetween local baseRowY = rl.baseRowY local hoverRow = 0 local rowsPerPage = DeathBoardClient.RowsPerPage or 6 local totalRows = DeathBoardClient.LastPageRowCount or 0 if totalRows < 0 then totalRows = 0 end local drawRows = rowsPerPage if totalRows > 0 and totalRows < rowsPerPage then drawRows = totalRows end for rowIndex = 1, drawRows do local rowY = baseRowY + (rowIndex - 1) * (rowH + rowGapBetween) local rowX1 = barX local rowX2 = barX + barW local rowY1 = rowY local rowY2 = rowY + rowH if mx >= rowX1 and mx <= rowX2 and my >= rowY1 and my <= rowY2 then hoverRow = rowIndex break end end DeathBoardClient.HoverRowIndex = hoverRow local pageBtnSize = 17 local spacingBtns = 40 local bottomY = y + height local pageY = bottomY - 15 - pageBtnSize local centerXBtn = x + math.floor(width / 2) local prevX = centerXBtn - math.floor(spacingBtns / 2) - pageBtnSize local nextX = centerXBtn + math.floor(spacingBtns / 2) local overPrev = (mx >= prevX and mx <= prevX + pageBtnSize and my >= pageY and my <= pageY + pageBtnSize) local overNext = (mx >= nextX and mx <= nextX + pageBtnSize and my >= pageY and my <= pageY + pageBtnSize) if overPrev then if DisableClickClient then DisableClickClient() end if CheckRepeatKey and CheckRepeatKey(1) == 1 then DeathBoardClient.ButtonPagePrevState = 2 else DeathBoardClient.ButtonPagePrevState = 1 end consumed = 1 if CheckReleasedKey and CheckReleasedKey(1) == 1 then local now = DB2_NowMs() local pageDelay = 500 if now < (DeathBoardClient.Cooldown.Page or 0) then -- ignore else DeathBoardClient.Cooldown.Page = now + pageDelay local currentPage = DeathBoardClient.CurrentPage or 1 if currentPage > 1 then local newPage = currentPage - 1 DeathBoardClient.RequestPage(newPage) end end end else DeathBoardClient.ButtonPagePrevState = 0 end if overNext then if DisableClickClient then DisableClickClient() end if CheckRepeatKey and CheckRepeatKey(1) == 1 then DeathBoardClient.ButtonPageNextState = 2 else DeathBoardClient.ButtonPageNextState = 1 end consumed = 1 if CheckReleasedKey and CheckReleasedKey(1) == 1 then local now = DB2_NowMs() local pageDelay = 500 if now < (DeathBoardClient.Cooldown.Page or 0) then -- ignore else DeathBoardClient.Cooldown.Page = now + pageDelay local currentPage = DeathBoardClient.CurrentPage or 1 local totalPages = DeathBoardClient.TotalPages or 1 if currentPage < totalPages then local newPage = currentPage + 1 DeathBoardClient.RequestPage(newPage) end end end else DeathBoardClient.ButtonPageNextState = 0 end return consumed end function DeathBoardClient_Protocol_Recv(Packet, PacketName) local handler = EnsureProtocolHandler() local ok, result = pcall(handler.Recv, Packet, PacketName) if ok then return result end DB2_ClientLog("Protocol Recv failed: " .. tostring(result)) return false end function DeathBoardClient.UpdateKey() if not Window.IsOpen then return 0 end if CheckPressedKey and Keys and CheckPressedKey(Keys.Escape) == 1 then DeathBoardClient.Close() if DisableClickClient then DisableClickClient() end return 1 end return 0 end