AddCSLuaFile( "cl_init.lua" ) -- Make sure clientside
AddCSLuaFile( "shared.lua" ) -- and shared scripts are sent.
  
include('shared.lua')

ENT.vClimber = false

ENT.vOtherEnd = nil

ENT.vOtherHitPos = nil

ENT.vMiddle = nil

ENT.vConstraint1 = nil
ENT.vRope1 = nil

ENT.vConstraint2 = nil
ENT.vRope2 = nil

ENT.vLength = nil
ENT.vTotalLength = nil
ENT.vLastTime = CurTime()
ENT.vSpeed = 128
ENT.vAnchor = nil 
ENT.vHanging = false
ENT.vDirection = 0

ENT.vBase1 = nil
ENT.vBase2 = nil

ENT.constant = 20000
ENT.damping = 50
ENT.rdamping = 1
ENT.material = "cable/rope"
ENT.width = 2

function ENT:Initialize()
	if (!SERVER) then return end
	self.Entity:SetModel( "models/Weapons/w_bugbait.mdl" )
	self.Entity:SetNoDraw( true )
	self.Entity:SetCollisionGroup(1)
	self.Entity:DrawShadow( false )
	self.Entity:PhysicsInit( SOLID_VPHYSICS )
	self.Entity:SetMoveType( MOVETYPE_VPHYSICS )
	self.Entity:SetSolid( SOLID_VPHYSICS )
	
 
	local phys = self.Entity:GetPhysicsObject()
	if (phys:IsValid()) then
		phys:Wake()
	end
end

function ENT:SetConstant( vConstant )
	self.constant = vConstant
end

function ENT:SetDamping( vDamping )
	self.damping = vDamping
end

function ENT:SetRDamping( vRDamping )
	self.rdamping = vRDamping
end

function ENT:SetMaterial( vMaterial )
	self.material = vMaterial
end

function ENT:SetWidth( vWidth )
	self.width = vWidth
end

function ENT:SetOtherEnd( vEntity, vHitPos, vSetLength, b1, b2 )
	if (!SERVER) then return end
	self.vBase = b1
	self.vBase = b2
	self.vOtherHitPos = vHitPos
	self.vOtherEnd = vEntity
	self.vHanging = true
	local trace = {}
	local vStart = self.Entity:GetPos()
	trace.start = vStart
	trace.endpos = vStart - Vector(0,0,vSetLength)
	trace.filter = self.Entity
	trace.filter = self.vOtherEnd
	trace.filter = self.vBase1
	trace.filter = self.vBase2
	local tr = util.TraceLine( trace )
	self.vAnchor = vStart
	self.vLength = ( vStart - self.vOtherHitPos ):Length( )
	local vAddLength = vSetLength - self.vLength
	if (tr.Hit) then
		vAddLength = (tr.HitPos - vStart):Length() - self.vLength - 40
	end
	if(vAddLength < 50) then
		self.Entity:Remove()
		return
	end
	self.vTotalLength = vAddLength + self.vLength
	local vConstraint, vRope = constraint.Rope( self.vOtherEnd, self.Entity, 0, 0, self.vOtherHitPos:GetNormal(), self.Entity:GetPos():GetNormal( ), self.vLength , vAddLength, 0, self.width, self.material, false )
	timer.Create( "climbable_rope_timer"..tostring(self.Entity), 0.1, 0, self.Moving, self )
end

function ENT:SetOtherPoint( vEntity, vHitPos, b1, b2 )
	if (!SERVER) then return end
	local phys = self.Entity:GetPhysicsObject()
	if ( phys ) then
		phys:EnableCollisions( false )
	end
	self.vBase = b1
	self.vBase = b2
	self.vOtherHitPos = vHitPos
	self.vOtherEnd = vEntity
	local vStart = self.Entity:GetPos()
	self.vAnchor = vStart
	self.vLength = ( vStart - self.vOtherHitPos ):Length( )
	self.vTotalLength = self.vLength
	local vConstraint, vRope = constraint.Rope( self.vOtherEnd, self.Entity, 0, 0, self.vOtherHitPos:GetNormal(), self.Entity:GetPos():GetNormal(), self.vTotalLength , 0, 0, self.width, self.material, false )
	timer.Create( "climbable_rope_timer"..tostring(self.Entity), 0.1, 0, self.Moving, self )
	self.Entity:GetPhysicsObject():SetMass( 10 )
end

function ENT:Think()
end 

