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

include('shared.lua')
include('schedules.lua')
include('tasks.lua')

////// DONT CHANGE ANYTHING BELOW THIS!!!

ENT.m_fMaxYawSpeed 				= 200
ENT.m_iClass					= CLASS_CITIZEN_REBEL

ENT.Model = "models/buildables/sentry1.mdl"
ENT.MinDistance = 1150

ENT.SpawnRagdollOnDeath = false
ENT.FadeOnDeath = false
ENT.Pain = false
ENT.DeathSound = "weapons/sentry_explode.wav"

AccessorFunc( ENT, "m_iClass", 			"NPCClass" )
AccessorFunc( ENT, "m_fMaxYawSpeed", 	"MaxYawSpeed" )

local schdAttack = ai_schedule.New( "Attack Enemy" ) 
schdAttack:EngTask( "TASK_PLAY_SEQUENCE", ACT_RANGE_ATTACK1 )

local schdReset = ai_schedule.New( "Reset" ) 
schdReset:EngTask( "TASK_RESET_ACTIVITY", 0 ) 

local schdUpgrade = ai_schedule.New( "Upgrade" ) 
schdUpgrade:AddTask( "PlaySequence", { Name = "upgrade", Speed = 1 } )

//local schdBuild = ai_schedule.New( "Build" ) 
//schdBuild:AddTask( "PlaySequence", { Name = "build", Speed = 1 } )

function ENT:Initialize()
	self.table_fear = {}
	self.f_headcrab_table = {}

	self:SetModel( self.Model )

	self:SetHullType( HULL_HUMAN );
	self:SetHullSizeNormal();

	self:SetSolid( SOLID_BBOX )
	self:SetMoveType( MOVETYPE_NONE )

	self:CapabilitiesAdd( CAP_FRIENDLY_DMG_IMMUNE )
	self:SetMaxYawSpeed( 5000 )

	if !self.health then
		self:SetHealth(sk_tf2_sentry_lvl1_health_value)
	end
	self:SetMaxHealth(self:Health())
	self.health = self:Health()
	
	if self.triggertarget and self.triggercondition == "3" then self.starthealth = self:Health() end
	self:SetUpEnemies( false, false, false, false, true )
	
	self.enemyTable_enemies_e = {}
	
	if !self.team then
		if self.owner and ValidEntity(self.owner) then
			self.team = self.owner:Team()
		else
			self.team = 0
		end
	end
	if self.team == 1 then
		self:SetSkin(1)
	end
	
	self:SetSchedule( 1 )
	self.init = true
	
	self.alertsound = CreateSound( self, "weapons/sentry_spot.wav" )
	self.idlesound = CreateSound( self, "weapons/sentry_scan.wav" )
	self.idlesound_lvl2 = CreateSound( self, "weapons/sentry_scan2.wav" )
	self.idlesound_lvl3 = CreateSound( self, "weapons/sentry_scan3.wav" )
	self.idlesound:Play()
	self.turnleft = true
	self.turnup = true
	self.muzzleleft = true
	self.allow_rocket = true
	
	if self.mapentity and !self.ammo then self.infiniteammo = true end
	if !self.level then self.level = 1 end
	if self.level != 3 then self.metalremain = 200; self:SetNetworkedInt( "upgrade", 200 ) else self:SetNetworkedInt( "rockets", 20 ) end
	if self.level == 1 then
		if !self.ammo then self.ammo = 44 end
	elseif self.level == 2 then
		if !self.ammo then self.ammo = 64 end
		self:SetModel( "models/buildables/sentry2.mdl" )
		self:SetHealth(sk_tf2_sentry_lvl2_health_value)
		self:SetMaxHealth(self:Health())
	else
		if !self.ammo then self.ammo = 160 end
		self:SetModel( "models/buildables/sentry3.mdl" )
		self:SetHealth(sk_tf2_sentry_lvl3_health_value)
		self:SetMaxHealth(self:Health())
	end
	self:SetNetworkedInt( "shells", self.ammo )
	self:SetNetworkedInt( "sentrygun_level", self.level )
	self:SetNetworkedInt( "health", self:Health() )
end

function ENT:OnCondition( iCondition )
end

