
--[[---------------------------------------------------------
	Name: gamemode:OnPhysgunFreeze( weapon, phys, ent, player )
	Desc: The physgun wants to freeze a prop
-----------------------------------------------------------]]
function GM:OnPhysgunFreeze( weapon, phys, ent, ply )

	-- Non vphysics entity, we don't know how to handle that
	if ( !IsValid( phys ) ) then return end

	-- Object is already frozen (!?)
	if ( !phys:IsMoveable() ) then return end
	if ( ent:GetUnFreezable() ) then return end

	phys:EnableMotion( false )

	-- Add it to the player's frozen props
	ply:AddFrozenPhysicsObject( ent, phys )

end

--[[---------------------------------------------------------
	Name: gamemode:OnPhysgunReload( weapon, player )
	Desc: The physgun wants to freeze a prop
-----------------------------------------------------------]]
function GM:OnPhysgunReload( weapon, ply )

	ply:PhysgunUnfreeze()

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerAuthed()
	Desc: Player's UniqueID was set
-----------------------------------------------------------]]
function GM:PlayerAuthed( ply, SteamID, UniqueID )
end

--[[---------------------------------------------------------
	Name: gamemode:PlayerCanPickupWeapon()
	Desc: Called when a player tries to pickup a weapon.
		return true to allow the pickup.
-----------------------------------------------------------]]
function GM:PlayerCanPickupWeapon( ply, entity )

	return true

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerCanPickupItem()
	Desc: Called when a player tries to pickup an item.
		 return true to allow the pickup.
-----------------------------------------------------------]]
function GM:PlayerCanPickupItem( ply, entity )

	return true

end

--[[---------------------------------------------------------
	Name: gamemode:CanPlayerUnfreeze()
	Desc: Can the player unfreeze this entity & physobject
-----------------------------------------------------------]]
function GM:CanPlayerUnfreeze( ply, entity, physobject )

	return true

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerDisconnected()
	Desc: Player has disconnected from the server.
-----------------------------------------------------------]]
function GM:PlayerDisconnected( ply )
end

--[[---------------------------------------------------------
	Name: gamemode:PlayerSay()
	Desc: A player (or server) has used say. Return a string
		 for the player to say. Return an empty string if the
		 player should say nothing.
-----------------------------------------------------------]]
function GM:PlayerSay( ply, text, teamonly )

	return text

end


--[[---------------------------------------------------------
	Name: gamemode:PlayerDeathThink( player )
	Desc: Called when the player is waiting to respawn
-----------------------------------------------------------]]
function GM:PlayerDeathThink( pl )

	if ( pl.NextSpawnTime && pl.NextSpawnTime > CurTime() ) then return end

	if ( pl:IsBot() || pl:KeyPressed( IN_ATTACK ) || pl:KeyPressed( IN_ATTACK2 ) || pl:KeyPressed( IN_JUMP ) ) then

		pl:Spawn()

	end

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerUse( player, entity )
	Desc: A player has attempted to use a specific entity
		Return true if the player can use it
------------------------------------------------------------]]
function GM:PlayerUse( ply, entity )

	return true

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerSilentDeath()
	Desc: Called when a player dies silently
-----------------------------------------------------------]]
function GM:PlayerSilentDeath( Victim )

	Victim.NextSpawnTime = CurTime() + 2
	Victim.DeathTime = CurTime()

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerDeath()
	Desc: Called when a player dies.
