local function sound_effects(ply, anim)
    if anim == "sur_closeencounter_johnny_win" then
        timer.Simple(0.4, function()
            if !IsValid(ply) then return end
            sound.Play("bsmod/EvilDead/punch/punch_swing_2d_0"..math.random(1,6)..".wav", ply:GetPos())
        end)
        timer.Simple(0.7, function()
            if !IsValid(ply) then return end
            sound.Play("bsmod/EvilDead/punch/punch_impact_face_2d_0"..math.random(1,9)..".wav", ply:GetPos())
        end)
    elseif anim == "sur_closeencounter_cook_win" then
        timer.Simple(0.6, function()
            if !IsValid(ply) then return end
            sound.Play("bsmod/EvilDead/punch/punch_swing_2d_0"..math.random(1,6)..".wav", ply:GetPos())
        end)
        timer.Simple(0.9, function()
            if !IsValid(ply) then return end
            sound.Play("bsmod/EvilDead/punch/punch_impact_body_2d_0"..math.random(1,6)..".wav", ply:GetPos())
        end)
    elseif anim == "sur_closeencounter_hitchhiker_win" then
        timer.Simple(0.4, function()
            if !IsValid(ply) then return end
            sound.Play("bsmod/EvilDead/punch/punch_swing_2d_0"..math.random(1,6)..".wav", ply:GetPos())
        end)
        timer.Simple(0.7, function()
            if !IsValid(ply) then return end
            sound.Play("bsmod/EvilDead/punch/punch_impact_face_2d_0"..math.random(1,9)..".wav", ply:GetPos())
        end)
    elseif anim == "sur_closeencounter_sissy_win" then
        timer.Simple(0.3, function()
            if !IsValid(ply) then return end
            sound.Play("bsmod/EvilDead/punch/punch_swing_2d_0"..math.random(1,6)..".wav", ply:GetPos())
        end)
        timer.Simple(0.5, function()
            if !IsValid(ply) then return end
            sound.Play("bsmod/EvilDead/punch/punch_impact_face_2d_0"..math.random(1,9)..".wav", ply:GetPos())
        end)
    end
end

function SuR:StopGrab(self, ent, takedown)
    if !IsValid(ent) then return end

    local function dis(dist)
        if IsValid(self) then
            self:RemoveEFlags(EFL_NO_THINK_FUNCTION)
            self:SetNoDraw(false)
            self:SetMaterial("")
            self.ExecutingTarget = false
            self:SetState()
            for i, ent in ipairs(self:GetChildren()) do 
                ent:SetMaterial("")
            end 
            if IsValid(self.grabmodel) then
                self.grabmodel:Remove()
            end
            if dist then
				self.Flinching = true
				self:StopAttacks(true)
				self.PlayingAttackAnimation = false
				local animTbl = self.AnimTbl_Flinch
				if HitgroupInfo != nil then animTbl = HitgroupInfo.Animation end
				local anim = istable(animTbl) and VJ_PICK(animTbl) or animTbl
				local animDur = self.NextMoveAfterFlinchTime
				if animDur == false and anim then
					animDur = self:DecideAnimationLength(anim, false, self.FlinchAnimationDecreaseLengthAmount)
				end
				animDur = tonumber(animDur) or 0.1
				if animDur <= 0 then
					animDur = 0.1
				end
				if anim then
					self:VJ_ACT_PLAYACTIVITY(anim, true, animDur, false, 0, {SequenceDuration=animDur, PlayBackRateCalculated=true})
				end
				timer.Create("timer_act_flinching"..self:EntIndex(), tonumber(animDur) or 0.1, 1, function() self.Flinching = false end)
				if isfunction(self.CustomOnFlinch_AfterFlinch) then
					self:CustomOnFlinch_AfterFlinch(dmginfo, hitgroup)
				end
				self.NextFlinchT = CurTime() + (tonumber(self.NextFlinchTime) or 0)
            end
        end
    
        ent:Freeze(false)
        ent:SetNoTarget(false)
        ent:SetSVAnimation("")
        ent.GrabDelay = CurTime()+5
        ent.Grabbed = false
        ent.ExecutingTarget = false
        ent.ExecutingEnt = nil
        ent.GrabPressCount = 0
        if IsValid(ent.LastPickupWeapon) then		
            local class = ent.LastPickupWeapon:GetClass()
            ent:SelectWeapon(class)
            ent.LastPickupWeapon = nil
        end
    end

    if takedown and IsValid(self) then
        dis()
        ent:SuR_Down()
    elseif not takedown and IsValid(self) then
        SuR:PlaySoundOnClient("surrounded/sfx/qte_success"..math.random(1,3)..".mp3", ent)

        local anim1 = "sur_"..ent.ExecutingEnt.grabanim.."win"
        local anim2 = ent.ExecutingEnt.grabanim.."lose"
		local long = tonumber(select(2, ent:LookupSequence(anim1))) or 0.1

        ent.Grabbed = false
        ent:SetSVAnimation(anim1, false, true, true)
        sound_effects(ent, anim1)
        ent.ExecutingEnt.grabmodel:ResetSequence(anim2)
        ent.ExecutingEnt.grabmodel:SetCycle(0)

        timer.Simple(long, function()
            if !IsValid(ent) then return end
            dis(true)
        end)
    else
        dis()
    end
end

