

local meta = FindMetaTable( "Player" );

--We have stat tables, so instead of calculating new strength or speed in real time,  
--we have a pre-calculated table.  Saves some CPU cycles.
TS.WalkSpeedTable = { }
TS.RunSpeedTable = { }
TS.DuckSpeedTable = { }
TS.SprintDegradeTable = { }
TS.SprintFillTable = { }
TS.JumpDegradeTable = { }
for n = 0, 100 do

	if( n <= 5 ) then

		TS.WalkSpeedTable[n] = 85;
		TS.RunSpeedTable[n] = 180;
		TS.DuckSpeedTable[n] = 180;
		
		TS.JumpDegradeTable[n] = 30;
		TS.SprintDegradeTable[n] = 9;
		TS.SprintFillTable[n] = 3;
		 
	elseif( n <= 15 ) then
	
		TS.WalkSpeedTable[n] = 90;
		TS.RunSpeedTable[n] = 195;
		TS.DuckSpeedTable[n] = 180;		
		
		TS.JumpDegradeTable[n] = 20;
		TS.SprintDegradeTable[n] = 11;
		TS.SprintFillTable[n] = 4;
		
	elseif( n <= 25 ) then
	
		TS.WalkSpeedTable[n] = 96;
		TS.RunSpeedTable[n] = 210;
		TS.DuckSpeedTable[n] = 180;	
	
		TS.JumpDegradeTable[n] = 15;
		TS.SprintDegradeTable[n] = 8;
		TS.SprintFillTable[n] = 5;
	
	elseif( n <= 35 ) then
	
		TS.WalkSpeedTable[n] = 100;
		TS.RunSpeedTable[n] = 240;
		TS.DuckSpeedTable[n] = 190;	
		
		TS.JumpDegradeTable[n] = 8;
		TS.SprintDegradeTable[n] = 6;
		TS.SprintFillTable[n] = 5;
	
	elseif( n <= 45 ) then
	
		TS.WalkSpeedTable[n] = 105;
		TS.RunSpeedTable[n] = 260;
		TS.DuckSpeedTable[n] = 190;	
		
		TS.JumpDegradeTable[n] = 6;
		TS.SprintDegradeTable[n] = 4;
		TS.SprintFillTable[n] = 6;
	
	elseif( n <= 55 ) then
	
		TS.WalkSpeedTable[n] = 115;
		TS.RunSpeedTable[n] = 280;
		TS.DuckSpeedTable[n] = 190;	
		
		TS.JumpDegradeTable[n] = 5;
		TS.SprintDegradeTable[n] = 3;
		TS.SprintFillTable[n] = 6;
	
	elseif( n <= 65 ) then
	
		TS.WalkSpeedTable[n] = 117;
		TS.RunSpeedTable[n] = 305;
		TS.DuckSpeedTable[n] = 195;	
		
		TS.JumpDegradeTable[n] = 4;
		TS.SprintDegradeTable[n] = 2;
		TS.SprintFillTable[n] = 7;
		
	else

		TS.WalkSpeedTable[n] = 120;
		TS.RunSpeedTable[n] = 320;
		TS.DuckSpeedTable[n] = 195;	
		
		TS.JumpDegradeTable[n] = 4;
		TS.SprintDegradeTable[n] = 2;
		TS.SprintFillTable[n] = 8;
	
	end
	
end

function meta:CreateLootableRagdoll()

	local ent = ents.Create( "prop_ragdoll" )
	
	ent:SetModel( self:GetModel() )
	ent:SetAngles( self:EyeAngles() )
	ent:SetPos( self:GetPos() )
	ent:Spawn()
	ent.inventory = {}
	
	ent:SetNWBool( "HasBeenIdentified", false )
	ent:SetNWString( "PlayerName", self:GetRPName() )
	ent:SetNWInt( "IsLootable", 1 )
	ent:SetNWInt( "IsLootableRagdoll", 1 )
	
	for i, v in pairs( self:GetTable().InventoryGrid ) do
	
		for x, c in pairs( v ) do
		
			for y, u in pairs( c ) do
				
				if( self:GetTable().InventoryGrid[i][x][y].ItemData ) then
				
					local id = self:GetTable().InventoryGrid[i][x][y].ItemData.ID
				
					table.insert( ent.inventory, id ) 
				
				end

			end

		end

	end
	
end

function meta:CanUseCommand()

	if( self:IsAdmin() ) then
		return true
	end	
	
	return false
	
end	

function meta:IsSuperAdmin()

	return self:IsUserGroup( "superadmin" )
	
end	

function meta:IsAdmin()

	return ( self:IsUserGroup( "superadmin" ) or self:IsUserGroup( "admin" ) or self:IsSuperAdmin() );

end

--Set the value for a player's serverside variable
function meta:SetField( name, val )

	self:GetTable()[name] = val;

end

--Get the value for a player's serverside variable
function meta:GetField( name )
	
	if( name != nil) then
		return self:GetTable()[name];
	end
end

function meta:CreateNewCharMenu()

	umsg.Start( "CCM", self ) 
	umsg.End()
		
	self:CallEvent( "PlayerMenuOff" );
	self:CallEvent( "MapViewOn" );
	self:Lock();
	self:DrawViewModel( false );
	
	timer.Simple( 0.2, function()
	
		self:RefreshShit()

	end )
	
end	

--Brings up character menu
function meta:PromptCharacterMenu()
		
	umsg.Start( "RAM", self ) umsg.End() --end account creation
	umsg.Start( "PCM", self ) umsg.End() --start 

end

function meta:PromptAccountMenu()

	local select = sql.Query( "SELECT * FROM `STALKERUsers` WHERE `SteamID` = '" .. self:SteamID() .. "'" )
	
	if( !select ) then

		umsg.Start( "ACM", self );		
			umsg.Bool( self:GetTable().Initialized );
		umsg.End();
		
		self:CallEvent( "MapViewOn" );	
		
	else

		self:PromptCharacterMenu()
		
	end	
	
end	

function meta:PromptCharacterCreateMenu()

	umsg.Start( "CCM", self );
	umsg.End();
	
	print( self:Nick() .. ": Prompting character create menu...")

end

function meta:RemoveCharacterCreateMenu()

	umsg.Start( "RCM", self );
	umsg.End();

end

function meta:RemoveCharacterMenu()

	umsg.Start( "RCM", self );
	umsg.End();

end