-----------------------------------------------------------]]
function GM:PlayerDeath( ply, inflictor, attacker )

	-- Don't spawn for at least 2 seconds
	ply.NextSpawnTime = CurTime() + 2
	ply.DeathTime = CurTime()

	if ( IsValid( attacker ) && attacker:GetClass() == "trigger_hurt" ) then attacker = ply end

	if ( IsValid( attacker ) && attacker:IsVehicle() && IsValid( attacker:GetDriver() ) ) then
		attacker = attacker:GetDriver()
	end

	if ( !IsValid( inflictor ) && IsValid( attacker ) ) then
		inflictor = attacker
	end

	-- Convert the inflictor to the weapon that they're holding if we can.
	-- This can be right or wrong with NPCs since combine can be holding a
	-- pistol but kill you by hitting you with their arm.
	if ( IsValid( inflictor ) && inflictor == attacker && ( inflictor:IsPlayer() || inflictor:IsNPC() ) ) then

		inflictor = inflictor:GetActiveWeapon()
		if ( !IsValid( inflictor ) ) then inflictor = attacker end

	end

	player_manager.RunClass( ply, "Death", inflictor, attacker )

	if ( attacker == ply ) then

		self:SendDeathNotice( nil, "suicide", ply, 0 )

		MsgAll( attacker:Nick() .. " suicided!\n" )

		return
	end

	if ( attacker:IsPlayer() ) then

		self:SendDeathNotice( attacker, inflictor:GetClass(), ply, 0 )

		MsgAll( attacker:Nick() .. " killed " .. ply:Nick() .. " using " .. inflictor:GetClass() .. "\n" )

		return
	end

	if ( !IsValid( attacker ) ) then attacker = game.GetWorld() end
	if ( !IsValid( inflictor ) ) then inflictor = attacker end

	local flags = 0
	if ( attacker:IsNPC() and attacker:Disposition( ply ) != D_HT ) then flags = flags + DEATH_NOTICE_FRIENDLY_ATTACKER end

	self:SendDeathNotice( self:GetDeathNoticeEntityName( attacker ), inflictor:GetClass(), ply, 0 )

	MsgAll( ply:Nick() .. " was killed by " .. attacker:GetClass() .. "\n" )

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerInitialSpawn()
	Desc: Called just before the player's first spawn
-----------------------------------------------------------]]
function GM:PlayerInitialSpawn( pl, transiton )

	pl:SetTeam( TEAM_UNASSIGNED )

	if ( GAMEMODE.TeamBased ) then
		pl:ConCommand( "gm_showteam" )
	end

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerSpawnAsSpectator()
	Desc: Player spawns as a spectator
-----------------------------------------------------------]]
function GM:PlayerSpawnAsSpectator( pl )

	pl:StripWeapons()

	if ( pl:Team() == TEAM_UNASSIGNED ) then

		pl:Spectate( OBS_MODE_FIXED )
		return

	end

	pl:SetTeam( TEAM_SPECTATOR )
	pl:Spectate( OBS_MODE_ROAMING )

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerSpawn()
	Desc: Called when a player spawns
-----------------------------------------------------------]]
function GM:PlayerSpawn( pl, transiton )

	--
	-- If the player doesn't have a team in a TeamBased game
	-- then spawn him as a spectator
	--
	if ( self.TeamBased && ( pl:Team() == TEAM_SPECTATOR || pl:Team() == TEAM_UNASSIGNED ) ) then

		self:PlayerSpawnAsSpectator( pl )
		return

	end

	-- Stop observer mode
	pl:UnSpectate()

	player_manager.OnPlayerSpawn( pl, transiton )
	player_manager.RunClass( pl, "Spawn" )

	-- If we are in transition, do not touch player's weapons
	if ( !transiton ) then
		-- Call item loadout function
		hook.Call( "PlayerLoadout", GAMEMODE, pl )
	end

	-- Set player model
	hook.Call( "PlayerSetModel", GAMEMODE, pl )

	pl:SetupHands()

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerSetModel()
	Desc: Set the player's model
-----------------------------------------------------------]]
function GM:PlayerSetModel( pl )

	player_manager.RunClass( pl, "SetModel" )

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerSetHandsModel()
	Desc: Sets the player's view model hands model
-----------------------------------------------------------]]
function GM:PlayerSetHandsModel( pl, ent )

	local info = player_manager.RunClass( pl, "GetHandsModel" )
	if ( !info ) then
		local playermodel = player_manager.TranslateToPlayerModelName( pl:GetModel() )
		info = player_manager.TranslatePlayerHands( playermodel )
	end

	if ( info ) then
		ent:SetModel( info.model )
		ent:SetSkin( info.matchBodySkin and pl:GetSkin() or info.skin )
		ent:SetBodyGroups( info.body )
	end

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerLoadout()
	Desc: Give the player the default spawning weapons/ammo
