-- PTSR gamemode by Green -- "a team based ptsr mode [suggestion] is about as old as takis" -luigi budd --[[ ~~ 2.2 CHANGELOG ~~ - Fixed bug that would break the end of the round if someone left the server at an inconvenient time (no chat message, no discord message & no winners?) - When there are 16 players, there is now only a chance for 4 teams to be made instead of it being a guaranteed 4 teams ]] PTSR.gm_team_versus = PTSR.RegisterGamemode("Team Versus", { parry_friendlyfire = true, allowrevive = false, dustdevil = false }) PTSR.default_playervars.team = 0 -- until beta5 PTSR.default_playervars.deathscore = 0 PTSR.default_playervars.deathlaps = 0 PTSR.default_playervars.deathrank = 0 local spots1 = {110*FU, 220*FU, 55*FU, 270*FU} local spots2 = {55*FU, 110*FU, 220*FU, 270*FU} local spots = {} local cspot = 165*FU local lbsorting = true PTSR.TV_w2s_mobjs = {} -- gray and orange are reserved for neutral & pizzaface -- note the indexes are the same local textcolours = {V_REDMAP, V_BLUEMAP, V_GREENMAP, V_YELLOWMAP, V_MAGENTAMAP, V_SKYMAP, V_PURPLEMAP, V_AQUAMAP, V_PERIDOTMAP, V_AZUREMAP, V_BROWNMAP, V_ROSYMAP, V_INVERTMAP} local skincolours = {SKINCOLOR_RED, SKINCOLOR_BLUE, SKINCOLOR_GREEN, SKINCOLOR_YELLOW, SKINCOLOR_MAGENTA, SKINCOLOR_SKY, SKINCOLOR_PURPLE, SKINCOLOR_AQUA, SKINCOLOR_PERIDOT, SKINCOLOR_AZURE, SKINCOLOR_BROWN, SKINCOLOR_ROSY, SKINCOLOR_JET} local asciicolours = {"\x85", "\x84", "\x83", "\x82", "\x81", "\x88", "\x89", "\x8A", "\x8B", "\x8C", "\x8D", "\x8E", "\x8F"} local radarcolours = {34, 149, 98, 72, 182, 132, 165, 122, 188, 170, 230, 203, 25} PTSR.teams = {--[[ A team's index in this table, once set, does not change. The gamemode relies on this. { -- "constants" set once when team is created team = true, (if false, indicates this entry is a neutral team. Normally, there is only one "neutral" team, and the gamemode assumes this. The neutral team will always be PTSR.teams[1]) name = "blue", realname = "Blue", (capitalised ver. for discord, game chat, etc.) skincolor = SKINCOLOR_BLUE, (for mobj color) colormap = V_BLUEMAP, (for hud) asciicolor = "\x84", (for chat) -- variables, updated in ThinkFrame plrs = {plr, plr2, plr3, etc.}, (sorted by score, plrs[1] is contributing the most to the team) alive = 3, (number of players still alive, calced by countPlayers("team")) points = 2000 (number of total points, calced by calcTeamPoints) }, etc. ]]} PTSR.teamleaderboard = {} -- holds the indexes for teams, in order of which is winning e.g. PTSR.teamleaderboard[1] is currently 1st place -- ^ Before pizzatime, this is instead ordered by which has the most players. assignTeam() relies on this, and it is to balance the teams. local teamspots = {} local TVdeathrings = {} local checkedbubbles = true local debug = 0 -- [[ TWEEN LIB (from FE) ]] -- local tween = {} local tweens = {} function tween.Setup(name, start, goal, duration, easefunc, overwrite) local found = false for i,v in ipairs(tweens) do if v.id == name then found = true if overwrite then v.sval = start v.fval = goal v.ctics = 0 v.cpos = start v.length = duration v.func = easefunc return i end end end if not found then table.insert(tweens, { id = name, sval = start, fval = goal, ctics = 0, cpos = start, length = duration, func = easefunc }) return #tweens end end function tween.Remove(name) for i,v in ipairs(tweens) do if v.id == name then table.remove(tweens, i) return true end end return false end function tween.Find(name) for i,v in ipairs(tweens) do if v.id == name then return i end end return nil end addHook("ThinkFrame", function() for i,v in ipairs(tweens) do if v.cpos == v.fval then table.remove(tweens, i) else v.ctics = $ + 1 v.cpos = v.func(FixedDiv(v.ctics*FU, v.length*FU), v.sval, v.fval) end end end) -- END OF TWEEN LIB -- freeslot("MT_PT_TV_DEATHRING") mobjinfo[MT_PT_TV_DEATHRING] = { doomednum = -1, spawnstate = S_RING, spawnhealth = 1000, deathstate = S_SPRK1, deathsound = sfx_hidden, radius = 16*FU, height = 24*FU, flags = MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT } addHook("NetVars", function(net) PTSR.teams = net($) PTSR.teamleaderboard = net($) spots = net($) teamspots = net($) checkedbubbles = net($) TVdeathrings = net($) end) local function capitaliseString(s) local sl = string.sub(s, 1, 1) local su = string.upper(sl) local ss = string.gsub(s, sl, su, 1) return ss end COM_AddCommand("ptsr_tv_lbsorting", function(plr, arg) if arg ~= nil then arg = string.lower(arg) end if arg == "1" or arg == "on" or arg == "true" then lbsorting = true elseif arg == "0" or arg == "off" or arg == "false" then lbsorting = false else CONS_Printf(plr, "ptsr_tv_lbsorting \nToggles sorting the leaderboard by teams in the gamemode Team Versus") end end, COM_LOCAL) COM_AddCommand("ptsr_tv_debugprint", function(plr, arg) if arg ~= nil then arg = string.lower(arg) end if arg == "1" or arg == "on" or arg == "true" then debug = 1 elseif arg == "0" or arg == "off" or arg == "false" then debug = 0 else CONS_Printf(plr, "ptsr_tv_debugprint \nToggles sending debug messages in the console in the gamemode Team Versus\nNote that this may flood your console and bloat your log file if left running for too long!") end end, COM_LOCAL) COM_AddCommand("ptsr_tv_forceteam", function(plr, arg1, arg2) if (not PTSR.IsPTSR()) then CONS_Printf(plr, "Command must be ran in the Pizza Time Spice Runners mode.") return end if PTSR.gamemode ~= PTSR.gm_team_versus then CONS_Printf(plr, "This command only works in the gamemode \"Team Versus\"!") return end if arg1 == nil then CONS_Printf(plr, "ptsr_forceteam \nChanges a player's team to the one specified.") return end if arg2 == nil then CONS_Printf(plr, "Both arguments must be present.") return end local node = tonumber(arg1) local targetplr = players[node] if targetplr ~= nil then local foundteam = 0 for i,v in ipairs(PTSR.teams) do if string.lower(v.name) == string.lower(arg2) then foundteam = i end end if foundteam then targetplr.ptsr.team = foundteam else CONS_Printf(plr, "Team does not exist.") end else CONS_Printf(plr, "Player does not exist.") return end end, COM_ADMIN) COM_AddCommand("ptsr_tv_createteam", function(plr, arg1) if (not PTSR.IsPTSR()) then CONS_Printf(plr, "Command must be ran in the Pizza Time Spice Runners mode.") return end if PTSR.gamemode ~= PTSR.gm_team_versus then CONS_Printf(plr, "This command only works in the gamemode \"Team Versus\"!") return end if arg1 == nil then CONS_Printf(plr, "ptsr_tv_createteam \nCreates a team of the specified skincolor. Only certain colours can be made due to how the gamemode works.") return end if arg1 ~= nil then local pcol = R_GetColorByName(string.lower(arg1)) if pcol ~= SKINCOLOR_NONE then local valid = true for i,v in ipairs(PTSR.teams) do if v.skincolor == pcol then valid = false end end if valid == true then local valid2 = 0 for i,v in ipairs(skincolours) do if v == pcol then valid2 = i end end if valid2 then -- setup & add local team = {} team.team = true team.name = R_GetNameByColor(pcol) team.realname = capitaliseString(team.name) team.skincolor = pcol team.colormap = textcolours[valid2] team.asciicolor = asciicolours[valid2] team.radarcolor = radarcolours[valid2] team.plrs = {} team.alive = 0 team.points = 0 table.insert(PTSR.teams, team) table.insert(PTSR.teamleaderboard, #PTSR.teams) table.insert(teamspots, #PTSR.teams) CONS_Printf(plr, "Created "..team.realname.." team!") else CONS_Printf(plr, "This skincolor can't be used!") return end else CONS_Printf(plr, "Team already exists!") return end else CONS_Printf(plr, "Skincolor doesn't exist!") return end end end, COM_ADMIN) local function isPlayerValid(plr) if plr and plr.valid and plr.mo and plr.mo.valid and (not plr.spectator) and plr.playerstate ~= PST_DEAD and (not plr.quittime) then return true else return false end end local function getDiscordIcon(player) -- (player.quittime and ":door:") or (isPlayerValid(player) and ":checkered_flag:") or ":skull_crossbones:" if player.quittime then return ":door:" elseif isPlayerValid(player) then return ":checkered_flag:" else return ":headstone:" end end local function countPlayers(arg1, arg2) local count = 0 if arg1 == nil then -- counts every player on the server for player in players.iterate do count = $ + 1 end elseif arg1 == "team" and arg2 ~= nil then -- countTeam for i,v in ipairs(PTSR.teams[arg2].plrs) do if isPlayerValid(v) then -- only counts ALIVE players for now count = $ + 1 end end elseif arg1 == "teamcount" then for i,v in ipairs(PTSR.teams) do if v.team == true and v.alive > 0 then -- only counts teams w/ alive players for now count = $ + 1 end end elseif arg1 == "allalive" then for player in players.iterate do if isPlayerValid(player) then count = $ + 1 end end elseif arg1 == "inteam" then for player in players.iterate do if player.ptsr.team then count = $ + 1 end end end return count -- to get neutral count, do PTSR.teams[1].alive or #PTSR.teams[1].plrs end local function calcTeamPoints(teamnum) local lteam = PTSR.teams[teamnum] if lteam.alive > 0 then if lteam.team == true then local points = 0 for i,v in ipairs(lteam.plrs) do if isPlayerValid(v) then points = $ + v.score end end points = $ / #lteam.plrs return points else local points = 0 local topplr = lteam.plrs[1] if topplr and topplr.valid then return topplr.score else return 0 end end else return 0 end end local function assignTeam(plr) -- Put player on the team with lowest player count local lteams = {} -- PTSR.teamleaderboard but local for this function. So it doesn't fuck shit up with table.sort for i,v in ipairs(PTSR.teams) do if v.team == true then table.insert(lteams, i) end end table.sort(lteams, function(a,b) local t1 = PTSR.teams[a] local t2 = PTSR.teams[b] return #t1.plrs > #t2.plrs end) if #lteams then if debug then print("DEBUG: "..PTSR.teams[lteams[#lteams]].realname.." is last in the list!") end plr.ptsr.team = lteams[#lteams] table.insert(PTSR.teams[lteams[#lteams]].plrs, plr) else plr.ptsr.team = 1 table.insert(PTSR.teams[1].plrs, plr) end end local function createTeam(typeofteam) local team = {} if typeofteam == nil then -- Pick a colour! local chosencolour = P_RandomRange(1, #skincolours) local loopcolours = true while loopcolours == true do local foundc = false for i,v in ipairs(PTSR.teams) do if v.skincolor == skincolours[chosencolour] then foundc = true end end if foundc == false then loopcolours = false else chosencolour = P_RandomRange(1, #skincolours) end end -- Setup & Add team.team = true team.name = R_GetNameByColor(skincolours[chosencolour]) team.realname = capitaliseString(team.name) team.skincolor = skincolours[chosencolour] team.colormap = textcolours[chosencolour] team.asciicolor = asciicolours[chosencolour] team.radarcolor = radarcolours[chosencolour] team.plrs = {} team.alive = 0 team.points = 0 table.insert(PTSR.teams, team) return #PTSR.teams elseif typeofteam == "neutral" then -- Setup & Add team.team = false team.name = "neutral" team.realname = "Neutral" team.skincolor = SKINCOLOR_GREY team.colormap = V_GRAYMAP team.asciicolor = "\x86" team.radarcolor = 17 team.plrs = {} team.alive = 0 team.points = 0 table.insert(PTSR.teams, team) return #PTSR.teams end end local function addTVw2sobject(mobj) local exists = false for i,v in ipairs(PTSR.TV_w2s_mobjs) do if v == mobj then exists = true break end end if not exists then table.insert(PTSR.TV_w2s_mobjs, mobj) end end addHook("MapLoad", function() if PTSR.gamemode ~= PTSR.gm_team_versus then -- reset people's skin colours after a team versus round for player in players.iterate do if player.mo.color ~= player.skincolor then player.mo.color = player.skincolor end end return end PTSR.teams = {} PTSR.teamleaderboard = {} teamspots = {} spots = {} checkedbubbles = false -- Decide how many teams there should be. 2 Min ; 4 Max. If you're playing solo, you are simply put as Neutral with No teams. local teamcount = 0 local pcount = countPlayers() if pcount > 1 then -- More than one player if pcount < 13 then -- 2 to 12 players = Guaranteed 2 teams teamcount = 2 elseif pcount < 16 then -- 12-15 players = 2 or 3 teams teamcount = P_RandomRange(2, 3) else -- 16+ players = 2 or 4 teams teamcount = P_RandomRange(2, 4) while teamcount == 3 do teamcount = P_RandomRange(2, 4) end end end local neutralteam = createTeam("neutral") table.insert(PTSR.teamleaderboard, neutralteam) while teamcount > 0 do local madeteam = createTeam() table.insert(PTSR.teamleaderboard, madeteam) table.insert(teamspots, madeteam) if debug then print("DEBUG: ".."Created "..PTSR.teams[madeteam].realname.." team!") end teamcount = $ - 1 end if #teamspots < 4 then spots = spots1 else spots = spots2 end table.insert(teamspots, neutralteam) -- set teams if #PTSR.teams > 1 then local lplrs = {} for player in players.iterate do if player and player.valid then table.insert(lplrs, player) end end local perteam = #lplrs / (#PTSR.teams - 1) local minplrs = (perteam * (#PTSR.teams - 1)) local inteam = countPlayers("inteam") for i=1,perteam do for i2,v in ipairs(PTSR.teams)do if v.team == true and #v.plrs < perteam then local rplrn = P_RandomRange(1, #lplrs) local rplr = lplrs[rplrn] if rplr and rplr.valid then rplr.ptsr.team = i2 end table.remove(lplrs, rplrn) end end end -- catch odd players who didn't get a team for player in players.iterate do if player and player.valid and not player.ptsr.team then assignTeam(player) end end else for player in players.iterate do if player and player.valid then player.ptsr.team = 1 end end end end) addHook("PlayerSpawn", function(player, mobj) if PTSR.gamemode ~= PTSR.gm_team_versus then return end if (not player.ptsr.team) and (not PTSR.pizzatime) and leveltime > 3 then -- late (but not too late) joiners get a team assignTeam(player) end end) addHook("ThinkFrame", function() if PTSR.gamemode ~= PTSR.gm_team_versus then return end if (not PTSR.gameover) then -- update dynamic parts of teams for i,v in ipairs(PTSR.teams) do local plrc = {} for plr in players.iterate do if plr and plr.valid and plr.ptsr.team == i then table.insert(plrc, plr) end end v.plrs = plrc v.alive = countPlayers("team", i) v.points = calcTeamPoints(i) -- note to self: -- true == a's key should be *lower* -- false == a's key should be *higher* table.sort(v.plrs, function(a,b) if a and a.valid and b and b.valid then if isPlayerValid(a) == true and isPlayerValid(b) == true then return a.score / #v.plrs > b.score / #v.plrs elseif isPlayerValid(a) == true and isPlayerValid(b) == false then return true elseif isPlayerValid(a) == false and isPlayerValid(b) == true then return false else -- neither valid return a.ptsr.deathscore or 0 > b.ptsr.deathscore or 0 end else return false -- idfk man just Don't do it if either of them don't exist end end) -- teamspots sorting local inspots = false for i2,v2 in ipairs(teamspots) do -- find team in teamspots if v2 == i then inspots = true if v.alive == 0 then table.remove(teamspots, i2) end end end if inspots == false then if v.alive > 0 then table.insert(teamspots, i) end end if i == 1 and PTSR.teams[i].alive > 0 and teamspots[#teamspots] ~= i then -- if neutral team is valid & isn't at the end, -- find neutral team in table & remove it for i2,v2 in ipairs(teamspots) do if v2 == i then table.remove(teamspots, i2) end end -- reinsert it at the end table.insert(teamspots, i) end -- END teamspots sorting --print(v.plrs[1].name) --print("Team name: "..v.realname.."\nNo. of Players: "..#v.plrs.."\nAlive Players: "..v.alive.."\nPoints: "..(tostring(v.points) or "nil")) -- would add to debug prints but it spams too much to see other debug prints end table.sort(PTSR.teamleaderboard, function(a,b) return PTSR.teams[a].points > PTSR.teams[b].points end) end -- Revive/Death ring if #TVdeathrings > 0 then for i,v in ipairs(TVdeathrings) do if (not v) or (not v.valid) then table.remove(TVdeathrings, i) else addTVw2sobject(v) end end end for player in players.iterate do if player.mo and player.mo.valid and player.ptsr.team then -- Set player skin colours. Doesn't set player.skincolor because that's what we'll use to reset it next round. if PTSR.teams[player.ptsr.team].team == true then player.mo.color = PTSR.teams[player.ptsr.team].skincolor else player.mo.color = player.skincolor end end --[[ sigh.. i'm not THAT mean. Yet. if PTSR.pizzatime and (not PTSR.gameover) and isPlayerValid(player) and (string.find(string.lower(player.name), "marcxworld") or string.find(string.lower(player.name), "cowboyketchup")) then CONS_Printf(player, player.name.." has horrible taste.") P_KillMobj(player.mo) end ]] end end) PTSR_AddHook("preparry", function(pmo, foundmobj) if PTSR.gamemode ~= PTSR.gm_team_versus then return end local player = pmo.player local foundplayer = foundmobj.player if foundplayer then if foundplayer.ptsr.team == player.ptsr.team and player.ptsr.team ~= 1 then -- if they're on the same team and not neutral, don't parry return true end end return false -- otherwise: fuck them up end) PTSR_AddHook("ongameend", function() if PTSR.gamemode ~= PTSR.gm_team_versus then return end -- final organisation of teams, since ThinkFrame isn't counting anymore & to ensure i got postgame combo points for i,v in ipairs(PTSR.teams) do local plrc = {} for plr in players.iterate do if plr.valid and plr.ptsr.team == i then table.insert(plrc, plr) end end v.plrs = plrc v.alive = countPlayers("team", i) v.points = calcTeamPoints(i) -- note to self: -- true == a's key should be *lower* -- false == a's key should be *higher* table.sort(v.plrs, function(a,b) if a and a.valid and b and b.valid then if isPlayerValid(a) == true and isPlayerValid(b) == true then return a.score / #v.plrs > b.score / #v.plrs elseif isPlayerValid(a) == true and isPlayerValid(b) == false then return true elseif isPlayerValid(a) == false and isPlayerValid(b) == true then return false else -- neither valid return a.ptsr.deathscore or 0 > b.ptsr.deathscore or 0 end end end) end table.sort(PTSR.teamleaderboard, function(a,b) return PTSR.teams[a].points > PTSR.teams[b].points end) local roundresult = "winner" if PTSR.teams[PTSR.teamleaderboard[1]].points == 0 then roundresult = "nowinners" elseif PTSR.teams[PTSR.teamleaderboard[2]] ~= nil then if PTSR.teams[PTSR.teamleaderboard[1]].points == PTSR.teams[PTSR.teamleaderboard[2]].points then roundresult = "tie" end end local ties = {} for player in players.iterate do if player and player.valid then player.ptsr.isWinner = false end end if roundresult == "winner" then local winner = PTSR.teams[PTSR.teamleaderboard[1]] if winner.team == true then chatprint(winner.asciicolor.."\*"..winner.realname.." team wins!") for i,v in ipairs(winner.plrs) do if player and player.valid then v.ptsr.isWinner = true end end else chatprint(winner.asciicolor.."\*"..winner.plrs[1].name.." wins!") if winner.plrs[1] and winner.plrs[1].valid then winner.plrs[1].ptsr.isWinner = true end end elseif roundresult == "tie" then local endmsg = "\x86\*It's a tie between " for i,v in ipairs(PTSR.teams) do if v.points == PTSR.teams[PTSR.teamleaderboard[1]].points then table.insert(ties, i) end end for i,v in ipairs(ties) do endmsg = $..PTSR.teams[v].asciicolor..PTSR.teams[v].realname if i == #ties-1 then endmsg = $.."\x86\ and " elseif i == #ties then endmsg = $.."!" else endmsg = $.."\x86\, " end end chatprint(endmsg) elseif roundresult == "nowinners" then chatprint("\x86\*Nobody wins. You ALL suck!") else print("\x82\WARNING\x80\: Green fucked shit up with Team Versus, tell him to check his \"ongameend\" hook\nAlso send Green any other WARNINGs you can see here (check \"latest-log.txt\"), It might help immensely!") end if DiscordBot or debug then local output = "" local winteam = PTSR.teams[PTSR.teamleaderboard[1]] for i,v in ipairs(PTSR.teamleaderboard) do local vteam = PTSR.teams[v] if i == 1 then -- Starter text then team 1's player list if roundresult == "winner" then if vteam.team == true then output = $.."\n:trophy: **"..vteam.realname.." team** won the round!\n" for i2,v2 in ipairs(vteam.plrs) do if v2 and v2.valid then if isPlayerValid(v2) == true then output = $.."- ["..#v2.."]"..getDiscordIcon(v2).."`"..v2.name.."`: "..(v2.score / #vteam.plrs) else output = $.."- ["..#v2.."]"..getDiscordIcon(v2).."`"..v2.name.."`: "..v2.ptsr.deathscore end output = $.."\n" end end output = $.."Final Score: "..calcTeamPoints(v).."\n\n" else -- Neutral output = $.."\n:trophy: **"..vteam.plrs[1].name.."** won the round!\n" output = $.."- ["..#vteam.plrs[1].."]"..getDiscordIcon(vteam.plrs[1]).."`"..vteam.plrs[1].name.."`: "..vteam.plrs[1].score or vteam.plrs[1].ptsr.deathscore output = $.."\n" if #vteam.plrs > 1 then output = $.."**__Other Neutral Players:__**\n" for i2,v2 in ipairs(vteam.plrs) do if i2 == 1 then continue end if v2 and v2.valid then output = $.."- ["..#v2.."]"..getDiscordIcon(v2).."`"..v2.name.."`: "..v2.score or v2.ptsr.deathscore output = $.."\n" end end output = $.."\n" end end elseif roundresult == "tie" then output = $.."\n:necktie: It's a tie! The teams that tied were:\n" -- We got the table of ties earlier, so we can reuse it! for i2,v2 in ipairs(ties) do if i == v2 then output = $.."- "..vteam.realname.."\n" end end if vteam.team == true then output = $.."\n**__"..vteam.realname.." team:__**\n" for i2,v2 in ipairs(vteam.plrs) do if v2 and v2.valid then if isPlayerValid(v2) == true then output = $.."- ["..#v2.."]"..getDiscordIcon(v2).."`"..v2.name.."`: "..(v2.score / #vteam.plrs) else output = $.."- ["..#v2.."]"..getDiscordIcon(v2).."`"..v2.name.."`: "..v2.ptsr.deathscore end output = $.."\n" end end output = $.."Final Score: "..calcTeamPoints(v).."\n\n" elseif #vteam.plrs > 0 then output = $.."**__Neutral Players:__**\n" for i2,v2 in ipairs(vteam.plrs) do if i2 ~= 1 then if v2 and v2.valid then output = $.."- ["..#v2.."]"..getDiscordIcon(v2).."`"..v2.name.."`: "..v2.score or v2.ptsr.deathscore output = $.."\n" end end end end elseif roundresult == "nowinners" then output = $.."\n:wastebasket:Nobody won. Everyone SUCKS!\n" if vteam.team == true then output = $.."\n**__"..vteam.realname.." team:__**\n" if #vteam.plrs > 0 then for i2,v2 in ipairs(vteam.plrs) do if v2 and v2.valid then if isPlayerValid(v2) == true then output = $.."- ["..#v2.."]"..getDiscordIcon(v2).."`"..v2.name.."`: "..(v2.score / #vteam.plrs) else output = $.."- ["..#v2.."]"..getDiscordIcon(v2).."`"..v2.name.."`: "..v2.ptsr.deathscore end output = $.."\n" end end output = $.."Final Score: "..calcTeamPoints(v).."\n\n" else output = $.."- Hmm... Looks like nobody was on this team!\n\n" end elseif #vteam.plrs > 0 then output = $.."**__Neutral Players:__**\n" for i2,v2 in ipairs(vteam.plrs) do if v2 and v2.valid then output = $.."- ["..#v2.."]"..getDiscordIcon(v2).."`"..v2.name.."`: "..v2.score or v2.ptsr.deathscore output = $.."\n" end end end else output = $.."\n<@138245011430047744> ongameend hook didn't get have a roundresult :( Now go fix it Boy" end else -- List players & Out! if vteam.team == true then output = $.."**__"..vteam.realname.." team:__**\n" if #vteam.plrs > 0 then for i2,v2 in ipairs(vteam.plrs) do if v2 and v2.valid then if isPlayerValid(v2) == true then output = $.."- ["..#v2.."]"..getDiscordIcon(v2).."`"..v2.name.."`: "..(v2.score / #vteam.plrs) else output = $.."- ["..#v2.."]"..getDiscordIcon(v2).."`"..v2.name.."`: "..v2.ptsr.deathscore end output = $.."\n" end end output = $.."Final Score: "..calcTeamPoints(v).."\n\n" else output = $.."- Hmm... Looks like nobody was on this team!\n\n" end elseif #vteam.plrs > 0 then output = $.."**__Neutral Players:__**\n" for i2,v2 in ipairs(vteam.plrs) do if i2 ~= 1 then if v2 and v2.valid then output = $.."- ["..#v2.."]"..getDiscordIcon(v2).."`"..v2.name.."`: "..v2.score or v2.ptsr.deathscore output = $.."\n" end end end end end end output = $.."--------------------------------------------------\n" if debug then print(output) end DiscordBot.Data.msgsrb2 = $..output end end) local oldcx = nil local oldcy = nil -- hud shite local TV_teamscores_hud = function(drawer, player) if not PTSR.IsPTSR() then return end if not multiplayer then return end if PTSR.gamemode ~= PTSR.gm_team_versus then return end if PTSR.pizzatime then for i,v in ipairs(teamspots) do if i > 4 then break end -- there's only 4 spots sorry vro local team = PTSR.teams[v] local toptext_y = 143*FU local bottomtext_y = 152*FU+FU/2 local xval = spots[i] if team ~= nil and team.team == true then drawer.drawString(xval, toptext_y, "["..tostring(team.alive).."]", team.colormap, "fixed-center") drawer.drawString(xval, bottomtext_y, team.realname.." TEAM\n"..tostring(team.points), team.colormap, "thin-fixed-center") elseif team ~= nil and team.team == false then local toptext = "top text is NOTHING you IDGIT!!!" local bottomtext = "bottom text emtpy :(" local nplayers = team.alive if nplayers > 1 then toptext = "["..tostring(nplayers).."]" bottomtext = team.realname elseif nplayers == 1 then toptext = "" bottomtext = team.plrs[1].name.."\n"..tostring(team.points) if #teamspots == 1 then xval = cspot toptext_y = 125*FU bottomtext_y = 135*FU end end drawer.drawString(xval, toptext_y, toptext, team.colormap, "fixed-center") drawer.drawString(xval, bottomtext_y, bottomtext, team.colormap, "thin-fixed-center") else -- uh oh, no hud for this team! continue end end -- crown if #teamspots > 1 then local crown = drawer.getSpritePatch(SPR_C9W3) local cx = 165*FU for i,v in ipairs(teamspots) do if i > 4 then break end if PTSR.teamleaderboard[1] == v then cx = spots[i] end if PTSR.teamleaderboard[2] ~= nil then if PTSR.teams[PTSR.teamleaderboard[1]].points == PTSR.teams[PTSR.teamleaderboard[2]].points then cx = 165*FU end end end local cy = (cx == 165*FU and 135*FU) or 140*FU local cxte = tween.Find("CrownX") if cxte then local cxt = tweens[cxte] if cxt.fval == cx then cx = cxt.cpos else local cxti = tween.Setup("CrownX", cxt.cpos, cx, 16, ease.outquart, true) cx = tweens[cxti].cpos end else local cxti = tween.Setup("CrownX", oldcx or cx, cx, 16, ease.outquart) cx = tweens[cxti].cpos end local cyte = tween.Find("CrownY") if cyte then local cyt = tweens[cyte] if cyt.fval == cy then cy = cyt.cpos elseif oldcy ~= cy then local cyti = tween.Setup("CrownY", cyt.cpos, cy, 16, ease.outquart, true) cy = tweens[cyti].cpos end else local cyti = tween.Setup("CrownY", oldcy or cy, cy, 16, ease.outquart) cy = tweens[cyti].cpos end drawer.drawScaled(cx, cy, FU/3, crown) oldcx = cx oldcy = cy end end end -- did i really just make a dupe of the ptsr leaderboard so my gamemode can have accurate skin colours and differently coloured player names? -- ... yes! local TV_scoreboard_hud = function(v, player) if not PTSR.IsPTSR() then return end if not multiplayer then return end local zinger_text = "LEADERBOARD" local zinger_x = 160*FU local zinger_y = 10*FU local player_sep = 17*FU -- separation of player infos local player_list = {} local faces = {} if PTSR.gamemode ~= PTSR.gm_team_versus then -- If not team versus, normal behaviour for _player in players.iterate do if _player.spectator then continue end if _player.ptsr.pizzaface then table.insert(faces, _player) continue end table.insert(player_list, _player) end table.sort(player_list, function(a,b) return a.score > b.score end) --pizzafaces go to the back for _, _player in ipairs(faces) do if _player and _player.valid then table.insert(player_list, _player) end end else -- If it's team versus, JANK TIME!!!! -- Teams should've already sorted their players in ThinkFrame, so we just need to add them in the right order if lbsorting == true then for i,v in ipairs(PTSR.teamleaderboard) do local vteam = PTSR.teams[v] for i2,v2 in ipairs(vteam.plrs) do if ((not v2) and (not v2.valid)) or v2.spectator or v2.ptsr.pizzaface then continue end table.insert(player_list, v2) end end for player in players.iterate do if player and player.valid and player.ptsr.pizzaface then table.insert(player_list, player) end end else -- If people don't like that sort-by-teams thing for _player in players.iterate do if _player.spectator then continue end if _player.ptsr.pizzaface then table.insert(faces, _player) continue end table.insert(player_list, _player) end table.sort(player_list, function(a,b) return a.score / #PTSR.teams[a.ptsr.team].plrs > b.score / #PTSR.teams[b.ptsr.team].plrs end) for _, _player in ipairs(faces) do if _player and _player.valid then table.insert(player_list, _player) end end end end --draw the bar OUTSIDE of the iterator, no reason to draw this 20 times v.drawFill(0, 25, 640, 1, V_SNAPTOTOP|V_SNAPTOLEFT) -- bar for i=1,#player_list do if i > 20 then break end local _player = player_list[i] local _pteam = _player.ptsr.team local _skinname = skins[_player.realmo.skin].name local _colormap = v.getColormap(_skinname, _player.realmo.color) local _skinpatch = v.getSprite2Patch(_player.realmo.skin, SPR2_XTRA) local commonflags = (V_SNAPTOLEFT|V_SNAPTOTOP) local playernameflags = nil if _player == consoleplayer and PTSR.gamemode ~= PTSR.gm_team_versus then playernameflags = V_YELLOWMAP elseif PTSR.gamemode ~= PTSR.gm_team_versus then if _player.ptsr.pizzaface then playernameflags = V_ORANGEMAP else playernameflags = V_GRAYMAP end elseif PTSR.gamemode == PTSR.gm_team_versus then if PTSR.teams[_pteam].team == false then playernameflags = V_GRAYMAP elseif _player.ptsr.pizzaface then playernameflags = V_ORANGEMAP elseif PTSR.teams[_pteam] then playernameflags = PTSR.teams[_pteam].colormap end end playernameflags = $|V_ALLOWLOWERCASE local aliveflag = (_player.playerstate ~= PST_LIVE or _player.spectator == true or _player.quittime > 0) and V_50TRANS or 0 local playerpingcolor local rawpingstring = (_player == server) and "SERV" or ((_player.quittime) and "QUIT" or _player.ping) local drawping = rawpingstring if _player.ping < 128 then playerpingcolor = V_GREENMAP elseif _player.ping < 256 then playerpingcolor = V_YELLOWMAP elseif _player.ping < INT32_MAX then playerpingcolor = V_REDMAP end if _player.quittime playerpingcolor = V_REDMAP|((leveltime/TICRATE % 2) and V_50TRANS or 0) --hacky else if (_player ~= server) then drawping = $.."ms" else playerpingcolor = $|V_BLUEMAP end end local _xcoord = 22*FU local _ycoord = 15*FU + (i*player_sep) if i > 10 then _xcoord = $ + 160*FU _ycoord = $ - (10*player_sep) commonflags = $|V_SNAPTORIGHT &~V_SNAPTOLEFT end if not _player.ptsr.pizzaface then if not (_skinpatch and _skinpatch.valid) then _skinpatch = v.cachePatch("MISSING") end -- [Player Icon] -- v.drawScaled(_xcoord - FU, _ycoord, FU/2, _skinpatch, (commonflags)|aliveflag, _colormap ) -- [Player Rank] -- v.drawScaled(_xcoord - 16*FU + 8*FU, _ycoord + 8*FU, FU/4, PTSR.r2p(v,_player.ptsr.rank), commonflags ) else -- [Pizzaface Icon] -- v.drawScaled(_xcoord - FU, _ycoord, FU/8, v.cachePatch("PTSR_FACEICON"), (commonflags)|aliveflag ) end local scorewidth = v.stringWidth(tostring(_player.score), (commonflags|playernameflags), "thin") + 11 local scoreandpingwidth = scorewidth + v.stringWidth(drawping, (commonflags), "thin") -- [Player Name] -- v.drawString(_xcoord + 16*FU, _ycoord, _player.name, (commonflags|playernameflags|aliveflag), "thin-fixed") if not _player.ptsr.pizzaface then -- [Player Score] -- local scorestring = tostring(_player.score) if PTSR.gamemode == PTSR.gm_team_versus and _player.ptsr.team then if PTSR.teams[_player.ptsr.team].team == true then scorestring = tostring(_player.score / #PTSR.teams[_player.ptsr.team].plrs) end end v.drawString(_xcoord + 16*FU, _ycoord + 8*FU, scorestring, (commonflags), "thin-fixed") -- [Player Ping] -- v.drawString( _xcoord +8*FU+(scorewidth*FU), _ycoord+8*FU, drawping, (commonflags|playerpingcolor), "thin-fixed") -- [Player Laps] -- v.drawString( _xcoord +16*FU+(scoreandpingwidth*FU), _ycoord+8*FU, "laps: ".._player.ptsr.laps, (commonflags), "thin-fixed") else -- [Pizzaface Ping] -- v.drawString(_xcoord + 16*FU, _ycoord + 8*FU, drawping, (commonflags|playerpingcolor), "thin-fixed") end -- show crown in leaderboard -- GAMEMODE: JUGGERNAUT exclusive if _player.realmo.hascrown then local crown_spr = v.getSpritePatch(SPR_C9W3) v.drawScaled(_xcoord, _ycoord+(4*FU), FU/4, crown_spr, (commonflags)|aliveflag ) end -- [Finish Flag] -- if (_player.ptsr.outofgame) v.drawScaled(_xcoord - 6*FU,_ycoord+11*FU,FU/2, v.getSpritePatch(SPR_FNSF,A,0), (commonflags)|V_FLIP ) end end customhud.CustomFontString(v, zinger_x, zinger_y, zinger_text, "PTFNT", (V_SNAPTOTOP), "center", FU/4, SKINCOLOR_BLUE) end customhud.disable("PTSR:DEFAULT:RANKINGS") -- DIE, PTSR HUD! DIEEEE!!! customhud.SetupItem("PTSR:TEAMVERSUS:RANKINGS", ptsr_hudmodname, TV_scoreboard_hud, "scores", 2) -- replace PTSR leaderboard; i know TEAMVERSUS isn't a hud style but whaterawjdfa kldlaw customhud.SetupItem("teamscorecount", ptsr_hudmodname, TV_teamscores_hud, "game", 0) -- Powerbubbles local scorebubble = PTSR:AddBubblePower({ name = "Score Boost", pickup_func = function(toucher) if toucher and toucher.valid and toucher.player and toucher.player.valid then local player = toucher.player local score_boost = 0 local topteam local topteami if PTSR.pizzatime then topteam = PTSR.teams[PTSR.teamleaderboard[1]] topteami = PTSR.teamleaderboard[1] else local lteams = {} for i,v in ipairs(PTSR.teams) do table.insert(lteams, i) end table.sort(lteams, function(a,b) return PTSR.teams[a].points > PTSR.teams[b].points end) topteam = PTSR.teams[lteams[1]] topteami = lteams[1] end -- Give players from "losing" teams a boost if player.ptsr.team ~= topteami then local pteam = PTSR.teams[player.ptsr.team] local diff = topteam.points - pteam.points score_boost = FixedInt(FixedMul(diff*FU, 65*FU/100)) else -- Give winning team small boost score_boost = 750 + (topteam.points / 20) end P_AddPlayerScore(player, score_boost) PTSR.add_wts_score(player, toucher, score_boost, 20, SKINCOLOR_LAVENDER) local playerteam = PTSR.teams[player.ptsr.team] print(playerteam.asciicolor..player.name.."\x80\ received \x89"..tostring(score_boost).." points!\x80") if DiscordBot then DiscordBot.Data.msgsrb2 = $.."["..#player.."] ("..playerteam.realname..") **"..player.name.."** received **"..tostring(score_boost).." points!**\n" end end end, sprite = SPR_TVTK, frame = C, pop_color = SKINCOLOR_FUCHSIA, disable_spawn = true, --disable_respawn = true }) local telebubble = PTSR:AddBubblePower({ name = "Teleport", pickup_func = function(toucher) if toucher and toucher.valid and toucher.player and toucher.player.valid then local player = toucher.player if countPlayers("allalive") > 1 then local plrs = {} for cplr in players.iterate do if isPlayerValid(player) and cplr ~= player and cplr.ptsr.team ~= player.ptsr.team then table.insert(plrs, cplr) end end local plrtotp = plrs[P_RandomRange(1, #plrs)] if plrtotp == nil or (not (plrtotp.valid and plrtotp.mo and plrtotp.mo.valid)) then COM_BufInsertText(player, "cechoduration 5") COM_BufInsertText(player, "cecho Couldn't find anyone to teleport to!") S_StartSound(player.mo, sfx_lose) return end local ogplrx, ogplry, ogplrz = player.mo.x, player.mo.y, player.mo.z local toplrx, toplry, toplrz = plrtotp.mo.x, plrtotp.mo.y, plrtotp.mo.z P_SetOrigin(player.mo, toplrx, toplry, toplrz) P_SetOrigin(plrtotp.mo, ogplrx, ogplry, ogplrz) S_StartSoundAtVolume(player.mo, sfx_mixup, 255) S_StartSoundAtVolume(plrtotp.mo, sfx_mixup, 255) P_FlashPal(player, 83, 25) P_FlashPal(plrtotp, 83, 25) local playerteam = PTSR.teams[player.ptsr.team] local plrtotpteam = PTSR.teams[plrtotp.ptsr.team] print(playerteam.asciicolor .. player.name .. "\x80 swapped places with " .. plrtotpteam.asciicolor .. plrtotp.name .. "\x80") if DiscordBot then DiscordBot.Data.msgsrb2 = $.."["..#player.."] ("..playerteam.realname..") **"..player.name.."** swapped places with ["..#plrtotp.."] ("..plrtotpteam.realname..") **"..plrtotp.name.."**\n" end end end end, sprite = SPR_TVMX, frame = C, pop_color = SKINCOLOR_EMERALD, disable_spawn = true, --disable_respawn = true }) addHook("PostThinkFrame", function() if PTSR.gamemode ~= PTSR.gm_team_versus then return end if checkedbubbles == false and PTSR.BubbleMobjList then checkedbubbles = true for i,bubble in ipairs(PTSR.BubbleMobjList) do if bubble and bubble.valid then if P_RandomRange(1, 12) == 3 then -- if more than 1 player at round start, roll for telebubble or scorebubble if #PTSR.teams > 2 then if P_RandomRange(1,2) == 1 then bubble.bubblepower = telebubble else bubble.bubblepower = scorebubble end else -- or just guaranteed scorebubble (1 player = telebubble useless) bubble.bubblepower = scorebubble end PTSR.RefreshBubbleIcon(bubble) end end end end end) -- Death/Revive Rings addHook("MobjSpawn", function(mobj) if PTSR.gamemode == PTSR.gm_team_versus then P_RemoveMobj(mobj) end end, MT_PT_DEATHRING) addHook("TouchSpecial", function(special, toucher) local tplayer = toucher.player local deathring = special if tplayer.ptsr.pizzaface then return true end if deathring and deathring.valid and toucher and toucher.valid and tplayer and tplayer.valid then -- check everything exists if deathring.deathring_used then return true end -- This is this person's first death AND the person revving is neutral OR the person who died is neutral OR they were on the same team as the reviver if (deathring.player_ref.ptsr.gotrevivedonce == false) and ((PTSR.teams[tplayer.ptsr.team].team == false) or (PTSR.teams[deathring.player_ref.ptsr.team].team == false) or (PTSR.teams[tplayer.ptsr.team].team == true and tplayer.ptsr.team == deathring.player_ref.ptsr.team)) then if deathring.player_ref and deathring.player_ref.valid then -- "dumb ass lua hack" local rplayer = deathring.player_ref if rplayer == tplayer then return true end G_DoReborn(#rplayer) rplayer.spectator = false rplayer.ctfteam = 1 rplayer.playerstate = PST_REBORN rplayer.ptsr.revivelocation = { x = deathring.x, y = deathring.y, z = deathring.z } if deathring.rings_kept then rplayer["ptsr_revive_await_rings"] = deathring.rings_kept end if deathring.score_kept then rplayer["ptsr_revive_await_score"] = deathring.score_kept end deathring.deathring_used = true P_ResetPlayer(rplayer) rplayer.mo.health = 1 rplayer.mo.flags = mobjinfo[MT_PLAYER].flags local rteam = PTSR.teams[rplayer.ptsr.team] local tteam = PTSR.teams[tplayer.ptsr.team] print(tteam.asciicolor .. tplayer.name .. "\x83 revived " .. rteam.asciicolor .. deathring.drop_name .. "\x80") if DiscordBot then DiscordBot.Data.msgsrb2 = $.."["..#tplayer.."] ("..tteam.realname..") **"..tplayer.name.."** revived ["..#rplayer.."] ("..rteam.realname..") **"..deathring.drop_name.."**\n" end PTSR:AddComboTime(tplayer, tplayer.ptsr.combo_maxtime) if rplayer == consoleplayer then displayplayer = consoleplayer chatprintf(consoleplayer, rteam.asciicolor.."You have been revived!") end rplayer.ptsr.justrevived = true rplayer.ptsr.gotrevivedonce = true end elseif deathring.player_ref.ptsr.gotrevivedonce then if deathring.rings_kept then local rplayer = deathring.player_ref local rteam = PTSR.teams[rplayer.ptsr.team] local tteam = PTSR.teams[tplayer.ptsr.team] tplayer.ptsr.rings_on_lap = $ + deathring.rings_kept P_GivePlayerRings(tplayer, deathring.rings_kept) print(tteam.asciicolor..tplayer.name.." \x83\stole "..deathring.rings_kept.." rings from "..rteam.asciicolor..deathring.drop_name) if DiscordBot then DiscordBot.Data.msgsrb2 = $.."["..#tplayer.."] ("..tteam.realname..") **"..tplayer.name.."** stole "..deathring.rings_kept.." rings from ["..#rplayer.."] ("..rteam.realname..") **"..deathring.drop_name.."**\n" end PTSR:AddComboTime(tplayer, tplayer.ptsr.combo_maxtime) deathring.deathring_used = true end else return true end else return true end end, MT_PT_TV_DEATHRING) addHook("MobjDeath", function(target, inflictor, source, damage, damagetype) if (not multiplayer) or PTSR.gamemode ~= PTSR.gm_team_versus then return end local player = target.player if target and target.valid and player and player.valid and (not PTSR.isOvertime()) then local deathring = P_SpawnMobj(target.x, target.y, target.z, MT_PT_TV_DEATHRING) deathring.player_ref = player deathring.rings_kept = player.rings deathring.score_kept = player.score deathring.drop_name = player.name deathring.color_ref = PTSR.teams[player.ptsr.team].skincolor deathring.scale = $*2 deathring.colorized = true deathring.color = deathring.color_ref table.insert(TVdeathrings, deathring) if player.rings and (not PTSR.pizzatime) then deathring.time_ref = "prePT" elseif PTSR.pizzatime and (not player.ptsr.gotrevivedonce) then deathring.time_ref = "firstDeath" elseif PTSR.pizzatime and player.ptsr.gotrevivedonce and player.rings then deathring.time_ref = "secondDeath" else P_RemoveMobj(deathring) end end end, MT_PLAYER) -- custom name tags for team versus death rings FMLLLLL -- Literally why must it be exclusive to specific mobjs. Why must I do this... eh tbf they didn't know when making it Whatevss hud.add(function(v, player, camera) if (not CV_FindVar("ptsr_nametags").value) then return end if (not PTSR.IsPTSR()) then return end if PTSR.gameover then return end if (player.awayviewmobj and player.spectator) return end for _, tmo in pairs(PTSR.TV_w2s_mobjs) do if (not tmo) or (not tmo.valid) then continue end if tmo.player and player == tmo.player then continue end if tmo.player and tmo.player.valid then if (not tmo.player.ptsr.pizzaface) then continue end end if (not tmo.type == MT_PT_TV_DEATHRING) then -- i know this makes it the same limitation PTSR has but i realllyy cba to make it fully mod friendly rn. Sorry continue end --how far away is the other mobj? local distance = R_PointToDist(tmo.x, tmo.y) local distlimit = 16000 if distance > distlimit*FRACUNIT then continue end --flipcam adjustment local flip = 1 if displayplayer.mo and displayplayer.mo.valid flip = P_MobjFlip(displayplayer.mo) end local tmoz = tmo.z if (flip == -1) tmoz = $ + tmo.height end if spectator tmoz = $ - 48*tmo.scale end local result = SG_ObjectTracking(v,player,camera,{ x = tmo.x, y = tmo.y, z = tmoz }) if (not result.onScreen) then continue end result.y = $+(18*result.scale) local name = "DEATH RING" local textcolor = SKINCOLOR_GREEN local namecolor = tmo.color local text_size = FRACUNIT/6 local namefont = "center" local ringfont = "center" local charwidth = 5 local lineheight = 8 local flash = (leveltime/(TICRATE/6))%2 == 0 if flash and tmo.health == 0 then textcolor = SKINCOLOR_RED end local distedit = max(0, distance - ((distlimit*FU)>>1)) * 2 local trans = min(9, (((distedit * 10) >> 16) / distlimit)) * V_10TRANS local dsm = displayplayer.realmo -- the z axis exists too yknow local dx = tmo.x-dsm.x local dy = tmo.y-dsm.y local dz = tmo.z-dsm.z local obj_dist = (FixedHypot(FixedHypot(dx,dy),dz))/FU obj_dist = $/10 --luigi budd: regular FU values are too big to easily discern distance --from the face, so divide by 10 to help with uh..... telling the distance if tmo.time_ref == "firstDeath" then name = PTSR.teams[tmo.player_ref.ptsr.team].realname.." REVIVE RING" else name = $ + "["..tostring(tmo.rings_kept).."x]" end customhud.CustomFontString(v, result.x, result.y, name, "PTFNT", trans, namefont, text_size, namecolor) customhud.CustomFontString(v, result.x, result.y+(4*FRACUNIT), obj_dist.."fu", "PTFNT", trans, namefont, text_size, SKINCOLOR_WHITE) end end, "game") -- clear every frame addHook("PreThinkFrame", function() PTSR.TV_w2s_mobjs = {} end)