-------------------------------------------------------------------------------- -------------------------------------------------------------------------------- function widget:GetInfo() return { name = "Chili Deluxe Player List - MOD", desc = "v0.202 Chili Deluxe Player List, Alpha Release", author = "CarRepairer, KingRaptor, CrazyEddie, MoriturusMortus", date = "2012-06-30", license = "GNU GPL, v2 or later", layer = 50, enabled = false, --detailsDefault = 1 -- based on v1.31 Chili Crude Player List by CarRepairer, KingRaptor, et al } end --[[ TODO: * Fast-and-Easy switching between large and small views, and between a single view hidden or shown * More granular control over which columns are displayed, and how (text vs. icons, etc) * More and better tooltips, and options for controlling whether and how they are displayed * Scaling x-axis dimensions proportionally with the fontsize * Profiling and improving performance * Tons of minor bugfixes, cosmetic enhancements, and code cleanup --]] -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- function SetupPlayerNames() end function ToggleVisibility() end local echo = Spring.Echo local spGetUnitIsStunned = Spring.GetUnitIsStunned local Chili local Image local Button local Checkbox local Window local ScrollPanel local StackPanel local Label local screen0 local color2incolor local incolor2color -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- local window_cpl, scroll_cpl options_path = 'Settings/HUD Panels/PlayerList' options_order = { 'visible', 'backgroundOpacity', 'text_height', 'name_width', 'mousewheel', 'alignToTop', 'alignToLeft', 'showSummaries', 'showClanSummaries', 'show_Damage_stats', 'show_unit_stats', 'show_income_stats', 'support_my_Ceiling', 'send_resources_amount', 'colorResourceStats', 'show_ccr', 'rank_as_text', 'round_elo', 'cpu_ping_as_text', 'show_tooltips', 'list_size'} --'support_ally_floor', options = { visible = { name = "Visible", type = 'bool', value = true, desc = "Set a hotkey here to toggle the playerlist on and off", OnChange = function() ToggleVisibility() end, }, backgroundOpacity = { name = "Background Opacity", type = "number", value = 0.5, min = 0, max = 1, step = 0.01, OnChange = function(self) scroll_cpl.backgroundColor = {1,1,1,self.value} scroll_cpl:Invalidate() end, }, text_height = { name = 'Font Size (10-14)', type = 'number', value = 13, min=10,max=14,step=1, OnChange = function() SetupPanels() end, advanced = true }, name_width = { name = 'Name Width (50-200)', type = 'number', value = 120, min=50,max=200,step=10, OnChange = function() SetupPanels() end, advanced = true }, mousewheel = { name = "Scroll with mousewheel", type = 'bool', value = false, OnChange = function(self) scroll_cpl.noMouseWheel = not self.value; end, }, alignToTop = { name = "Align to top", type = 'bool', value = true, desc = "Align to top and grow downwards (vs. align to bottom and grow upwards)", OnChange = function() SetupPlayerNames() end, }, alignToLeft = { name = "Align to left", type = 'bool', value = false, desc = "Align to left and grow rightwards (vs. align to right and grow leftwards)", OnChange = function() SetupScrollPanel() end, }, showSummaries = { name = "Show team summaries", type = 'bool', value = true, desc = "Display summary information for each team (note: even with this checked, summaries won't be displayed if all the teams are very small)", OnChange = function() SetupPlayerNames() end, }, --Clan Begin showClanSummaries = { name = "Show MyClan summaries", type = 'bool', value = true, desc = "Display summary information for your Clan", OnChange = function() SetupPlayerNames() end, }, --Clan End --Damage Begin show_Damage_stats = { name = "Show Damage summaries", type = 'bool', value = true, desc = "Display Damage statistics: dealt and taken damage.", OnChange = function() SetupPanels() end, }, --Damage End show_unit_stats = { name = "Show unit stats", type = 'bool', value = true, desc = "Display resource statistics: metal in mobile units and static defenses.", OnChange = function() SetupPanels() end, }, show_income_stats = { name = "Show income stats", type = 'bool', value = true, desc = "Display resource statistics: metal and energy income.", OnChange = function() SetupPanels() end, }, --Support Begin --[[ support_ally_floor = { name = 'Support: ally floor (1-500)', type = 'number', value = 50, desc = "Determine ally minimum threshold, cross the threshold = send resource.", min=1,max=500,step=10, OnChange = function() SetupPanels() end, advanced = true }, ]]-- support_my_Ceiling = { name = 'Support: my ceiling (10-5000)', type = 'number', value = 250, desc = "Determine your maximum threshold, cross the threshold = send resource.", min=10,max=5000,step=10, OnChange = function() SetupPanels() end, advanced = true }, --Support End --ShareResources Begin send_resources_amount = { name = 'Resources send on double click (10-500)', type = 'number', value = 50, desc = "Amount of resources to be send on double click", min=10,max=500,step=10, OnChange = function() SetupPanels() end, advanced = true }, --ShareResources END colorResourceStats = { name = "Show stats in color", type = 'bool', value = false, desc = "Display resource statistics such as unit metal and income in each player's color (vs. white)", OnChange = function() SetupPlayerNames() end, }, show_ccr = { name = "Show clan/country/rank", type = 'bool', value = true, desc = "Show the clan, country, and rank columns", OnChange = function() SetupPanels() end, }, rank_as_text = { name = "Show rank as text", type = 'bool', value = false, desc = "Show rank as text (vs. as an icon)", OnChange = function() SetupPlayerNames() end, }, --Elo Begin round_elo = { name = "Round elo", type = 'bool', value = true, desc = "Round elo", OnChange = function() SetupPanels() end, }, --Elo End cpu_ping_as_text = { name = "Show ping/cpu as text", type = 'bool', value = false, desc = "Show ping and cpu stats as text (vs. as an icon)", OnChange = function() SetupPanels() end, }, show_tooltips = { name = "Show tooltips", type = 'bool', value = true, desc = "Show tooltips where available (vs. hiding all tooltips)", OnChange = function() SetupPanels() end, }, list_size = { name = 'List Size: Who should be included?', type = 'list', value = 3, items = { { key = 0, name = "Nobody" }, { key = 1, name = "Just you" }, { key = 2, name = "Just your team" }, { key = 3, name = "All players" }, { key = 4, name = "All players and spectators" }, }, OnChange = function() SetupPlayerNames() end, }, } local name_width local green = '' local red = '' local orange = '' local yellow = '' local cyan = '' local white = '' local function IsFFA() local allyteams = Spring.GetAllyTeamList() local gaiaT = Spring.GetGaiaTeamID() local gaiaAT = select(6, Spring.GetTeamInfo(gaiaT)) local numAllyTeams = 0 for i=1,#allyteams do if allyteams[i] ~= gaiaAT then local teams = Spring.GetTeamList() if #teams > 0 then numAllyTeams = numAllyTeams + 1 end end end return numAllyTeams > 2 end -- The ceasefire functionality isn't working right now. -- I'm leaving all the code in place, but disabling the buttons. -- Someone can come back in and fix it later. -- -- local cf = IsFFA() local cf = false local localTeam = 0 local localAlliance = 0 local myID local myName local amSpec local myClan --Clan myID = Spring.GetMyPlayerID() myName,_,amSpec,_,_,_,_,_,_,CKeys = Spring.GetPlayerInfo(myID) --Clan Begin if CKeys then myClan = CKeys.clan end myClan = myClan or "" --Clan END localTeam = Spring.GetMyTeamID() localAlliance = Spring.GetMyAllyTeamID() -- This is awkward, but it's okay for now. -- I'll make it elegant when I implement x-scaling with fontsize -- local x_icon_clan local x_icon_country local x_icon_rank local x_elo local x_cf local x_status local x_name local x_share local x_m_mobiles local x_m_defense local x_c_damage local x_t_damage local x_m_income local x_e_income local x_m_fill local x_e_fill local x_cpu local x_ping local x_postping local x_bound local x_windowbound local function CalculateWidths() name_width = options.name_width.value or 120 x_icon_clan = 10 x_icon_country = x_icon_clan + 18 x_icon_rank = x_icon_country + 20 x_elo = options.show_ccr.value and x_icon_rank + 16 or x_icon_clan x_cf = x_elo + 32 x_status = cf and x_cf + 20 or x_cf x_name = x_status + 12 x_share = x_name + name_width --Damage Begin x_c_damage = not amSpec and x_share + 12 or x_share x_t_damage = x_c_damage + 36 --Damage End x_m_mobiles = options.show_Damage_stats.value and x_t_damage + 40 or x_c_damage x_m_defense = x_m_mobiles + 34 x_m_income = options.show_unit_stats.value and x_m_defense + 40 or x_m_mobiles x_e_income = x_m_income + 30 x_m_fill = options.show_income_stats.value and x_e_income + 26 or x_m_income x_e_fill = x_m_fill + 30 x_cpu = x_e_fill + (options.cpu_ping_as_text.value and 52 or 30) x_ping = x_cpu + (options.cpu_ping_as_text.value and 46 or 16) x_bound = x_ping + 28 x_windowbound = x_bound + 0 end CalculateWidths() local UPDATE_FREQUENCY = 0.8 -- seconds local cfCheckBoxes = {} local allyTeams = {} -- [id] = {team1, team2, ...} local allyClans = {} local teams = {} -- [id] = {leaderName = name, roster = {entity1, entity2, ...}} local teamZeroPlayers = {} -- entity = player (including specs) or bot -- ordered list; contains isAI, isSpec, playerID, teamID, name, namelabel, cpuImg, pingImg local entities = {} local allyTeamEntities = {} local allyTeamOrderRank = {} local allyTeamsDead = {} local allyTeamsElo = {} local playerTeamStatsCache = {} local finishedUnits = {} local numBigTeams = 0 local existsVeryBigTeam = nil local myTeamIsVeryBig = nil local specTeam = {roster = {}} local CausedDamageList = {} local TakenDamageList = {} --Clan Begin local allyClanEntities = {} local allyClanOrderRank = {} local allyClanDead = {} local existsVeryBigClan = nil local myClanIsVeryBig = nil local numBigClan = 0 --Clan END local sharePic = ":n:"..LUAUI_DIRNAME.."Images/playerlist/share.png" local cpuPic = ":n:"..LUAUI_DIRNAME.."Images/playerlist/cpu.png" local pingPic = ":n:"..LUAUI_DIRNAME.."Images/playerlist/ping.png" local row local fontsize local list_size local timer = 0 local lastSizeX local lastSizeY local lastChosenSizeX = 0 include("keysym.h.lua") -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- local function TableFind(f, l) if f == nil or l == nil then return false else local count = 0 for _, v in ipairs(l) do count = count + 1 if f == v then return count end end end return false end --Chat Begin local function ChatwithUser(playername) Spring.SendCommands("ChatAll","pastetext /w "..playername..' \1') end local function ChatwithTeam() Spring.SendCommands("ChatAlly","pastetext \1") end --Chat END --View Begin --Code taken from the Team View lua script created by BoredJoe local function ViewTeam(TeamList) local averagex = 0 local averagey = 0 local averagez = 0 local count = 0 for i=1,#TeamList do local teamID = TeamList[i] for _,unitID in ipairs(Spring.GetAllUnits()) do UnitTeamID = Spring.GetUnitTeam(unitID) if UnitTeamID == teamID then local x, y, z = Spring.GetUnitPosition(unitID) averagex = (averagex + x) averagey = (averagey + y) averagez = (averagez + z) count = (count + 1) end end end averagex = averagex / count averagey = averagey / count averagez = averagez / count if count ~= 0 then Spring.SetCameraTarget(averagex, averagey, averagez) end end --View END local function ShareUnits(playername, team) local selcnt = Spring.GetSelectedUnitsCount() if selcnt > 0 then Spring.SendCommands("say a: I gave "..selcnt.." units to "..playername..".") Spring.ShareResources(team, "units") else echo 'Player List: No units selected to share.' end end local function AskForUnits() local count = 0 local Units = Spring.GetTeamUnits(localTeam) for i=1,#Units do local UN = Units[i] local unitDefID = Spring.GetUnitDefID(UN) if (UnitDefs[unitDefID]["canReclaim"]) then count = count +1 end end if count <= 0 then Spring.SendCommands("say a: I need a Constructor") else Spring.SendCommands("say a: I need Unit support.") end end local function AskForResources(resource) Spring.SendCommands("say a: I need some "..resource..".") end --ShareResources Begin local function SendResources(playername, teamID, resource) local myCur,_,_,_,_,_,_,_ = Spring.GetTeamResources(localTeam, resource) local plCur,plMax,_,_,_,_,_,_ = Spring.GetTeamResources(teamID, resource) if myCur >= options.send_resources_amount.value and (plCur - 2 <= plMax) then Spring.ShareResources(teamID, resource, options.send_resources_amount.value) Spring.SendCommands("say a: I gave "..options.send_resources_amount.value.." "..resource.." to "..playername..".") else echo ("Not enough resources or storage.") end end --ShareResources END --Support Begin local m_SupportList = {} local e_SupportList = {} local m_TeamSupport = false local e_TeamSupport = false local m_ClanSupport = false local e_ClanSupport = false function Support(teamID) if m_SupportList[teamID] then SendSupport(teamID, 'metal') end if e_SupportList[teamID] then SendSupport(teamID, 'energy') end end function SendSupport(teamID, resource) local Transfer = 0 local myCeiling = options.support_my_Ceiling.value --echo(allyFloor) --echo(myCeiling) local Cur,Max,_,Income,Expense,_,_,_ = Spring.GetTeamResources(localTeam, resource) local Plus = Income-Expense --local Seer=Max-Plus --echo(Cur) local AllyCur,AllyMax,_,AllyIncome,AllyExpense,_,_,_ = Spring.GetTeamResources(teamID, resource) local AllyPlus = AllyIncome-AllyExpense --local AllySeer = AllyMax-(AllyPlus+Plus) --echo(AllyCur) local allyFloor = (AllyMax*0.9) -- options.support_ally_floor.value if AllyCur < allyFloor and Cur > myCeiling and AllyPlus < 0 then if (Cur-(AllyPlus*(-1))) >= myCeiling then Transfer = (Cur-myCeiling) else Transfer = (AllyPlus*-1) end if (AllyCur+Transfer) > allyFloor then Transfer = allyFloor-AllyCur end Spring.ShareResources(teamID, resource, Transfer) --echo(teamID.." "..resource.." "..Transfer) end end local function AddtoSupport(entity, resource) if resource == 'metal' then if m_SupportList[entity.teamID] then m_SupportList[entity.teamID] = false echo ("Removed "..entity.name.." to metal support list.") if entity.m_SupportButton then entity.m_SupportButton.backgroundColor = {0,0,0,0} end else m_SupportList = m_SupportList or {} m_SupportList[entity.teamID] = true echo ("Added "..entity.name.." to metal support list.") if entity.m_SupportButton then entity.m_SupportButton.backgroundColor = {0,1,0,1} end end elseif resource == 'energy' then if e_SupportList[entity.teamID] then e_SupportList[entity.teamID] = false echo ("Removed "..entity.name.." to energy support list.") if entity.e_SupportButton then entity.e_SupportButton.backgroundColor = {0,0,0,0} end else e_SupportList = e_SupportList or {} e_SupportList[entity.teamID] = true echo ("Added "..entity.name.." to energy support list.") if entity.e_SupportButton then entity.e_SupportButton.backgroundColor = {0,1,0,1} end end end end local ListSupportButton = {} local function AddTeamtoSupport(mainentity,resource) if resource == 'metal' then if m_TeamSupport then if ListSupportButton.teammetal then ListSupportButton.teammetal.backgroundColor = {0,0,0,0} end m_TeamSupport = false if ListSupportButton.clanmetal then ListSupportButton.clanmetal.backgroundColor = {0,0,0,0}; ListSupportButton.clanmetal:Invalidate() end m_ClanSupport = false for i=1,#allyTeams[localAlliance] do local teamID = allyTeams[localAlliance][i] if teamID ~= localTeam and teams[teamID].roster then for j=1,#teams[teamID].roster do local entity = teams[teamID].roster[j] m_SupportList[teamID] = false echo ("Removed "..entity.name.." to metal support list.") if entity.m_SupportButton then entity.m_SupportButton.backgroundColor = {0,0,0,0}; entity.m_SupportButton:Invalidate() end end end end elseif not m_TeamSupport then if ListSupportButton.teammetal then ListSupportButton.teammetal.backgroundColor = {0,1,0,1} end m_TeamSupport = true for i=1,#allyTeams[localAlliance] do local teamID = allyTeams[localAlliance][i] if teamID ~= localTeam and teams[teamID].roster then for j=1,#teams[teamID].roster do local entity = teams[teamID].roster[j] m_SupportList[teamID] = true echo ("Added "..entity.name.." to metal support list.") if entity.m_SupportButton then entity.m_SupportButton.backgroundColor = {0,1,0,1}; entity.m_SupportButton:Invalidate() end end end end end elseif resource == 'energy' then if e_TeamSupport then if ListSupportButton.teamenergy then ListSupportButton.teamenergy.backgroundColor = {0,0,0,0} end e_TeamSupport = false if ListSupportButton.clanenergy then ListSupportButton.clanenergy.backgroundColor = {0,0,0,0}; ListSupportButton.clanenergy:Invalidate() end e_ClanSupport = false for i=1,#allyTeams[localAlliance] do teamID = allyTeams[localAlliance][i] if teamID ~= localTeam and teams[teamID].roster then for j=1,#teams[teamID].roster do entity = teams[teamID].roster[j] e_SupportList[teamID] = false echo ("Removed "..entity.name.." to energy support list.") if entity.e_SupportButton then entity.e_SupportButton.backgroundColor = {0,0,0,0}; entity.e_SupportButton:Invalidate() end end end end elseif not e_TeamSupport then if ListSupportButton.teamenergy then ListSupportButton.teamenergy.backgroundColor = {0,1,0,1} end e_TeamSupport = true for i=1,#allyTeams[localAlliance] do teamID = allyTeams[localAlliance][i] if teamID ~= localTeam and teams[teamID].roster then for j=1,#teams[teamID].roster do entity = teams[teamID].roster[j] e_SupportList[teamID] = true echo ("Added "..entity.name.." to energy support list.") if entity.e_SupportButton then entity.e_SupportButton.backgroundColor = {0,1,0,1}; entity.e_SupportButton:Invalidate() end end end end end end end local function AddClantoSupport(mainentity,resource) --clanentity = mainentity if resource == 'metal' then if m_ClanSupport then if ListSupportButton.clanmetal then ListSupportButton.clanmetal.backgroundColor = {0,0,0,0} end m_ClanSupport = false for i=1,#allyClans[localAlliance] do local teamID = allyClans[localAlliance][i] if teamID ~= localTeam and teams[teamID].roster then for j=1,#teams[teamID].roster do local entity = teams[teamID].roster[j] m_SupportList[teamID] = false echo ("Removed "..entity.name.." to metal support list.") if entity.m_SupportButton then entity.m_SupportButton.backgroundColor = {0,0,0,0}; entity.m_SupportButton:Invalidate() end end end end elseif not m_ClanSupport then if ListSupportButton.clanmetal then ListSupportButton.clanmetal.backgroundColor = {0,1,0,1} end m_ClanSupport = true for i=1,#allyClans[localAlliance] do local teamID = allyClans[localAlliance][i] if teamID ~= localTeam and teams[teamID].roster then for j=1,#teams[teamID].roster do local entity = teams[teamID].roster[j] m_SupportList[teamID] = true echo ("Added "..entity.name.." to metal support list.") if entity.m_SupportButton then entity.m_SupportButton.backgroundColor = {0,1,0,1}; entity.m_SupportButton:Invalidate() end end end end end elseif resource == 'energy' then if e_ClanSupport then if ListSupportButton.clanenergy then ListSupportButton.clanenergy.backgroundColor = {0,0,0,0} end e_ClanSupport = false for i=1,#allyClans[localAlliance] do teamID = allyClans[localAlliance][i] if teamID ~= localTeam and teams[teamID].roster then for j=1,#teams[teamID].roster do entity = teams[teamID].roster[j] e_SupportList[teamID] = false echo ("Removed "..entity.name.." to energy support list.") if entity.e_SupportButton then entity.e_SupportButton.backgroundColor = {0,0,0,0}; entity.e_SupportButton:Invalidate() end end end end elseif not e_ClanSupport then if ListSupportButton.clanenergy then ListSupportButton.clanenergy.backgroundColor = {0,1,0,1} end e_ClanSupport = true for i=1,#allyClans[localAlliance] do teamID = allyClans[localAlliance][i] if teamID ~= localTeam and teams[teamID].roster then for j=1,#teams[teamID].roster do entity = teams[teamID].roster[j] e_SupportList[teamID] = true echo ("Added "..entity.name.." to energy support list.") if entity.e_SupportButton then entity.e_SupportButton.backgroundColor = {0,1,0,1}; entity.e_SupportButton:Invalidate() end end end end end end end --Support END -- makes a color char from a color table -- explanation for string.char: http://springrts.com/phpbb/viewtopic.php?f=23&t=24952 local function GetColorChar(colorTable) if colorTable == nil then return string.char(255,255,255,255) end local col = {} for i=1,4 do col[i] = math.ceil(colorTable[i]*255) end return string.char(col[4],col[1],col[2],col[3]) end local function FormatPingCpu(ping,cpu) -- guard against being called with nils ping = ping or 0 cpu = cpu or 0 -- guard against silly values ping = math.max(math.min(ping,999),0) cpu = math.max(math.min(cpu,9.99),0) local pingMult = 2/3 -- lower = higher ping needed to be red local pingCpuColors = { {0, 1, 0, 1}, {0.7, 1, 0, 1}, {1, 1, 0, 1}, {1, 0.6, 0, 1}, {1, 0, 0, 1} } local pingCol = pingCpuColors[ math.ceil( math.min(ping * pingMult, 1) * 5) ] or {.85,.85,.85,1} local cpuCol = pingCpuColors[ math.ceil( math.min(cpu, 1) * 5 ) ] or {.85,.85,.85,1} local pingText if ping < 1 then pingText = (math.floor(ping*1000) ..'ms') else pingText = ('' .. (math.floor(ping*100)/100)):sub(1,4) .. 's' end local cpuText = math.round(cpu*100) .. '%' return pingCol,cpuCol,pingText,cpuText end local function FormatMetalStats(stat,k) -- if k then -- stat = 1000 * math.floor((stat/1000) + .5) -- return string.format("%.0f", stat/1000) .. "k" -- else -- stat = 50 * math.floor((stat/50) + .5) return stat < 1000 and string.format("%.0f", stat) or string.format("%.1f", stat/1000) .. "k" -- end end --Damage Begin local function FormatDamageStats(damage) if (damage>=1000 and damage<100000) then return string.format("%.1f", damage/1000) .. "k" elseif (damage>=100000 and damage<100000000) then return string.format("%.1f", damage/1000000) .. "M" elseif (damage>=100000000 and damage<100000000000) then return string.format("%.1f", damage/1000000000) .. "G" elseif (damage>=100000000000) then return string.format("%.1f", damage/1000000000000) .. "T" else return string.format("%.0f", damage) end end --Damage END local function FormatElo(elo,full) local mult = full and 1 or 50 local elo_out if options.round_elo.value then elo_out = mult * math.floor((elo/mult) + .5) else elo_out = math.floor(elo) end local eloCol = {} local top = 1800 local mid = 1600 local bot = 1400 local tc = {1,1,1,1} local mc = {1,1,0,1} local bc = {1,.2,.2,1} if elo_out >= top then eloCol = tc elseif elo_out >= mid then local r = (elo_out-mid)/(top-mid) for i = 1,4 do eloCol[i] = (r * tc[i]) + ((1-r) * mc[i]) end elseif elo_out >= bot then local r = (elo_out-bot)/(mid-bot) for i = 1,4 do eloCol[i] = (r * mc[i]) + ((1-r) * bc[i]) end else eloCol = bc end return elo_out, eloCol end local function ProcessUnit(unitID, unitDefID, unitTeam, remove) local stats = playerTeamStatsCache[unitTeam] if UnitDefs[unitDefID] and stats then -- shouldn't need to guard against nil here, but I've had it happen local metal = UnitDefs[unitDefID].metalCost local speed = UnitDefs[unitDefID].speed local unarmed = UnitDefs[unitDefID].springCategories.unarmed local isbuilt = not select(3, spGetUnitIsStunned(unitID)) if metal and metal < 1000000 then -- tforms show up as 1million cost, so ignore them if remove then metal = -metal end -- for mobiles, count only completed units if speed and speed ~= 0 then if remove then finishedUnits[unitID] = nil stats.mMobs = stats.mMobs + metal elseif isbuilt then finishedUnits[unitID] = true stats.mMobs = stats.mMobs + metal end -- for static defense, include full cost of unfinished units so you can see when your teammates are trying to build too much elseif not unarmed then stats.mDefs = stats.mDefs + metal end end end end local function GetPlayerTeamStats(teamID) if not playerTeamStatsCache[teamID] then playerTeamStatsCache[teamID] = {mMobs = 0, mDefs = 0} local units = Spring.GetTeamUnits(teamID) for i=1,#units do local unitID = units[i] local unitDefID = Spring.GetUnitDefID(unitID) ProcessUnit(unitID, unitDefID, teamID) end end local stats = playerTeamStatsCache[teamID] local eCurr, eStor, ePull, eInco, eExpe, eShar, eSent, eReci = Spring.GetTeamResources(teamID, "energy") local mCurr, mStor, mPull, mInco, mExpe, mShar, mSent, mReci = Spring.GetTeamResources(teamID, "metal") if eStor then eStor = eStor - 10000 -- eStor has a "hidden 10k" to account for if eStor > 50000 then eStor = 1000 end -- fix for weirdness where sometimes storage is reported as huge, assume it should be 1000 end -- guard against dividing by zero later, when the fill bar percentage is calculated -- these probably aren't ever going to be zero, but better safe than sorry if mStore and mStore == 0 then mStore = 1000 end if eStore and eStore == 0 then eStore = 1000 end -- Default these to 1 if the value is nil for some reason. -- These should never be 1 in normal play, so if you see -- them showing up as 1 then that means that something got nil, -- which should perhaps then be looked into further. -- Whereas it's quite reasonable for them to sometimes be zero. stats.mInco = mInco or 1 stats.eInco = eInco or 1 stats.mCurr = mCurr or 1 stats.mStor = mStor or 1 stats.eCurr = eCurr or 1 stats.eStor = eStor or 1 return playerTeamStatsCache[teamID] end --Damage Begin local function GetTeamStatsHistory(teamID) if amSpec or Spring.AreTeamsAllied(localTeam,teamID) then local idx = Spring.GetTeamStatsHistory(localTeam) return Spring.GetTeamStatsHistory(teamID,idx)[1] else local d = { damageDealt = 0, damageReceived = 0 } return d end end --Damage END -- ceasefire button tooltip local function CfTooltip(allyTeam) local tooltip = '' tooltip = tooltip .. 'Check this box to vote for a ceasefire with '.. yellow ..''..white..'. ' ..'If everyone votes Yes, an offer will be made. If there is a ceasefire, ' ..'unchecking the box will break it.\n\n' tooltip = tooltip .. 'Your team\'s votes: \n' local teamList = Spring.GetTeamList(localAlliance) for _,teamID in ipairs(teamList) do local _,playerID = Spring.GetTeamInfo(teamID) local name = Spring.GetPlayerInfo(playerID) or '-' local vote = Spring.GetTeamRulesParam(teamID, 'cf_vote_' ..allyTeam)==1 and green..'Y'..white or red..'N'..white local teamColor = color2incolor(Spring.GetTeamColor(teamID)) tooltip = tooltip .. teamColor .. ' <' .. name .. '> ' .. white.. vote .. '\n' end if Spring.GetGameRulesParam('cf_' .. localAlliance .. '_' .. allyTeam) == 1 then tooltip = tooltip .. '\n\n' .. green .. 'Ceasefire in effect.' .. white else local theyOffer = Spring.GetGameRulesParam('cf_offer_' .. localAlliance .. '_' .. allyTeam) == 1 local youOffer = Spring.GetGameRulesParam('cf_offer_' .. allyTeam.. '_' .. localAlliance) == 1 if theyOffer then tooltip = tooltip .. '\n\n' .. yellow .. 'They have offered a ceasefire.' .. white end if youOffer then tooltip = tooltip .. '\n\n' .. cyan .. 'Your team has offered a ceasefire.' .. white end tooltip = tooltip .. '\n\n' .. red .. 'No ceasefire in effect.' .. white end return tooltip end local function MakeSpecTooltip() if (not options.show_tooltips.value) or list_size == 4 or (list_size == 3 and #specTeam.roster == 0) then scroll_cpl.tooltip = nil return end local windowTooltip local players = {} local spectators = {} local playerlist = Spring.GetPlayerList() for i=1, #playerlist do local playerID = playerlist[i] local name,active,spectator,teamID,allyTeamID,pingTime,cpuUsage,country,rank,customKeys = Spring.GetPlayerInfo(playerID) local pingCol, cpuCol, pingText, cpuText = FormatPingCpu(pingTime,cpuUsage) local cpuColChar = GetColorChar(cpuCol) local pingColChar = GetColorChar(pingCol) if active and not spectator then players[#players+1] = { name = name, pingText = pingText, cpuText = cpuText, pingColChar = pingColChar, cpuColChar = cpuColChar } elseif spectator then spectators[#spectators+1] = { name = name, pingText = pingText, cpuText = cpuText, pingColChar = pingColChar, cpuColChar = cpuColChar } end end table.sort (players, function(a,b) return a.name:lower() < b.name:lower() end ) table.sort (spectators, function(a,b) return a.name:lower() < b.name:lower() end ) if list_size <= 2 then windowTooltip = windowTooltip or "PLAYERS" for i=1, #players do windowTooltip = windowTooltip .. "\n\t"..players[i].name.."\t"..players[i].cpuColChar..(players[i].cpuText)..'\008' .. "\t"..players[i].pingColChar..(players[i].pingText).."\008" end end if #spectators ~= 0 then windowTooltip = windowTooltip and (windowTooltip .. "\n\n") or "" windowTooltip = windowTooltip .. "SPECTATORS" for i=1, #spectators do windowTooltip = windowTooltip .. "\n\t"..spectators[i].name.."\t"..spectators[i].cpuColChar..(spectators[i].cpuText)..'\008' .. "\t"..spectators[i].pingColChar..(spectators[i].pingText).."\008" end end scroll_cpl.tooltip = windowTooltip end -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- local function MakeNewLabel(entity,name,o) -- "o" is for options -- pass in x, width, caption, textColor -- pass in any optional params like align -- pass in anything to override these defaults: o.y = o.y or (fontsize+1) * row o.fontsize = o.fontsize or fontsize o.fontShadow = o.fontShadow or true o.autosize = o.autosize or false local newLabel = Label:New(o) entity[name] = newLabel scroll_cpl:AddChild(newLabel) end local function MakeNewBar(entity,name,o) -- pass in x, width, color, value -- pass in anything to override these defaults: o.y = o.y or ((fontsize+1) * row) + 6 o.height = o.height or 5 o.min = o.min or 0 o.max = o.max or 1 o.autosize = o.autosize or false local newBar = Chili.Progressbar:New(o) entity[name] = newBar scroll_cpl:AddChild(newBar) end local function MakeNewIcon(entity,name,o) -- pass in x, file -- pass in anything to override these defaults: o.y = o.y or ((fontsize+1) * row) + 2 o.width = o.width or fontsize + 3 o.height = o.height or fontsize + 3 o.tooltip = options.show_tooltips.value and o.tooltip or nil local newIcon = Chili.Image:New(o) entity[name] = newIcon scroll_cpl:AddChild(newIcon) end --TeamworkButtons Begin local function MakeNewButton(entity,name,o) -- pass in x, width, color, value -- pass in anything to override these defaults: o.y = o.y or ((fontsize+1) * row) + 6 o.height = o.height or 5 o.width = o.width or 24 o.caption = o.caption or '' o.fontsize = o.fontsize or fontsize o.fontShadow = o.fontShadow or true o.autosize = o.autosize or false o.backgroundColor = o.backgroundColor or {0,0,0,0} local newButton = Button:New(o) entity[name] = newButton scroll_cpl:AddChild(newButton) end --TeamworkButtons End local function AccumulatePlayerTeamStats(r,s) r.eCurr = r.eCurr + s.eCurr r.eStor = r.eStor + s.eStor r.eInco = r.eInco + s.eInco r.mCurr = r.mCurr + s.mCurr r.mStor = r.mStor + s.mStor r.mInco = r.mInco + s.mInco r.mMobs = r.mMobs + s.mMobs r.mDefs = r.mDefs + s.mDefs end --Damage Begin local function AccumulatePlayerTeamDamage(rd,sd) rd.damageDealt = rd.damageDealt + sd.damageDealt rd.damageReceived = rd.damageReceived + sd.damageReceived end --Damage END local function DrawPlayerTeamStats(entity,teamcolor,s,d) --Damage if not options.colorResourceStats.value then teamcolor = {.85,.85,.85,1} end --Damage Begin if options.show_Damage_stats.value then MakeNewLabel(entity,"m_cdamageLabel",{x=x_c_damage,width=36,caption = FormatDamageStats(d.damageDealt),textColor = teamcolor,align = 'right',}) MakeNewLabel(entity,"m_tdamageLabel",{x=x_t_damage,width=36,caption = FormatDamageStats(d.damageReceived),textColor = teamcolor,align = 'right',}) end --Damage END if options.show_unit_stats.value then MakeNewLabel(entity,"m_mobilesLabel",{x=x_m_mobiles,width=36,caption = FormatMetalStats(s.mMobs,true),textColor = teamcolor,align = 'right',}) MakeNewLabel(entity,"m_defenseLabel",{x=x_m_defense,width=36,caption = FormatMetalStats(s.mDefs),textColor = teamcolor,align = 'right',}) end if options.show_income_stats.value then MakeNewLabel(entity,"m_incomeLabel",{x=x_m_income,width=24,caption = string.format("%." .. (0) .. "f", s.mInco),textColor = teamcolor,align = 'right',}) MakeNewLabel(entity,"e_incomeLabel",{x=x_e_income,width=24,caption = string.format("%." .. (0) .. "f", s.eInco),textColor = teamcolor,align = 'right',}) end MakeNewBar(entity,"m_fillBar",{x=x_m_fill + 6,width=24,color = {.7,.75,.9,1},value = s.mCurr/s.mStor,}) MakeNewBar(entity,"e_fillBar",{x=x_e_fill + 2,width=24,color = {1,1,0,1}, value = s.eCurr/s.eStor,}) end --TeamworkButtons Begin local function MakePlayerTeamworkButtons(entity,teamcolor) local u_SendButtonFunc local m_SupportButtonFunc local e_SupportButtonFunc local m_SendButtonFunc local e_SendButtonFunc local u_SendButtonCol local u_SendButtonTip local m_SupportButtonTip local e_SupportButtonTip local m_SendButtonTip local e_SendButtonTip if entity.teamID ~= localTeam then u_SendButtonFunc = { function(self) ShareUnits(entity.name, entity.teamID) end, } m_SupportButtonFunc = { function(self) AddtoSupport(entity, "metal") end, } e_SupportButtonFunc = { function(self) AddtoSupport(entity, "energy") end, } m_SendButtonFunc = { function(self) SendResources(entity.name, entity.teamID, "metal") end, } e_SendButtonFunc = { function(self) SendResources(entity.name, entity.teamID, "energy") end, } u_SendButtonCol = {1, 1, 1, 1} u_SendButtonTip = 'Double click to share selected units to ' .. entity.name .. '.' m_SupportButtonTip = 'Click to Support ' .. entity.name .. '.' e_SupportButtonTip = 'Click to Support ' .. entity.name .. '.' m_SendButtonTip = 'Double click to share '.. options.send_resources_amount.value ..' metal to ' .. entity.name .. '.' e_SendButtonTip = 'Double click to share '.. options.send_resources_amount.value ..' energy to ' .. entity.name .. '.' elseif entity.teamID == localTeam then u_SendButtonFunc = { function(self) AskForUnits() end, } m_SupportButtonFunc = { function(self) end, } e_SupportButtonFunc = { function(self) end, } m_SendButtonFunc = { function(self) AskForResources("metal") end, } e_SendButtonFunc = { function(self) AskForResources("energy") end, } u_SendButtonCol = {1, .3, .3, 1} u_SendButtonTip = 'Double click to ask for Units.' m_SupportButtonTip = '' e_SupportButtonTip = '' m_SendButtonTip = 'Double click to ask for metal.' e_SendButtonTip = 'Double click to ask for energy.' end MakeNewButton(entity,"u_SendButton",{x = x_share, y = (fontsize+1)*(row+0.5)-2.5, height = fontsize, width = fontsize, tooltip = options.show_tooltips.value and u_SendButtonTip, padding = {0,0,0,0}, OnDblClick = u_SendButtonFunc, children = { Image:New{ x=0,y=0, height='100%', width='100%', color = u_SendButtonCol, file = sharePic, }, }, }) if options.show_income_stats.value and entity.teamID ~= localTeam then MakeNewButton(entity,"m_SupportButton",{x = x_m_income+5, align = 'right', tooltip = options.show_tooltips.value and m_SupportButtonTip, OnClick = m_SupportButtonFunc, }) MakeNewButton(entity,"e_SupportButton",{x = x_e_income+5, align = 'right', tooltip = options.show_tooltips.value and e_SupportButtonTip, OnClick = e_SupportButtonFunc, }) end MakeNewButton(entity,"m_SendButton",{x = x_m_fill + 6, tooltip = options.show_tooltips.value and m_SendButtonTip, OnDblClick = m_SendButtonFunc, }) MakeNewButton(entity,"e_SendButton",{x = x_e_fill + 2, tooltip = options.show_tooltips.value and e_SendButtonTip, OnDblClick = e_SendButtonFunc, }) end local function MakeListTeamworkButtons(entity,entitygroup,TeamList) local ClickFunc if amSpec then MakeNewButton(entity,"WatchButton",{x = x_name, width = name_width, tooltip = options.show_tooltips.value and 'View ' .. entitygroup .. '.', OnClick = { function(self) ViewTeam(TeamList) end, } }) else if entitygroup == "team" then ClickFunc = AddTeamtoSupport elseif entitygroup == "clan" then ClickFunc = AddClantoSupport end MakeNewButton(entity,entitygroup.."ChatButton",{x = x_name, width = name_width, tooltip = options.show_tooltips.value and 'Chat with your Team.', OnClick = { function(self) ChatwithTeam() end, } }) if options.show_income_stats.value then MakeNewButton(ListSupportButton,entitygroup.."metal",{x = x_m_income+5, align = 'right', tooltip = options.show_tooltips.value and 'Click to Support '..entitygroup..' Members with metal', OnClick = { function(self) ClickFunc(entity, "metal") end, }, }) MakeNewButton(ListSupportButton,entitygroup.."energy",{x = x_e_income+5, align = 'right', tooltip = options.show_tooltips.value and 'Click to Support '..entitygroup..' Members with energy', OnClick = { function(self) ClickFunc(entity, "energy") end, }, }) end end end --TeamworkButtons END local function UpdatePlayerTeamStats(entity,s,d) --Damage if entity.m_mobilesLabel then entity.m_mobilesLabel:SetCaption(FormatMetalStats(s.mMobs,true)) end if entity.m_defenseLabel then entity.m_defenseLabel:SetCaption(FormatMetalStats(s.mDefs)) end --Damage Begin if entity.m_cdamageLabel then entity.m_cdamageLabel:SetCaption(FormatDamageStats(d.damageDealt)) end if entity.m_tdamageLabel then entity.m_tdamageLabel:SetCaption(FormatDamageStats(d.damageReceived)) end --Damage END if entity.m_incomeLabel then entity.m_incomeLabel:SetCaption(string.format("%." .. (0) .. "f", s.mInco)) end if entity.e_incomeLabel then entity.e_incomeLabel:SetCaption(string.format("%." .. (0) .. "f", s.eInco)) end if entity.m_fillBar then entity.m_fillBar:SetValue(s.mCurr/s.mStor) end if entity.e_fillBar then entity.e_fillBar:SetValue(s.eCurr/s.eStor) end end -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- local function UpdatePingCpu(entity,pingTime,cpuUsage,pstatus) pingTime = pingTime or 0 cpuUsage = cpuUsage or 0 if pstatus == 'gone' then pingTime = 0 cpuUsage = 0 end local pingCol, cpuCol, pingText, cpuText = FormatPingCpu(pingTime,cpuUsage) if options.cpu_ping_as_text.value then if entity.cpuLabel then entity.cpuLabel.font:SetColor(cpuCol) ; entity.cpuLabel:SetCaption(cpuText) end if entity.pingLabel then entity.pingLabel.font:SetColor(pingCol) ; entity.pingLabel:SetCaption(pingText) end else if entity.cpuImg then entity.cpuImg.color = cpuCol if options.show_tooltips.value then entity.cpuImg.tooltip = ('CPU: ' .. cpuText) end entity.cpuImg:Invalidate() end if entity.pingImg then entity.pingImg.color = pingCol if options.show_tooltips.value then entity.pingImg.tooltip = ('Ping: ' .. pingText) end entity.pingImg:Invalidate() end end end --Clan Begin local function updateresourcetotals(allyListEntities, allyEntities) if allyListEntities then for k,v in pairs(allyListEntities) do local r = { eCurr = 0, eStor = 0, eInco = 0, mCurr = 0, mStor = 0, mInco = 0, mMobs = 0, mDefs = 0 } local rd = { damageDealt = 0, damageReceived = 0} --Damage if allyEntities[k] then for j=1,#allyEntities[k] do local teamID = allyEntities[k][j] if teamID then local s = GetPlayerTeamStats(teamID) AccumulatePlayerTeamStats(r,s) --Damage Begin local d = GetTeamStatsHistory(teamID) AccumulatePlayerTeamDamage(rd,d) --Damage END end end end UpdatePlayerTeamStats(v,r,rd) end end end --Clan END -- updates as needed: status, name, ping, cpu, resource stats, etc. local function UpdatePlayerInfo() for i=1,#entities do local teamID if entities[i].isAI then teamID = entities[i].teamID Support(teamID) --Support else local playerID = entities[i].playerID local name,active,spectator,localteamID,allyTeamID,pingTime,cpuUsage = Spring.GetPlayerInfo(playerID) teamID = localteamID local teamcolor = teamID and {Spring.GetTeamColor(teamID)} or {1,1,1,1} -- status (player status and team status) local pstatus = nil local tstatus = '' local tstatuscolor = {1,1,1,1} if not active then if Spring.GetGameSeconds() < 0.1 or cpuUsage > 1 then tstatus = '?' ; tstatuscolor = teamcolor else pstatus = 'gone' end elseif spectator and (teamID ~= 0 or teamZeroPlayers[playerID]) then pstatus = 'spec' end if pstatus == 'spec' or pstatus == 'gone' then if Spring.GetTeamUnitCount(teamID) > 0 then tstatus = '!!' tstatuscolor = {1,1,0,1} else tstatus = 'X' tstatuscolor = {1,0,0,1} end end local displayname local whitestring = GetColorChar({1,1,1,1}) local greystring = GetColorChar({.5,.5,.5,1}) local teamcolorstring = GetColorChar(teamcolor) -- these were part of a failed experiment that I might try to fix later if pstatus == 'spec' then displayname = (" ss: " .. name) elseif pstatus == 'gone' then displayname = (" xx: " .. name) else displayname = (name) end if entities[i].nameLabel then entities[i].nameLabel:SetCaption(displayname) end if entities[i].statusLabel then entities[i].statusLabel:SetCaption(tstatus) ; entities[i].statusLabel.font:SetColor(tstatuscolor) end UpdatePingCpu(entities[i],pingTime,cpuUsage,pstatus) if pstatus ~= 'spec' and pstatus ~= 'gone' then Support(teamID) end --Support end -- if not isAI -- update the resource stats for all entities, including AIs local s = GetPlayerTeamStats(teamID) local d = GetTeamStatsHistory(teamID) UpdatePlayerTeamStats(entities[i],s,d) end -- for entities -- update ping and cpu for the spectators if #specTeam.roster ~= 0 then for i = 1,#specTeam.roster do local playerID = specTeam.roster[i].playerID local name,active,spectator,localteamID,allyTeamID,pingTime,cpuUsage = Spring.GetPlayerInfo(playerID) specTeam.roster[i].pingTime = pingTime specTeam.roster[i].cpuUsage = cpuUsage if list_size == 4 then UpdatePingCpu(specTeam.roster[i],pingTime,cpuUsage) end end end MakeSpecTooltip() for allyTeam, cb in pairs(cfCheckBoxes) do cb.tooltip = CfTooltip(allyTeam) end --Clan Begin -- update resource totals for allyTeams and allyClans updateresourcetotals(allyTeamEntities, allyTeams) updateresourcetotals(allyClanEntities, allyClans) --Clan ENd end -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- local function AddTableHeaders() if cf then scroll_cpl:AddChild( Label:New{ x=x_cf, y=(fontsize+1) * row, caption = 'CF', fontShadow = true, fontsize = fontsize, } ) end --Damage Begin if options.show_Damage_stats.value then scroll_cpl:AddChild( Image:New{ x=x_c_damage - 10, y=((fontsize+1) * row) + 3, height = (fontsize)+1, file = 'LuaUI/Images/commands/Bold/settarget.png',} ) scroll_cpl:AddChild( Image:New{ x=x_t_damage - 7, y=((fontsize+1) * row) + 3, height = (fontsize)+1, file = 'LuaUI/Images/commands/Bold/health.png',} ) end --Damage END if options.show_unit_stats.value then scroll_cpl:AddChild( Image:New{ x=x_m_mobiles - 10, y=((fontsize+1) * row) + 3, height = (fontsize)+1, color = {1, .3, .3, 1}, file = 'LuaUI/Images/commands/Bold/attack.png',} ) scroll_cpl:AddChild( Image:New{ x=x_m_defense - 7, y=((fontsize+1) * row) + 3, height = (fontsize)+1, color = {.3, .3, 1, 1}, file = 'LuaUI/Images/commands/Bold/guard.png',} ) end if options.show_income_stats.value then scroll_cpl:AddChild( Image:New{ x=x_e_income - 15, y=((fontsize+1) * row) + 3, height = (fontsize)+1, file = 'LuaUI/Images/energy.png',} ) scroll_cpl:AddChild( Image:New{ x=x_m_income - 15, y=((fontsize+1) * row) + 3, height = (fontsize)+1, file = 'LuaUI/Images/ibeam.png',} ) end scroll_cpl:AddChild( Label:New{ x=x_cpu, y=(fontsize+1) * row, caption = 'C', fontShadow = true, fontsize = fontsize,} ) scroll_cpl:AddChild( Label:New{ x=x_ping, y=(fontsize+1) * row, caption = 'P', fontShadow = true, fontsize = fontsize,} ) end local function AddCfCheckbox(allyTeam) if cf and allyTeam ~= -1 and allyTeam ~= localAlliance then local cfCheck = Checkbox:New{ x=x_cf,y=(fontsize+1) * row + 3,width=20, caption='', checked = Spring.GetTeamRulesParam(localTeam, 'cf_vote_' ..allyTeam)==1, tooltip = CfTooltip(allyTeam), OnChange = { function(self) Spring.SendLuaRulesMsg('ceasefire:'.. (self.checked and 'n' or 'y') .. allyTeam) self.tooltip = CfTooltip(allyTeam) end }, } scroll_cpl:AddChild(cfCheck) cfCheckBoxes[allyTeam] = cfCheck end end -- adds all the entity information local function AddEntity(entity, teamID, allyTeamID) local teamcolor = (teamID and teamID ~= -1) and {Spring.GetTeamColor(teamID)} or {1,1,1,1} if entity.isAI then MakeNewLabel(entity,"nameLabel",{x=x_name,width=name_width,caption = entity.name,textColor = teamcolor,}) else -- clan/faction emblems, level, country, elo local icon = nil local icRank = nil local elo = nil local eloCol = nil local icCountry = entity.country and entity.country ~= '' and entity.country ~= '??' and "LuaUI/Images/flags/" .. (entity.country) .. ".png" or nil if options.show_ccr.value then if entity.clan and entity.clan ~= "" then icon = "LuaUI/Configs/Clans/" .. entity.clan ..".png" elseif entity.faction and entity.faction ~= "" then icon = "LuaUI/Configs/Factions/" .. entity.faction ..".png" end if entity.level and entity.level ~= "" then icRank = "LuaUI/Images/Ranks/" .. math.min((1+math.floor((entity.level or 0)/10)),9) .. ".png" end if icCountry then MakeNewIcon(entity,"countryIcon",{x=x_icon_country,file=icCountry,}) end if options.rank_as_text.value then if entity.level then MakeNewLabel(entity,"rankLabel",{x=x_icon_rank,width=14,caption = math.min(entity.level,99),textColor = {0.85,0.85,0.85,1},align = 'right',}) end else if icRank then MakeNewIcon(entity,"rankIcon",{x=x_icon_rank,file=icRank,}) end end if icon then MakeNewIcon(entity,"clanIcon",{x=x_icon_clan,file=icon,y=((fontsize+1)*row)+5,width=fontsize-1,height=fontsize-1}) end end if entity.elo and entity.elo ~= "" then elo, eloCol = FormatElo(entity.elo) end if elo then MakeNewLabel(entity,"eloLabel",{x=x_elo,caption = elo,textColor = eloCol,}) end -- status (player status and team status) local pstatus = nil local tstatus = '' local tstatuscolor = {1,1,1,1} if teamID ~= -1 then if not entity.isActive then if Spring.GetGameSeconds() < 0.1 or entity.cpuUsage > 1 then tstatus = '?' ; tstatuscolor = teamcolor else pstatus = 'gone' end elseif entity.isSpec and (teamID ~= 0 or teamZeroPlayers[entity.playerID]) then pstatus = 'spec' end end if pstatus == 'spec' or pstatus == 'gone' then if Spring.GetTeamUnitCount(teamID) > 0 then tstatus = '!!' tstatuscolor = {1,1,0,1} else tstatus = 'X' tstatuscolor = {1,0,0,1} end end MakeNewLabel(entity,"statusLabel",{x=x_status,width=16,caption = tstatus,textColor = tstatuscolor,}) -- name, including player status designators local displayname local whitestring = GetColorChar({1,1,1,1}) local greystring = GetColorChar({.5,.5,.5,1}) local teamcolorstring = GetColorChar(teamcolor) -- these were part of a failed experiment that I might try to fix later if pstatus == 'spec' then displayname = (" ss: " .. entity.name) elseif pstatus == 'gone' then displayname = (" xx: " .. entity.name) else displayname = (entity.name) end MakeNewLabel(entity,"nameLabel",{x=x_name,width=name_width,caption = displayname,textColor = teamcolor,}) --View Begin if teamID ~= -1 and amSpec and(teams[teamID] and (teams[teamID].isPlaying and not teams[teamID].isDead)) then MakeNewButton(entity,"WatchButton",{x = x_name, width = name_width, tooltip = options.show_tooltips.value and 'View ' .. entity.name ..'.', textColor = teamcolor, OnClick = { function(self) ViewTeam({ entity.teamID }) end, } }) end --View END --Chat Begin if teamID ~= -1 and not amSpec and (teams[teamID] and (teams[teamID].isPlaying and not teams[teamID].isDead)) and allyTeamID == localAlliance and teamID ~= localTeam then MakeNewButton(entity,"ChatButton",{x = x_name, width = name_width, tooltip = options.show_tooltips.value and "Chat with " .. entity.name, textColor = teamcolor, OnClick = { function(self) ChatwithUser(entity.name) end, } }) end --Chat END if teamID == localTeam then end -- ping and cpu icons / labels local pingCol, cpuCol, pingText, cpuText = FormatPingCpu(pstatus == 'gone' and 0 or entity.pingTime,pstatus == 'gone' and 0 or entity.cpuUsage) if options.cpu_ping_as_text.value then MakeNewLabel(entity,"cpuLabel",{x=x_cpu,width = (fontsize+3)*10/16,caption = cpuText,textColor = cpuCol,align = 'right',}) MakeNewLabel(entity,"pingLabel",{x=x_ping,width = (fontsize+3)*10/16,caption = pingText,textColor = pingCol,align = 'right',}) else MakeNewIcon(entity,"cpuImg",{x=x_cpu,file=cpuPic,width = (fontsize+3)*10/16,keepAspect = false,tooltip = 'CPU: ' .. cpuText,}) MakeNewIcon(entity,"pingImg",{x=x_ping,file=pingPic,width = (fontsize+3)*10/16,keepAspect = false,tooltip = 'Ping: ' .. pingText,}) entity.cpuImg.color = cpuCol entity.pingImg.color = pingCol function entity.cpuImg:HitTest(x,y) return self end function entity.pingImg:HitTest(x,y) return self end end end -- if not isAI -- mobile and defense metal, metal and energy income, resource bars if teamID ~= -1 and (allyTeamID == localAlliance or amSpec) then local s = GetPlayerTeamStats(teamID) local d = GetTeamStatsHistory(teamID) DrawPlayerTeamStats(entity,teamcolor,s,d) end --TeamworkButtons Begin if teamID ~= -1 and not amSpec and (teams[teamID] and (teams[teamID].isPlaying and not teams[teamID].isDead)) and allyTeamID == localAlliance then MakePlayerTeamworkButtons(entity,teamcolor) end --TeamworkButtons END row = row + 1 end local function AddTeamSummarie(allyTeamID) local allyTeamResources local allyTeamDamage if allyTeams[allyTeamID] then for j=1,#allyTeams[allyTeamID] do local teamID = allyTeams[allyTeamID][j] if teamID then local s = GetPlayerTeamStats(teamID) allyTeamResources = allyTeamResources or {} allyTeamResources[allyTeamID] = allyTeamResources[allyTeamID] or { eCurr = 0, eStor = 0, eInco = 0, mCurr = 0, mStor = 0, mInco = 0, mMobs = 0, mDefs = 0 } AccumulatePlayerTeamStats(allyTeamResources[allyTeamID],s) --Damage BEGIN local d = GetTeamStatsHistory(teamID) allyTeamDamage = allyTeamDamage or {} allyTeamDamage[allyTeamID] = allyTeamDamage[allyTeamID] or { damageDealt = 0, damageReceived = 0 } AccumulatePlayerTeamDamage(allyTeamDamage[allyTeamID],d) --Damage END end end end if allyTeamResources and allyTeamDamage then --Damage if allyTeamResources[allyTeamID] and allyTeamDamage[allyTeamID] and allyTeams[allyTeamID] then --Damage allyTeamEntities[allyTeamID] = allyTeamEntities[allyTeamID] or {} local allyTeamColor local elo local eloCol if allyTeamsElo[allyTeamID] then elo, eloCol = FormatElo(allyTeamsElo[allyTeamID].total / allyTeamsElo[allyTeamID].count, true) end if (localTeam ~= 0 or teamZeroPlayers[myID]) and allyTeamID == localAlliance then allyTeamColor = {Spring.GetTeamColor(localTeam)} else allyTeamColor = {Spring.GetTeamColor(allyTeams[allyTeamID][1])} end if options.show_ccr.value then MakeNewLabel(allyTeamEntities[allyTeamID],"nameLabel",{x=x_icon_clan,width=150,caption = ("TEAM " .. allyTeamID+1 .. "."),textColor = allyTeamColor,}) LabelCaption = "Overall:" else LabelCaption = "Team " .. allyTeamID+1 .. ". Overall:" end MakeNewLabel(allyTeamEntities[allyTeamID],"nameLabel",{x=x_name,width=150,caption = LabelCaption,textColor = allyTeamColor,}) DrawPlayerTeamStats(allyTeamEntities[allyTeamID],allyTeamColor,allyTeamResources[allyTeamID],allyTeamDamage[allyTeamID]) --Damage MakeListTeamworkButtons(allyTeamEntities[allyTeamID],"team",allyTeams[allyTeamID]) if elo then MakeNewLabel(allyTeamEntities[allyTeamID],"eloLabel",{x=x_elo,caption = elo,textColor = eloCol,}) end if allyTeamsDead[allyTeamID] then MakeNewLabel(allyTeamEntities[allyTeamID],"statusLabel",{x=x_status,width=16,caption = "X",textColor = {1,0,0,1},}) end row = row + 1 end end end local function AddTeamSummarieNonSpec(allyTeamID) if allyTeams[allyTeamID] then allyTeamEntities[allyTeamID] = allyTeamEntities[allyTeamID] or {} local allyTeamColor local elo local eloCol local LabelX if allyTeamsElo[allyTeamID] then elo, eloCol = FormatElo(allyTeamsElo[allyTeamID].total / allyTeamsElo[allyTeamID].count, true) end if (localTeam ~= 0 or teamZeroPlayers[myID]) and allyTeamID == localAlliance then allyTeamColor = {Spring.GetTeamColor(localTeam)} else allyTeamColor = {Spring.GetTeamColor(allyTeams[allyTeamID][1])} end if options.show_ccr.value then LabelX = x_icon_clan else LabelX = x_name end MakeNewLabel(allyTeamEntities[allyTeamID],"nameLabel",{x=LabelX,width=150,caption = ("TEAM " .. allyTeamID+1 .. ":"),textColor = allyTeamColor,}) if elo then MakeNewLabel(allyTeamEntities[allyTeamID],"eloLabel",{x=x_elo,caption = elo,textColor = eloCol,}) end if allyTeamsDead[allyTeamID] then MakeNewLabel(allyTeamEntities[allyTeamID],"statusLabel",{x=x_status,width=16,caption = "X",textColor = {1,0,0,1},}) end row = row + 1 end end local function AddClanSummarie(allyTeamID) local allyClanResources local allyClanDamage if allyClans[allyTeamID] then for j=1,#allyClans[allyTeamID] do local teamID = allyClans[allyTeamID][j] if teamID then local s = GetPlayerTeamStats(teamID) allyClanResources = allyClanResources or {} allyClanResources[allyTeamID] = allyClanResources[allyTeamID] or { eCurr = 0, eStor = 0, eInco = 0, mCurr = 0, mStor = 0, mInco = 0, mMobs = 0, mDefs = 0 } AccumulatePlayerTeamStats(allyClanResources[allyTeamID],s) --Damage BEGIN local d = GetTeamStatsHistory(teamID) allyClanDamage = allyClanDamage or {} allyClanDamage[allyTeamID] = allyClanDamage[allyTeamID] or { damageDealt = 0, damageReceived = 0 } AccumulatePlayerTeamDamage(allyClanDamage[allyTeamID],d) --Damage END end end end if allyClanResources and allyClanDamage then --Damage if allyClanResources[allyTeamID] and allyClanDamage[allyTeamID] and allyClans[allyTeamID] then --Damage allyClanEntities[allyTeamID] = allyClanEntities[allyTeamID] or {} local allyTeamColor local elo local eloCol local LabelCaption if allyClansElo[allyTeamID] then elo, eloCol = FormatElo(allyClansElo[allyTeamID].total / allyClansElo[allyTeamID].count, true) end if (localTeam ~= 0 or teamZeroPlayers[myID]) and allyTeamID == localAlliance then allyTeamColor = {Spring.GetTeamColor(localTeam)} else allyTeamColor = {Spring.GetTeamColor(allyClans[allyTeamID][1])} end if options.show_ccr.value then MakeNewLabel(allyClanEntities[allyTeamID],"nameLabel",{x=x_icon_clan,width=150,caption = ("CLAN " .. allyTeamID+1 .. "."),textColor = allyTeamColor,}) LabelCaption = "Overall:" else LabelCaption = "CLAN " .. allyTeamID+1 .. ". Overall:" end MakeNewLabel(allyClanEntities[allyTeamID],"nameLabel",{x=x_name,width=150,caption = LabelCaption,textColor = allyTeamColor,}) DrawPlayerTeamStats(allyClanEntities[allyTeamID],allyTeamColor,allyClanResources[allyTeamID],allyClanDamage[allyTeamID]) --Damage MakeListTeamworkButtons(allyClanEntities[allyTeamID],"clan",allyClans[allyTeamID]) if elo then MakeNewLabel(allyClanEntities[allyTeamID],"eloLabel",{x=x_elo,caption = elo,textColor = eloCol,}) end if allyTeamsDead[allyTeamID] then MakeNewLabel(allyClanEntities[allyTeamID],"statusLabel",{x=x_status,width=16,caption = "X",textColor = {1,0,0,1},}) end row = row + 1 end end end local function AddClanSummarieNonSpec(allyTeamID) if allyClans[allyTeamID] then allyClanEntities[allyTeamID] = allyClanEntities[allyTeamID] or {} local allyTeamColor local elo local eloCol local LabelX if allyClansElo[allyTeamID] then elo, eloCol = FormatElo(allyClansElo[allyTeamID].total / allyClansElo[allyTeamID].count, true) end if (localTeam ~= 0 or teamZeroPlayers[myID]) and allyTeamID == localAlliance then allyTeamColor = {Spring.GetTeamColor(localTeam)} else allyTeamColor = {Spring.GetTeamColor(allyClans[allyTeamID][1])} end if options.show_ccr.value then LabelX = x_icon_clan else LabelX = x_name end MakeNewLabel(allyClanEntities[allyTeamID],"nameLabel",{x=LabelX,width=150,caption = ("CLAN " .. allyTeamID+1 .. "."),textColor = allyTeamColor,}) if elo then MakeNewLabel(allyClanEntities[allyTeamID],"eloLabel",{x=x_elo,caption = elo,textColor = eloCol,}) end if allyTeamsDead[allyTeamID] then MakeNewLabel(allyClanEntities[allyTeamID],"statusLabel",{x=x_status,width=16,caption = "X",textColor = {1,0,0,1},}) end row = row + 1 end end local function AlignScrollPanel() local height = math.ceil(row * (fontsize+1.5) + 8) scroll_cpl.height = math.min(height, window_cpl.height) if not (options.alignToTop.value) then scroll_cpl.y = (window_cpl.height) - scroll_cpl.height else scroll_cpl.y = 0 end end -- sort each allyteam's list of playerteams: -- own pteam first, then by elo of the first player on the pteam, and alphabetically otherwise -- dead pteams (even your own) come after alive pteams -- dead pteams whose first player is speccing come before dead pteams whose first player is gone local function sorteachteamlist(allyEntitiesSorted, allyEntities) for i=1,#allyEntitiesSorted do -- for every ally team local allyTeamID = allyEntitiesSorted[i] if allyEntities[allyTeamID] then table.sort (allyEntities[allyTeamID], function(a,b) if (teams[a].isDead or not teams[a].isPlaying) and not (teams[b].isDead or not teams[b].isPlaying) then return false end if (teams[b].isDead or not teams[b].isPlaying) and not (teams[a].isDead or not teams[a].isPlaying) then return true end if (teams[a].isDead or not teams[a].isPlaying) and (teams[b].isDead or not teams[b].isPlaying) then if teams[a].roster[1].isActive and not teams[b].roster[1].isActive then return true end if teams[b].roster[1].isActive and not teams[a].roster[1].isActive then return false end end if localTeam ~= 0 or teamZeroPlayers[myID] then if a == localTeam then return true end if b == localTeam then return false end end if teams[a].roster[1].elo and teams[b].roster[1].elo then return teams[a].roster[1].elo > teams[b].roster[1].elo end return a > b end ) end end end -- add the player entities local function addplayerentities(allyEntities, allyTeamID, alreadyadded) if allyEntities[allyTeamID] and (list_size >= 3 or allyTeamID == localAlliance) then AddCfCheckbox(allyTeamID) for i=1,#allyEntities[allyTeamID] do -- for every player team on the ally team local teamID = allyEntities[allyTeamID][i] if alreadyadded then if not TableFind(teamID, allyClans[allyTeamID]) then if teams[teamID] and teams[teamID].roster and (list_size >= 2 or teamID == localTeam) then for k=1,#teams[teamID].roster do -- for every player on the player team AddEntity(teams[teamID].roster[k], teamID, allyTeamID) end end end else if teams[teamID] and teams[teamID].roster and (list_size >= 2 or teamID == localTeam) then for k=1,#teams[teamID].roster do -- for every player on the player team AddEntity(teams[teamID].roster[k], teamID, allyTeamID) end end end end end end SetupPlayerNames = function() entities = {} teams = {} allyTeams = {} allyTeamsElo = {} --Clan Begin allyClans = {} allyClansElo = {} --Clan END specTeam = {roster = {}} playerTeamStatsCache = {} fontsize = options.text_height.value scroll_cpl:ClearChildren() local playerlist = Spring.GetPlayerList() local teamsSorted = Spring.GetTeamList() local allyTeamsSorted = Spring.GetAllyTeamList() local allyClanSorted = {} list_size = options.list_size.value or 3 if not (localTeam ~= 0 or teamZeroPlayers[myID]) then if list_size == 1 then list_size = 0 end if list_size == 2 then list_size = 3 end end -- register any AIs as entities, assign teams to allyTeams for i=1,#teamsSorted do local teamID = teamsSorted[i] if teamID ~= Spring.GetGaiaTeamID() then teams[teamID] = teams[teamID] or {roster = {}} local _,leader,isDead,isAI,_,allyTeamID = Spring.GetTeamInfo(teamID) if isAI then local skirmishAIID, name, hostingPlayerID, shortName, version, options = Spring.GetAIInfo(teamID) name = '<'.. name ..'> '.. shortName local entityID = #entities + 1 entities[entityID] = {name = name, teamID = teamID, isAI = true} local index = #teams[teamID].roster + 1 teams[teamID].roster[index] = entities[entityID] teams[teamID].isPlaying = true end teams[teamID].leader = leader allyTeams[allyTeamID] = allyTeams[allyTeamID] or {} allyTeams[allyTeamID][#allyTeams[allyTeamID]+1] = teamID if isDead then teams[teamID].isDead = true end end --if teamID ~= Spring.GetGaiaTeamID() end --for each team -- go through all players, register as entities, assign to teams -- also store the data needed later to calculate the team average elo for i=1, #playerlist do local playerID = playerlist[i] local name,active,spectator,teamID,allyTeamID,pingTime,cpuUsage,country,rank,customKeys = Spring.GetPlayerInfo(playerID) local clan, faction, level, elo if customKeys then clan = customKeys.clan faction = customKeys.faction level = customKeys.level elo = customKeys.elo end if teamID == 0 and not spectator then teamZeroPlayers[playerID] = true end if teamID ~= 0 or teamZeroPlayers[playerID] then local entityID = #entities + 1 entities[entityID] = { playerID = playerID, name = name, isActive = active, isSpec = spectator, teamID = teamID, pingTime = pingTime, cpuUsage = cpuUsage, country = country, rank = rank, clan = clan, faction = faction, level = level, elo = elo, } local index = #teams[teamID].roster + 1 teams[teamID].roster[index] = entities[entityID] if not (spectator or (not active and Spring.GetGameSeconds() > 0.1 and cpuUsage < 1)) then teams[teamID].isPlaying = true if allyTeamID and elo then if allyTeamsElo[allyTeamID] then allyTeamsElo[allyTeamID].total = allyTeamsElo[allyTeamID].total + elo allyTeamsElo[allyTeamID].count = allyTeamsElo[allyTeamID].count + 1 else allyTeamsElo[allyTeamID] = { ["total"] = elo, ["count"] = 1} end end --Clan Begin if allyTeamID and clan == myClan then if not TableFind(allyTeamID, allyClanSorted) then allyClanSorted[#allyClanSorted+1] = allyTeamID end allyClans[allyTeamID] = allyClans[allyTeamID] or {} allyClans[allyTeamID][#allyClans[allyTeamID]+1] = teamID if allyClansElo[allyTeamID] then allyClansElo[allyTeamID].total = allyClansElo[allyTeamID].total + elo allyClansElo[allyTeamID].count = allyClansElo[allyTeamID].count + 1 else allyClansElo[allyTeamID] = { ["total"] = elo, ["count"] = 1} end end --Clan End end end if spectator and active then specTeam.roster[#(specTeam.roster) + 1] = { playerID = playerID, name = name, isActive = active, isSpec = spectator, teamID = teamID, pingTime = pingTime, cpuUsage = cpuUsage, country = country, rank = rank, clan = clan, faction = faction, level = level, elo = elo, } end end -- sort each playerteam's roster: self first, then by elo, and alphabetically otherwise for i=1,#teams do if teams[i].roster then table.sort (teams[i].roster, function(a,b) if localTeam ~= 0 or teamZeroPlayers[myID] then if a.playerID == myID then return true end if b.playerID == myID then return false end end return a.elo > b.elo end ) end end --Clan Begin sorteachteamlist(allyTeamsSorted, allyTeams) sorteachteamlist(allyClanSorted, allyClans) --Caln End -- if we haven't ever done so before, build the ally team sort order -- ally teams are sorted by total elo, so larger teams will be above smaller teams -- ally team sort order is determined at the very start, and doesn't change even if -- the team composition changes (except that dead teams will fall to the bottom) -- -- while we're at it, determine whether or not to show the ally team summary lines -- if #allyTeamOrderRank == 0 then if #allyTeams[localAlliance] > 2 then myTeamIsVeryBig = true end for i=1,#allyTeamsSorted do -- for every ally team local allyTeamID = allyTeamsSorted[i] allyTeamOrderRank[allyTeamID] = 0 if allyTeams[allyTeamID] then if #allyTeams[allyTeamID] > 2 then existsVeryBigTeam = true end if #allyTeams[allyTeamID] > 1 then numBigTeams = numBigTeams + 1 end for j=1,#allyTeams[allyTeamID] do -- for every player team on the ally team local teamID = allyTeams[allyTeamID][j] if teams[teamID] and teams[teamID].roster then for k=1,#teams[teamID].roster do -- for every player on the player team if teams[teamID].roster[k].elo then allyTeamOrderRank[allyTeamID] = allyTeamOrderRank[allyTeamID] + teams[teamID].roster[k].elo end end end end end end end --Clan Begin if #allyClanOrderRank == 0 then if allyClans[localAlliance] then if #allyClans[localAlliance] > 1 then myClanIsVeryBig = true end for i=1,#allyClanSorted do -- for every ally team local allyTeamID = allyClanSorted[i] allyClanOrderRank[allyTeamID] = 0 if allyClans[allyTeamID] then if #allyClans[allyTeamID] > 1 then existsVeryBigClan = true end --Have to be diffrent extra var if #allyClans[allyTeamID] > 1 then numBigClan = numBigClan + 1 end --here too for j=1,#allyClans[allyTeamID] do -- for every player team on the ally team local teamID = allyClans[allyTeamID][j] if teams[teamID] and teams[teamID].roster then for k=1,#teams[teamID].roster do -- for every player on the player team if teams[teamID].roster[k].elo then allyClanOrderRank[allyTeamID] = allyClanOrderRank[allyTeamID] + teams[teamID].roster[k].elo end end end end end end end end --Clan End -- find out which ally teams are dead allyTeamsDead = {} for i=1,#allyTeamsSorted do -- for every ally team local allyTeamID = allyTeamsSorted[i] allyTeamsDead[allyTeamID] = true if allyTeams[allyTeamID] then for j=1,#allyTeams[allyTeamID] do -- for every player team on the ally team local teamID = allyTeams[allyTeamID][j] if teams[teamID] and not (teams[teamID].isDead or not teams[teamID].isPlaying) then allyTeamsDead[allyTeamID] = nil end end end end --Clan Begin allyClanDead = {} for i=1,#allyClanSorted do local allyTeamID = allyClanSorted[i] allyClanDead[allyTeamID] = true if allyClans[allyTeamID] then for j=1,#allyClans[allyTeamID] do -- for every player team on the ally team local teamID = allyTeams[allyTeamID][j] if teams[teamID] and not (teams[teamID].isDead or not teams[teamID].isPlaying) then allyClanDead[allyTeamID] = nil end end end end --Clan END -- sort allyteams: own at top, others in order by total elo, dead teams at bottom table.sort(allyTeamsSorted, function(a,b) if allyTeamsDead[a] and not allyTeamsDead[b] then return false end if allyTeamsDead[b] and not allyTeamsDead[a] then return true end if allyTeamsDead[b] and allyTeamsDead[a] and allyTeams[a] and allyTeams[b] then local teamIDa = allyTeams[a][1] local teamIDb = allyTeams[b][1] if teamIDa and teamIDb and teams[teamIDa].roster and teams[teamIDb].roster then if teams[teamIDa].roster[1].isActive and not teams[teamIDb].roster[1].isActive then return true end if teams[teamIDb].roster[1].isActive and not teams[teamIDa].roster[1].isActive then return false end end end if localTeam ~= 0 or teamZeroPlayers[myID] then if a == localAlliance then return true end if b == localAlliance then return false end end return allyTeamOrderRank[a] > allyTeamOrderRank[b] end ) -- Clan Begin table.sort(allyClanSorted, function(a,b) if allyClanDead[a] and not allyClanDead[b] then return false end if allyClanDead[b] and not allyClanDead[a] then return true end if allyClanDead[b] and allyClanDead[a] and allyClans[a] and allyClans[b] then local teamIDa = allyClans[a][1] local teamIDb = allyClans[b][1] if teamIDa and teamIDb and teams[teamIDa].roster and teams[teamIDb].roster then if teams[teamIDa].roster[1].isActive and not teams[teamIDb].roster[1].isActive then return true end if teams[teamIDb].roster[1].isActive and not teams[teamIDa].roster[1].isActive then return false end end end if localTeam ~= 0 or teamZeroPlayers[myID] then if a == localAlliance then return true end if b == localAlliance then return false end end return allyClanOrderRank[a] > allyClanOrderRank[b] end ) --Clan END -- sort the specteam table.sort(specTeam.roster, function(a,b) return a.name:lower() < b.name:lower() end) row = 0 AddTableHeaders() row = row + 1 -- Clan Begin local clanmebers = false if list_size ~= 0 then if amSpec then if options.showSummaries.value and (existsVeryBigTeam or numBigTeams > 2) then for i=1,#allyTeamsSorted do local allyTeamID = allyTeamsSorted[i] AddTeamSummarie(allyTeamID) row = row + 0.2 if options.showClanSummaries.value and allyClans[allyTeamID] and #allyClans[allyTeamID] > 1 then AddClanSummarie(allyTeamID) row = row + 0.2 clanmebers = true addplayerentities(allyClans, allyTeamID, false) row = row + 0.4 end if allyTeams[allyTeamID] then addplayerentities(allyTeams, allyTeamID, clanmebers) end if i ~= (#allyTeamsSorted-1) then row = row + 0.5 end end else for i=1,#allyTeamsSorted do -- for every ally team local allyTeamID = allyTeamsSorted[i] if allyTeams[allyTeamID] then addplayerentities(allyTeams, allyTeamID, clanmebers) if numBigTeams > 2 then row = row + 0.6 end end end end else if options.showSummaries.value and myTeamIsVeryBig then --AddTeamSummarie({localTeam}) AddTeamSummarie(localAlliance) row = row + 0.2 if options.showClanSummaries.value and allyClans[localAlliance] and #allyClans[localAlliance] > 1 then AddClanSummarie(localAlliance) row = row + 0.2 clanmebers = true addplayerentities(allyClans, localAlliance, false) row = row + 0.4 end if allyTeams[localAlliance] then addplayerentities(allyTeams, localAlliance, clanmebers) end clanmebers = false row = row + 0.5 for i=1,#allyTeamsSorted do if localAlliance ~= allyTeamsSorted[i] then local allyTeamID = allyTeamsSorted[i] AddTeamSummarieNonSpec(allyTeamID) row = row + 0.2 if options.showClanSummaries.value and allyClans[allyTeamID] and #allyClans[allyTeamID] > 1 then AddClanSummarieNonSpec(allyTeamID) row = row + 0.2 clanmebers = true addplayerentities(allyClans, allyTeamID, false) row = row + 0.4 end if allyTeams[allyTeamID] then addplayerentities(allyTeams, allyTeamID, clanmebers) end clanmebers = false if i ~= (#allyTeamsSorted-1) then row = row + 0.5 end end end else for i=1,#allyTeamsSorted do -- for every ally team local allyTeamID = allyTeamsSorted[i] if allyTeams[allyTeamID] then addplayerentities(allyTeams, allyTeamID, clanmebers) if numBigTeams > 2 then row = row + 0.6 end end end end end end -- Clan END -- add the spectator entities if list_size == 4 and #specTeam.roster ~= 0 then teams[-1] = specTeam allyTeams[-1] = {-1} for k=1,#specTeam.roster do AddEntity(specTeam.roster[k], -1, -1) end end MakeSpecTooltip() -- ceasefire: restricted zones button if cf then scroll_cpl:AddChild( Checkbox:New{ x=5, y=(fontsize+1) * (row + 0.5), height=fontsize * 1.5, width=160, caption = 'Place Restricted Zones', checked = WG.rzones.rZonePlaceMode, OnChange = { function(self) WG.rzones.rZonePlaceMode = not WG.rzones.rZonePlaceMode; end }, } ) row = row + 1.5 end AlignScrollPanel() end SetupScrollPanel = function () if scroll_cpl then scroll_cpl:Dispose() end local scpl = { parent = window_cpl, --width = "100%", --height = "100%", backgroundColor = {1,1,1,options.backgroundOpacity.value}, borderColor = {1,1,1,options.backgroundOpacity.value}, padding = {0, 5, 0, 5}, --autosize = true, --right = 0, scrollbarSize = 6, width = x_bound, horizontalScrollbar = false, hitTestAllowEmpty = true, noMouseWheel = not options.mousewheel.value, } if options.alignToLeft.value then scpl.left = 0 else scpl.right = 0 end scroll_cpl = ScrollPanel:New(scpl) SetupPlayerNames() end SetupPanels = function () CalculateWidths() local x,y,height,width if window_cpl then y = window_cpl.y height = window_cpl.height width = math.max(x_windowbound, lastChosenSizeX) x = options.alignToLeft.value and window_cpl.x or (window_cpl.x + window_cpl.width - width) window_cpl:Dispose() else x = screen0.width - x_windowbound y = screen0.height - 150 width = x_windowbound height = 150 end lastSizeX = width window_cpl = Window:New{ dockable = true, name = "Player List", color = {0,0,0,0}, x = x, y = y, width = width, height = height, padding = {0, 0, 0, 0}; parent = screen0, draggable = false, resizable = false, tweakDraggable = true, tweakResizable = true, minimizable = false, minWidth = x_windowbound, OnMouseDown={ function(self) local alt, ctrl, meta, shift = Spring.GetModKeyState() if not meta then return false end WG.crude.OpenPath(options_path) WG.crude.ShowMenu() return true end }, } SetupScrollPanel() end function ToggleVisibility() if window_cpl and scroll_cpl then if options.visible.value then window_cpl:AddChild(scroll_cpl) else window_cpl:RemoveChild(scroll_cpl) end end end function PlayersChanged() if amSpec then SetupPlayerNames() else local _,_,amNowSpec = Spring.GetPlayerInfo(myID) if amNowSpec then amSpec = amNowSpec SetupPanels() else SetupPlayerNames() end end end -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- function widget:Shutdown() Spring.SendCommands({"info 1"}) end -- Part of an experiment to try to improve performance, will come back to it later -- local updateModulus = 8 -- local updateCount = 0 function widget:Update(s) timer = timer + s if timer > UPDATE_FREQUENCY then timer = 0 if (lastSizeX ~= window_cpl.width or lastSizeY ~= window_cpl.height) then --//if user resize the player-list AlignScrollPanel() lastChosenSizeX = window_cpl.width lastSizeX = window_cpl.width lastSizeY = window_cpl.height else -- for i=updateCount+1,#playerTeamStatsCache,updateModulus do -- playerTeamStatsCache[i] = nil -- end -- updateCount = (updateCount + 1) % updateModulus if (not scroll_cpl.parent) then return end --//don't update when window is hidden UpdatePlayerInfo() end end end function widget:PlayerChanged(playerID) PlayersChanged() end function widget:PlayerAdded(playerID) PlayersChanged() end function widget:PlayerRemoved(playerID) PlayersChanged() end function widget:TeamDied(teamID) PlayersChanged() end function widget:TeamChanged(teamID) PlayersChanged() end -- workaround for stupidity function widget:GameStart() SetupPanels() end ----------------------------------------------------------------------- -- we need both UnitCreated and UnitFinished because mobiles and static defense aren't treated the same >:< function widget:UnitCreated(unitID, unitDefID, unitTeam) local speed = UnitDefs[unitDefID].speed local unarmed = UnitDefs[unitDefID].springCategories.unarmed if (speed == nil or speed == 0) and not unarmed then -- is static-d ProcessUnit(unitID, unitDefID, unitTeam) end end function widget:UnitFinished(unitID, unitDefID, unitTeam) local speed = UnitDefs[unitDefID].speed local unarmed = UnitDefs[unitDefID].springCategories.unarmed if speed and speed ~= 0 and (not finishedUnits[unitID]) then -- mobile unit ProcessUnit(unitID, unitDefID, unitTeam) end end function widget:UnitDestroyed(unitID, unitDefID, unitTeam) local speed = UnitDefs[unitDefID].speed local unarmed = UnitDefs[unitDefID].springCategories.unarmed if speed and speed ~= 0 then -- mobile unit if finishedUnits[unitID] then ProcessUnit(unitID, unitDefID, unitTeam, true) end elseif not unarmed then -- static defense ProcessUnit(unitID, unitDefID, unitTeam, true) end end function widget:UnitGiven(unitID, unitDefID, newTeamID, teamID) -- doing this twice is a bit inefficient but bah ProcessUnit(unitID, unitDefID, teamID, true) ProcessUnit(unitID, unitDefID, newTeamID) end ----------------------------------------------------------------------- function widget:Initialize() if (not WG.Chili) then widgetHandler:RemoveWidget() return end Chili = WG.Chili Image = Chili.Image Button = Chili.Button Checkbox = Chili.Checkbox Window = Chili.Window ScrollPanel = Chili.ScrollPanel StackPanel = Chili.StackPanel Label = Chili.Label screen0 = Chili.Screen0 color2incolor = Chili.color2incolor incolor2color = Chili.incolor2color green = color2incolor(0,1,0,1) red = color2incolor(1,0,0,1) orange = color2incolor(1,0.4,0,1) yellow = color2incolor(1,1,0,1) cyan = color2incolor(0,1,1,1) white = color2incolor(1,1,1,1) SetupPanels() Spring.SendCommands({"info 0"}) lastSizeX = window_cpl.width lastSizeY = window_cpl.height self:LocalColorRegister() end function widget:Shutdown() self:LocalColorUnregister() end function widget:LocalColorRegister() if WG.LocalColor and WG.LocalColor.RegisterListener then WG.LocalColor.RegisterListener(widget:GetInfo().name, SetupPlayerNames) end end function widget:LocalColorUnregister() if WG.LocalColor and WG.LocalColor.UnregisterListener then WG.LocalColor.UnregisterListener(widget:GetInfo().name) end end