-----------------------------------------------------------]]
function GM:PlayerLoadout( pl )

	player_manager.RunClass( pl, "Loadout" )

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerSelectTeamSpawn( player )
	Desc: Find a spawn point entity for this player's team
-----------------------------------------------------------]]
function GM:PlayerSelectTeamSpawn( TeamID, pl )

	local SpawnPoints = team.GetSpawnPoints( TeamID )
	if ( !SpawnPoints || table.IsEmpty( SpawnPoints ) ) then return end

	local ChosenSpawnPoint = nil

	for i = 0, 6 do

		ChosenSpawnPoint = table.Random( SpawnPoints )
		if ( hook.Call( "IsSpawnpointSuitable", GAMEMODE, pl, ChosenSpawnPoint, i == 6 ) ) then
			return ChosenSpawnPoint
		end

	end

	return ChosenSpawnPoint

end


--[[---------------------------------------------------------
	Name: gamemode:IsSpawnpointSuitable( player )
	Desc: Find out if the spawnpoint is suitable or not
-----------------------------------------------------------]]
local spawnpointmin = Vector( -16, -16, 0 )
local spawnpointmax = Vector( 16, 16, 64 )
function GM:IsSpawnpointSuitable( pl, spawnpointent, bMakeSuitable )

	local Pos = spawnpointent:GetPos()

	-- Note that we're searching the default hull size here for a player in the way of our spawning.
	-- This seems pretty rough, seeing as our player's hull could be different.. but it should do the job
	-- (HL2DM kills everything within a 128 unit radius)
	if ( pl:Team() == TEAM_SPECTATOR ) then return true end

	local Blockers = 0
	for k, v in ipairs( ents.FindInBox( Pos + spawnpointmin, Pos + spawnpointmax ) ) do
		if ( IsValid( v ) && v != pl && v:GetClass() == "player" && v:Alive() ) then

			Blockers = Blockers + 1

			if ( bMakeSuitable ) then
				v:Kill()
			end

		end
	end

	if ( bMakeSuitable ) then return true end
	if ( Blockers > 0 ) then return false end
	return true

end

-- List of all known spawnpoint entity classes
local SpawnPointEntityClasses = {
	-- Half-Life 2 (Deathmatch) Maps
	["info_player_start"] = true,
	["info_player_deathmatch"] = true,
	["info_player_combine"] = true,
	["info_player_rebel"] = true,

	-- Portal 2 Coop
	["info_coop_spawn"] = true,

	-- CS Maps
	["info_player_counterterrorist"] = true,
	["info_player_terrorist"] = true,

	-- DOD Maps
	["info_player_axis"] = true,
	["info_player_allies"] = true,

	-- (Old) GMod Maps
	["gmod_player_start"] = true,

	-- TF Maps
	["info_player_teamspawn"] = true,

	-- INS Maps
	["ins_spawnpoint"] = true,

	-- AOC Maps
	["aoc_spawnpoint"] = true,

	-- Dystopia Maps
	["dys_spawn_point"] = true,

	-- PVKII Maps
	["info_player_pirate"] = true,
	["info_player_viking"] = true,
	["info_player_knight"] = true,

	-- DIPRIP Maps
	["diprip_start_team_blue"] = true,
	["diprip_start_team_red"] = true,

	-- OB Maps
	["info_player_red"] = true,
	["info_player_blue"] = true,

	-- SYN Maps
	["info_player_coop"] = true,

	-- ZPS Maps
	["info_player_human"] = true,
	["info_player_zombie"] = true,

	-- ZM Maps
	["info_player_zombiemaster"] = true,

	-- FOF Maps
	["info_player_fof"] = true,
	["info_player_desperado"] = true,
	["info_player_vigilante"] = true,

	-- L4D Maps
	["info_survivor_rescue"] = true,
	-- Removing this one for the time being, c1m4_atrium has one of these in a box under the map
	--["info_survivor_position"] = true,

	-- NEOTOKYO Maps
	["info_player_attacker"] = true,
	["info_player_defender"] = true,

	-- Fortress Forever Maps
	["info_ff_teamspawn"] = true
}