function ENT:SetUpEnemies( NoZombies, allied, trialliance, mil, noply )
	local tbl_combine = { "npc_combine_s", "npc_hunter", "npc_rollermine", "npc_turret_floor", "npc_metropolice", "npc_clawscanner", "npc_cscanner", "npc_manhack" }
	local tbl_headcrabs = { "npc_headcrab", "npc_headcrab_black", "npc_headcrab_poison", "npc_headcrab_fast", "monster_babycrab", "monster_headcrab", "monster_bigmomma" }
	local tbl_zombies = { "npc_fastzombie_torso", "npc_fastzombie",  "npc_poisonzombie", "npc_zombie", "npc_zombie_torso", "npc_zombine", "monster_zombie" }
	local tbl_allies = { "monster_barney", "npc_gman", "monster_gman", "npc_alyx", "npc_barney", "npc_citizen", "npc_vortigaunt", "npc_monk", "npc_breen", "npc_dog", "npc_eli", "npc_fisherman", "monster_scientist", "monster_sitting_scientist", "npc_kleiner", "npc_magnusson", "npc_mossman" }
	local tbl_trialliance = { "monster_alien_controller", "monster_alien_grunt", "monster_alien_slave" }
	local tbl_other = { "npc_antlion", "npc_antlion_worker", "npc_antlionguard", "npc_stalker" }
	local tbl_mil = { "monster_human_grunt", "monster_human_assassin", "monster_sentry", "monster_hwgrunt" }
	local tbl_monster = { "monster_bullchicken", "monster_gargantua", "monster_houndeye", "monster_ichthyosaur", "monster_archer", "monster_panthereye", "monster_snark", "monster_parasite" }
	local player = "player"
	//local tbl_friendly = { "monster_generic", "generic_actor" }

	self.enemyTable = {}
	if !noply and !allied then table.insert( self.enemyTable, player ) end
	for k, v in pairs( tbl_combine ) do table.insert( self.enemyTable, v ) end
	if !allied then
		for k, v in pairs( tbl_allies ) do table.insert( self.enemyTable, v ) end
	end
	if !trialliance then
		for k, v in pairs( tbl_trialliance ) do table.insert( self.enemyTable, v ) end
	end
	if !mil then
		for k, v in pairs( tbl_mil ) do table.insert( self.enemyTable, v ) end
	end
	for k, v in pairs( tbl_other ) do table.insert( self.enemyTable, v ) end
	for k, v in pairs( tbl_monster ) do if v != self:GetClass() then table.insert( self.enemyTable, v ) end end
	if NoZombies then return end
	for k, v in pairs( tbl_headcrabs ) do if v != self:GetClass() then table.insert( self.enemyTable, v ) end end
	for k, v in pairs( tbl_zombies ) do if v != self:GetClass() then table.insert( self.enemyTable, v ) end end
end

function ENT:CreateSounds()
	if self.soundscreated then return end
	self.soundscreated = true
	self.alertsound = CreateSound( self, "weapons/sentry_spot.wav" )
	self.idlesound = CreateSound( self, "weapons/sentry_scan.wav" )
	self.idlesound_lvl2 = CreateSound( self, "weapons/sentry_scan2.wav" )
	self.idlesound_lvl3 = CreateSound( self, "weapons/sentry_scan3.wav" )
end

function ENT:Upgrade()
	if self.level == 3 then return false end
	if self.level == 1 then
		self.upgrading = true
		self:SetModel( "models/buildables/sentry2_heavy.mdl" )
		self:SetHealth(sk_tf2_sentry_lvl2_health_value)
		self:SetMaxHealth(sk_tf2_sentry_lvl2_health_value)
		self.health = sk_tf2_sentry_lvl2_health_value
		self.ammo = 64
	else
		self.upgrading = true
		self:SetModel( "models/buildables/sentry3_heavy.mdl" )
		self:SetHealth(sk_tf2_sentry_lvl3_health_value)
		self:SetMaxHealth(sk_tf2_sentry_lvl3_health_value)
		self.health = sk_tf2_sentry_lvl3_health_value
		self.ammo = 160
	end
	self:SetNetworkedInt( "shells", self.ammo )
	self:SetNetworkedInt( "health", self:Health() )
	local SequenceID = self:LookupSequence( "upgrade" )
	
	self:ResetSequence( SequenceID )
	self:SetNPCState( NPC_STATE_SCRIPT )
	
	self.upgradedelay = CurTime() +self:SequenceDuration()
	//self:StartSchedule(schdUpgrade)
	//self.upgradedelay = CurTime() +self:SequenceDuration()
	return true
end

function ENT:SetUp()
	self:SetModel( "models/buildables/sentry1_heavy.mdl" )
	//self:ResetSequence( self:LookupSequence( "build" ) )
	
	self:SetPlaybackRate( 1 )
	self:RestartGesture(ACT_OBJ_ASSEMBLING)
	
	self.settingup = true
	self.upgradedelay = CurTime() +5
	self.totalseqduration = 5
end

function ENT:SpeedUp()
	self.speedup = false
	/*
	timer.Destroy( tostring(self) .. "_speeduptimer" )
	self:SetPlaybackRate( 1 )
	local buildtime = (self:SequenceDuration() *2 -(self.setupdelay -CurTime())) /2
	self.upgradedelay = CurTime() +(self:SequenceDuration() -buildtime)
	self.totalseqduration = self:SequenceDuration()
	self.speedup = true
	
	local function SpeedDown()
		if !self or !ValidEntity(self) or self.built then return end
		self:SetPlaybackRate( 0.5 )
		local buildtime = (self:SequenceDuration() -(self.upgradedelay -CurTime())) *2
		self.upgradedelay = CurTime() +(self:SequenceDuration() *2 -buildtime)
		self.totalseqduration = self:SequenceDuration() *2
		self.speedup = false
	end
	timer.Create( tostring(self) .. "_speeduptimer", 1, 1, SpeedDown ) */