--Prompt MOTD
function meta:PromptMOTD()

	self:SendMOTD();

	umsg.Start( "MOTD", self );
	umsg.End();
	
	self:GetTable().HasSeenMOTD = true;

end


--Will set player's movement speeds based off the player stats/situation
function meta:ComputePlayerSpeeds()

	local spd = self:GetPlayerSpeed();
	local mul = 1;
	
	local max = 9999;
	
	if( self:GetTable().SlowDownForHit ) then
	
		mul = .45;
		max = 40;
	
	end
	
	if( self:GetTable().StatusIsInjured ) then
	
		--mul = .85;
		max = 82;
	
	end
	
	if( self:IsTied() ) then
	
		--mul = .85;
		max = 100;
	
	end
	
	self:SetWalkSpeed( math.Clamp( TS.WalkSpeedTable[spd] * mul, 0, max ) );
	self:SetCrouchedWalkSpeed( math.Clamp( TS.DuckSpeedTable[spd] * mul, 0, max ) );
	
	if( not self:GetTable().StatusIsInjured or not self:IsTied() ) then 
	
		if( self:GetPlayerSprint() <= 0 ) then
			self:SetRunSpeed( math.Clamp( TS.RunSpeedTable[spd] * .5 * mul, TS.WalkSpeedTable[spd], 9999 ) );
		else
			self:SetRunSpeed( math.Clamp( TS.RunSpeedTable[spd] * mul, 0, max ) );
		end
	
	else
	
		self:SetWalkSpeed( math.Clamp( TS.WalkSpeedTable[spd] * mul, 0, max ) );
		self:SetRunSpeed( math.Clamp( TS.RunSpeedTable[spd] * mul, 0, max ) );
	
	end


end

function meta:ResetBodyDamage()

	self:GetTable().HelmetHealth = 0;
	self:GetTable().BodyArmorHealth = 0;
	self:GetTable().SuperResistantArmor = false;
	self:GetTable().SuperResistantHelmet = false;
	
	self:GetTable().StatusIsInjured = false;
	self:GetTable().StatusIsCrippled = false;
	
	self:SetPlayerCanSprint( true );
	
end

function meta:ResetBleeding()

	self:SetPlayerBleeding( false );
	self:GetTable().LastBleedDecal = 0;
	self:GetTable().LastBleedDmg = 0; 
	self:GetTable().BleedingDmgPerSec = 0; 
	self:GetTable().BleedingIncDmgPerSec = 0; 
	self:GetTable().BleedingHealthCatcher = 0;

end

function meta:SendItemData( id )

	if( self:GetTable().ItemsDownloaded[id] ) then return; end

	local data = TS.ItemsData[id];
	
	if( not id ) then return; end

	umsg.Start( "RID", self );
		umsg.String( id .. ":" .. data.Name );
		umsg.String( data.Model or "" );
		umsg.Vector( data.LookAt or Vector( 0, 0, 0 ) );
		umsg.Vector( data.CamPos or Vector( 0, 0, 0 ) );
		umsg.Short( data.FOV or 0 );
		umsg.Short( data.Width or 1 );
		umsg.Short( data.Height or 1 );
		umsg.String( data.Flags );
		umsg.Short( data.Price or 0 );
		umsg.String( data.ContainsModel or "" );
	umsg.End();
	
	if( data.Description ) then
	
		local pieceamount = math.ceil( string.len( data.Description ) / 200 );
	
		for n = 1, pieceamount do
	
			local function rdp()
	
				umsg.Start( "RDP", self );
					umsg.String( id );
					umsg.String( string.sub( data.Description, ( n - 1 ) * 200 + 1, ( n - 1 ) * 200 + 200 ) );
				umsg.End();
				
			end
			timer.Simple( n * .2, rdp );
		
		end
		
	end
	
	self:GetTable().ItemsDownloaded[id] = { }

end

function meta:CanAfford( amt )

	if( amt < 0 ) then
		return false;
	end
	
	if( self:GetPlayerRubles() - amt < 0 ) then 
		return false;
	end

	return true;

end

function meta:GiveMoney( amt ) 

	if( amt < 0 ) then return; end
	
	self:SetPlayerRubles( self:GetPlayerRubles() + amt );
	self:SaveData( "Tokens", self:GetPlayerRubles() );
	--self:SaveCharacter();

end

function meta:TakeMoney( amt )

	if( amt < 0 ) then return; end
	
	--self:SetPlayerMoney( math.Clamp( self:GetPlayerMoney() - amt, 0, self:GetPlayerMoney() ) );
	self:SetPlayerRubles( self:GetPlayerRubles() - amt );
	self:SaveData( "Tokens", self:GetPlayerRubles() );
	--self:SaveCharacter();

end


function meta:GiveHealth( amt )

	self:SetHealth( math.Clamp( self:Health() + amt, 0, self:GetTable().MaxHealth ) );

end

function meta:TakeHealth( amt )

	self:SetHealth( math.Clamp( self:Health() - amt, 0, self:GetTable().MaxHealth ) );

	if( self:Health() <= 0 ) then
	
		self:Die();
	
	end	

end

function meta:Die()

	TS.PrintMessageAll( 2, self:GetRPName() .. " died" );
	self:GetTable().ForceDeath = true;
	self:GetTable().BypassUnconscious = true;
	self:Kill();

end

function meta:IsAwesome()

	--Waffler STEAM_0:1:17476605

	if( self:SteamID() == "STEAM_0:1:17476605"  ) then
		return true
	end

	return false
	
end	

function meta:SendMOTD()

	umsg.Start( "RMOTD", self ); umsg.End();

	local parts = math.ceil( string.len( TS.MOTD ) / 240 );
	
	local delay = 0;
	
	for n = 1, parts do
	
		local function smp()
	
			umsg.Start( "SMP", self );
				umsg.String( string.sub( TS.MOTD, ( n - 1 ) * 240 + 1, n * 240 ) );
			umsg.End();
	
		end
		timer.Simple( delay, smp );
		
		delay = delay + .2;
	
	end

end

function meta:ConsoleLog( text )

	umsg.Start( "MiscCon", self );
		umsg.String( text );
	umsg.End();

end

