AddCSLuaFile("cl_init.lua")
AddCSLuaFile("shared.lua")

include("shared.lua")

ENT.bCollided = false
ENT.Projectile = {
	Class = "mg_bullet",
	Speed = 4000,
	Gravity = 1
}
ENT.Maxs = Vector(1, 1, 1)
ENT.Model = Model("models/viper/mw/attachments/crossbow/attachment_vm_sn_crossbow_mag.mdl")
ENT.AoeEntity = nil

function ENT:Initialize()
	self:SetModel(self.Model)
	self:PhysicsInitBox(Vector(-10, -1, -1), Vector(10, 1, 1))
	self:GetPhysicsObject():Wake()
	self:GetPhysicsObject():SetMaterial("default_silent")
	self:GetPhysicsObject():AddGameFlag(FVPHYSICS_NO_PLAYER_PICKUP)
	self:GetPhysicsObject():AddGameFlag(FVPHYSICS_NO_IMPACT_DMG)
	self:GetPhysicsObject():AddGameFlag(FVPHYSICS_HEAVY_OBJECT)
	self:GetPhysicsObject():EnableMotion(true)
	self:GetPhysicsObject():EnableDrag(false)
	self:GetPhysicsObject():SetMass(1000)
	self:SetSolid(SOLID_VPHYSICS)
	self:SetCollisionGroup(COLLISION_GROUP_IN_VEHICLE) --doesn't collide with anything, no traces
	self:AddEFlags(EFL_NO_DAMAGE_FORCES)
	self:AddEFlags(EFL_DONTWALKON)
	self:AddEFlags(EFL_DONTBLOCKLOS)
	self:AddEFlags(EFL_NO_PHYSCANNON_INTERACTION)

	self.Projectile = table.Copy(self.Weapon.Projectile)
	self:GetPhysicsObject():SetVelocityInstantaneous(self:GetAngles():Forward() * self.Projectile.Speed)
	self.LastPos = self:GetOwner():EyePos()
end

function ENT:Think()
	--[[if (IsValid(self:GetParent()) && self:GetParent():Health() <= 0 && self:GetParent():GetMaxHealth() > 1) then
		self:SetMoveType(MOVETYPE_VPHYSICS)
		self:SetCollisionGroup(COLLISION_GROUP_DEBRIS)
		self:GetPhysicsObject():EnableMotion(true)
		self:GetPhysicsObject():Wake()
		self:SetParent(NULL)
	end]]
end

ENT.m_gravity = 0

function ENT:PhysicsUpdate(phys)
	if (!phys:IsMotionEnabled()) then
		return
	end

	self.m_gravity = math.Clamp(self.m_gravity + (self.Projectile.Gravity), -90, 90)

	phys:SetAngles(phys:GetAngles() + Angle(self.m_gravity, 0, 0) * FrameTime())
	phys:SetPos(self.LastPos + phys:GetAngles():Forward() * (self.Projectile.Speed * FrameTime()))
	
	--Aim assist
	if (GetConVar("mgbase_debug_projectiles"):GetInt() > 0) then
		debugoverlay.Box(phys:GetPos(), -self.Maxs, self.Maxs, 0, Color(0, 200, 50, 10))
	end

	local trData = {
		start = self.LastPos,
		endpos = phys:GetPos(),
		filter = {self:GetOwner(), self},
		mask = MASK_SHOT_PORTAL,
		collisiongroup = COLLISION_GROUP_PROJECTILE,
		mins = -self.Maxs,
		maxs = self.Maxs
	}

	local tr = util.TraceHull(trData)

	if (tr.Hit && (tr.Entity:IsPlayer() || tr.Entity:IsNPC())) then
		self:Impact(tr, phys, true)
		return
	end

	--Normal hitscan
	if (GetConVar("mgbase_debug_projectiles"):GetInt() > 0) then
		debugoverlay.Line(self.LastPos, phys:GetPos(), 1, Color(255, 0, 0, 1))
	end

	tr = util.TraceLine(trData)

	if (tr.Hit) then
		self:Impact(tr, phys, false)
		return
	end

	self.LastPos = phys:GetPos()
end

local function parentEntity(tr, ent)
	local arrowHitAng = tr.Normal:Angle()
	local arrowHitPos = tr.HitPos + arrowHitAng:Forward() * -3 + arrowHitAng:Up() * -1.5
	local boneId = tr.Entity:GetHitboxBone(tr.HitBox, 0)

	if (boneId != nil) then
		if (tr.Entity:IsRagdoll()) then
			boneId = tr.Entity:TranslatePhysBoneToBone(tr.PhysicsBone)
		end

		ent:FollowBone(tr.Entity, boneId)
		debugoverlay.Text(tr.HitPos, tr.Entity:GetBoneName(boneId), 1)
	else
		if (!tr.Entity:IsWorld()) then
			ent:SetParent(tr.Entity)
		end
	end

	ent:SetAngles(arrowHitAng)
	ent:SetPos(arrowHitPos)