end

function ENT:PlayIdleSound()
	if self.mapentity then self:CreateSounds() end
	if self.level == 1 then
		self.idlesound:Stop()
		self.idlesound:Play()
	elseif self.level == 2 then
		self.idlesound_lvl2:Stop()
		self.idlesound_lvl2:Play()
	else
		self.idlesound_lvl3:Stop()
		self.idlesound_lvl3:Play()
	end
end

function ENT:Sap()
	self.sapper:SetParent( self )
	self.sapper:Fire( "SetParentAttachment", "build_point_0", 0 )
	self.sapper:SetModel( "models/buildables/sapper_sentry" .. self.level .. ".mdl" )
	
	self.sapped = true
	self:SetNetworkedBool("sapped", true)
	
	if self.owner and ValidEntity( self.owner ) and self.owner:Health() > 0 then
		self.owner:EmitSound( "vo/engineer_autoattackedbyspy03.wav", 100, 100 )
	end
	
	self.idlesound:Stop()
	self.idlesound_lvl2:Stop()
	self.idlesound_lvl3:Stop()
end

function ENT:KeyValue( key, value )
	if( key == "TeamNum" ) then
		if value == "3" then
			self.team = 1
		else
			self.team = 2
		end
		self.built = true
		self.mapentity = true
	end
	if( key == "health" ) then
		self.health = value
	end
	if( key == "defaultupgrade" ) then
		if value != "0" then
			self.level = tonumber(value) +1
		end
	end
	if key == "ammo" then
		if value == "0" then self.infiniteammo = true
		else
			self.ammo = tonumber(value)
		end
	end
	if( key == "spawnflags" ) then
		self.spawnflags = tonumber(value)
		self:CheckSpawnflags()
	end
end

function ENT:CheckSpawnflags()
	local spawnflags = { 131072, 65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }
	for k, v in pairs( spawnflags ) do
		if v <= self.spawnflags and !self.used then
			local value_a = v
			local value_b = self.spawnflags -v
			if table.HasValue( spawnflags, value_a ) then
				self:GetSpawnflag( value_a )
			end
			if self.spawnflags != v then
				if table.HasValue( spawnflags, value_b ) then
					self.used = true
					self:GetSpawnflag( value_b )
				else
					self.spawnflags = value_b
					self.used = false
					self:CheckSpawnflags()
				end
			else
				self.used = true
			end
		end
	end
end

function ENT:GetSpawnflag( value )
	local spawnflags = { 131072, 65536, 32768, 16384, 8192, 4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8, 4, 2, 1 }
	if !table.HasValue( spawnflags, value ) then return false end
	if value == 2 then
		self.invulnerable = true
	end
	if value == 4 then
		self.upgradeable = true
	end
	return true
end

function ENT:CheckEnemy( rel )
	if rel == 1 then
		if self.enemy and ValidEntity( self.enemy ) and self.enemy:Health() > 0 and self:GetPos():Distance(self.enemy:GetPos()) <= self.MinDistance then
			local tracedata = {}
			tracedata.start = self:GetPosCenter()
			tracedata.endpos = self.enemy:GetPosCenter()
			tracedata.filter = self
			local trace = util.TraceLine(tracedata) 
			if ValidEntity(trace.Entity) and (trace.Entity:IsPlayer() or trace.Entity:IsNPC()) then
				return true
			else
				self.enemy = NULL
			end
		elseif self.enemy then
			self.enemy = NULL
		end
		return false
	end
	if self.enemy_fear and ValidEntity( self.enemy_fear ) and self.enemy_fear:Health() > 0 then
			local tracedata = {}
			tracedata.start = self:GetPosCenter()
			tracedata.endpos = self.enemy:GetPosCenter()
			tracedata.filter = self
			local trace = util.TraceLine(tracedata) 
			if !trace.HitWorld then
				return true
			else
				self.enemy_fear = NULL
				return false
			end
	elseif self.enemy_fear then
		self.enemy_fear = NULL
		return false
	end
end