function meta:SnapOutOfStance()

	if( self:GetTable().StanceLean or self:GetTable().StanceGroundSit or 
		self:GetTable().StanceSit ) then
		--self:Freeze( false );
	end

	self:SetPlayerFreezeHead( false );
	self:GetTable().InStanceAction = false;
	self:GetTable().StanceLean = false;	
	self:GetTable().StanceGroundSit = false;
	self:GetTable().StanceSit = false;
	self:GetTable().StanceATW = false;
	self:GetTable().ForcedAnimationMode = false;
	self:GetTable().StanceSnapToPlayerPos = nil;

end

function meta:RemoveHandPickUp()

	self:GetTable().HandPickUpSent:Remove();
	self:GetTable().HandPickUpSent = nil;

end

function meta:HandPickUp( ent, bone )

	if( self:GetTable().HandPickUpSent and self:GetTable().HandPickUpSent:IsValid() ) then
		self:RemoveHandPickUp();
	end

	self:GetTable().HandPickUpSent = ents.Create( "ts2_pickup" );
	self:GetTable().HandPickUpSent:SetPos( ent:GetPos() );
	self:GetTable().HandPickUpSent:SetModel( "models/props_junk/popcan01a.mdl" );
	self:GetTable().HandPickUpSent:SetPlayer( self );
	self:GetTable().HandPickUpSent:SetTarget( ent );
	self:GetTable().HandPickUpSent:Spawn();
	self:GetTable().HandPickUpTarget = ent;
	constraint.Weld( self:GetTable().HandPickUpSent, ent, 0, bone, 9999 );	

end

function meta:Noclip( bool )

	self:GetTable().NoclipMode = bool;
		
	self:SetNotSolid( bool );
	
	if( bool ) then
		self:SetMoveType( 8 );
		self:GodEnable();
	else
		self:SetMoveType( 2 );
		self:GodDisable();
	end
	
end

function meta:Observe( bool )

	self:GetTable().ObserveMode = bool;
	self:SetNoDraw( bool );
	
	if( self:GetActiveWeapon() and self:GetActiveWeapon():IsValid() ) then
		self:GetActiveWeapon():SetNoDraw( bool );
	end
		
	self:SetNotSolid( bool );
	
	if( bool ) then
		self:SetMoveType( 8 );
		self:GodEnable();
	else
		self:SetMoveType( 2 );
		self:GodDisable();
	end
	
end

function meta:HandleTeamSpawn()

	if( self:Team() == 1 ) then
		self:WearItem( "clothes_citizen" );
	end
		
	if( self:IsAwesome() ) then
		self:Give( "ts2_kanyewest" );
	end
	
	if( self:GetTable().AdvancedTT == true ) then
	
		self:Give( "gmod_tool" )
		
	end	
	

end

function meta:OpenBlackmarketMenu()

	--Create the storage menu
    umsg.Start( "CBMM", self );
	umsg.End();
	
	local delay = 0;
	
	--Send all of the item data for each item in the storage
	for x, v in pairs( BlackmarketGrid ) do
	
		for y, k in pairs( v ) do
			
			if( k.ItemData ) then
				
				local id = k.ItemData.ID;
				
				local give = function() 
				
					if( not self:GetTable().ItemsDownloaded[id] ) then 
						self:SendItemData( id );
					end
					
					if( BlackmarketGrid[x][y].ItemData and
						BlackmarketGrid[x][y].ItemData.ID == id ) then
				
						umsg.Start( "IBMI", self );
							umsg.String( id );
							umsg.Short( x );
							umsg.Short( y );
						umsg.End();
					
					end
					
				end
				timer.Simple( delay, give );
				
				delay = delay-- + .2;
				
			end
			
		end
	
	end
	
end

function meta:OpenContrabandMenu()

	--Create the storage menu
    umsg.Start( "CCBM", self );
	umsg.End();
	
	local delay = 0;
	
	--Send all of the item data for each item in the storage
	for x, v in pairs( ContrabandGrid ) do
	
		for y, k in pairs( v ) do
			
			if( k.ItemData ) then
				
				local id = k.ItemData.ID;
				
				local give = function() 
				
					if( not self:GetTable().ItemsDownloaded[id] ) then 
						self:SendItemData( id );
					end
					
					if( ContrabandGrid[x][y].ItemData and
						ContrabandGrid[x][y].ItemData.ID == id ) then
				
						umsg.Start( "ICI", self );
							umsg.String( id );
							umsg.Short( x );
							umsg.Short( y );
						umsg.End();
					
					end
					
				end
				timer.Simple( delay, give );
				
				delay = delay-- + .2;
				
			end
			
		end
	
	end
	
end

function meta:OpenStorageMenu( ent )

	if( type( ent ) == "table" ) then
	
		local itemdata = { }
		
		for k, v in pairs( ent ) do
			itemdata[k] = v;
		end
		
		ent = { }
		ent.tbl = { }
		ent.tbl.ItemData = itemdata;
		
		ent.GetTable = function( self )
			return self.tbl;
		end
	
	end

	if( not table.HasValue( ent:GetTable().ItemData.RecPlayers, self ) ) then
		table.insert( ent:GetTable().ItemData.RecPlayers, self );
	end
	
	self:GetTable().StorageEntity = ent;
	
	local w = ent:GetTable().ItemData.Width;
	local h = ent:GetTable().ItemData.Height;
	
	if( ent:GetTable().ItemData.InvWidth and ent:GetTable().ItemData.InvHeight ) then
	
		w = ent:GetTable().ItemData.InvWidth;
		h = ent:GetTable().ItemData.InvHeight;
	
	end

	--Create the storage menu
	umsg.Start( "CSM", self );
		umsg.String( ent:GetTable().ItemData.Name );
		umsg.Short( w );
		umsg.Short( h );
		umsg.String( ent:GetTable().ItemData.Flags or "" );
	umsg.End();
	
	local delay = 0;
	
	--Send all of the item data for each item in the storage
	for x, v in pairs( ent:GetTable().ItemData.InventoryGrid ) do
	
		for y, k in pairs( v ) do
			
			if( k.ItemData ) then
				
				local id = k.ItemData.ID;
				
				local give = function() 
				
					if( not self:GetTable().ItemsDownloaded[id] ) then 
						self:SendItemData( id );
					end
					
					if( ent:GetTable().ItemData.InventoryGrid[x][y].ItemData and
						ent:GetTable().ItemData.InventoryGrid[x][y].ItemData.ID == id ) then
				
						umsg.Start( "ISI", self );
							umsg.String( id );
							umsg.Short( x );
							umsg.Short( y );
						umsg.End();
					
					end
					
				end
				timer.Simple( delay, give );
				
				delay = delay + .2;
				
			end
			
		end
	
	end
	

	
