--[[
| This file was obtained through the combined efforts
| of Madbluntz & Plymouth Antiquarian Society.
|
| Credits: lifestorm, Gregory Wayne Rossel JR.,
| 	Maloy, DrPepper10 @ RIP, Atle!
|
| Visit for more: https://plymouth.thetwilightzone.ru/
--]]

ENT.Base 			= "base_entity"
ENT.Type 			= "anim"
ENT.PrintName 		= "NPC Controller Base"
ENT.Author 			= "DrVrej"
ENT.Contact 		= "http://steamcommunity.com/groups/vrejgaming"
ENT.Purpose 		= "To make my (S)NPCs controllable."
ENT.Instructions 	= "Don't change anything."
ENT.Category		= "VJ Base"

ENT.Spawnable 		= false
ENT.AdminSpawnable 	= false
---------------------------------------------------------------------------------------------------------------------------------------------
if CLIENT then
	function ENT:Draw() end
	
	local vec0 = Vector(0, 0, 0)
	local vec1 = Vector(1, 1, 1)
	local viewLerpVec = Vector(0, 0, 0)
	local viewLerpAng = Angle(0, 0, 0)
	---------------------------------------------------------------------------------------------------------------------------------------------
	hook.Add("CalcView", "VJ_MyCalcView", function(ply, origin, angles, fov)
		if !ply.IsControlingNPC then return end
		local camera = ply.VJCE_Camera -- Camera entity
		local npc = ply.VJCE_NPC -- The NPC that's being controlled
		if !IsValid(ply.VJCE_Camera) or !IsValid(ply.VJCE_NPC) then return end
		if IsValid(ply:GetViewEntity()) && ply:GetViewEntity():GetClass() == "gmod_cameraprop" then return end
		local cameraMode = ply.VJC_Camera_Mode

		local pos = origin -- The position that will be set
		local ang = ply:EyeAngles()
		if cameraMode == 2 then -- First person
			local setPos = npc:EyePos() + npc:GetForward()*20
			local offset = ply.VJC_FP_Offset
			//camera:SetLocalPos(camera:GetLocalPos() + ply.VJC_TP_Offset) -- Help keep the camera stable
			if ply.VJC_FP_Bone != -1 then -- If the bone does exist, then use the bone position
				local bonePos, boneAng = npc:GetBonePosition(ply.VJC_FP_Bone)
				setPos = bonePos
				if ply.VJC_FP_CameraBoneAng > 0 then
					ang[3] = boneAng[ply.VJC_FP_CameraBoneAng] + ply.VJC_FP_CameraBoneAng_Offset
				end
				if ply.VJC_FP_ShrinkBone then
					npc:ManipulateBoneScale(ply.VJC_FP_Bone, vec0) -- Bone manipulate to make it easier to see
				end
			end
			pos = setPos + (npc:GetForward()*offset.x + npc:GetRight()*offset.y + npc:GetUp()*offset.z)
		else -- Third person
			if ply.VJC_FP_Bone != -1 then -- Reset the NPC's bone manipulation!
				ply.VJCE_NPC:ManipulateBoneScale(ply.VJC_FP_Bone, vec1)
			end
			local offset = ply.VJC_TP_Offset + Vector(0, 0, npc:OBBMaxs().z - npc:OBBMins().z) // + vectp
			//camera:SetLocalPos(camera:GetLocalPos() + ply.VJC_TP_Offset) -- Help keep the camera stable
			local tr = util.TraceHull({
				start = npc:GetPos() + npc:OBBCenter(),
				endpos = npc:GetPos() + npc:OBBCenter() + angles:Forward()*-camera.Zoom + (npc:GetForward()*offset.x + npc:GetRight()*offset.y + npc:GetUp()*offset.z),
				filter = {ply, camera, npc},
				mins = Vector(-5, -5, -5),
				maxs = Vector(5, 5, 5),
				mask = MASK_SHOT,
			})
			pos = tr.HitPos + tr.HitNormal*2
		end

		if npc.Controller_CalcView then -- Allows custom calcview overrides
			local data = npc:Controller_CalcView(ply, pos, ang, fov, origin, angles, cameraMode)
			if data then
				pos = data.origin or pos
				ang = data.angles or ang
				fov = data.fov or fov
			end
		end
		
		-- Lerp the position and the angle
		local lerpSpeed = ply:GetInfoNum("vj_npc_cont_cam_speed", 6)
		viewLerpVec = (cameraMode == 2 and pos) or LerpVector(FrameTime()*lerpSpeed, viewLerpVec, pos)
        viewLerpAng = LerpAngle(FrameTime()*lerpSpeed, viewLerpAng, ang)
		
		-- Send the player's hit position to the controller entity
		local tr = util.TraceLine({start = viewLerpVec, endpos = viewLerpVec + viewLerpAng:Forward()*32768, filter = {ply, camera, npc}})
		//ParticleEffect("vj_impact1_centaurspit", tr.HitPos, Angle(0,0,0), npc)
		net.Start("vj_controller_cldata")
			net.WriteVector(tr.HitPos)
		net.SendToServer()
		
		local view = {
			origin = viewLerpVec,
			angles = viewLerpAng,
			fov = fov,
			drawviewer = false, //(cameraMode == 2 and true) or false
		}
		return view
	end)
	---------------------------------------------------------------------------------------------------------------------------------------------
	hook.Add("PlayerBindPress", "vj_controller_PlayerBindPress", function(ply, bind, pressed)
		-- For scroll wheel zooming
		if (bind == "invprev" or bind == "invnext") && ply.IsControlingNPC && IsValid(ply.VJCE_Camera) && ply.VJC_Camera_Mode != 2 then
			ply.VJCE_Camera.Zoom = ply.VJCE_Camera.Zoom or 100
			if bind == "invprev" then
				ply.VJCE_Camera.Zoom = math.Clamp(ply.VJCE_Camera.Zoom - ply:GetInfoNum("vj_npc_cont_cam_zoomspeed", 10), 0, 500)
			else
				ply.VJCE_Camera.Zoom = math.Clamp(ply.VJCE_Camera.Zoom + ply:GetInfoNum("vj_npc_cont_cam_zoomspeed", 10), 0, 500)
			end
		end
	end)
	---------------------------------------------------------------------------------------------------------------------------------------------
	net.Receive("vj_controller_data", function(len)
		//print("Data Sent!")
		//print(len)
		local ply = LocalPlayer()
		ply.IsControlingNPC = net.ReadBool()
		ply.VJCE_Camera = ents.GetByIndex(net.ReadUInt(14))
		ply.VJCE_Camera.Zoom = ply.VJCE_Camera.Zoom or 100
		-- If the controller has stopped then reset the NPC's bone manipulation!
		if ply.IsControlingNPC == false && IsValid(ply.VJCE_NPC) && ply.VJC_FP_Bone != -1 then
			ply.VJCE_NPC:ManipulateBoneScale(ply.VJC_FP_Bone, vec1)
		end
		ply.VJCE_NPC = ents.GetByIndex(net.ReadUInt(14))
		ply.VJC_Camera_Mode = net.ReadUInt(2)
		ply.VJC_TP_Offset = net.ReadVector()
		ply.VJC_FP_Offset = net.ReadVector()
		ply.VJC_FP_Bone = net.ReadInt(10)
		ply.VJC_FP_ShrinkBone = net.ReadBool()
		ply.VJC_FP_CameraBoneAng = net.ReadUInt(2) or 0
		ply.VJC_FP_CameraBoneAng_Offset = net.ReadInt(10) or 0
	end)
	---------------------------------------------------------------------------------------------------------------------------------------------
	local lerp_hp = 0
	local atk_col_red = Color(255, 60, 60, 255)
	local atk_col_orange = Color(204, 123, 60, 255)
	local atk_col_green = Color(60, 220, 60, 255)
	local mat_icon_melee = Material("vj_base/hud_controller/melee.png")
	local mat_icon_range = Material("vj_base/hud_controller/range.png")
	local mat_icon_leap = Material("vj_base/hud_controller/leap.png")
	local mat_icon_grenade = Material("vj_base/hud_controller/grenade.png")
	local mat_icon_gun = Material("vj_base/hud_controller/gun.png")
	local mat_icon_camera = Material("vj_base/hud_controller/camera.png")
	local mat_icon_zoom = Material("vj_base/hud_controller/camera_zoom.png")
	net.Receive("vj_controller_hud", function(len)
		//print(len)
		local enabled = net.ReadBool()
		local maxhp = net.ReadFloat()
		local hp = net.ReadFloat()
		local name = net.ReadString()
		local AtkTbl = net.ReadTable()
		local ply = LocalPlayer()
		hook.Add("HUDPaint", "vj_controller_HUD", function()
			draw.RoundedBox(1, ScrW() / 2.25, ScrH()-120, 220, 100, Color(0, 0, 0, 150))
			draw.SimpleText(name, "VJFont_Trebuchet24_SmallMedium", ScrW() / 2.21, ScrH()-115, Color(255,255,255,255), 0, 0)
			
			local hp_r = 255
			local hp_g = 153
			local hp_b = 0
			lerp_hp = Lerp(5*FrameTime(),lerp_hp,hp)
			draw.RoundedBox(0, ScrW() / 2.21, ScrH()-95, 180, 20, Color(hp_r,hp_g,hp_b,40))
			draw.RoundedBox(0, ScrW() / 2.21, ScrH()-95, (190*math.Clamp(lerp_hp,0,maxhp))/maxhp,20, Color(hp_r,hp_g,hp_b,255))
			surface.SetDrawColor(hp_r,hp_g,hp_b,255)
			surface.DrawOutlinedRect( ScrW() / 2.21, ScrH()-95,180,20)
			
			local finalhp = tostring(string.format("%.0f", lerp_hp).."/"..maxhp)
			local distlen = string.len(finalhp)
			local move = 0
			if distlen > 1 then
				move = move - (0.009*(distlen-1))
			end
			draw.SimpleText(finalhp, "VJFont_Trebuchet24_SmallMedium", ScrW() / (2-move), ScrH()-94, Color(255,255,255,255), 0, 0)
			
			-- Attack Icons
			surface.SetMaterial(mat_icon_melee)
			surface.SetDrawColor((AtkTbl["MeleeAttack"] == false and atk_col_red) or ((AtkTbl["MeleeAttack"] == 2 and atk_col_orange) or atk_col_green))
			surface.DrawTexturedRect(ScrW() / 2.21, ScrH()-73, 28, 28)
			
			surface.SetMaterial(mat_icon_range)
			surface.SetDrawColor((AtkTbl["RangeAttack"] == false and atk_col_red) or ((AtkTbl["RangeAttack"] == 2 and atk_col_orange) or atk_col_green))
			surface.DrawTexturedRect(ScrW() / 2.14, ScrH()-73, 28, 28)
			
			surface.SetMaterial(mat_icon_leap)
			surface.SetDrawColor((AtkTbl["LeapAttack"] == false and atk_col_red) or ((AtkTbl["LeapAttack"] == 2 and atk_col_orange) or atk_col_green))
			surface.DrawTexturedRect(ScrW() / 2.065, ScrH()-73, 28, 28)
			
			surface.SetMaterial(mat_icon_grenade)
			surface.SetDrawColor((AtkTbl["GrenadeAttack"] == false and atk_col_red) or ((AtkTbl["GrenadeAttack"] == 2 and atk_col_orange) or atk_col_green))
			surface.DrawTexturedRect(ScrW() / 2.005, ScrH()-73, 28, 28)
			
			surface.SetMaterial(mat_icon_gun)
			surface.SetDrawColor((AtkTbl["WeaponAttack"] != true and atk_col_red) or ((AtkTbl["Ammo"] <= 0 and atk_col_orange) or atk_col_green))
			surface.DrawTexturedRect(ScrW() / 1.94, ScrH()-73, 28, 28) // 1.865
			draw.SimpleText(AtkTbl["Ammo"], "VJFont_Trebuchet24_Medium", ScrW() / 1.885, ScrH()-70, (AtkTbl["WeaponAttack"] != true and atk_col_red) or ((AtkTbl["Ammo"] <= 0 and atk_col_orange) or atk_col_green), 0, 0)
			
			-- Camera
			surface.SetMaterial(mat_icon_camera)
			surface.SetDrawColor(Color(255, 255, 255, 255))
			surface.DrawTexturedRect(ScrW() / 2.21, ScrH()-45, 22, 22)
			draw.SimpleText((ply.VJC_Camera_Mode == 1 and "Third") or "First", "VJFont_Trebuchet24_SmallMedium", ScrW() / 2.155, ScrH()-43, Color(255, 255, 255, 255), 0, 0) // VJFont_Trebuchet24_SmallMedium
			
			-- Zoom Camera
			surface.SetMaterial(mat_icon_zoom)
			surface.SetDrawColor(Color(255, 255, 255, 255))
			surface.DrawTexturedRect(ScrW() / 2.065, ScrH()-45, 22, 22)
			draw.SimpleText(ply.VJCE_Camera.Zoom, "VJFont_Trebuchet24_Medium", ScrW() / 2.005, ScrH()-45, Color(255, 255, 255, 255), 0, 0) // VJFont_Trebuchet24_SmallMedium
		end)
		if enabled != true then hook.Remove("HUDPaint", "vj_controller_HUD") end
	end)
end