function ENT:Think()
	self:SetPlaybackRate( 0.5 )
	if GetConVarNumber("ai_disabled") == 1 or self.efficient then return end
	if self.sapped then return end
	//self:NextThink( CurTime() +0.01 )
	if self.allow_rocket_delay and CurTime() > self.allow_rocket_delay then
		self.allow_rocket_delay = nil
		self.allow_rocket = true
	end
	
	if self.enemy and self:CheckEnemy( 1 ) then
		local pos_a = self:GetPos()
		local pos_b = self.enemy:GetPosCenter()
		local pos_ang = (pos_b -pos_a):Angle()
		pos_ang.y = pos_ang.y *-1
		if pos_ang.y < 0 then
			pos_ang.y = 360 +pos_ang.y
		end
		
		pos_ang.p = 360 -pos_ang.p
		if pos_ang.p < 0 then
			pos_ang.p = 360 +pos_ang.p
		end
		local ang = self:GetAngles() +pos_ang
		if ang.y > 360 then ang.y = ang.y -360 end
		
		local pitch_sentry = self:GetPoseParameter( "aim_pitch" )
		local negate = false
		if ang.p > 90 then ang.p = (360 -ang.p) *-1 end
		
		//player.GetAll()[1]:PrintMessage( HUD_PRINTTALK, "Sentry pitch = " .. tostring(pitch_sentry) )
		//player.GetAll()[1]:PrintMessage( HUD_PRINTTALK, "Pitch to enemy = " .. tostring(ang.p) )
		
		if self:GetPoseParameter( "aim_pitch" ) -ang.p > 4 or self:GetPoseParameter( "aim_pitch" ) -ang.p < -4 then
			if pitch_sentry < ang.p then
				self:SetPoseParameter( "aim_pitch", self:GetPoseParameter( "aim_pitch" ) +4 )
			elseif pitch_sentry > ang.p then
				self:SetPoseParameter( "aim_pitch", self:GetPoseParameter( "aim_pitch" ) -4 )
			end
		end
		
		local yaw_sentry = self:GetPoseParameter( "aim_yaw" )
		if yaw_sentry < 0 then yaw_sentry = 360 +yaw_sentry end
	
		if yaw_sentry -ang.y > 8 or (yaw_sentry -ang.y < -8 and yaw_sentry -ang.y > -352) then
			local ang = yaw_sentry -ang.y
			if ang < 0 then ang = 360 +ang end
			if ang <= 180 then
				self:SetPoseParameter( "aim_yaw", self:GetPoseParameter( "aim_yaw" ) -8 )
			else
				self:SetPoseParameter( "aim_yaw", self:GetPoseParameter( "aim_yaw" ) +8 )
			end
		end
	end
	
	if !self.settingup and !self:CheckEnemy(1) then
		if self.turnleft then
			self:SetPoseParameter( "aim_yaw", self:GetPoseParameter( "aim_yaw" ) +4 ) 
			if self:GetPoseParameter( "aim_yaw" ) >= 45 then
				self.turnleft = false
				self.turnright = true
				
				self:PlayIdleSound()
				if self.turnup then
					self:SetPoseParameter( "aim_pitch", self:GetPoseParameter( "aim_pitch" ) +4 ) 
				else
					self:SetPoseParameter( "aim_pitch", self:GetPoseParameter( "aim_pitch" ) -4 ) 
				end
				if self:GetPoseParameter( "aim_pitch" ) >= 8 then
					self.turnup = false
					self.turndown = true
				elseif self:GetPoseParameter( "aim_pitch" ) <= -8 then
					self.turnup = true
					self.turndown = false
				end
			end
		else
			self:SetPoseParameter( "aim_yaw", self:GetPoseParameter( "aim_yaw" ) -4 ) 
			if self:GetPoseParameter( "aim_yaw" ) <= -45 then
				self.turnleft = true
				self.turnright = false
				self:PlayIdleSound()
				if self.turnup then
					self:SetPoseParameter( "aim_pitch", self:GetPoseParameter( "aim_pitch" ) +4 ) 
				else
					self:SetPoseParameter( "aim_pitch", self:GetPoseParameter( "aim_pitch" ) -4 ) 
				end
				if self:GetPoseParameter( "aim_pitch" ) >= 8 then
					self.turnup = false
					self.turndown = true
				elseif self:GetPoseParameter( "aim_pitch" ) <= -8 then
					self.turnup = true
					self.turndown = false
				end
			end
		end
	end
	
	if self.upgradedelay then
		if CurTime() > self.upgradedelay then
			self.upgradedelay = nil
			if !self.settingup then
				self.level = self.level +1
				if self.level == 2 then
					self:SetModel( "models/buildables/sentry2.mdl" )
				else
					self:SetModel( "models/buildables/sentry3.mdl" )
					self:SetNetworkedInt( "rockets", 20 )
				end
				self:SetNetworkedInt( "sentrygun_level", self.level )
			else
				self.settingup = false
				self:SetModel( "models/buildables/sentry1.mdl" )
				self.built = true
				self:SetNetworkedBool("building", false)
				if self:Health() >= self:GetMaxHealth() -5 then self:SetHealth(self:GetMaxHealth()); self:SetNetworkedInt("health",self:GetMaxHealth()) end
			end
			self.upgrading = false
		elseif self.settingup then
			self:SetHealth(((self.totalseqduration -(self.upgradedelay -CurTime())) /self.totalseqduration) *self.health)
			self:SetNetworkedInt("health", self:Health())
		end
	end
	
	for k, v in pairs( self.enemyTable ) do
		local enemyTable_enemies = ents.FindByClass( v )
		for k, v in pairs( enemyTable_enemies ) do
			if( !table.HasValue( self.enemyTable_enemies_e, v ) ) then
				table.insert( self.enemyTable_enemies_e, v )
				if( !v:IsPlayer() ) then
					v:AddEntityRelationship( self, 1, 10 )
				end
				self:AddEntityRelationship( v, 1, 10 )
			end
		end
	end
