GMAD[]Lumiens MapVote{ "description": "Description", "type": "servercontent", "tags": [] }Author Namelua/autorun/mapvote.lua|FPSlua/autorun/server/sv_autovote.luar"lua/mapvote/cl_mapvote.lua )4lua/mapvote/rtv.luaW Zlua/mapvote/sv_mapvote.luavlua/ulx/modules/sh/mapvote.lua'5ۊMapVote = {} MapVote.Config = {} -- Default Config MapVoteConfigDefault = { MapLimit = 24, TimeLimit = 28, AllowCurrentMap = false, EnableCooldown = true, MapsBeforeRevote = 3, RTVPlayerCount = 3, MapPrefixes = {"ttt_"}, AutoGamemode = false } -- Default Config hook.Add("Initialize", "MapVoteConfigSetup", function() if not file.Exists("mapvote", "DATA") then file.CreateDir("mapvote") end if not file.Exists("mapvote/config.txt", "DATA") then file.Write("mapvote/config.txt", util.TableToJSON(MapVoteConfigDefault)) end end) MapVote.CurrentMaps = {} MapVote.Votes = {} MapVote.Allow = false MapVote.UPDATE_VOTE = 1 MapVote.UPDATE_WIN = 3 if SERVER then AddCSLuaFile() AddCSLuaFile("mapvote/cl_mapvote.lua") include("mapvote/sv_mapvote.lua") include("mapvote/rtv.lua") else include("mapvote/cl_mapvote.lua") end hook.Add("Initialize", "AutoTTTMapVote", function() if GAMEMODE_NAME == "terrortown" then function CheckForMapSwitch() -- Check for mapswitch local rounds_left = math.max(0, GetGlobalInt("ttt_rounds_left", 6) - 1) SetGlobalInt("ttt_rounds_left", rounds_left) local time_left = math.max(0, (GetConVar("ttt_time_limit_minutes"):GetInt() * 60) - CurTime()) local switchmap = false local nextmap = string.upper(game.GetMapNext()) if rounds_left <= 0 then LANG.Msg("limit_round", {mapname = nextmap}) switchmap = true elseif time_left <= 0 then LANG.Msg("limit_time", {mapname = nextmap}) switchmap = true end if switchmap then timer.Stop("end2prep") MapVote.Start(nil, nil, nil, nil) end end end if GAMEMODE_NAME == "deathrun" then function RTV.Start() MapVote.Start(nil, nil, nil, nil) end end if GAMEMODE_NAME == "zombiesurvival" then hook.Add("LoadNextMap", "MAPVOTEZS_LOADMAP", function() MapVote.Start(nil, nil, nil, nil) return true end) end end) surface.CreateFont("RAM_VoteFont", { font = "Trebuchet MS", size = 19, weight = 700, antialias = true, shadow = true }) surface.CreateFont("RAM_VoteFontCountdown", { font = "Tahoma", size = 32, weight = 700, antialias = true, shadow = true }) surface.CreateFont("RAM_VoteSysButton", {font = "Marlett", size = 13, weight = 0, symbol = true}) MapVote.EndTime = 0 MapVote.Panel = false net.Receive("RAM_MapVoteStart", function() MapVote.CurrentMaps = {} MapVote.Allow = true MapVote.Votes = {} local amt = net.ReadUInt(32) for i = 1, amt do local map = net.ReadString() local playCount = net.ReadUInt(32) local object = {} object["map"] = map object["playcount"] = playCount MapVote.CurrentMaps[#MapVote.CurrentMaps + 1] = object end MapVote.EndTime = CurTime() + net.ReadUInt(32) if (IsValid(MapVote.Panel)) then MapVote.Panel:Remove() end MapVote.Panel = vgui.Create("RAM_VoteScreen") MapVote.Panel:SetMaps(MapVote.CurrentMaps) end) net.Receive("RAM_MapVoteUpdate", function() local update_type = net.ReadUInt(3) if (update_type == MapVote.UPDATE_VOTE) then local ply = net.ReadEntity() if (IsValid(ply)) then local map_id = net.ReadUInt(32) MapVote.Votes[ply:SteamID()] = map_id if (IsValid(MapVote.Panel)) then MapVote.Panel:AddVoter(ply) end end elseif (update_type == MapVote.UPDATE_WIN) then if (IsValid(MapVote.Panel)) then MapVote.Panel:Flash(net.ReadUInt(32)) end end end) net.Receive("RAM_MapVoteCancel", function() if IsValid(MapVote.Panel) then MapVote.Panel:Remove() end end) net.Receive("RTV_Delay", function() chat.AddText(Color(102, 255, 51), "[RTV]", Color(255, 255, 255), " The vote has been rocked, map vote will begin on round end") end) local PANEL = {} function PANEL:Init() self:ParentToHUD() self.startTime = SysTime() self.Canvas = vgui.Create("Panel", self) self.Canvas:MakePopup() self.Canvas:SetKeyboardInputEnabled(false) self.countDown = vgui.Create("DLabel", self.Canvas) self.countDown:SetTextColor(color_white) self.countDown:SetFont("RAM_VoteFontCountdown") self.countDown:SetText("") self.countDown:SetPos(0, 14) self.countDown:SetAlpha(0) self.countDown:AlphaTo(255, 0.8, 0) function self.countDown:PerformLayout() self:SizeToContents() self:CenterHorizontal() end self.mapList = vgui.Create("DPanelList", self.Canvas) self.mapList:SetDrawBackground(false) self.mapList:SetSpacing(4) self.mapList:SetPadding(4) self.mapList:EnableHorizontal(true) self.mapList:EnableVerticalScrollbar() self.closeButton = vgui.Create("DButton", self.Canvas) self.closeButton:SetText("") self.closeButton.Paint = function(panel, w, h) derma.SkinHook("Paint", "WindowCloseButton", panel, w, h) end self.closeButton.DoClick = function() print("HI") self:SetVisible(false) end self.maximButton = vgui.Create("DButton", self.Canvas) self.maximButton:SetText("") self.maximButton:SetDisabled(true) self.maximButton.Paint = function(panel, w, h) derma.SkinHook("Paint", "WindowMaximizeButton", panel, w, h) end self.minimButton = vgui.Create("DButton", self.Canvas) self.minimButton:SetText("") self.minimButton:SetDisabled(true) self.minimButton.Paint = function(panel, w, h) derma.SkinHook("Paint", "WindowMinimizeButton", panel, w, h) end self.Voters = {} end function PANEL:PerformLayout() local cx, cy = chat.GetChatBoxPos() self:SetPos(0, 0) self:SetSize(ScrW(), ScrH()) local extra = math.Clamp(1250 - 640, 0, ScrW() - 640) self.Canvas:StretchToParent(0, 0, 0, 0) self.Canvas:SetWide(640 + extra) self.Canvas:SetTall(ScrH() - 100) self.Canvas:SetPos(0, 0) self.Canvas:CenterHorizontal() self.Canvas:SetZPos(0) self.mapList:StretchToParent(0, 90, 0, 0) local buttonPos = 640 + extra - 31 * 3 self.closeButton:SetPos(buttonPos - 31 * 0, 4) self.closeButton:SetSize(31, 31) self.closeButton:SetVisible(true) self.maximButton:SetPos(buttonPos - 31 * 1, 4) self.maximButton:SetSize(31, 31) self.maximButton:SetVisible(true) self.minimButton:SetPos(buttonPos - 31 * 2, 4) self.minimButton:SetSize(31, 31) self.minimButton:SetVisible(true) end local heart_mat = Material("icon16/heart.png") local star_mat = Material("icon16/star.png") local shield_mat = Material("icon16/shield.png") function PANEL:AddVoter(voter) for k, v in pairs(self.Voters) do if (v.Player and v.Player == voter) then return false end end local icon_container = vgui.Create("Panel", self.mapList:GetCanvas()) local icon = vgui.Create("AvatarImage", icon_container) icon:SetSize(32, 32) icon:SetZPos(1000) icon_container.Player = voter icon:SetPlayer(voter, 32) icon_container:SetSize(36, 36) icon:SetPos(4, 4) icon_container.Paint = function(s, w, h) -- draw.RoundedBox(4, 0, 0, w, h, Color(255, 0, 0, 80)) if (icon_container.img) then surface.SetMaterial(icon_container.img) surface.SetDrawColor(Color(255, 255, 255)) surface.DrawTexturedRect(2, 2, 16, 16) end end icon_container:SetMouseInputEnabled( false ) icon_container:SetAlpha(200) table.insert(self.Voters, icon_container) end function PANEL:Think() for k, v in pairs(self.mapList:GetItems()) do v.NumVotes = 0 end for k, v in pairs(self.Voters) do if (not IsValid(v.Player)) then v:Remove() else if (not MapVote.Votes[v.Player:SteamID()]) then v:Remove() else local bar = self:GetMapButton(MapVote.Votes[v.Player:SteamID()]) local row = math.floor(bar.NumVotes / 5) local column = bar.NumVotes % 5 local layer = math.floor(row / 4) row = row - (layer) * 4; bar.NumVotes = bar.NumVotes + 1 if (IsValid(bar)) then local CurrentPos = Vector(v.x, v.y, 0) local NewPos = Vector( (bar.x + column * 40), bar.y + row * 36 + 25, 0) if (not v.CurPos or v.CurPos ~= NewPos) then v:MoveTo(NewPos.x, NewPos.y, 0.3) v.CurPos = NewPos end end end end end local timeLeft = math.Round(math.Clamp(MapVote.EndTime - CurTime(), 0, math.huge)) self.countDown:SetText(tostring(timeLeft or 0) .. " seconds") if (timeLeft < 10) then self.countDown:SetTextColor(Color(255,0,0)) end self.countDown:SizeToContents() self.countDown:CenterHorizontal() end function PANEL:SetMaps(maps) self.mapList:Clear() local transCounter = 0 for k, v in RandomPairs(maps) do local map = v["map"] local playCount = v["playcount"] local panel = vgui.Create("DLabel", self.mapList) panel.ID = k panel.NumVotes = 0 panel:SetSize( 200, 200 ) panel:SetText("") panel:SetAlpha(0) panel:SetPaintBackgroundEnabled( false ) panel:AlphaTo(255, 0.8, transCounter/40) transCounter = transCounter + 1 function panel:PerformLayout() self:SetBGColor(0,150,0,255) end local button = vgui.Create("DImageButton", panel) button:SetImage(getMapThumbnail(map)) function button:OnMousePressed(code) net.Start("RAM_MapVoteUpdate") net.WriteUInt(MapVote.UPDATE_VOTE, 3) net.WriteUInt(panel.ID, 32) net.SendToServer() end button:SetPos(2,2); button:SetSize( 196, 196 ) local playCountLabel = vgui.Create( "DLabel", button ) playCountLabel:SetPos( 0, 0 ) playCountLabel:SetSize(196, 25) playCountLabel:SetText(playCount .. "") playCountLabel:SetContentAlignment( 5 ) playCountLabel:SetFont("RAM_VoteFont") playCountLabel:SetPaintBackgroundEnabled( true ) function playCountLabel:PerformLayout() self:SetBGColor(0,0,0,220) end local text = vgui.Create( "DLabel", button ) text:SetPos( 0, 173 ) text:SetSize(196, 25) text:SetText(map) text:SetContentAlignment( 5 ) text:SetFont("RAM_VoteFont") text:SetPaintBackgroundEnabled( true ) function text:PerformLayout() self:SetBGColor(0,0,0,220) end self.mapList:AddItem(panel) end end function getMapThumbnail(name) if file.Exists("maps/thumb/" .. name .. ".png", "GAME") then return "maps/thumb/" .. name .. ".png" elseif file.Exists("maps/" .. name .. ".png", "GAME") then return "maps/" .. name .. ".png" else return "maps/thumb/noicon.png" end end function PANEL:GetMapButton(id) for k, v in pairs(self.mapList:GetItems()) do if (v.ID == id) then return v end end return false end function PANEL:Paint() Derma_DrawBackgroundBlur(self, self.startTime) end function PANEL:Flash(id) self:SetVisible(true) local bar = self:GetMapButton(id) if (IsValid(bar)) then timer.Simple(0.0, function() bar:SetPaintBackgroundEnabled( true ) surface.PlaySound("hl1/fvox/blip.wav") end) timer.Simple(0.2, function() bar:SetPaintBackgroundEnabled( false ) end) timer.Simple(0.4, function() bar:SetPaintBackgroundEnabled( true ) surface.PlaySound("hl1/fvox/blip.wav") end) timer.Simple(0.6, function() bar:SetPaintBackgroundEnabled( false ) end) timer.Simple(0.8, function() bar:SetPaintBackgroundEnabled( true ) surface.PlaySound("hl1/fvox/blip.wav") end) timer.Simple(1.0, function() bar:SetBGColor(255,0,255,255) bar:SetPaintBackgroundEnabled( true ) end) end end derma.DefineControl("RAM_VoteScreen", "", PANEL, "DPanel") RTV = RTV or {} RTV.ChatCommands = {"!rtv", "/rtv", "rtv"} RTV.TotalVotes = 0 RTV.Wait = 60 -- The wait time in seconds. This is how long a player has to wait before voting when the map changes. RTV._ActualWait = CurTime() + RTV.Wait RTV.PlayerCount = MapVote.Config.RTVPlayerCount or 3 function RTV.ShouldChange() return RTV.TotalVotes >= math.Round(#player.GetAll() * 0.66) end function RTV.RemoveVote() RTV.TotalVotes = math.Clamp(RTV.TotalVotes - 1, 0, math.huge) end function RTV.Start() if GAMEMODE_NAME == "terrortown" then net.Start("RTV_Delay") net.Broadcast() hook.Add("TTTEndRound", "MapvoteDelayed", function() MapVote.Start(nil, nil, nil, nil) end) elseif GAMEMODE_NAME == "deathrun" then net.Start("RTV_Delay") net.Broadcast() hook.Add("RoundEnd", "MapvoteDelayed", function() MapVote.Start(nil, nil, nil, nil) end) else PrintMessage(HUD_PRINTTALK, "The vote has been rocked, map vote imminent") timer.Simple(4, function() MapVote.Start(nil, nil, nil, nil) end) end end function RTV.AddVote(ply) if RTV.CanVote(ply) then RTV.TotalVotes = RTV.TotalVotes + 1 ply.RTVoted = true MsgN(ply:Nick() .. " has voted to Rock the Vote.") PrintMessage(HUD_PRINTTALK, ply:Nick() .. " has voted to Rock the Vote. (" .. RTV.TotalVotes .. "/" .. math.Round(#player.GetAll() * 0.66) .. ")") if RTV.ShouldChange() then RTV.Start() end end end hook.Add("PlayerDisconnected", "Remove RTV", function(ply) if ply.RTVoted then RTV.RemoveVote() end timer.Simple(0.1, function() if RTV.ShouldChange() then RTV.Start() end end) end) function RTV.CanVote(ply) local plyCount = table.Count(player.GetAll()) if RTV._ActualWait >= CurTime() then return false, "You must wait a bit before voting!" end if GetGlobalBool("In_Voting") then return false, "There is currently a vote in progress!" end if ply.RTVoted then return false, "You have already voted to Rock the Vote!" end if RTV.ChangingMaps then return false, "There has already been a vote, the map is going to change!" end if plyCount < RTV.PlayerCount then return false, "You need more players before you can rock the vote!" end return true end function RTV.StartVote(ply) local can, err = RTV.CanVote(ply) if not can then ply:PrintMessage(HUD_PRINTTALK, err) return end RTV.AddVote(ply) end concommand.Add("rtv_start", RTV.StartVote) hook.Add("PlayerSay", "RTV Chat Commands", function(ply, text) if table.HasValue(RTV.ChatCommands, string.lower(text)) then RTV.StartVote(ply) return "" end end) util.AddNetworkString("RAM_MapVoteStart") util.AddNetworkString("RAM_MapVoteUpdate") util.AddNetworkString("RAM_MapVoteCancel") util.AddNetworkString("RTV_Delay") MapVote.Continued = false net.Receive("RAM_MapVoteUpdate", function(len, ply) if (MapVote.Allow) then if (IsValid(ply)) then local update_type = net.ReadUInt(3) if (update_type == MapVote.UPDATE_VOTE) then local map_id = net.ReadUInt(32) if (MapVote.CurrentMaps[map_id]) then MapVote.Votes[ply:SteamID()] = map_id net.Start("RAM_MapVoteUpdate") net.WriteUInt(MapVote.UPDATE_VOTE, 3) net.WriteEntity(ply) net.WriteUInt(map_id, 32) net.Broadcast() end end end end end) if file.Exists("mapvote/recentmaps.txt", "DATA") then recentmaps = util.JSONToTable(file.Read("mapvote/recentmaps.txt", "DATA")) else recentmaps = {} end if file.Exists("mapvote/playcount.txt", "DATA") then playCount = util.JSONToTable(file.Read("mapvote/playcount.txt", "DATA")) else playCount = {} end if file.Exists("mapvote/config.txt", "DATA") then MapVote.Config = util.JSONToTable(file.Read("mapvote/config.txt", "DATA")) else MapVote.Config = {} end function CoolDownDoStuff() cooldownnum = MapVote.Config.MapsBeforeRevote or 3 if table.getn(recentmaps) == cooldownnum then table.remove(recentmaps) end local curmap = game.GetMap():lower() .. ".bsp" if not table.HasValue(recentmaps, curmap) then table.insert(recentmaps, 1, curmap) end if playCount[curmap] == nil then playCount[curmap] = 1 else playCount[curmap] = playCount[curmap] + 1 end file.Write("mapvote/recentmaps.txt", util.TableToJSON(recentmaps)) file.Write("mapvote/playcount.txt", util.TableToJSON(playCount)) end function MapVote.Start(length, current, limit, prefix, callback) current = current or MapVote.Config.AllowCurrentMap or false length = length or MapVote.Config.TimeLimit or 28 limit = limit or MapVote.Config.MapLimit or 24 cooldown = MapVote.Config.EnableCooldown or MapVote.Config.EnableCooldown == nil and true prefix = prefix or MapVote.Config.MapPrefixes autoGamemode = autoGamemode or MapVote.Config.AutoGamemode or MapVote.Config.AutoGamemode == nil and true local is_expression = false if not prefix then local info = file.Read(GAMEMODE.Folder .. "/" .. GAMEMODE.FolderName .. ".txt", "GAME") if (info) then local info = util.KeyValuesToTable(info) prefix = info.maps else error("MapVote Prefix can not be loaded from gamemode") end is_expression = true else if prefix and type(prefix) ~= "table" then prefix = {prefix} end end local maps = file.Find("maps/*.bsp", "GAME") local vote_maps = {} local play_counts = {} local amt = 0 for k, map in RandomPairs(maps) do local mapstr = map:sub(1, -5):lower() local plays = playCount[map] if (plays == nil) then plays = 0 end if (not ((not current and game.GetMap():lower() .. ".bsp" == map) or (cooldown and table.HasValue(recentmaps, map)))) then if is_expression then if (string.find(map, prefix)) then -- This might work (from gamemode.txt) vote_maps[#vote_maps + 1] = map:sub(1, -5) play_counts[#play_counts + 1] = plays amt = amt + 1 end else for k, v in pairs(prefix) do if string.find(map, "^" .. v) then vote_maps[#vote_maps + 1] = map:sub(1, -5) play_counts[#play_counts + 1] = plays amt = amt + 1 break end end end if (limit and amt >= limit) then break end end end net.Start("RAM_MapVoteStart") net.WriteUInt(#vote_maps, 32) for i = 1, #vote_maps do net.WriteString(vote_maps[i]) net.WriteUInt(play_counts[i], 32) end net.WriteUInt(length, 32) net.Broadcast() MapVote.Allow = true MapVote.CurrentMaps = vote_maps MapVote.Votes = {} timer.Create("RAM_MapVote", length, 1, function() MapVote.Allow = false local map_results = {} for k, v in pairs(MapVote.Votes) do if (not map_results[v]) then map_results[v] = 0 end for k2, v2 in pairs(player.GetAll()) do if (v2:SteamID() == k) then map_results[v] = map_results[v] + 1 end end end CoolDownDoStuff() local winner = table.GetWinningKey(map_results) or 1 net.Start("RAM_MapVoteUpdate") net.WriteUInt(MapVote.UPDATE_WIN, 3) net.WriteUInt(winner, 32) net.Broadcast() local map = MapVote.CurrentMaps[winner] local gamemode = nil if (autoGamemode) then -- check if map matches a gamemode's map pattern for k, gm in pairs(engine.GetGamemodes()) do -- ignore empty patterns if (gm.maps and gm.maps ~= "") then -- patterns are separated by "|" for k2, pattern in pairs(string.Split(gm.maps, "|")) do if (string.match(map, pattern)) then gamemode = gm.name break end end end end else print("not enabled") end timer.Simple(4, function() if (hook.Run("MapVoteChange", map) ~= false) then if (callback) then callback(map) else -- if map requires another gamemode then switch to it if (gamemode and gamemode ~= engine.ActiveGamemode()) then RunConsoleCommand("gamemode", gamemode) end RunConsoleCommand("changelevel", map) end end end) end) end hook.Add("Shutdown", "RemoveRecentMaps", function() if file.Exists("mapvote/recentmaps.txt", "DATA") then file.Delete("mapvote/recentmaps.txt") end end) function MapVote.Cancel() if MapVote.Allow then MapVote.Allow = false net.Start("RAM_MapVoteCancel") net.Broadcast() timer.Destroy("RAM_MapVote") end end local CATEGORY_NAME = "MapVote" ------------------------------ VoteMap ------------------------------ function AMB_mapvote( calling_ply, votetime, should_cancel ) if not should_cancel then MapVote.Start(votetime, nil, nil, nil) ulx.fancyLogAdmin( calling_ply, "#A called a votemap!" ) else MapVote.Cancel() ulx.fancyLogAdmin( calling_ply, "#A canceled the votemap" ) end end local mapvotecmd = ulx.command( CATEGORY_NAME, "mapvote", AMB_mapvote, "!mapvote" ) mapvotecmd:addParam{ type=ULib.cmds.NumArg, min=15, default=25, hint="time", ULib.cmds.optional, ULib.cmds.round } mapvotecmd:addParam{ type=ULib.cmds.BoolArg, invisible=true } mapvotecmd:defaultAccess( ULib.ACCESS_ADMIN ) mapvotecmd:help( "Invokes the map vote logic" ) mapvotecmd:setOpposite( "unmapvote", {_, _, true}, "!unmapvote" )NV