end

function meta:AttachProp( model, attachment )

	local ent = ents.Create( "ts2_attachment" );
	ent:SetModel( model );
	ent:SetPlayer( self, attachment );
	--ent:SetAngleOffset( Angle( 0, 45, 0 ) );
	ent:Spawn();
	
	table.insert( self:GetTable().AttachedProps, { Model = model, Attachment = attachment, Entity = ent } );

	return ent;

end

function meta:RemoveAttachmentFrom( attachment )

	for k, v in pairs( self:GetTable().AttachedProps ) do
	
		if( v.Attachment == attachment ) then
		
			v.Entity:Remove();
			self:GetTable().AttachedProps[k] = nil;
		
		end
	
	end

end

function meta:DropCurrentWeapon()

	local weap = self:GetActiveWeapon();
	
	if( weap and weap:IsValid() ) then
	
		local class = weap:GetClass();
		
		if( string.find( class, "ts2_" ) and
			class ~= "ts2_hands" and
			class ~= "ts2_keys" and
			class ~= "ts2_kanyewest" ) then
	
			self:DropItemProp( class );
			self:StripWeapon( class );
	
		end
		
		if( class == self:GetTable().TempWeaponClass ) then
			self:GetTable().HasTempWeapon = false;
		end
	
	end

end

function meta:DropTempWeapon()

	if( self:GetTable().HasTempWeapon ) then
	
		if( self:HasWeapon( self:GetTable().TempWeaponClass ) ) then
	
			local weap = self:GetActiveWeapon();
			
			if( weap and weap:IsValid() ) then
			
				self:DropItemProp( self:GetTable().TempWeaponClass );
			
			end
			
			self:StripWeapon( self:GetTable().TempWeaponClass );
			self:GetTable().HasTempWeapon = false;
			
		end
		
	end

end

function meta:GiveTempWeapon( class )

	if( self:HasWeapon( class ) ) then
		return;
	end

	self:DropTempWeapon();
	
	self:GetTable().HasTempWeapon = true;
	self:GetTable().TempWeaponClass = class;
	self:Give( class );
	self:SelectWeapon( class );

end

function meta:HandleWeaponChangeTo( class )
	
	if( self:GetActiveWeapon():IsValid() and self:GetTable().ObserveMode ) then
		self:GetActiveWeapon():SetNoDraw( true );
	end

	if( class ~= "weapon_physgun" and
		class ~= "weapon_physcannon" and
		class ~= "gmod_tool" and
		class ~= "ts2_mapeditor" ) then
		
		--self:DrawViewModel( false );
		self:SetPlayerHolstered( true );
		
		umsg.Start( "HPOS", self ); umsg.End();
		
	else
	
		self:DrawViewModel( true );
		self:SetPlayerHolstered( false );
		
	end
	
	self:GetTable().LastWeapon = class;

end

function meta:HandleStunStickHit( attacker, perc, hitgroup, type )
	
	local islethal = false;
	local dmgamount = 0;
	local uncamount = 0;
	
	if( hitgroup == HITGROUP_HEAD ) then
	
		if( perc >= .4 and self:GetTable().HelmetHealth < 5 ) then
	
			islethal = true;
			
		end
		
		if( self:GetTable().HelmetHealth > 10 ) then
		
			dmgamount = 10 * perc;
			uncamount = 5 * perc;
		
		else
		
			umsg.Start( "DSSS", self ); umsg.End();
		
			dmgamount = 40 * perc;
			uncamount = 90 * perc;
			
			if( type == 2 ) then
			
				self:GetTable().SlowDownForHit = true;
				self:ComputePlayerSpeeds();
				timer.Create( "SlowDownHit" .. self:UserID(), 4, 0, function()  if( not self:IsValid() ) then return; end self:GetTable().SlowDownForHit = false; self:ComputePlayerSpeeds(); end );
			
			end
			
		end
		
	elseif( hitgroup == HITGROUP_CHEST or hitgroup == HITGROUP_GENERIC or hitgroup == HITGROUP_STOMCH ) then
	
		if( perc >= .8 and self:GetTable().BodyArmorHealth < 0 ) then
	
			islethal = true;
			
		end
		
		if( self:GetTable().BodyArmorHealth > 5 ) then
		
			dmgamount = 4 * perc;
			uncamount = 0;
		
		else
		
			umsg.Start( "DSSS", self ); umsg.End();
		
			dmgamount = 35 * perc;
			uncamount = 45 * perc;
			
			if( type == 2 ) then
			
				self:GetTable().SlowDownForHit = true;
				self:ComputePlayerSpeeds();
				timer.Create( "SlowDownHit" .. self:UserID(), 4, 0, function()  if( not self:IsValid() ) then return; end self:GetTable().SlowDownForHit = false; self:ComputePlayerSpeeds(); end );
			
			end
		
		end	

	else
	
		dmgamount = 5 * perc;
		uncamount = 4 * perc;
	
	end
	
	if( type == 2 ) then
	
		dmgamount = dmgamount * .3;
		uncamount = uncamount * 2;
	
	else
	
		uncamount = uncamount * 1.4;
	
	end
	
	local minhealth = 1;
	
	if( islethal ) then
	
		minhealth = 0;
	
	end
	
	self:SetHealth( math.Clamp( self:Health() - dmgamount, minhealth, 1000 ) );
	self:SetPlayerConsciousness( math.Clamp( self:GetPlayerConsciousness() - uncamount, 0, 100 ) );

	if( self:Health() <= 0 ) then
		self:Kill();
	end

end

function meta:OpenPlayerMenu( hitpos, ent )

	--[[CreateActionMenu( "Options", self, hitpos - Vector( 0, 0, 10 ) );
		SetActionMenuEntity( ent );	
		if( ent:GetNWInt( "IsLootable" ) == 1 and ent:GetNWInt( "IsLootableRagdoll" ) == 1 ) then
			AddActionOption( "Loot Body", "eng_loot" );
		else
			AddActionOption( "This body has already been looted!", "" )
		end	
	EndActionMenu();--]]

end