end

function ENT:Explode()
	local gibcount
	if self.level == 1 or self.level == 2 then
		gibcount = 4
	else
		gibcount = 5
	end
	for i = 1,gibcount do
		local gib = ents.Create( "obj_gib" )
		if self.level != 3 then
			gib:SetModel( "models/buildables/Gibs/sentry" .. self.level .. "_Gib" .. i .. ".mdl" )
		elseif i < gibcount then
			gib:SetModel( "models/buildables/Gibs/sentry2_Gib" .. i .. ".mdl" )
		else
			gib:SetModel( "models/buildables/Gibs/sentry3_Gib1.mdl" )
		end
		gib:SetPos( self:GetPos() )
		gib:SetAngles( self:GetAngles() )
		gib:Spawn()
		gib:Activate()
		gib:GetPhysicsObject():ApplyForceCenter( Vector(0,0,500) )
		gib:SetSkin(self:GetSkin())
	end

	self:EmitSound( "weapons/sentry_explode.wav", 100, 100 )
	local explosion = ents.Create( "info_particle_system" )
	explosion:SetKeyValue( "effect_name", "ExplosionCore_buildings" )
	explosion:SetPos( self:GetPos() +self:OBBCenter() ) 
	explosion:SetAngles( self:GetAngles() )
	explosion:Spawn()
	explosion:Activate() 
	explosion:Fire( "Start", "", 0 )
	explosion:Fire( "Kill", "", 0.1 )
	self:Remove()
end

function ENT:CheckAngle(ent)
	local angle = self:GetAngles().y -((ent:GetPos() -self:GetPos()):Angle().y +self:GetPoseParameter("aim_yaw"))
	if angle > 360 then angle = angle -360 end
	if angle < -180 then angle = 360 +angle end
	//player:GetAll()[1]:PrintMessage( HUD_PRINTTALK, "angle.y = " .. angle )
	if angle >= -10 and angle <= 10 then
		return true
	end
	return false
end

