-- j2b2's MEDIC! freeslot("S_PLAY_REVIVE", "S_REVIVE_ICON1", "S_REVIVE_ICON2", "S_REVIVE_ICON3", "S_REVIVE_ICON4", "S_REVIVE_COMPASS1", "S_REVIVE_COMPASS2", "S_REVIVE_COMPASS3", "S_REVIVE_COMPASS4", "S_REVIVE_COMPASS5", "S_REVIVE_COMPASS6", "MT_REVIVE_COMPASS" ) states[S_PLAY_REVIVE] = { sprite = SPR_PLAY, --TODO: Consider using SPR2_CNT1 instead of SPR2_PAIN frame = SPR2_PAIN | TR_TRANS50, tics = -1 } states[S_REVIVE_ICON1] = { sprite = SPR_WHAT, frame = A | FF_FULLBRIGHT, tics = 3, var1 = 0, var2 = 50, nextstate = S_REVIVE_ICON2 } states[S_REVIVE_ICON2] = { sprite = SPR_WHAT, frame = B | FF_FULLBRIGHT, tics = 3, var1 = 0, var2 = 50, nextstate = S_REVIVE_ICON3 } states[S_REVIVE_ICON3] = { sprite = SPR_WHAT, frame = C | FF_FULLBRIGHT, tics = 3, var1 = 0, var2 = 50, nextstate = S_REVIVE_ICON4 } states[S_REVIVE_ICON4] = { sprite = SPR_WHAT, frame = D | FF_FULLBRIGHT, tics = 3, var1 = 0, var2 = 50, nextstate = S_REVIVE_ICON3 } states[S_REVIVE_COMPASS1] = { sprite = SPR_SSPK, frame = A | FF_FULLBRIGHT, tics = 1, action = A_HomingChase, var1 = 40*FRACUNIT, var2 = 0, nextstate = S_REVIVE_COMPASS2 } states[S_REVIVE_COMPASS2] = { sprite = SPR_SSPK, frame = B | FF_FULLBRIGHT, tics = 1, action = A_HomingChase, var1 = 40*FRACUNIT, var2 = 0, nextstate = S_REVIVE_COMPASS3 } states[S_REVIVE_COMPASS3] = { sprite = SPR_SSPK, frame = C | FF_FULLBRIGHT, tics = 1, action = A_HomingChase, var1 = 40*FRACUNIT, var2 = 0, nextstate = S_REVIVE_COMPASS4 } states[S_REVIVE_COMPASS4] = { sprite = SPR_SSPK, frame = B | FF_FULLBRIGHT, tics = 1, action = A_HomingChase, var1 = 40*FRACUNIT, var2 = 0, nextstate = S_REVIVE_COMPASS5 } states[S_REVIVE_COMPASS5] = { sprite = SPR_SSPK, frame = A | FF_FULLBRIGHT, tics = 1, action = A_HomingChase, var1 = 30*FRACUNIT, var2 = 0, nextstate = S_REVIVE_COMPASS6 } states[S_REVIVE_COMPASS6] = { sprite = SPR_SSPK, frame = A | FF_FULLBRIGHT, tics = 0, action = A_Repeat, var1 = 4, var2 = S_REVIVE_COMPASS1, nextstate = S_NULL } mobjinfo[MT_REVIVE_COMPASS] = { spawnstate = S_REVIVE_COMPASS1, radius = 5*FRACUNIT, height = 5*FRACUNIT, mass = 0, flags = MF_SPECIAL | MF_NOCLIP | MF_NOCLIPHEIGHT | MF_NOGRAVITY } local COMPASS_DISTANCE_COLORS = { {15000*FRACUNIT,SKINCOLOR_NEON}, {10000*FRACUNIT,SKINCOLOR_CERULEAN}, {5000*FRACUNIT,SKINCOLOR_EMERALD}, {2500*FRACUNIT,SKINCOLOR_YELLOW}, {1000*FRACUNIT,SKINCOLOR_RUST}, {0,SKINCOLOR_CRIMSON} } local CACEE_BURNFACTOR = TICRATE*3 local CACEE_BURNTHRESHOLD = tr_trans40*CACEE_BURNFACTOR local function IsValid(a) if a ~= nil then return a.valid end return false end local cvCoopLives = CV_FindVar("cooplives") local cmdRetry = COM_AddCommand("medic_stuck",function(player) if player.playerstate ~= PST_DEAD then player.starpostnum = 0 player.starpostx = 0 player.starposty = 0 player.starpostz = 0 player.starposttime = 0 player.starpostangle = 0 player.starpostscale = 0 if IsValid(player.mo) then P_KillMobj(player.mo) end player.reviveX = 0 player.reviveY = 0 player.reviveZ = 0 player.reviveAngle = 0 end end) local function CheckAllNeedsRevive(player) local playerCount = 0 local playersNeedRevive = 0 for p in players.iterate() do if p ~= player then playerCount = playerCount+1 if p.needsRevive then playersNeedRevive = playersNeedRevive + 1 end end end if playerCount == playersNeedRevive then print("All players are down. Restarting level...") if cvCoopLives.value == 0 then COM_BufInsertText(server,"cooplives 3") end for p in players.iterate() do p.lives = 0 if p.playerstate ~= PST_DEAD and IsValid(p.mo) then P_KillMobj(p.mo) end end end end local function Revive(player) player.needsRevive = false for i=1,#player.powers do player.powers[i-1] = 0 end player.powers[pw_flashing] = 2*TICRATE player.pflags = player.pflags & ~PF_INVIS player.mo.state = S_PLAY_STND if IsValid(player.reviveIcon) then P_RemoveMobj(player.reviveIcon) end player.reviveIcon = nil S_StartSound(nil,sfx_3db06) end local function FakeKill(player) player.needsRevive = true player.pflags = player.pflags | PF_INVIS player.rings = 0 player.spheres = 0 for i=1,#player.powers do player.powers[i-1] = 0 end player.mo.state = S_PLAY_REVIVE player.reviveIcon = P_SpawnMobj(player.mo.x,player.mo.y,player.mo.z,MT_OVERLAY) player.reviveIcon.target = player.mo player.reviveIcon.state = S_REVIVE_ICON1 P_RestoreMusic(player) CheckAllNeedsRevive() end local function GetClosestPlayerNeedsRevive(x,y,z) local minDist local minDistPlayer for p in players.iterate() do if IsValid(p.mo) and p.playerstate ~= PST_DEAD and p.needsRevive then local dist = R_PointToDist2(x,0,p.mo.x,R_PointToDist2(y,z,p.mo.y,p.mo.z)) if minDist == nil then minDist = dist minDistPlayer = p elseif dist < minDist then minDist = dist minDistPlayer = p end end end return minDistPlayer, minDist end addHook("PlayerThink",function(player) if multiplayer and not G_IsSpecialStage(gamemap) and G_GametypeUsesLives() then player.lastButtons = player.lastButtons or 0 if player.playerstate == PST_DEAD and player.deadtimer > 3*TICRATE and player.lives > 0 then G_DoReborn(#player) end if IsValid(player.mo) and player.needsRevive and player.spectator == false then player.mo.state = S_PLAY_REVIVE player.panim = PA_DASH player.powers[pw_underwater] = 7000 player.powers[pw_spacetime] = 7000 player.powers[pw_nocontrol] = 7000 player.powers[pw_ignorelatch] = 39768 return true elseif IsValid(player.mo) and (player.powers[pw_underwater] ~= 0 and player.powers[pw_underwater] < 3) or (player.powers[pw_spacetime] ~= 0 and player.powers[pw_spacetime] < 3) then S_StartSound(player.mo,sfx_drown) FakeKill(player) elseif IsValid(player.mo) and IsValid(player.reviveCompass) == false and player.playerstate ~= PST_DEAD and player.cmd.buttons & BT_FIRENORMAL > 0 and player.lastButtons & BT_FIRENORMAL == 0 then local closestPlayer, closestPlayerDist = GetClosestPlayerNeedsRevive(player.mo.x,player.mo.y,player.mo.z) if closestPlayer then player.reviveCompass = P_SpawnMobj(player.mo.x,player.mo.y,player.mo.z+10*FRACUNIT,MT_REVIVE_COMPASS) player.reviveCompass.target = closestPlayer.mo player.reviveCompass.colorized = true for _,t in pairs(COMPASS_DISTANCE_COLORS) do if closestPlayerDist > t[1] then player.reviveCompass.color = t[2] break end end end end player.lastButtons = player.cmd.buttons end end) addHook("PlayerQuit",CheckAllNeedsRevive) addHook("MapChange",function() for player in players.iterate() do player.reviveAngle = nil player.reviveX = nil player.reviveY = nil player.reviveZ = nil player.needsRevive = nil player.reviveCompass = nil end end) addHook("PlayerSpawn",function(player) if multiplayer and not G_IsSpecialStage(gamemap) and G_GametypeUsesLives() then player.deadtimer = 0 if player.needsRevive == true and player.spectator == false then if player.reviveX then P_TeleportMove(player.mo,player.reviveX,player.reviveY,player.reviveZ) player.mo.angle = player.reviveAngle player.drawangle = player.reviveAngle player.reviveAngle = nil player.reviveX = nil player.reviveY = nil player.reviveZ = nil end FakeKill(player) return true elseif player.spectator == false then player.lives = INFLIVES end end end) addHook("ShouldDamage",function(target, inflictor, source, damage, damagetype) if target.player.needsRevive and damagetype ~= DMG_DEATHPIT and damagetype ~= DMG_CRUSHED then return false end end,MT_PLAYER) addHook("MobjDamage",function(target, inflictor, source, damage, damagetype) if multiplayer and not G_IsSpecialStage(gamemap) and G_GametypeUsesLives() then if damagetype ~= DMG_DEATHPIT and damagetype ~= DMG_CRUSHED then if target.player.rings == 0 and target.player.powers[pw_shield] == SH_NONE and (target.skin ~= "cacee" or target.player.caceeburn == nil or not (damagetype == DMG_FIRE and not ((target.eflags & MFE_UNDERWATER) and not (target.eflags & MFE_TOUCHLAVA)) and target.player.caceeburn < CACEE_BURNTHRESHOLD)) then P_DoPlayerPain(target.player,source,inflictor) S_StartSound(target.player.mobj,skins[target.player.mo.skin].soundsid[SKSPLDET1]) FakeKill(target.player) return true elseif target.player.needsRevive then return true end end end end,MT_PLAYER) addHook("MobjDeath",function(target, inflictor, source, damagetype) if multiplayer and not G_IsSpecialStage(gamemap) and G_GametypeUsesLives() then if damagetype ~= DMG_DEATHPIT and damagetype ~= DMG_CRUSHED then target.player.reviveX = target.x target.player.reviveY = target.y target.player.reviveZ = target.z target.player.reviveAngle = target.angle end if IsValid(target.player.reviveIcon) then P_RemoveMobj(target.player.reviveIcon) end target.player.needsRevive = true end end,MT_PLAYER) addHook("MobjCollide",function(mobj, toucher) if multiplayer and not G_IsSpecialStage(gamemap) and G_GametypeUsesLives() then if toucher.type == MT_PLAYER and mobj.z <= toucher.z+toucher.height and mobj.z+mobj.height >= toucher.z then if mobj.player.playerstate == PST_LIVE and mobj.player.needsRevive and not toucher.player.needsRevive then Revive(mobj.player) elseif toucher.player.playerstate == PST_LIVE and toucher.player.needsRevive and not mobj.player.needsRevive then Revive(toucher.player) end end end end,MT_PLAYER) addHook("TouchSpecial",function(mobj, toucher) if mobj.target == toucher then P_RemoveMobj(mobj) end return true end,MT_REVIVE_COMPASS) local HUD_X = 20 local HUD_Y = 182 local hudEnabled = false hud.add(function(v) if multiplayer and not G_IsSpecialStage(gamemap) and G_GametypeUsesLives() then --TODO: Make per player or just for first player local playersNeedRevive = {} for player in players.iterate() do if player.needsRevive then table.insert(playersNeedRevive,player) end end if hud.enabled("lives") then hud.disable("lives") hudEnabled = true end if #playersNeedRevive > 0 then local iconPatch = v.getSpritePatch(SPR_WHAT,C) local xPatch = v.cachePatch("STLIVEX") v.drawScaled(HUD_X*FRACUNIT,(HUD_Y+16)*FRACUNIT,FRACUNIT/2,iconPatch,V_SNAPTOBOTTOM | V_SNAPTOLEFT | V_HUDTRANS) v.draw(HUD_X+16,HUD_Y+2,xPatch,V_SNAPTOBOTTOM | V_SNAPTOLEFT | V_HUDTRANS) -- v.drawString(HUD_X+24,HUD_Y,playersNeedRevive,V_SNAPTOBOTTOM | V_SNAPTOLEFT | V_HUDTRANS) for i = #playersNeedRevive, 1, -1 do local player = playersNeedRevive[i] local playerPatch = v.getSprite2Patch(player.realmo.skin, SPR2_XTRA, player.powers[pw_super] > 0, C) if playerPatch == nil then playerPatch = v.getSprite2Patch("sonic", SPR2_XTRA, player.powers[pw_super] > 0, C) end v.drawScaled((HUD_X+28+((i-1)*8))*FRACUNIT, (HUD_Y+8)*FRACUNIT, FRACUNIT/2, playerPatch, V_SNAPTOBOTTOM | V_SNAPTOLEFT | V_HUDTRANS, v.getColormap(player.realmo.skin, player.skincolor)) end end elseif hudEnabled == true then hud.enable("lives") hudEnabled = false end end,"game") -- j2b2's MEDIC!