function meta:ScaleDamageFromAttack( attacker, weapon, hitgroup, dmginfo )

	local highpowered = false;
	local bleeding = true;
	
	if( weapon:GetTable().Primary ) then 
	
		highpowered = weapon:GetTable().Primary.HighPowered;
	
	end
	
	if( weapon:GetTable().Primary ) then 
	
		bleeding = weapon:GetTable().Primary.CanCauseBleeding;
	
	end
	
	local incamt = 0;
	local dmgamt = 0;
	local helmetdmg = 0;
	local armordmg = 0;
	
	--Lower chances of head shot (for players only)
	if( hitgroup == HITGROUP_HEAD and attacker:IsPlayer() ) then
	
		local tr = attacker:GetEyeTrace();
	
		if( tr.Entity == self and tr.HitGroup ~= HITGROUP_HEAD ) then
		
			if( math.random( 1, 7 ) ~= 4 ) then
			
				hitgroup = HITGROUP_CHEST;
			
			end
		
		end
	
	end
	
	--Increases chance of chest/stomach shot if shooting at unholstered arms
	if( hitgroup == HITGROUP_LEFTARM or hitgroup == HITGROUP_RIGHTARM ) then
	
		if( not self:GetPlayerHolstered() ) then
		
			local val = math.random( 1, 8 );
			
			if( val > 3 ) then
			
				if( val >= 6 ) then
					hitgroup = HITGROUP_CHEST;
				else
					hitgroup = HITGROUP_STOMACH;
				end
			
			end
		
		end		
	
	end
	
	if( hitgroup == HITGROUP_HEAD ) then
	
		dmgamt = 1000 * dmginfo:GetDamage();
		incamt = 0;
	
		if( not highpowered ) then
		
			if( self:GetTable().HelmetHealth > 0 ) then
		
				dmgamt = 0;
				
				if( self:GetTable().SuperResistantHelmet ) then
					helmetdmg = dmginfo:GetDamage();
				else
					helmetdmg = dmginfo:GetDamage() * 2;	
				end
				
				local excess = helmetdmg - self:GetTable().HelmetHealth;
				if( excess > 0 ) then
				
					dmgamt = excess * 1000;
				
				end	
				
			end
		
		else
		
			if( self:GetTable().SuperResistantHelmet and self:GetTable().HelmetHealth >= 20 ) then
			
				dmgamt = 0;
				helmetdmg = dmginfo:GetDamage() * 1.5;
			
				local excess = helmetdmg - self:GetTable().HelmetHealth;
				if( excess > 10 ) then
				
					dmgamt = excess * 1000;
				
				end	
			
			end
		
		end
	
	elseif( hitgroup == HITGROUP_CHEST ) then
	
		dmgamt = dmginfo:GetDamage() * 4;
		incamt = dmginfo:GetDamage() * 2;
		
		local penetrate = false;
		
		if( not highpowered ) then
		
			if( self:GetTable().BodyArmorHealth + 10 > 0 ) then
		
				dmgamt = 0;
				incamt = 0;
				
				if( self:GetTable().SuperResistantArmor ) then
					armordmg = dmginfo:GetDamage() * .75;
				else
					armordmg = dmginfo:GetDamage() * 2;
				end
				
				local excess = armordmg - self:GetTable().BodyArmorHealth;
				if( excess > 0 ) then
				
					dmgamt = excess * 2;
					incamt = excess * 1.1;
					
					if( bleeding ) then
					
						self:SetPlayerBleeding( true );
						self:GetTable().BleedingDmgPerSec = self:GetTable().BleedingDmgPerSec + math.Clamp( dmgamt * .2, 0, .5 );
						self:GetTable().BleedingIncDmgPerSec = self:GetTable().BleedingIncDmgPerSec + math.Clamp( dmgamt * .3, 0, 2.5 );
						
					end
					
					penetrate = true;
					
				end	
				
			else
			
				if( bleeding ) then
			
					self:SetPlayerBleeding( true );
					self:GetTable().BleedingDmgPerSec = self:GetTable().BleedingDmgPerSec + math.Clamp( dmgamt * .2, 0, 1 );
					self:GetTable().BleedingIncDmgPerSec = self:GetTable().BleedingIncDmgPerSec + math.Clamp( dmgamt * .3, 0, 3 );
					
				
				end
				
				penetrate = true;
				
			end
		
		else
		
			if( self:GetTable().SuperResistantArmor and self:GetTable().BodyArmorHealth >= 15 ) then
			
				dmgamt = 0;
				incamt = 0;
				armordmg = dmginfo:GetDamage();
			
				local excess = armordmg - self:GetTable().BodyArmorHealth;
				if( excess > 5 ) then
				
					dmgamt = excess;
					incamt = excess * .5;
					
					if( bleeding ) then
					
						self:SetPlayerBleeding( true );
						self:GetTable().BleedingDmgPerSec = self:GetTable().BleedingDmgPerSec + math.Clamp( dmgamt * .2, 0, 1 );
						self:GetTable().BleedingIncDmgPerSec = self:GetTable().BleedingIncDmgPerSec + math.Clamp( dmgamt * .4, 0, 2 );
						
					end
					
					penetrate = true;
				
				end	
			
			else
			
				if( bleeding ) then
			
					self:SetPlayerBleeding( true );
					self:GetTable().BleedingDmgPerSec = self:GetTable().BleedingDmgPerSec + math.Clamp( dmgamt * .2, 0, 1 );
					self:GetTable().BleedingIncDmgPerSec = self:GetTable().BleedingIncDmgPerSec + math.Clamp( dmgamt * .4, 0, 2 );
					
				end
					
				penetrate = true;
				
			end
		
		end
		
		if( penetrate and ( not self:IsOW() and not self:IsOWElite() ) ) then
		
			self:GetTable().SlowDownForHit = true;
			self:ComputePlayerSpeeds();
			timer.Create( "SlowDownHit" .. self:UserID() .. self:UserID(), 1, 0, function() if( self and self:IsValid() ) then self:GetTable().SlowDownForHit = false; self:ComputePlayerSpeeds(); end end );
		
		end
	
	elseif( hitgroup == HITGROUP_STOMACH or hitgroup == HITGROUP_GENERIC ) then
	
		dmgamt = dmginfo:GetDamage() * 2;
		incamt = dmginfo:GetDamage() * 1.3;
		
		local penetrate = false;
	
		if( not highpowered ) then
		
			if( self:GetTable().BodyArmorHealth > 0 ) then
		
				dmgamt = 0;
				incamt = 0;
				
				if( self:GetTable().SuperResistantArmor ) then
					armordmg = dmginfo:GetDamage() * .5;
				else
					armordmg = dmginfo:GetDamage();
				end
				
				local excess = armordmg - self:GetTable().BodyArmorHealth;
				if( excess > 0 ) then
				
					dmgamt = excess * 1.5;
					incamt = excess * .7;
					
					if( bleeding ) then
					
						self:SetPlayerBleeding( true );
						self:GetTable().BleedingDmgPerSec = self:GetTable().BleedingDmgPerSec + math.Clamp( dmgamt * .2, 0, 1 );
						self:GetTable().BleedingIncDmgPerSec = self:GetTable().BleedingIncDmgPerSec + math.Clamp( dmgamt * .1, 0, 2 );
					
					end
					
					penetrate = true;
				
				end	
				
			else
			
				if( bleeding ) then
			
					self:SetPlayerBleeding( true );
					self:GetTable().BleedingDmgPerSec = self:GetTable().BleedingDmgPerSec + math.Clamp( dmgamt * .2, 0, 1 );
					self:GetTable().BleedingIncDmgPerSec = self:GetTable().BleedingIncDmgPerSec + math.Clamp( dmgamt * .1, 0, 2 );
					
				end
				
				penetrate = true;		
		
			end
		
		else
		
			if( self:GetTable().SuperResistantArmor and self:GetTable().BodyArmorHealth >= 15 ) then
			
				dmgamt = 0;
				incamt = 0;
				armordmg = dmginfo:GetDamage();
			
				local excess = armordmg - self:GetTable().BodyArmorHealth;
				if( excess > 5 ) then
				
					dmgamt = excess;
					incamt = excess * .5;
					
					if( bleeding ) then
					
						self:SetPlayerBleeding( true );
						self:GetTable().BleedingDmgPerSec = self:GetTable().BleedingDmgPerSec + math.Clamp( dmgamt * .2, 0, 4 );
						self:GetTable().BleedingIncDmgPerSec = self:GetTable().BleedingIncDmgPerSec + math.Clamp( dmgamt * .3, 0, 5 );
						
					end
					
					penetrate = true;
				
				end	
			
			else
			
				if( bleeding ) then
			
					self:SetPlayerBleeding( true );
					self:GetTable().BleedingDmgPerSec = self:GetTable().BleedingDmgPerSec + math.Clamp( dmgamt * .2, 0, 4 );
					self:GetTable().BleedingIncDmgPerSec = self:GetTable().BleedingIncDmgPerSec + math.Clamp( dmgamt * .3, 0, 5 );
				end
				
				penetrate = true;
				
			end
			
		
		end
		
		if( penetrate ) then
		
			if( penetrate and ( not self:IsOW() and not self:IsOWElite() ) ) then
			
				self:GetTable().SlowDownForHit = true;
				self:ComputePlayerSpeeds();
				timer.Create( "SlowDownHit" .. self:UserID(), 1, 0, function() if( not self:IsValid() ) then return; end self:GetTable().SlowDownForHit = false; self:ComputePlayerSpeeds(); end );
			
			end
	
		end
	
	else
	
		if( not self:IsOW() and not self:IsOWElite() ) then
	
			if( hitgroup == HITGROUP_RIGHTLEG or
				hitgroup == HITGROUP_LEFTLEG ) then
				
				self:GetTable().StatusIsInjured = true;
				self:SetPlayerCanSprint( false );
				self:ComputePlayerSpeeds();
	
			end
			
		end
	
		dmgamt = 0;
		incamt = 0;
		
		--if( bleeding ) then
		
		self:SetPlayerBleeding( true );
		self:GetTable().BleedingDmgPerSec = self:GetTable().BleedingDmgPerSec + math.Clamp( dmginfo:GetDamage() * .3, 0, 1.5 );
		self:GetTable().BleedingIncDmgPerSec = self:GetTable().BleedingIncDmgPerSec + math.Clamp( dmginfo:GetDamage() * .2, 0, 1 );
	
		--end
						
	end

	if( dmgamt == 0 ) then
	
		dmgamt = -1000;
	
	end
	
	if( self:GetPlayerConscious() ) then
		
		if( self:IsCitizen() and math.random( 1, 2 ) == 2 ) then
	
			local num = math.random( 1, 9 );
			
			if( not string.find( string.lower( self:GetModel() ), "female" ) ) then
				self:EmitSound( Sound( "vo/npc/male01/pain0" .. num .. ".wav" ) );
			else
				self:EmitSound( Sound( "vo/npc/female01/pain0" .. num .. ".wav" ) );
			end
			
		end
		
		if( self:HasCPModel() and math.random( 1, 2 ) == 2 ) then
		
			local num = math.random( 1, 4 );
			
			self:EmitSound( Sound( "npc/metropolice/pain" .. num .. ".wav" ) );
		
		end
		
		if( ( self:IsOW() or self:IsOWElite() ) and math.random( 1, 3 ) == 2 ) then
		
			local num = math.random( 1, 3 );
			
			self:EmitSound( Sound( "npc/combine_soldier/pain" .. num .. ".wav" ) );
		
		end
		
	end

	dmginfo:SetDamage( dmgamt );
	
	self:GetTable().HelmetHealth = self:GetTable().HelmetHealth - helmetdmg;
	self:GetTable().BodyArmorHealth = self:GetTable().BodyArmorHealth - armordmg;
	
	self:SetPlayerConsciousness( math.Clamp( self:GetPlayerConsciousness() - incamt, 0, 100 ) );
	
	return dmginfo;