local function AbilityGrab(self, ent)
    if self.ExecutingTarget or ent.ExecutingTarget or ent.Grabbed or ent:IsDowned() or ent.GrabDelay and ent.GrabDelay > CurTime() or ent:IsZombie() then return end

    local angz = self:GetAngles():SnapTo("y", 45)
    local posz = ent:GetPos()
	local isnpc = ent:IsNPC()
	local startpos = self:GetPos()

    local att_name = ""
    local rnd = math.random(1,4)
    if rnd == 1 then
        att_name = "hitchhiker"
    elseif rnd == 2 then
        att_name = "sissy"
    elseif rnd == 3 then
        att_name = "johnny"
    else
        att_name = "cook"
    end 
    local anim = "closeencounter_"..att_name.."_"

    local ent2 = ent
	local ent1 = ents.Create("sur_anim")
	ent1.ent = self
	ent1.enable_ent = true
	ent1:SetModel("models/surrounded/test/tcm_misc.mdl")
	ent1:SetAngles(angz)
    ent1:SetPos(posz)
	ent1:Spawn()
    ent1:SetPos(posz-ent1:GetForward()*28)
	ent1:ResetSequence(anim.."intro")
	ent1:ResetSequenceInfo()
	ent1:SetCycle(0)
    self.grabmodel = ent1
    self.grabanim = anim

	local bd = ents.Create("base_anim")
	SuR:TransferModelData(self, bd)
	bd:SetPos(posz)
	bd:SetParent(ent1)
	bd:AddEffects(1)
	bd:Spawn()

    self:RemoveAllDecals()
    self:AddEFlags(EFL_NO_THINK_FUNCTION)
    self:SetMaterial("null")
	self:SetNoDraw(true)
    self:SetVelocity(-self:GetVelocity())
    self.ExecutingPos = posz
    self:SetPos(posz)
    self:SetAngles(angz)
    self.ExecutingTarget = true
	self.ExecutingModel = ent1
	for i, ent in ipairs(self:GetChildren()) do 
		ent:SetMaterial("null")
	end 

    ent:Freeze(true)
	ent:SetNoTarget(true)
    ent:SetPos(posz)
	ent:SetEyeAngles(angz+Angle(0,180,0))
    ent:SetVelocity(-ent:GetVelocity()+Vector(50,0,0))
	ent:SetSVAnimation("sur_"..anim.."intro", false, true, true)
    ent.ExecutingTarget = true
    ent.ExecutingPos = posz
    ent.ExecutingEnt = self
    ent.Grabbed = true
    ent.GrabPressMax = math.floor(math.Clamp(self:Health()/15, 10, 50))
    ent.GrabPressCount = 0
    if !IsValid(ent.LastPickupWeapon) then
        ent.LastPickupWeapon = ent:GetActiveWeapon()
    end
    ent:SetActiveWeapon(nil)
    SuR:PlaySoundOnClient("surrounded/sfx/qte_start"..math.random(1,3)..".mp3", ent)

    timer.Simple(select(2, ent:LookupSequence("sur_"..anim.."intro")), function()
        if !IsValid(ent) or !IsValid(self) or !ent.Grabbed or !IsValid(ent1) then return end

        ent:SetSVAnimation("sur_"..anim.."grappleloop", false, true, true, true)
        ent1:ResetSequence(anim.."grappleloop")
    end)
end

hook.Add("OnNPCKilled", "SuR_GrabsFixed", function(ent)
    if ent.ExecutingTarget then
        ent:SetNoDraw(true)
        ent:SetMaterial("")
    end
end)

local allow_specials = {
    ["npc_vj_lnrhl2_rebel_hvy"] = true,
    ["npc_vj_lnr_carrier"] = true,
    ["npc_vj_lnr_butcher"] = true,
    ["npc_vj_lnr_riot"] = true,
    ["npc_vj_lnr_riot_shield"] = true,
    ["npc_vj_lnr_evo"] = true,
    ["npc_vj_lnr_bomber"] = true,
    ["npc_vj_lnr_tesla"] = true,
    ["npc_vj_lnr_healer"] = true,
    ["npc_vj_lnr_hazmat"] = true,
}

hook.Add("EntityTakeDamage", "SuR_Grabs", function(tar, dmg)
    local att = dmg:GetAttacker()
	if not tar.NoGrab and att:IsNPC() and att:GetPos():DistToSqr(tar:GetPos()) < 20000 and att.IsZombie and not att.LNR_Crawler and (!att.IsSpecial and math.random(1,100) <= SuR.Config.Zombie_Grab_Chance or allow_specials[att:GetClass()] and math.random(1,100) <= SuR.Config.Zombie_Special_Grab_Chance or SuR.ActiveMutator == 3 and math.random(1,100) <= SuR.Config.Zombie_Special_Grab_Chance*2) and tar:IsPlayer() then
        AbilityGrab(att, tar)
        dmg:SetDamage(0)
	end
    if tar:IsPlayer() and tar.Grabbed and dmg:GetDamage() >= tar:Health() then
        dmg:SetDamage(0)
    end
end)

hook.Add("PlayerButtonDown", "SuR_ButtonHookQTE", function(ply, but)
    if ply.Grabbed and ply:Alive() and but == KEY_SPACE and string.match(ply:GetSVAnim(), "grappleloop") then
        if ply.GrabPressCount < ply.GrabPressMax then
            if ply:GetPlayerClass() == "survivor" and ply:GetLevelState() >= 2 then
                ply.GrabPressCount = ply.GrabPressCount + 4
            else
                ply.GrabPressCount = ply.GrabPressCount + 1
            end
        else
            SuR:StopGrab(ply.ExecutingEnt, ply, false)
        end
    end
end)
