
-----------------------------------------------------
-------------------------------------
---------------- Cuffs --------------
-------------------------------------
-- Copyright (c) 2015 Nathan Healy --
-------- All rights reserved --------
-------------------------------------
-- sv_handcuffs.lua         SERVER --
--                                 --
-- Server-side handcuff stuff.     --
-------------------------------------

if CLIENT then return end

util.AddNetworkString( "Cuffs_GagPlayer" )
util.AddNetworkString( "Cuffs_BlindPlayer" )
util.AddNetworkString( "Cuffs_FreePlayer" )
util.AddNetworkString( "Cuffs_DragPlayer" )

util.AddNetworkString( "Cuffs_TiePlayers" )
util.AddNetworkString( "Cuffs_UntiePlayers" )

local function GetTrace( ply )
	local tr = util.TraceLine( {start=ply:EyePos(), endpos=ply:EyePos()+(ply:GetAimVector()*100), filter=ply} )
	if IsValid(tr.Entity) and tr.Entity:IsPlayer() then
		local cuffed,wep = tr.Entity:IsHandcuffed()
		if cuffed then return tr,wep end
	end
end

//
// Standard hooks
CreateConVar( "cuffs_restrictsuicide", 1, {FCVAR_ARCHIVE,FCVAR_SERVER_CAN_EXECUTE} )
hook.Add( "CanPlayerSuicide", "Cuffs RestrictSuicide", function( ply )
	if ply:IsHandcuffed() and cvars.Bool("cuffs_restrictarrest") then return false end
end)
CreateConVar( "cuffs_restrictteams", 1, {FCVAR_ARCHIVE,FCVAR_SERVER_CAN_EXECUTE} )
hook.Add( "PlayerCanJoinTeam", "Cuffs RestrictTeam", function( ply )
	if ply:IsHandcuffed() and cvars.Bool("cuffs_restrictteams") then return false end
end)
hook.Add( "PlayerCanSeePlayersChat", "Cuffs ChatGag", function( _,_,_, ply )
	if not IsValid(ply) then return end
	
	local cuffed,wep = ply:IsHandcuffed()
	if cuffed and wep:GetIsGagged() then return false end
end)
hook.Add( "PlayerCanHearPlayersVoice", "Cuffs VoiceGag", function( _, ply )
	if not IsValid(ply) then return end
	
	local cuffed,wep = ply:IsHandcuffed()
	if cuffed and wep:GetIsGagged() then return false end
end)

// 
// DarkRP
CreateConVar( "cuffs_restrictwarrant", 1, {FCVAR_ARCHIVE,FCVAR_SERVER_CAN_EXECUTE,FCVAR_REPLICATED} )
hook.Add( "canRequestWarrant", "Cuffs PreventWarrant", function( crim, cop, reason ) if cvars.Bool("cuffs_restrictwarrant") and cop:IsHandcuffed() then return false,"You can issue warrants when cuffed!" end end)
hook.Add( "canWanted", "Cuffs PreventWarrant", function( crim, cop, reason ) if cvars.Bool("cuffs_restrictwarrant") and cop:IsHandcuffed() then return false,"You can issue warrants when cuffed!" end end)

CreateConVar( "cuffs_autoarrest", 0, {FCVAR_ARCHIVE,FCVAR_SERVER_CAN_EXECUTE} )
CreateConVar( "cuffs_restrictarrest", 0, {FCVAR_ARCHIVE,FCVAR_SERVER_CAN_EXECUTE} )
hook.Add( "canArrest", "Cuffs RestrictArrest", function( cop, crim ) // DarkRP Arrest hook
	if IsValid(crim) and cvars.Bool("cuffs_restrictarrest") and not crim:IsHandcuffed() then return false,"You must handcuff a suspect to arrest them!" end
end)
hook.Add( "playerCanChangeTeam", "Cuffs RestrictTeam", function( ply, tm, force )
	if ply:IsHandcuffed() and cvars.Bool("cuffs_restrictteams") and not force then return false,"You can't change jobs when cuffed!" end
end)
hook.Add( "CanChangeRPName", "Cuffs RestrictName", function( ply )
	if ply:IsHandcuffed() then return false,"You can't change your name when cuffed!" end
end)

//
// Think
local NextTieHookCleanup
hook.Add( "Think", "Cuffs ForceJump CleanupTieHooks", function()
	for _,v in pairs(player.GetAll()) do
		if v.Cuff_ForceJump then
			if not v:OnGround() then return end
			
			local tr = util.TraceLine( {start = v:GetPos(), endpos = v:GetPos()+Vector(0,0,20), filter = v} )
			if tr.Hit then return end
			
			v:SetPos(v:GetPos()+Vector(0,0,5) )
			
			v.Cuff_ForceJump = nil
		end
	end
	
	if CurTime()>=(NextTieHookCleanup or 0) then
		for _,v in pairs(ents.GetAll()) do
			if v.IsHandcuffHook and v.TiedHandcuffs then
				for i=#v.TiedHandcuffs,0,-1 do
					if not IsValid(v.TiedHandcuffs[i]) then
						table.remove( v.TiedHandcuffs, i )
					end
				end
				if #v.TiedHandcuffs<=0 then
					v:Remove()
					continue
				end
			end
		end
	end
end)

