local function setMainPlayer(player) if server == nil --You're not a netgame, dumbass. print("Trying to set mainplayer while server is not available. This should never happen and you should let FuriousFox know if it does.") return false end local oldmainplayer = server.mainplayer if server.mainplayer == nil or not server.mainplayer.valid server.mainplayer = server elseif player != nil and player.valid server.mainplayer = player end if oldmainplayer != nil and server.mainplayer != oldmainplayer print(server.mainplayer.name.." is now the main player") server.mainplayer.health = server.mhealth server.mainplayer.mo.health = server.mhealth server.mainplayer.lives = server.mlives server.mainplayer.starpostx = server.mstarpostx server.mainplayer.starposty = server.mstarposty server.mainplayer.starpostz = server.mstarpostz server.mainplayer.starpostnum = server.mstarpostnum server.mainplayer.starpostangle = server.mstarpostangle server.mainplayer.starposttime = server.mstarposttime end end local function main(playermo) local player = playermo.player setMainPlayer(nil) if server.leash == nil server.leash = 8192 server.changeOnDeath = false server.changeOnNewMap = false server.nextMainPlayer = "Cycle" end if player.showMainPlayerName == nil player.showMainPlayerName = true end if gametype == GT_COOP and not (maptol & TOL_NIGHTS) -- Main Player if player == server.mainplayer and player.lives > 0 and player.health > 0 server.mhealth = server.mainplayer.health server.mlives = server.mainplayer.lives if player.exiting == 0 server.mstarpostx = server.mainplayer.starpostx server.mstarposty = server.mainplayer.starposty server.mstarpostz = server.mainplayer.starpostz server.mstarpostnum = server.mainplayer.starpostnum server.mstarpostangle = server.mainplayer.starpostangle server.mstarposttime = server.mainplayer.starposttime server.mxtralife = server.mainplayer.xtralife else server.mstarpostx = 0 server.mstarposty = 0 server.mstarpostz = 0 server.mstarpostnum = 0 server.mstarpostangle = 0 server.mstarposttime = 0 server.mxtralife = 0 end if server.mainplayer.respawn == nil for plr in players.iterate if plr != server.mainplayer plr.respawn = nil CONS_Printf(plr, server.mainplayer.name.." has died. Respawning with "..server.mainplayer.name.."...") end end server.mainplayer.respawn = true elseif player.mo.subsector != nil --Only change the exit back if it's touched by the server.mainplayer if (player.mo.subsector.sector.special >= -12288 and player.mo.subsector.sector.special < -8192) player.mo.subsector.sector.special = $1 - 4096*11 end for fof in player.mo.subsector.sector.ffloors() --FOFs too! if (fof.sector.special >= -12288 and fof.sector.special < -8192) and fof.topheight >= player.mo.z and fof.bottomheight <= player.mo.z + player.mo.height fof.sector.special = $1 - 4096*11 end end end -- Assist Player elseif player != server.mainplayer and player.lives > 0 if server.mainplayer.lives <= 0 and playermo.valid player.lives = 1 P_KillMobj(playermo) --Game over elseif player.health > 0 player.lives = 2 --Joiners always have 2 lives, so they can't game over unless the host does player.health = 1 --Joiners always have 0 rings. But they can't die as long as the server.mainplayer has some. player.mo.health = 1 -- if this player scores, give it to the host if player.score > server.mainplayer.score P_AddPlayerScore(server.mainplayer, player.score-server.mainplayer.score) else player.score = server.mainplayer.score end player.starpostnum = 255 -- this player cannot activate starposts if server.mainplayer.mo.valid and ((server.leash != 0 and R_PointToDist2(playermo.x, playermo.y, server.mainplayer.mo.x, server.mainplayer.mo.y) > server.leash*FRACUNIT) or player.respawn == nil) if player.respawn != nil CONS_Printf(player, "You are too far from "..server.mainplayer.name..". Teleporting back to "..server.mainplayer.name.."...") end player.respawn = true P_TeleportMove(playermo, server.mainplayer.mo.x, server.mainplayer.mo.y, server.mainplayer.mo.z) playermo.momx = 0 playermo.momy = 0 playermo.momz = 0 playermo.angle = server.mainplayer.mo.angle player.powers[pw_flashing] = 104 --Setting this to 105 (3*TICRATE) doesn't seem to work right. It doesn't count down. --Added this to deal with zoom tubes, rope hangs, and swings player.pflags = 0 player.mo.state = S_PLAY_STND player.mo.tracer = nil -- Copy server.mainplayer's gravity and scale settings player.mo.scale = server.mainplayer.mo.scale if server.mainplayer.mo.flags2 & MF2_OBJECTFLIP playermo.flags2 = $1 | MF2_OBJECTFLIP else playermo.flags2 = $1 & ~MF2_OBJECTFLIP end end end end end end local function shouldHurt(targetmo, inflictormo, sourcemo) if gametype == GT_COOP and not (maptol & TOL_NIGHTS) --If something fired by a player hits you, don't take damage if sourcemo != nil and sourcemo.type == MT_PLAYER and targetmo != nil and targetmo.type == MT_PLAYER return false --If server.mainplayer has rings, just bounce back and play shield loss sound. elseif targetmo.player != nil and targetmo.player != server.mainplayer and targetmo.player.powers[pw_flashing] == 0 and server.mainplayer.mo.health > 1 S_StartSound(targetmo, sfx_shldls) P_DoPlayerPain(targetmo.player) --Joiners can't break monitors. elseif (targetmo.flags & MF_MONITOR and inflictormo.player != nil and inflictormo.player != server.mainplayer and not (maptol & TOL_MARIO)) --Reusing my old bumper code to prevent assist players from getting stuck inside monitors local horizontalAngle = R_PointToAngle2(targetmo.x,targetmo.y,inflictormo.x,inflictormo.y) local verticalAngle = R_PointToAngle2(FixedHypot(targetmo.x,targetmo.y),targetmo.z,FixedHypot(inflictormo.x,inflictormo.y),inflictormo.z) local bumperStrength = 8 P_SetObjectMomZ(inflictormo, bumperStrength*sin(verticalAngle), false) P_InstaThrust(inflictormo, horizontalAngle, bumperStrength*abs(cos(verticalAngle))) return false end end end local function death(mo) if mo.player != nil and mo.player != server.mainplayer mo.player.respawn = nil mo.player.health = 0 mo.player.mo.health = 0 elseif mo.player != nil and mo.player == server.mainplayer mo.player.respawn = nil if server.changeOnDeath and mo.player.lives > 1 server.mlives = $1 - 1 server.mhealth = 1 server.mxtralife = 0 if server.nextMainPlayer == "Random" setMainPlayer(players[P_RandomRange(0,#players-1)]) else local i = 1 while players[(#server.mainplayer+i)%32] == nil i = $1 + 1 end setMainPlayer(players[(#server.mainplayer+i)%32]) end end end end local function getRing(ringmo, playermo) --All rings go to the server.mainplayer if gametype == GT_COOP and maptol & ~TOL_NIGHTS and playermo.player != server.mainplayer and P_CanPickupItem(playermo.player) P_GivePlayerRings(server.mainplayer, 1) end end local function getEmmy(emmymo, playermo) --Only the mainplayer can pick up emeralds or tokens (and Fire Flowers) if gametype == GT_COOP and maptol & ~TOL_NIGHTS and playermo.player != server.mainplayer return true end end local function drawHud(v, player) if gametype == GT_COOP and not (maptol & TOL_NIGHTS) and server.mainplayer != nil and server.mainplayer.valid hud.disable("lives") hud.disable("rings") --Draw server.mainplayer's rings if server.mainplayer.health <= 1 and leveltime%10 >= 5 --Exactly lined up with SRB2's blinking RINGS counter. Excellent. v.draw(16,42,v.cachePatch("RRINGS"),V_HUDTRANS|V_SNAPTOTOP|V_SNAPTOLEFT) else v.draw(16,42,v.cachePatch("SBORINGS"),V_HUDTRANS|V_SNAPTOTOP|V_SNAPTOLEFT) end if server.mainplayer.health > 0 v.drawNum(112,42,server.mainplayer.health-1,V_HUDTRANS|V_SNAPTOTOP|V_SNAPTOLEFT) else v.drawNum(112,42,0,V_HUDTRANS|V_SNAPTOTOP|V_SNAPTOLEFT) end if splitscreen hud.disable("score") hud.disable("time") v.draw(16,10,v.cachePatch("SBOSCORE"),V_HUDTRANS|V_SNAPTOTOP|V_SNAPTOLEFT) v.drawNum(128,10,server.mainplayer.score,V_HUDTRANS|V_SNAPTOTOP|V_SNAPTOLEFT) v.draw(17,26,v.cachePatch("SBOTIME"),V_HUDTRANS|V_SNAPTOTOP|V_SNAPTOLEFT) v.drawNum(88,26,server.mainplayer.realtime/(60*TICRATE),V_HUDTRANS|V_SNAPTOTOP|V_SNAPTOLEFT) v.draw(88,26,v.cachePatch("SBOCOLON"),V_HUDTRANS|V_SNAPTOTOP|V_SNAPTOLEFT) v.drawPaddedNum(112,26,(server.mainplayer.realtime/TICRATE)%60,2,V_HUDTRANS|V_SNAPTOTOP|V_SNAPTOLEFT) else hud.enable("score") hud.enable("time") end --Draw server.mainplayer's lives local patchcolor = v.getColormap(server.mainplayer.mo.skin, server.mainplayer.mo.color) if player.showMainPlayerName v.drawString(34,176,server.mainplayer.name,V_YELLOWMAP|V_SNAPTOBOTTOM|V_SNAPTOLEFT) else v.drawString(34,176,skins[server.mainplayer.mo.skin].hudname,V_YELLOWMAP|V_SNAPTOBOTTOM|V_SNAPTOLEFT) end if server.mainplayer.powers[pw_super] > 0 v.drawScaled(16*FRACUNIT,176*FRACUNIT,FRACUNIT/2,v.cachePatch(skins[server.mainplayer.mo.skin].superface),V_SNAPTOBOTTOM|V_SNAPTOLEFT,v.getColormap(server.mainplayer.mo.skin, server.mainplayer.mo.color)) else v.drawScaled(16*FRACUNIT,176*FRACUNIT,FRACUNIT/2,v.cachePatch(skins[server.mainplayer.mo.skin].face),V_SNAPTOBOTTOM|V_SNAPTOLEFT,v.getColormap(server.mainplayer.mo.skin, server.mainplayer.mo.color)) end v.drawString(74,184,server.mainplayer.lives,V_SNAPTOBOTTOM|V_SNAPTOLEFT,"right") v.draw(38,186,v.cachePatch("STLIVEX"),V_SNAPTOBOTTOM|V_SNAPTOLEFT) else hud.enable("score") hud.enable("time") hud.enable("rings") hud.enable("lives") end end function A_1upThinker(actor) --Always show the server.mainplayer as the 1UP icon if gametype == GT_COOP and not (maptol & TOL_NIGHTS) if (actor.tracer == nil) actor.tracer = P_SpawnMobj(actor.x, actor.y, actor.z, MT_OVERLAY) actor.tracer.target = actor actor.tracer.state = actor.info.seestate end setMainPlayer(nil) if server.mainplayer.mo != nil actor.tracer.color = server.mainplayer.mo.color actor.tracer.skin = server.mainplayer.mo.skin end else super(actor) end end local function changeExit() --Change all the exit sectors to some unaccounted for sector type collectgarbage("collect") --Will this help with sync failures? Not sure, but lets try it. if gametype == GT_COOP and not (maptol & TOL_NIGHTS) for sector in sectors.iterate if sector.special / 4096 == 2 sector.special = $1 + (4096*11) end end if server.changeOnNewMap if server.nextMainPlayer == "Random" setMainPlayer(players[P_RandomRange(0,#players-1)]) else local i = 1 while players[(#server.mainplayer+i)%32] == nil i = $1 + 1 end setMainPlayer(players[(#server.mainplayer+i)%32]) end end end end local function mainPlayer(player, arg) if server == nil CONS_Printf(player, "You must be in a game to use this command.") return false end setMainPlayer(nil) if arg == nil CONS_Printf(player, "Determines the main player in Classic Coop.") CONS_Printf(player, "MAINPLAYER is "..server.mainplayer.name..".") elseif player != server and player != server.mainplayer CONS_Printf(player, "Only the server or main player can use this command.") elseif tonumber(arg) != nil and players[tonumber(arg)] != nil setMainPlayer(players[tonumber(arg)]) else for plr in players.iterate if string.lower(arg) == string.lower(plr.name) setMainPlayer(plr) return true end end CONS_Printf(player, "Player \""..arg.."\" not found.") end end local function leash(player, arg) if server == nil CONS_Printf(player, "You must be in a game to use this command.") return false end setMainPlayer(nil) if arg == nil CONS_Printf(player, "Determines how far players can get from the main player in Classic Coop.") CONS_Printf(player, "LEASH is "..server.leash..", default is 8192.") elseif player != server and player != server.mainplayer CONS_Printf(player, "Only the server or main player can use this command.") elseif string.lower(arg) == "default" server.leash = 8192 elseif tonumber(arg) == nil CONS_Printf(player, "\""..arg.."\" is not a number.") elseif tonumber(arg) < 1 server.leash = 0 else server.leash = arg end end local function suicide(player) if server == nil CONS_Printf(player, "You must be in a game to use this command.") return false end if player.mo.valid and player.health > 0 P_KillMobj(player.mo) player.health = 0 player.mo.health = 0 end end local function changeOnDeath(player, arg) if server == nil CONS_Printf(player, "You must be in a game to use this command.") return false end setMainPlayer(nil) if arg == nil CONS_Printf(player, "Sets whether the Main Player changes when the current Main Player dies.") CONS_Printf(player, "CC_CHANGEONDEATH is "+server.changeOnDeath+", default is false.") elseif string.lower(arg) == "default" or string.lower(arg) == "0" or string.lower(arg) == "false" or string.lower(arg) == "no" or string.lower(arg) == "off" server.changeOnDeath = false else server.changeOnDeath = true end end local function changeOnNewMap(player, arg) if server == nil CONS_Printf(player, "You must be in a game to use this command.") return false end setMainPlayer(nil) if arg == nil CONS_Printf(player, "Sets whether the Main Player changes when the level is finished.") CONS_Printf(player, "CC_CHANGEONNEWMAP is "+server.changeOnNewMap+", default is false.") elseif string.lower(arg) == "default" or string.lower(arg) == "0" or string.lower(arg) == "false" or string.lower(arg) == "no" or string.lower(arg) == "off" server.changeOnNewMap = false else server.changeOnNewMap = true end end local function nextMainPlayer(player, arg) if server == nil CONS_Printf(player, "You must be in a game to use this command.") return false end setMainPlayer(nil) if arg == nil CONS_Printf(player, "Usage: CC_NEXTMAINPLAYER ") CONS_Printf(player, "Sets how the next Main Player is determined.") CONS_Printf(player, "CC_NEXTMAINPLAYER is "..server.nextMainPlayer..", default is Cycle.") elseif string.lower(arg) == "default" or string.lower(arg) == "cycle" server.nextMainPlayer = "Cycle" elseif string.lower(arg) == "random" server.nextMainPlayer = "Random" end end local function showMainPlayerName(player, arg) if server == nil CONS_Printf(player, "You must be in a game to use this command.") return false end if arg == nil CONS_Printf(player, "Sets whether to show the Main Player's in the lives counter instead of the skin name.") CONS_Printf(player, "CC_CHANGEONNEWMAP is "+player.showMainPlayerName+", default is true.") elseif string.lower(arg) == "0" or string.lower(arg) == "false" or string.lower(arg) == "no" or string.lower(arg) == "off" player.showMainPlayerName = false else player.showMainPlayerName = true end end COM_AddCommand("MAINPLAYER", mainPlayer, 0) COM_AddCommand("LEASH", leash, 0) COM_AddCommand("SUICIDE", suicide, 0) COM_AddCommand("CC_CHANGEONDEATH", changeOnDeath, 1) COM_AddCommand("CC_CHANGEONNEWMAP", changeOnNewMap, 1) --COM_AddCommand("CC_NEXTMAINPLAYER", nextMainPlayer, 1) --Random isn't working right, and frankly I don't feel like fixing it right now. COM_AddCommand("CC_SHOWMAINPLAYERNAME", showMainPlayerName, 0) addHook("MobjThinker", main, MT_PLAYER) hud.add(drawHud, "game") addHook("MapLoad",changeExit) addHook("ShouldDamage", shouldHurt, MT_PLAYER) addHook("MobjDeath", death, MT_PLAYER) for i = 0, #mobjinfo-1, 1 if mobjinfo[i].flags & MF_MONITOR addHook("ShouldDamage",shouldHurt,i) end end addHook("TouchSpecial", getRing, MT_RING) addHook("TouchSpecial", getRing, MT_FLINGRING) addHook("TouchSpecial", getRing, MT_COIN) addHook("TouchSpecial", getRing, MT_FLINGCOIN) addHook("TouchSpecial", getEmmy, MT_EMMY) addHook("TouchSpecial", getEmmy, MT_TOKEN) addHook("TouchSpecial", getEmmy, MT_EMERALD1) addHook("TouchSpecial", getEmmy, MT_EMERALD2) addHook("TouchSpecial", getEmmy, MT_EMERALD3) addHook("TouchSpecial", getEmmy, MT_EMERALD4) addHook("TouchSpecial", getEmmy, MT_EMERALD5) addHook("TouchSpecial", getEmmy, MT_EMERALD6) addHook("TouchSpecial", getEmmy, MT_EMERALD7) addHook("TouchSpecial", getEmmy, MT_FIREFLOWER)