end

function ENT:Think()
	if (!self:GetNailed()) then
		return
	end

	if (self.Projectile.PickUp) then
		local dist = self:GetOwner():NearestPoint(self:GetPos()):DistToSqr(self:GetPos())

		if (dist < 32 * 32) then
			self:GetOwner():EmitSound("viper/shared/iw8_mp_scavenger_pack_pickup.wav")
			self:GetOwner():SetAmmo(self:GetOwner():GetAmmoCount("XBowBolt") + 1, "XBowBolt")
			self:Remove()
		end
	end
end
 
function ENT:Impact(tr1, phys, bHull)
	SafeRemoveEntityDelayed(self, 20)

	phys:EnableMotion(false)

	if (self.Projectile.PickUp) then
		self:AddEffects(EF_ITEM_BLINK)
	end

	self:SetMoveType(MOVETYPE_NONE)
	self:SetNailed(true)

	self:StopParticles()
	
	self:FireBullets({
		Attacker = self:GetOwner(),
		Num = 1,
		Tracer = 0,
		Src = self.LastPos,
		Dir = (phys:GetPos() - self.LastPos):GetNormalized(),
		HullSize = bHull && self.Maxs:Length() * 2 || 1,
		IgnoreEntity = self,
		Callback = function(attacker, tr, dmgInfo)
			dmgInfo:SetInflictor(IsValid(self.Weapon) && self.Weapon || self)
			dmgInfo:SetDamageType(dmgInfo:GetDamageType() + DMG_DIRECT + self:GetDamageType())

			if (IsValid(self.Weapon)) then
				self.Weapon:BulletCallback(attacker, tr, dmgInfo)
			end

			if (tr.HitSky) then
				self:Remove()
				return
			end

			if (self.AoeEntity != nil) then
				local aoe = ents.Create(self.AoeEntity)
				aoe:SetOwner(self:GetOwner())
				parentEntity(tr, aoe)
				aoe:SetPos(tr.HitPos + tr.HitNormal * 3)

				local angle = tr.HitNormal:Angle()
				angle:RotateAroundAxis(angle:Right(), 270)

				aoe:SetAngles(angle)
				aoe:Spawn()
				aoe.arrow = self
				self.aoe = aoe
			end

			if (tr.Entity:IsPlayer() || tr.Entity:IsNPC() || tr.Entity:IsNextBot() || tr.Entity:IsRagdoll()) then
				sound.Play("MW19_Crossbow.HitBody", tr.HitPos + tr.HitNormal * 5)
				self:Remove()
				return
			end

			sound.Play("MW19_Crossbow.Hit", tr.HitPos)

			parentEntity(tr, self)

			self:FireBullets({
				Attacker = self:GetOwner(),
				Num = 1,
				Tracer = 0,
				Src = tr.HitPos + self:GetAngles():Forward() * 11,
				Dir = tr.Normal * -1,
				IgnoreEntity = self.aoe,
				Damage = 0,
				Callback = function(attacker, trBack, dmgInfo)
					if (trBack.Entity != tr.Entity) then
						--constraint.Weld(tr.Entity, trBack.Entity, tr.PhysicsBone, trBack.PhysicsBone, 0, false)
						local lPos1 = LocalToWorld(tr.HitPos, tr.HitNormal:Angle(), tr.Entity:GetPos(), tr.Entity:GetAngles())
						lPos1 = tr.Entity:GetPhysicsObject():WorldToLocal(tr.HitPos + tr.HitNormal)

						local lPos2 = LocalToWorld(tr.HitPos, tr.HitNormal:Angle(), trBack.Entity:GetPos(), trBack.Entity:GetAngles())

						self.constraint = constraint.Axis(tr.Entity, trBack.Entity, tr.PhysicsBone, trBack.PhysicsBone, lPos1, lPos2, 0, 0, 0, false)
						self:EmitSound("MW19_Crossbow.Skewer")
					end
				end
			})
		end
	})
end

function ENT:GetDamageType()
	return DMG_SNIPER + DMG_SLASH
end

function ENT:OnRemove()
	if (IsValid(self.constraint)) then
		self.constraint:Remove()
	end
end