function ENT:Attack()
	local function Fire()
		if !self.enemy or !ValidEntity( self.enemy ) or self.enemy:Health() <= 0 or self.enemy:GetPos():Distance( self:GetPos() ) > self.MinDistance then self.attacking = false; return true end
		if self.level == 3 and self.allow_rocket and self:GetNetworkedInt( "rockets" ) > 0 then
			self:FireRocket()
			self.allow_rocket = false
			self.allow_rocket_delay = CurTime() +4
			self:RestartGesture(ACT_RANGE_ATTACK2)
		end
		if self.ammo <= 0 then
			if self.level == 1 then
				self:RestartGesture( ACT_RANGE_ATTACK1 )
			else
				self:RestartGesture( ACT_RANGE_ATTACK1_LOW )
			end
			self:EmitSound( "weapons/sentry_empty.wav", 100, 100 )
			return
		end
		self:RestartGesture(ACT_RANGE_ATTACK1)
		if self.level == 1 then
			self:EmitSound( "weapons/sentry_shoot.wav", 100, 100 )
		elseif self.level == 2 then
			self:EmitSound( "weapons/sentry_shoot2.wav", 100, 100 )
		else
			self:EmitSound( "weapons/sentry_shoot3.wav", 100, 100 )
		end
		local pos_a = self:GetPos()
		local pos_b = self.enemy:GetPosCenter()
		local pos_ang = (pos_b -pos_a):Angle()
		pos_ang.y = pos_ang.y *-1
		pos_ang.p = 360 -pos_ang.p
		local ang = self:GetAngles() +pos_ang
		//self:SetPoseParameter( "aim_yaw", ang.y ) 
		//self:SetPoseParameter( "aim_pitch", ang.p ) 
		local AttachAngPos
		if self.level == 1 then
			AttachAngPos = self:GetAttachment( self:LookupAttachment( "muzzle" ) )
		else
			if self.muzzleleft then
				AttachAngPos = self:GetAttachment( self:LookupAttachment( "muzzle_l" ) )
				self.muzzleleft = false
				self.muzzleright = true
			elseif self.muzzleright then
				AttachAngPos = self:GetAttachment( self:LookupAttachment( "muzzle_r" ) )
				self.muzzleleft = true
				self.muzzleright = false
			end
		end
		local enemy_pos = self.enemy:GetPos()
			
		local npcclass = self.enemy:GetClass()
		if( npcclass == "npc_zombie_torso" or npcclass == "npc_fastzombie_torso" ) then
			enemy_pos.z = enemy_pos.z -42
		end
			
		if( npcclass == "npc_fastzombie" or npcclass == "npc_poisonzombie" or npcclass == "npc_combine_s" ) then
			enemy_pos.z = enemy_pos.z -22
		end
								
		if( npcclass == "npc_clawscanner" or npcclass == "npc_cscanner" or npcclass == "npc_manhack" ) then
			enemy_pos.z = enemy_pos.z -46
		end
								
		if( npcclass == "npc_rollermine" or npcclass == "npc_headcrab" or npcclass == "npc_headcrab_black" or npcclass == "npc_headcrab_poison" or npcclass == "npc_headcrab_fast" or npcclass == "monster_headcrab" ) then
			enemy_pos.z = enemy_pos.z -38
		end
			
		if npcclass == "monster_babycrab" or npcclass == "monster_snark" then
			enemy_pos.z = enemy_pos.z -55
		end
									
		if( npcclass == "monster_houndeye" or npcclass == "monster_bullchicken" or npcclass == "monster_panthereye" ) then
			enemy_pos.z = enemy_pos.z -33
		end
			
		if( self.enemy:IsPlayer() and self.enemy:KeyDown( IN_DUCK ) ) then
			enemy_pos.z = enemy_pos.z -22
		end
			
		if self.owner and ValidEntity(self.owner) then
			self.attack_inflictor = self.owner
		else
			self.killicon_ent = ents.Create( "sent_tf_killicon" )
			self.killicon_ent:SetKeyValue( "classname", "sent_tf_killicon_sentry" )
			self.killicon_ent:Spawn()
			self.killicon_ent:Activate()
			self.killicon_ent:Fire( "kill", "", 0.1 )
			self.attack_inflictor = self.killicon_ent
		end
			
		local enemy_sh_vec = (enemy_pos - self:GetPos()):Normalize()
		bullet = {}
		bullet.Num = 1
		bullet.Src = AttachAngPos["Pos"]
		bullet.Attacker = self.attack_inflictor
		bullet.Dir = enemy_sh_vec
		bullet.Spread = Vector(0.015,0.015,0)
		bullet.Tracer = 1
		if self.level == 1 then
			bullet.Force = 4
		else
			bullet.Force = 8
		end
		bullet.Damage = math.random(13,20) 
		bullet.Callback = function( attacker, tr, dmginfo )
			local victim = tr.Entity
			local dmg = dmginfo:GetDamage()
			if tr.HitGroup == 1 then
				dmg = dmg*10
			elseif tr.HitGroup != 0 then
				dmg = dmg*0.25
			end
			
			if victim:IsNPC() and victim:Health() -dmg <= 0 then
				if self.enemy_memory and table.Count( self.enemy_memory ) > 0 then
					self.enemy_memory_valid = false
					for k, v in pairs( self.enemy_memory ) do
						if ValidEntity( v ) and v != victim and self:Disposition( v ) == 1 then
							self.enemy_memory_valid = true
						end
					end
				else
					self.enemy_memory_valid = false
				end
			end
		end
			
		self:FireBullets(bullet) 
		local muzzle_effect
		if self.level == 1 then
			muzzle_effect = "muzzle_sentry"
		else
			muzzle_effect = "muzzle_sentry2"
		end
		if !self.infiniteammo then self.ammo = self.ammo -1 end
		self:SetNetworkedInt( "shells", self.ammo )
		
		ParticleEffect( muzzle_effect, AttachAngPos["Pos"], AttachAngPos["Ang"], self )
	end
	//if self.level == 1 then Fire() end
	Fire()
	
	local delay
	if self.level == 1 then delay = 0.17 else delay = 0.28 end
	timer.Simple( delay, function() if self and ValidEntity(self) then self.attacking = false; self:SelectSchedule() end end )
end

function ENT:FireRocket()
	self:EmitSound( "weapons/sentry_rocket.wav", 100, 100 )
	local AttachAngPos = self:GetAttachment( self:LookupAttachment( "rocket" ) )
	local rocket = ents.Create( "sentry_rocket" )
	rocket:SetPos( AttachAngPos["Pos"] )
	rocket:SetAngles( AttachAngPos["Ang"] )
	rocket:SetPhysicsAttacker( self )
	local owner
	if self.owner and ValidEntity(self.owner) then
		rocket.sentryowner = self.owner
	end
	rocket.owner = self
	rocket.enemy = self.enemy
	rocket:Spawn()
	rocket:Activate()
	self:SetNetworkedInt( "rockets", self:GetNetworkedInt( "rockets" ) -1 )
end

function ENT:AcceptInput( cvar_name, activator, caller )
	if GetConVarNumber("sv_cheats") == 0 then return end
	if cvar_name == "upgrade" and self.level != 3 then
		self:Upgrade()
	end
end