--[[---------------------------------------------------------
	Name: gamemode:PlayerSelectSpawn( player )
	Desc: Find a spawn point entity for this player
-----------------------------------------------------------]]
function GM:PlayerSelectSpawn( pl, transiton )

	-- If we are in transition, do not reset player's position
	if ( transiton ) then return end

	if ( self.TeamBased ) then
		local ent = self:PlayerSelectTeamSpawn( pl:Team(), pl )
		if ( IsValid( ent ) ) then return ent end
	end

	-- Save information about all of the spawn points
	-- in a team based game you'd split up the spawns
	if ( !IsTableOfEntitiesValid( self.SpawnPoints ) ) then
		self.LastSpawnPoint = 0
		self.HasMasterSpawnPoints = false

		self.SpawnPoints = {}
		for _, ent in ipairs( ents.GetAll() ) do
			if ( SpawnPointEntityClasses[ ent:GetClass() ] ) then
				self.SpawnPoints[#self.SpawnPoints + 1] = ent

				if ( ent:HasSpawnFlags( 1 ) ) then
					self.HasMasterSpawnPoints = true
				end
			end
		end
	end

	local Count = #self.SpawnPoints
	if ( Count == 0 ) then
		Msg("[PlayerSelectSpawn] Error! No spawn points!\n")
		return nil
	end

	-- If any of the spawnpoints have a MASTER flag then only use that one.
	-- This is needed for single player maps.
	if ( self.HasMasterSpawnPoints ) then
		for _, ent in ipairs( self.SpawnPoints ) do
			if ( ent:HasSpawnFlags( 1 ) && hook.Call( "IsSpawnpointSuitable", GAMEMODE, pl, ent, true ) ) then
				return ent
			end
		end
	end

	local ChosenSpawnPoint = nil

	-- Try to work out the best, random spawnpoint
	for i = 1, Count do

		ChosenSpawnPoint = self.SpawnPoints[math.random( Count )]

		if ( IsValid( ChosenSpawnPoint ) && ChosenSpawnPoint:IsInWorld() ) then
			if ( ( ChosenSpawnPoint == pl:GetVar( "LastSpawnpoint" ) || ChosenSpawnPoint == self.LastSpawnPoint ) && Count > 1 ) then continue end

			if ( hook.Call( "IsSpawnpointSuitable", GAMEMODE, pl, ChosenSpawnPoint, i == Count ) ) then

				self.LastSpawnPoint = ChosenSpawnPoint
				pl:SetVar( "LastSpawnpoint", ChosenSpawnPoint )
				return ChosenSpawnPoint

			end

		end

	end

	return ChosenSpawnPoint

end

--[[---------------------------------------------------------
	Name: gamemode:WeaponEquip( weapon )
	Desc: Player just picked up (or was given) weapon
-----------------------------------------------------------]]
function GM:WeaponEquip( weapon )
end

--[[---------------------------------------------------------
	Name: gamemode:ScalePlayerDamage( ply, hitgroup, dmginfo )
	Desc: Scale the damage based on being shot in a hitbox
		 Return true to not take damage
-----------------------------------------------------------]]
function GM:ScalePlayerDamage( ply, hitgroup, dmginfo )

	-- More damage if we're shot in the head
	if ( hitgroup == HITGROUP_HEAD ) then

		dmginfo:ScaleDamage( 2 )

	end

	-- Less damage if we're shot in the arms or legs
	if ( hitgroup == HITGROUP_LEFTARM ||
		 hitgroup == HITGROUP_RIGHTARM ||
		 hitgroup == HITGROUP_LEFTLEG ||
		 hitgroup == HITGROUP_RIGHTLEG ||
		 hitgroup == HITGROUP_GEAR ) then

		dmginfo:ScaleDamage( 0.25 )

	end

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerDeathSound()
	Desc: Return true to not play the default sounds
-----------------------------------------------------------]]
function GM:PlayerDeathSound()
	return false