//
// Cuffed player interaction
net.Receive( "Cuffs_GagPlayer", function(_,ply)
	if (not IsValid(ply)) or ply:IsHandcuffed() then return end
	
	local target = net.ReadEntity()
	if (not IsValid(target)) or target==ply then return end
	
	local cuffed, cuffs = target:IsHandcuffed()
	if not (cuffed and IsValid(cuffs) and cuffs:GetCanGag()) then return end
	
	local tr = GetTrace(ply)
	if not (tr and tr.Entity==target) then return end
	
	local shouldGag = net.ReadBit()==1
	cuffs:SetIsGagged( shouldGag )
	hook.Call( shouldGag and "OnHandcuffGag" or "OnHandcuffUnGag", GAMEMODE, ply, target, cuffs )
end)
net.Receive( "Cuffs_BlindPlayer", function(_,ply)
	if (not IsValid(ply)) or ply:IsHandcuffed() then return end
	
	local target = net.ReadEntity()
	if (not IsValid(target)) or target==ply then return end
	
	local cuffed, cuffs = target:IsHandcuffed()
	if not (cuffed and IsValid(cuffs) and cuffs:GetCanBlind()) then return end
	
	local tr = GetTrace(ply)
	if not (tr and tr.Entity==target) then return end
	
	local shouldBlind = net.ReadBit()==1
	cuffs:SetIsBlind( shouldBlind )
	hook.Call( shouldBlind and "OnHandcuffBlindfold" or "OnHandcuffUnBlindfold", GAMEMODE, ply, target, cuffs )
end)
net.Receive( "Cuffs_FreePlayer", function(_,ply)
	if (not IsValid(ply)) or ply:IsHandcuffed() then return end
	
	local target = net.ReadEntity()
	if (not IsValid(target)) or target==ply then return end
	
	local cuffed, cuffs = target:IsHandcuffed()
	if not (cuffed and IsValid(cuffs)) then return end
	if IsValid(cuffs:GetFriendBreaking()) then return end
	
	local tr = GetTrace(ply)
	if not (tr and tr.Entity==target) then return end
	
	cuffs:SetFriendBreaking( ply )
end)
net.Receive( "Cuffs_DragPlayer", function(_,ply)
	if (not IsValid(ply)) or ply:IsHandcuffed() then return end
	
	local target = net.ReadEntity()
	if (not IsValid(target)) or target==ply then return end
	
	local cuffed, cuffs = target:IsHandcuffed()
	if not (cuffed and IsValid(cuffs) and cuffs:GetRopeLength()>0) then return end
	
	local tr = GetTrace(ply)
	if not (tr and tr.Entity==target) then return end
	
	local shouldDrag = net.ReadBit()==1
	if shouldDrag then
		if not (IsValid(cuffs:GetKidnapper())) then
			cuffs:SetKidnapper( ply )
			hook.Call( "OnHandcuffStartDragging", GAMEMODE, ply, target, cuffs )
		end
	else
		if ply==cuffs:GetKidnapper() then
			cuffs:SetKidnapper( nil )
			hook.Call( "OnHandcuffStopDragging", GAMEMODE, ply, target, cuffs )
		end
	end
end)