function ENT:FindInSphere( searchDist )
	local convar_ignoreply = GetConVarNumber("ai_ignoreplayers")
	local closestDist = searchDist
	
	local function GetEnemies( tbl )
		for k, v in pairs( tbl ) do
			if v and ValidEntity( v ) and v != self and ( v:IsNPC() or ( v:IsPlayer() and v:Alive() and convar_ignoreply == 0 and !self.ignoreplys ) ) and v:Health() > 0 then
				local trd = {}
				trd.start = self:GetPos()
				trd.endpos = v:GetPosCenter()
				trd.filter = {self}
				local tr = util.TraceLine(trd)
				if tr.Entity and ValidEntity( tr.Entity ) and tr.Entity == v then
					local d = v:GetPos():Distance(self:GetPos())
					if d < closestDist then
						if( self:Disposition( v ) == 1 or (v:GetClass() == "obj_sentrygun" and v.team != self.team) or (v:IsPlayer() and v:Team() != self.team and (v:Team() == 1 or v:Team() == 2)) ) then
							if ( !self.enemy or !ValidEntity( self.enemy ) ) then
								if self.mapentity then self:CreateSounds() end
								self.alertsound:Stop()
								self.alertsound:Play()
							end
							closestDist = d
							self.enemy = v
						end
					end
				end
			end
		end
	end
	GetEnemies( ents.FindInSphere( self:GetPos(), searchDist ) )
end

/*---------------------------------------------------------
 Name: SelectSchedule
//-------------------------------------------------------*/
function ENT:SelectSchedule()
	if self.efficient or self.upgrading or self.settingup or self.sapped then return end
	local convar_ai = GetConVarNumber("ai_disabled")
	if( !self.attacking and convar_ai == 0 ) then
		if !self.searchdelay then
			self.searchdelay = CurTime() +0.15
		end
		if self.searchdelay < CurTime() then
			self:FindInSphere( self.MinDistance )
			self.searchdelay = nil
		end
		if( self:CheckEnemy( 1 ) and self:CheckAngle(self.enemy) ) then
			self.attacking = true
			self.idle = 0
			self:Attack()
		end
		self:SetEnemy( NULL )
	end
end 


function ENT:DamageLevel(dmglvl)
	if self.dmgparticle and self.dmgparticle.lvl == dmglvl then return false end
	if self.dmgparticle and self.dmgparticle.lvl > dmglvl then self.dmgparticle:Remove(); self.dmgparticle = nil; return false end
	if self.dmgparticle and ValidEntity(self.dmgparticle) then self.dmgparticle:Remove(); self.dmgparticle = nil end
	self.dmgparticle = ents.Create( "info_particle_system" )
	self.dmgparticle:SetKeyValue( "effect_name", "sentrydamage_" .. dmglvl )
	self.dmgparticle:SetKeyValue( "start_active", "1" )
	self.dmgparticle:SetPos( self:GetPos() )
	self.dmgparticle:SetParent( self )
	self.dmgparticle:Spawn()
	self.dmgparticle:Activate()
	if self.level == 3 then self.dmgparticle:Fire( "SetParentAttachment", "sentrydamage", 0 ) end
	self.dmgparticle.lvl = dmglvl
	return true
end

function ENT:CheckDamageLevel()
	if self:Health() == self:GetMaxHealth() then self:DamageLevel(0); return end
	if self:Health() <= self:GetMaxHealth() -(self:GetMaxHealth() *0.25) and self:Health() > self:GetMaxHealth() -(self:GetMaxHealth() *0.5) then
		self:DamageLevel(1)
	elseif self:Health() <= self:GetMaxHealth() -(self:GetMaxHealth() *0.5) and self:Health() > self:GetMaxHealth() -(self:GetMaxHealth() *0.75) then
		self:DamageLevel(2)
	elseif self:Health() <= self:GetMaxHealth() -(self:GetMaxHealth() *0.75) and self:Health() > self:GetMaxHealth() -(self:GetMaxHealth() *0.85) then
		self:DamageLevel(3)
	elseif self:Health() <= self:GetMaxHealth() -(self:GetMaxHealth() *0.85) then
		self:DamageLevel(4)
	end
end

function ENT:CheckDamageLevel()
	if self:Health() == self:GetMaxHealth() then self:DamageLevel(0); return end
	if self:Health() <= self:GetMaxHealth() -(self:GetMaxHealth() *0.25) and self:Health() > self:GetMaxHealth() -(self:GetMaxHealth() *0.5) then
		self:DamageLevel(1)
	elseif self:Health() <= self:GetMaxHealth() -(self:GetMaxHealth() *0.5) and self:Health() > self:GetMaxHealth() -(self:GetMaxHealth() *0.75) then
		self:DamageLevel(2)
	elseif self:Health() <= self:GetMaxHealth() -(self:GetMaxHealth() *0.75) and self:Health() > self:GetMaxHealth() -(self:GetMaxHealth() *0.85) then
		self:DamageLevel(3)
	elseif self:Health() <= self:GetMaxHealth() -(self:GetMaxHealth() *0.85) then
		self:DamageLevel(4)
	end
end