end

--[[---------------------------------------------------------
	Name: gamemode:SetupPlayerVisibility()
	Desc: Add extra positions to the player's PVS
-----------------------------------------------------------]]
function GM:SetupPlayerVisibility( pPlayer, pViewEntity )
	--AddOriginToPVS( vector_position_here )
end

--[[---------------------------------------------------------
	Name: gamemode:OnDamagedByExplosion( ply, dmginfo)
	Desc: Player has been hurt by an explosion
-----------------------------------------------------------]]
local MIN_SHOCK_AND_CONFUSION_DAMAGE = 30
local MIN_EAR_RINGING_DISTANCE = 240

function GM:OnDamagedByExplosion( ply, info )

	local ear_ringing = false
	local inflictor = info:GetInflictor()
	if ( IsValid( inflictor ) ) then
		local delta = ply:GetPos() - inflictor:GetPos()
		ear_ringing = delta:Length() < MIN_EAR_RINGING_DISTANCE
	end

	local shock = info:GetDamage() >= MIN_SHOCK_AND_CONFUSION_DAMAGE

	if ( !shock and !ear_ringing ) then return end

	-- The effect names are actually backwards
	if ( shock ) then
		ply:SetDSP( math.random( 35, 37 ), false )
		return
	end

	ply:SetDSP( math.random( 32, 34 ), false )
end

--[[---------------------------------------------------------
	Name: gamemode:CanPlayerSuicide( ply )
	Desc: Player typed KILL in the console. Can they kill themselves?
-----------------------------------------------------------]]
function GM:CanPlayerSuicide( ply )
	return true
end

--[[---------------------------------------------------------
	Name: gamemode:CanPlayerEnterVehicle( player, vehicle, role )
	Desc: Return true if player can enter vehicle
-----------------------------------------------------------]]
function GM:CanPlayerEnterVehicle( ply, vehicle, role )
	return true
end

--[[---------------------------------------------------------
	Name: gamemode:PlayerEnteredVehicle( player, vehicle, role )
	Desc: Player entered the vehicle fine
-----------------------------------------------------------]]
function GM:PlayerEnteredVehicle( ply, vehicle, role )
end

--[[---------------------------------------------------------
	Name: gamemode:CanExitVehicle()
	Desc: If the player is allowed to leave the vehicle, return true
-----------------------------------------------------------]]
function GM:CanExitVehicle( vehicle, passenger )
	return true
end

--[[---------------------------------------------------------
	Name: gamemode:PlayerLeaveVehicle()
	Desc: Player left the vehicle
-----------------------------------------------------------]]
function GM:PlayerLeaveVehicle( ply, vehicle )
end

--[[---------------------------------------------------------
	Name: gamemode:PlayerSwitchFlashlight()
	Desc: Return true to allow action
-----------------------------------------------------------]]
function GM:PlayerSwitchFlashlight( ply, SwitchOn )
	return ply:CanUseFlashlight()
end

--[[---------------------------------------------------------
	Name: gamemode:PlayerCanJoinTeam( ply, teamid )
	Desc: Allow mods/addons to easily determine whether a player
		can join a team or not
-----------------------------------------------------------]]
function GM:PlayerCanJoinTeam( ply, teamid )

	local TimeBetweenSwitches = GAMEMODE.SecondsBetweenTeamSwitches or 10
	if ( ply.LastTeamSwitch && RealTime() - ply.LastTeamSwitch < TimeBetweenSwitches ) then
		ply.LastTeamSwitch = ply.LastTeamSwitch + 1
		ply:ChatPrint( Format( "Please wait %i more seconds before trying to change team again", ( TimeBetweenSwitches - ( RealTime() - ply.LastTeamSwitch ) ) + 1 ) )
		return false
	end

	-- Already on this team!
	if ( ply:Team() == teamid ) then
		ply:ChatPrint( "You're already on that team" )
		return false
	end

	return true

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerRequestTeam()
	Desc: Player wants to change team
