// ################################################## // Possession // By RabidToaster // ################################################## local command = "!possess" // ######################### // Don't change down here! // ######################### if ( SERVER ) then AddCSLuaFile( "Possession.lua" ) local function SetPossessing( pPlayer, pPossessed ) // If we were possessing someone, tell them they're not being possessed any more. if ( pPlayer.Possessing && pPlayer.Possessing:IsValid() ) then pPlayer.Possessing:SetNWBool( "Possessed", false ) pPlayer.Possessing.Possessed = nil end // We're now possessing the other player. pPlayer.Possessing = pPossessed pPlayer:SetNetworkedEntity( "Possessing", pPossessed or GetWorldEntity() ) pPlayer:SetViewEntity( pPossessed ) // Tell the other player we're possessing them. if ( pPossessed ) then pPossessed.Possessed = true pPossessed:SetNWBool( "Possessed", true ) end end local tChat = {} local function PlayerSay( pPlayer, sText, bPublic ) // Crappy command :D local tArgs = string.Explode( " ", sText ) if ( pPlayer:IsAdmin() && string.lower( tArgs[ 1 ] or "" ) == command ) then if ( pPlayer.Possessed ) then pPlayer:ChatPrint( "You cannot possess while being possessed." ) return "" end if ( pPlayer.Possessing == nil ) then table.remove( tArgs, 1 ) local sName = string.lower( table.concat( tArgs, " " ) ) if ( sName == "" ) then pPlayer:ChatPrint( "Usage: !possess " ) return "" end for _, pOther in pairs( player.GetAll() ) do if ( !pOther.Possessed && pOther != pPlayer && string.find( string.lower( pOther:Name() ), sName ) ) then SetPossessing( pPlayer, pOther ) pPlayer:ChatPrint( "You are possessing " .. pOther:Name() .. "." ) pOther:ChatPrint( "You are being possessed!" ) return "" end end pPlayer:ChatPrint( "No matches found for '" .. sName .. "'." ) else pPlayer:ChatPrint( "You stopped possessing " .. pPlayer.Possessing:Name() .. "." ) pPlayer.Possessing:ChatPrint( "You're no longer possessed." ) SetPossessing( pPlayer, nil ) end return "" end // If the player is possessing someone, make the possesed player say it. if ( bPublic && pPlayer.Possessing != nil && pPlayer.Possessing:IsValid() ) then tChat[ pPlayer.Possessing ] = tChat[ pPlayer.Possessing ] or {} table.insert( tChat[ pPlayer.Possessing ], sText ) pPlayer.Possessing:ConCommand( "say " .. sText ) return "" end // Don't let the possessed player say anything apart from what they're supposed to. if ( pPlayer.Possessed ) then for iA, sAllowed in pairs( tChat[ pPlayer ] or {} ) do if ( sText == sAllowed ) then table.remove( tChat, iA ) return end end return "" end end hook.Add( "PlayerSay", "Possession.PlayerSay", PlayerSay ) // Forwards commands to the possessed player. local function ForwardBind( pPlayer, _, tArguments ) if ( pPlayer.Possessing && pPlayer.Possessing:IsValid() ) then pPlayer.Possessing:ConCommand( table.concat( tArguments, " " ) ) end end concommand.Add( "possession_forward", ForwardBind ) // Keep eye angles synced. // TODO: Keep possessing players eyes the same. local function Think() for _, pP in pairs( player.GetAll() ) do if ( pP.Possessing && pP.Possessing:IsValid() ) then pP.Possessing:SetEyeAngles( pP:EyeAngles() ) end end end hook.Add( "Think", "Possession.Think", Think ) // Stop the player running around when they're possessing someone. local function SetupMove( pPlayer, mdData ) if ( pPlayer.Possessing && pPlayer.Possessing:IsValid() ) then mdData:SetForwardSpeed( 0 ) mdData:SetSideSpeed( 0 ) mdData:SetUpSpeed( 0 ) end end hook.Add( "SetupMove", "Possession.SetupMove", SetupMove ) // Horribly ineffiecient. D: // TODO: Find a better way! local function WeaponEquip() for _, pOwner in pairs( player.GetAll() ) do if ( pOwner.Possessing && pOwner.Possessing:IsValid() ) then local eOActive = pOwner:GetActiveWeapon() local ePActive = pOwner.Possessing:GetActiveWeapon() if ( eOActive:IsValid() && ePActive:IsValid() ) then if ( eOActive:GetClass() != ePActive:GetClass() ) then pOwner.Possessing:SelectWeapon( eOActive:GetClass() ) end end end end end timer.Create( "WeaponTest", 0.5, 0, WeaponEquip ) local tSpawn = {} local function PlayerSpawnSomething( pPlayer, sCommand, sType ) if ( pPlayer.Possessing && pPlayer.Possessing:IsValid() ) then tSpawn[ pPlayer.Possessing ] = tSpawn[ pPlayer.Possessing ] or {} table.insert( tSpawn[ pPlayer.Possessing ], sType ) pPlayer.Possessing:ConCommand( sCommand .. " " .. sType ) return false end if ( pPlayer.Possessed ) then for _, sAllowed in pairs( tSpawn[ pPlayer ] or {} ) do if ( sType == sAllowed ) then table.remove( tSpawn[ pPlayer ], iA ) return end end return false end end hook.Add( "PlayerSpawnProp", "Possession.PlayerSpawnProp", function( p, m ) return PlayerSpawnSomething( p, "gm_spawn", m ) end ) hook.Add( "PlayerSpawnVehicle", "Possession.PlayerSpawnVehicle", function( p ) if ( p.Possessed or p.Possessing ) then return false end end ) hook.Add( "PlayerSpawnNPC", "Possession.PlayerSpawnNPC", function( p, c ) return PlayerSpawnSomething( p, "gmod_spawnnpc", c ) end ) hook.Add( "PlayerSpawnSENT", "Possession.PlayerSpawnSENT", function( p, c ) return PlayerSpawnSomething( p, "gm_spawnsent", c ) end ) end if ( CLIENT ) then local bFirstPerson = false local iAttack = 0 // Forwards binds up to the server, for bouncing down to the possessed player. local tDontBlock = { "messagemode", "messagemode2", "toggleconsole", "invnext", "invprev", "+menu", "-menu", "gm_giveswep" } local tLetThrough = { "messagemode", "messagemode2", "toggleconsole" } // Commands possessed players can do. local allowCommands = { "noclip", "gm_spawn", "gm_spawnsent", "gm_spawnvehicle", "gmod_spawnnpc", "gmod_setnpcweapon", "gm_giveswep", "undo" } local function PlayerBindPress( pPlayer, sBind, bPressed ) local eP = pPlayer:GetNWEntity( "Possessing" ) if ( eP && eP:IsValid() ) then if ( !bPressed && string.Left( sBind, 1 ) != "+" ) then return end if ( !bPressed ) then sBind = string.Replace( sBind, "+", "-" ) end local left = string.Left( sBind, 1 ) if ( left == "+" || left == "-" || table.HasValue( allowCommands, sBind ) ) then RunConsoleCommand( "possession_forward", sBind ) end if ( sBind == "invnext" || sBind == "invprev" ) then iAttack = CurTime() + 6 end if ( ( sBind == "+attack" && CurTime() < iAttack ) || sBind == "-attack" ) then iAttack = 0 return end if ( !table.HasValue( tDontBlock, sBind ) ) then return true end end if ( pPlayer:GetNWBool( "Possessed", false ) == true ) then if ( !table.HasValue( tLetThrough, sBind ) ) then return true end end end hook.Add( "PlayerBindPress", "Possession.PlayerBindPress", PlayerBindPress ) // Move the players view to the possessed player. local function CalcView( pPlayer, vOrigin, aAngle, iFOV ) local eP = pPlayer:GetNWEntity( "Possessing" ) if ( eP && eP:IsValid() ) then if ( bFirstPerson ) then return { origin = eP:GetShootPos() + ( eP:GetAimVector() * 16 ) } else return { origin = ( eP:GetShootPos() - ( eP:GetAimVector() * 64 ) ) + Vector( 0, 0, 32 ) } end end end hook.Add( "CalcView", "Possession.CalcView", CalcView ) // Draw the thirdperson crosshair. local function HUDPaint() if ( bFirstPerson ) then return end local eP = LocalPlayer():GetNWEntity( "Possessing" ) if ( eP && eP:IsValid() ) then local vTarget = util.QuickTrace( eP:GetShootPos(), eP:GetAimVector() * 8192, eP ).HitPos local tTarget = vTarget:ToScreen() local iX, iY = tTarget.x, tTarget.y local iSize = 3 // Black outline. surface.SetDrawColor( 0, 0, 0, 200 ) surface.DrawRect( ( iX - 1 ) - iSize, iY - 1, ( iSize * 2 ) + 3, 3 ) surface.DrawRect( iX - 1, ( iY - 1 ) - iSize, 3, ( iSize * 2 ) + 3 ) // White inside. surface.SetDrawColor( 255, 255, 255, 200 ) surface.DrawLine( iX - iSize, iY, iX + iSize, iY ) surface.DrawLine( iX, iY - iSize, iX, iY + iSize ) end end hook.Add( "HUDPaint", "Possession.HUDPaint", HUDPaint ) // Allows us to change from view easily. concommand.Add( "possession_toggleview", function() bFirstPerson = !bFirstPerson end ) end