/*---------------------------------------------------------
   Name: OnTakeDamage
   Desc: Entity takes damage
---------------------------------------------------------*/
function ENT:OnTakeDamage(dmg)
	if self.dead or self.invulnerable then return end
	local attacker = dmg:GetAttacker()
	if attacker and ValidEntity(attacker) and attacker:IsPlayer() and attacker:Team() == self.team and self.team != 1001 then return end
	if self.ScaleDmg then dmg:ScaleDamage(self.ScaleDmg); gamemode.Call( "ScaleNPCDamage", self, 1, dmg ) end
	if self.owner and self:Health() == self:GetMaxHealth() and self:Health() -dmg:GetDamage() > 0 then self.owner:ConCommand("playgamesound misc/hud_warning.wav"); timer.Simple(2, function() if self and ValidEntity(self) and self:Health() > 0 and self:Health() < self:GetMaxHealth() and self.owner and ValidEntity(self.owner) then self.owner:ConCommand("playgamesound misc/hud_warning.wav") end end ) end
	self:SetHealth(self:Health() - dmg:GetDamage())
	self:CheckDamageLevel()
	self:SetNetworkedInt( "health", self:Health() )
	if self.triggertarget and self.triggercondition == "2" then
		self:GotTriggerCondition()
	elseif self.starthealth and self:Health() <= (self.starthealth /2) then
		self:GotTriggerCondition()
	end

	local damage = dmg:GetDamage()
	local inflictor = dmg:GetInflictor()
	local attacker = dmg:GetAttacker()

	if ( self:Health() <= 0 and !self.dead ) then //run on death
		self.dead = true
		if self.triggertarget and self.triggercondition == "4" then self:GotTriggerCondition() end
		if self.DeathSkin then self:SetSkin( self.DeathSkin ) end
		gamemode.Call( "OnNPCKilled", self, attacker, inflictor )
		if self.DeathSound then
			if string.find( self.DeathSound, ".wav" ) then
				self:EmitSound( self.DeathSound, 500, 100)
			else
				self:EmitSound( self.DeathSound ..math.random(1,self.DeathSoundCount).. ".wav", 500, 100)
			end
		end

		if attacker:IsPlayer() then
			attacker:AddFrags( 1 )
		end
		
		if( attacker:GetClass() != "npc_barnacle" and !dmg:IsDamageType( DMG_DISSOLVE ) ) then
			if self.SpawnRagdollOnDeath then self:SpawnRagdoll( dmg:GetDamageForce() ) end
			if self.WaterMonster and !self.SpawnRagdollOnDeath then self:DeathFloat() end
			if self.drophealthkit then self:DropHealthkit() end
			if self.PrintDeathDecal then self:SetPos( Vector( self:GetPos().x, self:GetPos().y, self:GetPos().z +4 ) ); self:SpawnBloodDecal( "YellowBlood", self ) end
			self:SetNPCState( NPC_STATE_DEAD )
			self:Explode()
		elseif( dmg:IsDamageType( DMG_DISSOLVE ) ) then
			self:SetNPCState( NPC_STATE_DEAD )
			self:SetSchedule( SCHED_DIE_RAGDOLL )
		end
	end
end

/*---------------------------------------------------------
Name: OnRemove
Desc: Called just before entity is deleted
//-------------------------------------------------------*/
function ENT:OnRemove()
	if self.owner and ValidEntity(self.owner) then self.owner.sentry = nil end
	if self.dmgparticle and ValidEntity(self.dmgparticle) then self.dmgparticle:Remove() end
	
	if self.owner and ValidEntity(self.owner) then self.owner:SetNetworkedEntity( "sentrygun", NULL ) end
	timer.Destroy( "self.enemy_occluded_timer" .. self:EntIndex() )
	timer.Destroy( "damage_count_reset_timer" .. self.Entity:EntIndex( ) )
	timer.Destroy( "self.alert_allow_timer" .. self:EntIndex() )
end

/*---------------------------------------------------------
   Name: Use
---------------------------------------------------------*/
function ENT:Use( activator, caller, type, value )
end


/*---------------------------------------------------------
   Name: StartTouch
---------------------------------------------------------*/
function ENT:StartTouch( entity )
end


/*---------------------------------------------------------
   Name: EndTouch
---------------------------------------------------------*/
function ENT:EndTouch( entity )
end


/*---------------------------------------------------------
   Name: Touch
---------------------------------------------------------*/
function ENT:Touch( entity )
end

/*---------------------------------------------------------
   Name: GetRelationship
		Return the relationship between this NPC and the 
		passed entity. If you don't return anything then
		the default disposition will be used.
---------------------------------------------------------*/
function ENT:GetRelationship( entity )
	//return D_NU;
end

/*---------------------------------------------------------
   Name: ExpressionFinished
		Called when an expression has finished. Duh.
---------------------------------------------------------*/
function ENT:ExpressionFinished( strExp )
end

/*---------------------------------------------------------
   Name: GetAttackSpread
		How good is the NPC with this weapon? Return the number
		of degrees of inaccuracy for the NPC to use.
---------------------------------------------------------*/
function ENT:GetAttackSpread( Weapon, Target )
	return 0.1
end