function ENT:GetHit()
	if (!SERVER) then return end
	if(!self.vOtherEnd:IsValid())then return end
	local trace = {}
	local vStart = self.Entity:GetPos()
	trace.start = vStart
	trace.endpos = self.vOtherEnd:GetPos()
	trace.filter = self.Entity
	trace.filter = self.vOtherEnd
	trace.filter = self.vBase1
	trace.filter = self.vBase2
	local tr = util.TraceLine( trace )
	if( tr.Hit && !tr.HitWorld ) then
		all_ropes = ents.FindByClass( "climb_rope" )
		for _, ropes in pairs(all_ropes) do
			if(tr.Entity == ropes:GetOwner()) then return end
		end
		if (!tr.Entity:IsPlayer()) then return end
		tr.Entity:SetOwner( tr.Entity )
		self.vClimber = true
		constraint.RemoveConstraints( self.Entity, "Rope" )
		self.Entity:SetOwner( tr.Entity )
		
		self.vMiddle = ents.Create( "player_constraint" )
			self.vMiddle:SetPos( tr.Entity:GetOwner():GetPos() + Vector(0,0,60) )
			self.vMiddle:SetAngles( tr.Entity:GetAngles() )
			self.vMiddle:SetOwner( tr.Entity )
		self.vMiddle:Spawn( )
		
		self.vMiddle:GetPhysicsObject():SetMass( 10 )
		self.vLength = ( self.vOtherEnd:GetPos() - self.vMiddle:GetPos() ):Length( )
		
		self.vConstraint1, self.vRope1 = constraint.Elastic( self.vOtherEnd, self.vMiddle, 0, 0, self.vOtherHitPos:GetNormal(), self.vMiddle:GetPos():GetNormal( ), self.constant , self.damping, self.rdamping, self.material, self.width, 0 )
		self.vConstraint2, self.vRope2 = constraint.Elastic( self.Entity, self.vMiddle, 0, 0, self.Entity:GetPos():GetNormal(), self.vMiddle:GetPos():GetNormal( ), self.constant , self.damping, self.rdamping, self.material, self.width, 0 )
		
		self.vMiddle:GetTable():Constrain()
	end
end

function ENT:Moving()
	if (!SERVER) then return end
	if (self.Entity == nil) then return end
	local TimeDiff = CurTime() - self.vLastTime
	self.vLastTime = CurTime()
	if( self.vOtherEnd == nil ) then return end
	if(self.vClimber) then
		if(self.Entity:GetOwner():KeyDown( IN_JUMP ) and self.Entity:GetOwner():KeyDown( IN_DUCK ) ) then
			self.Entity:GetOwner():SetLocalVelocity( self.Entity:GetOwner():GetAngles():Forward() * 500 )
			self:RemovePlayer()
			return
		elseif(self.Entity:GetOwner():IsOnGround() && self.Entity:GetOwner():KeyDown( IN_DUCK ) ) then
			self:RemovePlayer()
			return
		elseif(!self.Entity:GetOwner():Alive()) then
			self:RemovePlayer()
			return
		end
		if(self.Entity:GetOwner() == nil) then return end
		if( self.Entity:GetOwner():KeyDown(IN_JUMP) ) then
			if ( self.vLength > 10 ) then
				self.vDirection = -1
			else
				self.vDirection = 0
			end
		elseif ( self.Entity:GetOwner():KeyDown( IN_DUCK ) ) then
			if ( self.vLength < self.vTotalLength ) then
				self.vDirection = 1
			else
				self.vDirection = 0
			end
		else
			self.vDirection = 0
		end
		local vDist = self.vDirection * self.vSpeed * TimeDiff
		self.vLength = vDist + self.vLength
		self.vConstraint1:Fire("SetSpringLength", self.vLength, 0)
		if (self.vRope1) then	self.vRope1:Fire("SetLength", self.vLength, 0)	end
		self.vConstraint2:Fire("SetSpringLength", self.vTotalLength - self.vLength, 0)
		if (self.vRope2) then	self.vRope2:Fire("SetLength", self.vTotalLength - self.vLength, 0)	end
		
		self.vMiddle:GetTable():Constrain()
		if(self.vHanging) then
			self:Swing()
		end
	else
		self:GetHit()
	end

end

function ENT:Swing()
	if(self.Entity:GetOwner():KeyPressed(IN_FORWARD) ) then
		local vAngles = self.Entity:GetOwner():GetAngles()
		local vForward = vAngles:Forward()
		vForward.z = 0
		self.vMiddle:GetPhysicsObject():ApplyForceCenter( vForward * 500 )
	end
end

function ENT:OnRemove()
	if(	self.vClimber ) then
		self:RemovePlayer()
	end
	if(self.vOtherEnd ~= nil) then
		if(self.vOtherEnd:IsValid()) then
			self.vOtherEnd:Remove()
		end
	end
	if(self.Entity ~= nil) then
		if(self.Entity:IsValid()) then
			self.Entity:Remove()
		end
	end
	if(self.vMiddle ~= nil) then
		if(self.vMiddle:IsValid()) then
			self.vMiddle:Remove()
		end
	end
	if (timer.IsTimer("climbable_rope_timer"..tostring(self.Entity))) then
		timer.Remove("climbable_rope_timer"..tostring(self.Entity))
	end
end


function ENT:RemovePlayer()
	if (!SERVER) then return end
	self.vMiddle:Remove()
	self.vClimber = false
	self.Entity:SetOwner(nil)
	local tempPos = self.Entity:GetPos()
	local vConstraint, vRope = constraint.Rope( self.vOtherEnd, self.Entity, 0, 0, self.vOtherHitPos:GetNormal(), self.Entity:GetPos():GetNormal( ), self.vTotalLength , 0, 0, self.width, self.material, false )
	self.Entity:SetPos(tempPos)
end