-----------------------------------------------------------]]
function GM:PlayerRequestTeam( ply, teamid )

	-- No changing teams if not teambased!
	if ( !GAMEMODE.TeamBased ) then return end

	-- This team isn't joinable
	if ( !team.Joinable( teamid ) ) then
		ply:ChatPrint( "You can't join that team" )
	return end

	-- This team isn't joinable
	if ( !GAMEMODE:PlayerCanJoinTeam( ply, teamid ) ) then
		-- Messages here should be outputted by this function
	return end

	GAMEMODE:PlayerJoinTeam( ply, teamid )

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerJoinTeam()
	Desc: Make player join this team
-----------------------------------------------------------]]
function GM:PlayerJoinTeam( ply, teamid )

	local iOldTeam = ply:Team()

	if ( ply:Alive() ) then
		if ( iOldTeam == TEAM_SPECTATOR || iOldTeam == TEAM_UNASSIGNED ) then
			ply:KillSilent()
		else
			ply:Kill()
		end
	end

	ply:SetTeam( teamid )
	ply.LastTeamSwitch = RealTime()

	GAMEMODE:OnPlayerChangedTeam( ply, iOldTeam, teamid )

end

--[[---------------------------------------------------------
	Name: gamemode:OnPlayerChangedTeam( ply, oldteam, newteam )
-----------------------------------------------------------]]
function GM:OnPlayerChangedTeam( ply, oldteam, newteam )

	-- Here's an immediate respawn thing by default. If you want to
	-- re-create something more like CS or some shit you could probably
	-- change to a spectator or something while dead.
	if ( newteam == TEAM_SPECTATOR ) then

		-- If we changed to spectator mode, respawn where we are
		local Pos = ply:EyePos()
		ply:Spawn()
		ply:SetPos( Pos )

	elseif ( oldteam == TEAM_SPECTATOR ) then

		-- If we're changing from spectator, join the game
		ply:Spawn()

	else

		-- If we're straight up changing teams just hang
		-- around until we're ready to respawn onto the
		-- team that we chose

	end

	PrintMessage( HUD_PRINTTALK, Format( "%s joined '%s'", ply:Nick(), team.GetName( newteam ) ) )

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerSpray()
	Desc: Return true to prevent player spraying
-----------------------------------------------------------]]
function GM:PlayerSpray( ply )

	return false

end

--[[---------------------------------------------------------
	Name: gamemode:OnPlayerHitGround()
	Desc: Return true to disable default action
-----------------------------------------------------------]]
function GM:OnPlayerHitGround( ply, bInWater, bOnFloater, flFallSpeed )

	-- Apply damage and play collision sound here
	-- then return true to disable the default action
	--MsgN( ply, bInWater, bOnFloater, flFallSpeed )
	--return true

end

--[[---------------------------------------------------------
	Name: gamemode:GetFallDamage()
	Desc: return amount of damage to do due to fall
-----------------------------------------------------------]]
local mp_falldamage = GetConVar( "mp_falldamage" )

function GM:GetFallDamage( ply, flFallSpeed )

	if ( mp_falldamage:GetBool() ) then -- realistic fall damage is on
		return ( flFallSpeed - 526.5 ) * ( 100 / 396 ) -- the Source SDK value
	end

	return 10

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerCanSeePlayersChat()
	Desc: Can this player see the other player's chat?
-----------------------------------------------------------]]
function GM:PlayerCanSeePlayersChat( strText, bTeamOnly, pListener, pSpeaker )

	if ( bTeamOnly ) then
		if ( !IsValid( pSpeaker ) || !IsValid( pListener ) ) then return false end
		if ( pListener:Team() != pSpeaker:Team() ) then return false end
	end

	return true