end

function meta:BleedOutADecal()

	local trace = { }
	trace.start = self:EyePos();
	trace.endpos = trace.start - Vector( 0, 0, 80 );
	
	local front = false;
	local back = false;
	local mid = false;
	
	if( math.random( 1, 3 ) == 2 ) then
		back = true;
	elseif( math.random( 3, 6 ) == 4 ) then
		mid = true;
	else
		front = true;
	end
	
	if( front ) then
		trace.endpos = trace.endpos + self:GetForward() * 30;
	elseif( back ) then
		trace.endpos = trace.endpos + self:GetForward() * -30;
	end
	
	local left = false;
	local right = false;
	local center = false;
	
	if( mid ) then
		if( math.random( 1, 3 ) == 2 ) then
			left = true;
		else
			right = true;
		end
	else
		if( math.random( 1, 3 ) == 2 ) then
			left = true;
		elseif( math.random( 4, 7 ) == 5 ) then
			right = true;
		else
			center = true;
		end
	end
	
	if( left ) then
		trace.endpos = trace.endpos + self:GetRight() * -30;
	elseif( right ) then
		trace.endpos = trace.endpos + self:GetRight() * 30;
	end

	trace.filter = self;
	
	local tr = util.TraceLine( trace );
	
	local pos = tr.HitPos;
	local norm = tr.HitNormal;

	util.Decal( "Blood", pos + norm, pos - norm );