local HookModel = Model("models/props_c17/TrapPropeller_Lever.mdl")
net.Receive( "Cuffs_TiePlayers", function(_,ply)
	if (not IsValid(ply)) or ply:IsHandcuffed() then return end
	
	local DraggedCuffs = {}
	for _,c in pairs(ents.FindByClass("weapon_handcuffed")) do
		if c:GetRopeLength()>0 and c:GetKidnapper()==ply then
			table.insert( DraggedCuffs, c )
		end
	end
	if #DraggedCuffs<=0 then return end
	
	local tr = util.TraceLine( {start=ply:EyePos(), endpos=ply:EyePos()+(ply:GetAimVector()*100), filter=ply} )
	if not tr.Hit then return end
	
	if IsValid(tr.Entity) then // Pass to another player
		if tr.Entity:IsPlayer() then
			for i=1,#DraggedCuffs do
				if DraggedCuffs[i].Owner==tr.Entity then
					DraggedCuffs[i]:SetKidnapper(nil)
					hook.Call( "OnHandcuffStopDragging", GAMEMODE, ply, DraggedCuffs[i].Owner, DraggedCuffs[i] )
				else
					DraggedCuffs[i]:SetKidnapper(tr.Entity)
					hook.Call( "OnHandcuffStopDragging", GAMEMODE, ply, DraggedCuffs[i].Owner, DraggedCuffs[i] )
					hook.Call( "OnHandcuffStartDragging", GAMEMODE, tr.Entity, DraggedCuffs[i].Owner, DraggedCuffs[i] )
				end
			end
			return
		elseif tr.Entity.IsHandcuffHook and tr.Entity.TiedHandcuffs then
			for i=1,#DraggedCuffs do
				DraggedCuffs[i]:SetKidnapper(tr.Entity)
				table.insert( tr.Entity.TiedHandcuffs, DraggedCuffs[i] )
				hook.Call( "OnHandcuffStopDragging", GAMEMODE, ply, DraggedCuffs[i].Owner, DraggedCuffs[i] )
				hook.Call( "OnHandcuffTied", GAMEMODE, ply, DraggedCuffs[i].Owner, DraggedCuffs[i], tr.Entity )
			end
			return
		end
	end
	
	local hk = ents.Create("prop_physics")
	hk:SetPos( tr.HitPos + tr.HitNormal )
	local ang = tr.HitNormal:Angle()
	ang:RotateAroundAxis( ang:Up(), -90 )
	hk:SetAngles( ang )
	hk:SetModel( HookModel )
	hk:Spawn()
	
	-- hk:SetMoveType( MOVETYPE_NONE )
	if IsValid(tr.Entity) then
		hk:SetParent( tr.Entity )
		hk:SetMoveType( MOVETYPE_VPHYSICS )
	else
		hk:SetMoveType( MOVETYPE_NONE )
	end
	hk:SetCollisionGroup( COLLISION_GROUP_WEAPON )
	
	hk:SetNWBool("Cuffs_TieHook", true)
	hk.IsHandcuffHook = true
	hk.TiedHandcuffs = {}
	
	for i=1,#DraggedCuffs do
		DraggedCuffs[i]:SetKidnapper( hk )
		table.insert( hk.TiedHandcuffs, DraggedCuffs[i] )
		hook.Call( "OnHandcuffStopDragging", GAMEMODE, ply, DraggedCuffs[i].Owner, DraggedCuffs[i] )
		hook.Call( "OnHandcuffTied", GAMEMODE, ply, DraggedCuffs[i].Owner, DraggedCuffs[i], hk )
	end
end)

local function DoUntie( ply, ent )
	for i=1,#ent.TiedHandcuffs do
		if not IsValid(ent.TiedHandcuffs[i]) then continue end
		
		ent.TiedHandcuffs[i]:SetKidnapper( ply )
		hook.Call( "OnHandcuffUnTied", GAMEMODE, ply, ent.TiedHandcuffs[i].Owner, ent.TiedHandcuffs[i], ent )
		hook.Call( "OnHandcuffStartDragging", GAMEMODE, ply, ent.TiedHandcuffs[i].Owner, ent.TiedHandcuffs[i] )
	end
	
	ent:Remove()
end
net.Receive( "Cuffs_UntiePlayers", function(_,ply)
	if (not IsValid(ply)) or ply:IsHandcuffed() then return end
	
	local tr = util.TraceLine( {start=ply:EyePos(), endpos=ply:EyePos()+(ply:GetAimVector()*100), filter=ply} )
	if IsValid(tr.Entity) and tr.Entity.IsHandcuffHook and tr.Entity.TiedHandcuffs then
		DoUntie( ply, tr.Entity )
	end
end)
hook.Add( "AllowPlayerPickup", "Cuffs UntieHook", function(ply,ent)
	if IsValid(ent) and ent.IsHandcuffHook and ent.TiedHandcuffs then
		if (not IsValid(ply)) or ply:IsHandcuffed() then return end
		
		DoUntie( ply, ent )
		return false
	end
end)

//
// GmodDayZ support
// Created by and added with permission of Phoenix129 ( http://steamcommunity.com/profiles/76561198039440140/ )
hook.Add("OnHandcuffed", "DayZCuffs RemoveInventoryItem", function(ply, cuffedply, handcuffs)
	if engine.ActiveGamemode() == "dayz" then
		ply:TakeCharItem( handcuffs.CuffType )
	end
end)
 
hook.Add("OnHandcuffBreak", "DayZCuffs GiveInventoryItemIfFriend", function(cuffedply, handcuffs, friend)
	if engine.ActiveGamemode() == "dayz" then
		if IsValid(friend) and handcuffs.CuffType and handcuffs.CuffType~="" then
			friend:GiveItem(handcuffs.CuffType, 1)
		end
	end
end)

hook.Add( "CuffsCanHandcuff", "DayZCuffs SafezoneProtectCuffs", function( ply, target )
	if engine.ActiveGamemode() == "dayz" then
		if target.SafeZone or target.SafeZoneEdge or target.Loading or !target.Ready then
			return false
		end
	end
end)
hook.Add("PlayerDisconnected", "DayZCuffs DieHandcuffs", function(ply)
	if engine.ActiveGamemode() == "dayz" then
		if ply:IsHandcuffed() then ply:Kill() end
	end
end)