end

local sv_alltalk = GetConVar( "sv_alltalk" )

--[[---------------------------------------------------------
	Name: gamemode:PlayerCanHearPlayersVoice()
	Desc: Can this player see the other player's voice?
		Returns 2 bools.
		1. Can the player hear the other player
		2. Can they hear them spacially
-----------------------------------------------------------]]
function GM:PlayerCanHearPlayersVoice( pListener, pTalker )

	local alltalk = sv_alltalk:GetInt()
	if ( alltalk >= 1 ) then return true, alltalk == 2 end

	return pListener:Team() == pTalker:Team(), false

end

--[[---------------------------------------------------------
	Name: gamemode:NetworkIDValidated()
	Desc: Called when Steam has validated this as a valid player
-----------------------------------------------------------]]
function GM:NetworkIDValidated( name, steamid )

	-- MsgN( "GM:NetworkIDValidated", name, steamid )

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerShouldTaunt( ply, actid )
-----------------------------------------------------------]]
function GM:PlayerShouldTaunt( ply, actid )

	-- The default behaviour is to always let them act
	-- Some gamemodes will obviously want to stop this for certain players by returning false
	return true

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerStartTaunt( ply, actid, length )
-----------------------------------------------------------]]
function GM:PlayerStartTaunt( ply, actid, length )
end

--[[---------------------------------------------------------
	Name: gamemode:AllowPlayerPickup( ply, object )
-----------------------------------------------------------]]
function GM:AllowPlayerPickup( ply, object )

	-- Should the player be allowed to pick this object up (using ENTER)?
	-- If no then return false. Default is HELL YEAH

	return true

end

--[[---------------------------------------------------------
	Name: gamemode:PlayerDroppedWeapon()
	Desc: Player has dropped a weapon
-----------------------------------------------------------]]
function GM:PlayerDroppedWeapon( ply, weapon )
end

--[[---------------------------------------------------------
	These are buttons that the client is pressing. They're used
	in Sandbox mode to control things like wheels, thrusters etc.
-----------------------------------------------------------]]
function GM:PlayerButtonDown( ply, btn ) end
function GM:PlayerButtonUp( ply, btn ) end

concommand.Add( "changeteam", function( pl, cmd, args ) hook.Call( "PlayerRequestTeam", GAMEMODE, pl, tonumber( args[ 1 ] ) ) end )

--[[---------------------------------------------------------
	Name: gamemode:HandlePlayerArmorReduction()
	Desc: Handle player armor reduction
-----------------------------------------------------------]]
function GM:HandlePlayerArmorReduction( ply, dmginfo )

	-- If no armor, or special damage types, bypass armor
	if ( ply:Armor() <= 0 || bit.band( dmginfo:GetDamageType(), DMG_FALL + DMG_DROWN + DMG_POISON + DMG_RADIATION ) != 0 ) then return end

	local flBonus = 1.0 -- Each Point of Armor is worth 1/x points of health
	local flRatio = 0.2 -- Armor Takes 80% of the damage
	if ( GetConVar( "player_old_armor" ):GetBool() ) then
		flBonus = 0.5
	end

	local flNew = dmginfo:GetDamage() * flRatio
	local flArmor = (dmginfo:GetDamage() - flNew) * flBonus

	if ( !GetConVar( "player_old_armor" ):GetBool() ) then
		if ( flArmor < 0.1 ) then flArmor = 0 end -- Let's not have tiny amounts of damage reduce a lot of our armor
		else if ( flArmor < 1.0 ) then flArmor = 1.0 end
	end

	-- Does this use more armor than we have?
	if ( flArmor > ply:Armor() ) then

		flArmor = ply:Armor() * ( 1 / flBonus )
		flNew = dmginfo:GetDamage() - flArmor
		ply:SetArmor( 0 )

	else
		ply:SetArmor( ply:Armor() - flArmor )
	end

	dmginfo:SetDamage( flNew )

end