end

function meta:DropClothes()

	local itemdata = TS.ItemsData[self:GetTable().CurrentClothes];
	
	if( itemdata.InvCanDrop == false ) then
		return;
	end
	
	local item = self:DropItemProp( self:GetTable().CurrentClothes );
	local invname = itemdata.InvName;
	
	item:ConvertToStorage();
	
	item:GetTable().ItemData.HelmetHealth = self:GetTable().HelmetHealth;
	item:GetTable().ItemData.BodyArmorHealth = self:GetTable().BodyArmorHealth;

	if( itemdata.InvStayWithClothes ) then
		item:GetTable().ItemData:CopyFromPlayerInventory( self, invname );
		self:RemoveInventory( invname );
	end
	
end

function meta:WearItem( item )

	local invgrid = nil;

	if( type( item ) == "string" ) then

		if( not TS.ItemsData[item] ) then
			return;
		end
		
		self:GetTable().CurrentClothes = item;
		
		item = TS.ItemsData[item];
		
	else

		invgrid = item.InventoryGrid;
		self:GetTable().CurrentClothes = item.ID;
	
	end

	if( item.InvName ) then
	
		if( not self:HasInventory( item.InvName ) ) then
		
			local b = false;
			
			if( item.InvStayWithClothes == nil ) then
				b = true;
			else
				b = item.InvStayWithClothes;
			end
		
			self:AddInventory( item.InvName, item.InvWidth, item.InvHeight, !b, item.CanRevertToCitizensCloth or false, true );
		
			if( invgrid ) then
				
				self:CopyFromInventoryTable( item.InvName, invgrid );
			end
			
		end
	
	end
	
	self:GetTable().HelmetHealth = item.HelmetHealth or 0;
	self:GetTable().BodyArmorHealth = item.BodyArmorHealth or 0;

	if( item.CitizenSkinBased and item.CitizenSkinModel and self:GetTable().CitizenModel ) then

		local curskin = "male_01";
		
		local pos = string.find( string.lower( self:GetTable().CitizenModel ), "female" );
		
		if( pos ) then
		
			curskin = string.sub( self:GetTable().CitizenModel, pos, pos + 8 );
		
		else
		
			pos = string.find( string.lower( self:GetTable().CitizenModel ), "male" );
		
			if( pos ) then
			
				curskin = string.sub( self:GetTable().CitizenModel, pos, pos + 6 );
			
			end
		
		end
		
		local mdl = string.gsub( item.CitizenSkinModel, "#", curskin );
		
		self:SetModel( mdl );
	
	elseif( item.ClothesModel ) then
		
		self:SetModel( item.ClothesModel );
	
	end
	
end

local BonesToSet = {

	"ValveBiped.Bip01_Pelvis",
	"ValveBiped.Bip01_Head1",
	"ValveBiped.Bip01_Spine1",
	"ValveBiped.Bip01_Spine2",
	"ValveBiped.Bip01_Spine4",
	"ValveBiped.Bip01_R_UpperArm",
	"ValveBiped.Bip01_R_ForeArm",
	"ValveBiped.Bip01_L_UpperArm",
	"ValveBiped.Bip01_L_ForeArm",
	"ValveBiped.Bip01_R_Thigh",
	"ValveBiped.Bip01_R_Calf",
	"ValveBiped.Bip01_L_Thigh",
	"ValveBiped.Bip01_L_Calf",

};


function meta:RagdollPlayer()

	if( self:GetTable().IsRagdolled ) then
		return;
	end

	local ragdoll = ents.Create( "prop_ragdoll" );
	
	ragdoll:SetPos( self:GetPos() );
	ragdoll:SetAngles( self:GetAngles() );
	ragdoll:SetModel( self:GetModel() );
	ragdoll:SetOwner( self );
	
	ragdoll:Spawn();
	
	local c = ragdoll:GetPhysicsObjectCount();
	
	local vel = self:GetVelocity();
	
	for n = 1, c do
		
		local bone = ragdoll:GetPhysicsObjectNum( n );
		
		if( bone and bone:IsValid() ) then
		
			local bonepos, boneang = self:GetBonePosition( ragdoll:TranslatePhysBoneToBone(n) );
			
			bone:SetPos( bonepos );
			bone:SetAngle( boneang );
			bone:AddVelocity( vel );
			
		end
		
	end

	self:SnapOutOfStance();

	self:MakeInvisible();
	
	self:Freeze( true );
	
	self:SetNWBool( "IsRagdoll", true )
	
	self:GetTable().IsRagdolled = true;
	self:GetTable().RagdollEntity = ragdoll;
	ragdoll:GetTable().IsPlayerRagdoll = true;
	ragdoll:GetTable().Player = self;
	
	table.insert( TS.PlayerRagdolls, ragdoll );
	
	return ragdoll;
	
end

function meta:UnragdollPlayer()

	self:GetTable().IsRagdolled = false;

	self:MakeNotInvisible();
	self:Freeze( false );
	self:CrosshairDisable();

	local spawnpos;

	if( self:GetTable().RagdollEntity and self:GetTable().RagdollEntity:IsValid() ) then
	
		if( self:Health() > 0 ) then
			spawnpos = self:GetTable().RagdollEntity:GetPos();
			self:RemoveRagdoll();
		end
		
	end
	
	if( self:Health() > 0 ) then
	
		self:Spawn();

		if( spawnpos ) then
		
			self:SetPos( spawnpos );
		
		end

	end

end

function meta:RemoveRagdoll()

	if( self:GetTable().RagdollEntity and self:GetTable().RagdollEntity:IsValid() ) then
		
		self:GetTable().RagdollEntity:Remove();
		self:GetTable().RagdollEntity = nil;

	end

end

function meta:MakeInvisible( b )

	if( b == nil ) then
		b = true;
	end

	self:SetNotSolid( b );
	self:SetNoDraw( b );
	self:DrawViewModel( !b );
	
	if( self:GetActiveWeapon():IsValid() ) then
		self:GetActiveWeapon():SetNoDraw( b );
	end

end

function meta:MakeNotInvisible()

	self:MakeInvisible( false );

end

function meta:Unconscious()

	--self:DropCurrentWeapon();

	local ent = self:RagdollPlayer();
	
	self:GetTable().RagdollHealth = self:Health();
	
	self:Spectate( OBS_MODE_CHASE );
	self:SpectateEntity( ent );
	
	timer.Simple( 3, self.UnSpectate, self );
	
	self:SetPlayerConscious( false );

end

function meta:Conscious()

	self:UnragdollPlayer();

	self:SetHealth( self:GetTable().RagdollHealth );

	self:SetPlayerConscious( true );

end

		

function meta:SearchedBy( ply )

	--Create the storage menu
	umsg.Start( "CPSM", ply );
		umsg.String( self:GetRPName() .. "'s Inventory" );
		umsg.Short( 10 );
		umsg.Short( 10 );
		umsg.String( "" );
	umsg.End();
	
	local delay = 0;
	
	local xother = 0;
	local yother = 0;
	
	for i, v in pairs( self:GetTable().InventoryGrid ) do
	
		for x, c in pairs( v ) do
		
			for y, u in pairs( c ) do
				
				if( self:GetTable().InventoryGrid[i][x][y].ItemData ) then
				
				local id = self:GetTable().InventoryGrid[i][x][y].ItemData.ID

				
					if( not ply:GetTable().ItemsDownloaded[id] ) then 
						ply:SendItemData( id );
					end
					
				local ipsi = function()
						umsg.Start( "IPSI", ply );
							umsg.String( id );
							--umsg.Short( x );
							--umsg.Short( y );
							umsg.Short( xother );
							umsg.Short( yother );
						umsg.End();
						
							xother = xother + 2
							if( xother >= 8 ) then
								xother = 0
								yother = yother + 2
							end
				end
				timer.Simple( .5, ipsi );

				end
			
			end
		
		end
	
	end

end

function meta:TiedUpBy( ply )

	if( self:GetTable().HandPickUpSent and self:GetTable().HandPickUpSent:IsValid() ) then
		self:RemoveHandPickUp();
	end
	
	if( self:GetTable().RightHandEntity and self:GetTable().RightHandEntity:IsValid() ) then
	
		self:GetTable().RightHandEntity:Remove();
		self:GetTable().RightHandEntity = nil;
	
	end 
	
	self:DrawViewModel( false );
	
	self:GetTable().IsTied = true;
	
	self:PrintMessage( 3, "Tied up by " .. ply:GetRPName() );
	ply:PrintMessage( 3, "You've tied up " .. self:GetRPName() );
	
	self:SelectWeapon( "ts2_hands" );
	self:SetPlayerHolstered( true );

end

function meta:UnTiedUpBy( ply )
	
	self:DrawViewModel( true );
	
	self:GetTable().IsTied = false;
	
	self:PrintMessage( 3, "Untied up by " .. ply:GetRPName() );
	ply:PrintMessage( 3, "You've untied up " .. self:GetRPName() );
	

end

function meta:TryDoorRam()

	if( self:IsTied() or not self:GetPlayerConscious() ) then
		return;
	end
		if( not self:OnGround() ) then return; end

		local trace = { }
		trace.start = self:EyePos();
		trace.endpos = trace.start + self:GetAimVector() * 75;
		trace.filter = self;
		
		local tr = util.TraceLine( trace );
		
		if( ValidEntity( tr.Entity ) and tr.Entity:IsDoor() and not string.find( tr.Entity:GetTable().DoorFlags, "n" ) and not string.find( tr.Entity:GetTable().DoorFlags, "s" ) ) then
		
			--self:SetField( "LastDoorKick", CurTime() );
		
			if( tr.Entity:GetPos():Distance( self:GetPos() ) > 65 ) then
			
				tr.Entity:EmitSound( Sound(  "physics/wood/wood_box_impact_hard3.wav" ) );
			
				if( SERVER ) then
				
					self:ViewPunch( Angle( -25, 0, 0 ) );
					self:SetVelocity( Vector( self:GetAimVector().x, self:GetAimVector().y, 0 ) * 500 );
					
					local team = self:Team();
					local dounlock = false;
					
					if( team == 1 or team == 5 ) then
						if( math.random( 1, 10 ) == 5 ) then
							dounlock = true;
						end
					elseif( team == 2 ) then
						if( math.random( 1, 2 ) == 2 ) then
							dounlock = true;
						end
					else
						dounlock = true;
					end
					
					if( dounlock ) then
					
						tr.Entity:Fire( "unlock", "", 0 );
						self:ConCommand( "+use\n" );
						timer.Simple( .1, self.ConCommand, self, "-use\n" );
						if( tr.Entity:GetTable().BoobyTrapped ) then
							tr.Entity:SplodeDoor();	
						end
						
					end
					
				end
			
			else
				self:PrintMessage( 3, "Too close to door" );
			end
		
		end
		
	--end

end

fbcur = {}
fblast = {}

function meta:AttemptFlash()

local range = 1028*4
local pos = self:GetPos()
		
for i,pl in pairs(player.GetAll()) do
	local plp = pl:GetShootPos()
	local pla = pl:GetAimVector()
			
	if (plp - pos):Length() <= range and pl ~= self then
		local trace = {}
		trace.start = plp
		trace.endpos = pos
		trace.filter = pl
		trace.mask = COLLISION_GROUP_PLAYER
		trace = util.TraceLine(trace)
				
		if trace.Fraction == 1 then
			local dot = -((plp - pos):Normalize():DotProduct(pla))
			if dot <= .35 then
				dot = .35
			end
					
			dot = dot * ((range - ((plp - pos):Length()))/range)
					
			local userid = pl:UniqueID()
			local cur = fbcur[userid] or 0
			local cur = cur + dot
			if cur > 1 then
				cur = 1
			end
					
			fbcur[userid] = cur
			if cur == fblast[userid] then
				cur = -cur
			end
			pl:SetNetworkedFloat("flashed",cur)
			fblast[userid] = cur
		end
	end
end

end

function meta:Slay()

	self:SetField( "slay", 1 );
	self:Kill();

end

function meta:SlaySilent()

	self:SetField( "slay", 1 );
	self:KillSilent();

end



