--[GL]
Script:LoadScript("SCRIPTS/Server/GLcommands.lua");
Script:LoadScript("SCRIPTS/Server/GLplayertrack.lua");
Script:LoadScript("SCRIPTS/Server/ServerCmds.lua");
Script:LoadScript("SCRIPTS/Server/Task.lua");
--Script:LoadScript("SCRIPTS/GL/GLmessage.lua");
--[end GL]

------------------------------------------------------------------------
--	Helper functions for GameRules implementation
--
--	created by Alberto
--  modified by MartinM
-- 	RenEvo SSM & Kerpo SSM & GodLike SSM mixed by KoRi
--
-------------------------------------------------------------------------

-- teams:
--
-- players
-- spectators

-- If someone in script needs to know if we are running multiplayer or single player game.
GameRules.bMultiplayer = 1;
GameRules.bSingleplayer = nil;
GameRules.bIsTeamBased = nil;

CGS_INPROGRESS = 0;
CGS_COUNTDOWN = 1;
CGS_PREWAR = 2;
CGS_INTERMISSION = 3;

GameRules.states_map=
{
	[CGS_INPROGRESS] ="INPROGRESS",
	[CGS_COUNTDOWN] ="COUNTDOWN",
	[CGS_PREWAR] ="PREWAR",
	[CGS_INTERMISSION] = "INTERMISSION",
}
GameRules.TeamText = {red = "@TeamNameRed", blue = "@TeamNameBlue", players = "@TeamNamePlayers", spectators = "@TeamNameSpectators",};

GameRules.RespawnPoints={};										-- {[1]={x=,y=,z=,xA=,yA=,zA=,name=,lastUsedTime=},} use RecreateTableOfRespawnPoints(), lastUsedTime might be nil
GameRules.idCurrentMissionObject=nil;					-- used to make only one MissionObject entity active at a time
GameRules.idScoreboard=nil;										-- scoreboard entity id

if not GameRules.InitialPlayerStatistics then
	GameRules.InitialPlayerStatistics = {};			-- used for MP statistics but can be used for SP as well
end


GameRules.InitialPlayerStatistics["nKill"]=0;					-- this entry can be used by MPStatistics:AddStatisticsDataEntity();
GameRules.InitialPlayerStatistics["nSelfKill"]=0;			-- this entry can be used by MPStatistics:AddStatisticsDataEntity();
GameRules.InitialPlayerStatistics["nBulletShot"]=0;		-- this entry can be used by MPStatistics:AddStatisticsDataEntity();
GameRules.InitialPlayerStatistics["nBulletHit"]=0;			-- this entry can be used by MPStatistics:AddStatisticsDataEntity();
GameRules.InitialPlayerStatistics["nBulletTarget"]=0;		-- this entry can be used by MPStatistics:AddStatisticsDataEntity();
GameRules.InitialPlayerStatistics["nHeadshot"]=0;			-- this entry can be used by MPStatistics:AddStatisticsDataEntity();
GameRules.InitialPlayerStatistics["nDeaths"]=0;
GameRules.InitialPlayerStatistics["nTeam"]="spectators";
GameRules.InitialPlayerStatistics["nClass"]="-1";
GameRules.InitialPlayerStatistics["nBadWords"]=0;


-- e.g. GameRules.ClientCommandTable["CPC"]=function(...
if not GameRules.ClientCommandTable then
	GameRules.ClientCommandTable={};				-- used from OnClientCmd() to process commands on the server from the client
end

--[GL playertracking]
GLplayerTrack:Init();
--[end GL]

--------------------------------------------------------------------
-- Return Server Rules Table from 1..n
-- Each Rule is a table from 1..2 with 1=rulename 2=value
--------------------------------------------------------------------
function GameRules:GetServerRules()
	function PushRule(RuleTable, Rule)
		tinsert(RuleTable, {Rule, getglobal(Rule),});
	end

	local Rules = {};
	PushRule(Rules, "gr_ScoreLimit");
	PushRule(Rules, "gr_TimeLimit");
	PushRule(Rules, "gr_DamageScale");
	PushRule(Rules, "gr_HeadshotMultiplier");
	PushRule(Rules, "gr_RespawnTime");
	PushRule(Rules, "gr_PrewarOn");
	PushRule(Rules, "gr_DropFadeTime");
	PushRule(Rules, "gr_FriendlyFire");
	PushRule(Rules, "gr_MinTeamLimit");
	PushRule(Rules, "gr_MaxTeamLimit");
	PushRule(Rules, "gr_InvulnerabilityTimer");	

	PushRule(Rules, "gr_NextMap");
	PushRule(Rules, "gr_DedicatedServer");
	
	PushRule(Rules, "sv_punkbuster");
	
	-- DISCARD REMAINING AMMO IN CLIP START --
	PushRule(Rules, "gr_discardclip");	
	-- DISCARD REMAINING AMMO IN CLIP END --

	-- DISABLE RL START --
	PushRule(Rules, "gr_disable_sniperrl");
	-- DISABLE RL STOP --

	-- BADWORDS --
	PushRule(Rules, "gr_bwk_enable");
--	PushRule(Rules, "gr_bwk_words");
	PushRule(Rules, "gr_bwk_time");
	PushRule(Rules, "gr_bwk_limit");
	-- BADWORDS END--

	-- auto teampick --
	PushRule(Rules, "gr_auto_pickteam");
	-- auto teampick end --

	return Rules;
end


-------------------------------------------------------------------------------
--
function GetSlotPlayer(slot)
    return System:GetEntity(slot:GetPlayerId());
end

-------------------------------------------------------------------------------
--
function GameRules:GetPlayerScoreInfo(ServerSlot, Stream)
	Stream:WriteByte(ServerSlot:GetId());
	Stream:WriteShort(ServerSlot:GetPing());
end

-------------------------------------------------------------------------------
--
function DeletePlayer(id)
--	System:Log("DeletePlayer("..tostring(id)..")");
	if(id and id~=0)then
		Server:RemoveFromTeam(id);
		Server:RemoveEntity(id);
	end
end

-------------------------------------------------------------------------------
--
function GetPlayers()
	local players={}
	local slots=Server:GetServerSlotMap()
	for i, slot in slots do
	    local ent = System:GetEntity(slot:GetPlayerId());
		if ent and ent.type=="Player" then
		   players[ent.id]=ent;
		end
	end
	return players;
end



-------------------------------------------------------------------------------
--
function GameRules:Restart(x, quiet)
	local rrtime = 1;

	if(tonumber(x))then						-- can be converted to a number
		rrtime = tonumber(x);
	end
	
	self.restartbegin = _time;
	self.restartend = _time + rrtime;
	
	if (not quiet) then
		Server:BroadcastText("@GameRestartIn $3"..rrtime.."$o @GameStartingInSeconds", 1);
	end
end

-------------------------------------------------------------------------------
--
function GameRules:SetPlayerTimeLimit(Slot)
	local bReady = Slot:IsContextReady();
	
	if (bReady and tonumber(bReady) ~= 0) then
		local dam=format("%.2f", tonumber(self.timelimit));

		Slot:SendCommand("UTL "..dam);
	end
end


-------------------------------------------------------------------------------
--
function GameRules:UpdateTimeLimit(timelimit)
	self.timelimit = timelimit;
	
	local SlotMap = Server:GetServerSlotMap();
	
	for i, Slot in SlotMap do
		self:SetPlayerTimeLimit(Slot);
	end
end

-------------------------------------------------------------------------------
-- force scoreboard on all players
function GameRules:ForceScoreBoard(yes)
	local SlotMap = Server:GetServerSlotMap();
	
	for i, Slot in SlotMap do
		Game:ForceScoreBoard(Slot:GetPlayerId(), yes);
	end
end

-------------------------------------------------------------------------------
-- multicast the gamestate (one of the CGS_* above)
function GameRules:NewGameState(state)
	local slots = Server:GetServerSlotMap();
	
	for i, slot in slots do
		slot:SetGameState(state, _time-self.mapstart);
  end
  
--  System:Log("self:GotoState("..self.states_map[state]..");");
 	self:GotoState(self.states_map[state]);
end

-------------------------------------------------------------------------------
-- stats for respawning
--function GameRules:GetInitialPlayerProperties(server_slot)
--	return self.InitialPlayerProperties;
--end


-------------------------------------------------------------------------------
-- spawn a new player and put it in back in a team
-- custom properties can be nil(if the mod is not class based)
function GameRules:SpawnPlayer(server_slot,classid,team,custom_properties)

	--create a new player entity(in this MOD it always connect as spectator)
--	if(team)then
--		System:Log("GameRules:SpawnPlayer team="..team)
--	end
	GLplayerTrack:OnRespawn(server_slot);
	GLplayerTrack:SetBySs(server_slot, "lastteam", team, 0);
	
	local locInitialPlayerProperties=self:GetInitialPlayerProperties(server_slot);
	if locInitialPlayerProperties then
		GLplayerTrack:SetBySs(server_slot, "lastclass", locInitialPlayerProperties.sPlayerClass, 0);
	end
	
	local _model = server_slot:GetModel();
	local team_color;
	local rp;
	local iSpecies=0;


	-- make sure to reset viewlayers
	server_slot:SendCommand("GI WS");		

	if(team=="spectators") then				
		rp=Server:GetRandomRespawnPoint(team);
		if(rp==nil)then
			rp = Server:GetRandomRespawnPoint();
			if (rp==nil) then
				System:Log("GameRules:SpawnPlayer spawnpoints are missing")
				rp = {x=0, y=0, z=0, xA=0, yA=0, zA=0};
			else
				System:Log("GameRules:SpawnPlayer spawnpoints for '"..team.."' are missing")
			end
			--if toNumberOrZero(getglobal("gl_allow_spectators"))==0 then
			--	rp = {x=1000, y=1000, z=1000, xA=0, yA=0, zA=0};
			--end			
		end
	else	-- not "spectators"
		rp=self:GetTeamRespawnPoint(team,System:GetEntity(server_slot:GetPlayerId()));	--NOTE GetTeamRespawnPoint has to be implemented in the gamerules for each team MOD
		if (rp==nil) then
			System:Log("GameRules:SpawnPlayer haven't found a TeamRespawnPoint")
			rp=Server:GetRandomRespawnPoint();
			if (rp==nil) then
				rp={x=0, y=0, z=0, xA=0, yA=0, zA=0};
			end
		end

		if locInitialPlayerProperties and locInitialPlayerProperties.model then
			_model=locInitialPlayerProperties.model;
		end
		local Player=GetSlotPlayer(server_slot);
		if(team=="red")then
			team_color={1,0,0};
			iSpecies=1;
		elseif(team=="blue")then
			team_color={0,0,1};
			iSpecies=0;
		end
	end
	
	if(not team_color)then
	
		local sColor=tonumber(server_slot:GetColor())+1;			-- [0|1|2|3|4|5|6|7|8|9]
		
		team_color = MultiplayerUtils.ModelColor[sColor];
		
		if (not team_color) then
			team_color = {0,0,0};
		end
--		System:Log("sColor = "..sColor);
	end
	
--	System:Log("***********************************************");
--	dump(team_color);
		
    -- TODO: replace randomness by actual collision detection
	local pos = {x = rp.x, y = rp.y, z = rp.z};
	local ang = {x = rp.xA, y = rp.yA, z = rp.zA};
	local cproperties = {
		bHasArmor = 0,
		bHelmetOnStart = 0,
		eye_height = 2.0,
		species = iSpecies,
		max_health = 255,
		groupid = iSpecies,
		bTrackable=1,
		}	
	local sEntName=server_slot:GetName();
	local newt={}
	if (custom_properties) then
		mergef(newt,custom_properties,1);
	end
	
	mergef(newt,cproperties,1); 
	local newent=Server:SpawnEntity({
		classid=classid,
		name=sEntName,
		pos=pos,
		angles=ang,
		model=_model,
		color=team_color,
		properties=newt,
	})
	local oldid=server_slot:GetPlayerId();

	--bind the new created player to the serverslot
	server_slot:SetPlayerId(newent.id);
	
	--delete the old player entity
	if oldid~=0 then
		DeletePlayer(oldid);
	end
	
	local debug=GetSlotPlayer(server_slot);
	if debug~=newent then
		error.error=1;
	end
	if locInitialPlayerProperties then
--		Server:BroadcastText("slot species = "..newent.Properties.species);
--		Server:BroadcastText("slot groupid = "..newent.Properties.groupid);
	end
	self:OnAfterSpawnEntity(server_slot);	
	Game:ForceScoreBoard(newent.id, 0);
	
	return newent;
end


-------------------------------------------------------------------------------
-- is updating GameRules.RespawnPoints
function GameRules:RecreateTableOfRespawnPoints()
	GameRules.RespawnPoints={};
	local i=1;
	
--	System:Log("RecreateTableOfRespawnPoints");--debug

	-- spawn at the first unoccupied tagpoint
	local pt = Server:GetFirstRespawnPoint();

	while(pt)do
		local entry=new(pt);
		GameRules.RespawnPoints[i] = entry;
		i=i+1;
		
--		System:Log("RecreateTableOfRespawnPoints "..i);--debug

		pt=Server:GetNextRespawnPoint();
	end	
end


-------------------------------------------------------------------------------
function GameRules:GetFreeTeamRespawnPoint(team,entityToIgnore)
	local respawnPoint=nil;
	local isPointExisting=0;
	local dz=0; -- Used to raise the spawn posititions if all checkpoints are full

	local ents=System:GetEntities();		-- very inefficient, needed for IsIntersectingPlayerOrVehicle

--	System:Log("GetFreeTeamRespawnPoint -------------------------");--debug

	-- Repeat until we find a point
	while (respawnPoint==nil) do
		-- spawn at the first unoccupied tagpoint
		
		for key,pt in GameRules.RespawnPoints do
			if(pt.name==team)then
				isPointExisting=1;
--				pt.z = pt.z + dz;
--				System:Log("GetFreeTeamRespawnPoint ...");--debug
--				dump(pt);	--debug
				if(GameRules:IsIntersectingPlayerOrVehicle(pt.x, pt.y, pt.z+dz, ents, entityToIgnore)==0)then
--					System:Log("IsIntersectingPlayerOrVehicle =0");	--debug
					if(respawnPoint)then
						if(respawnPoint.lastUsedTime and (not pt.lastUsedTime or pt.lastUsedTime<respawnPoint.lastUsedTime))then 
							respawnPoint = pt;		-- found a better one
--							System:Log("IsIntersectingPlayerOrVehicle a");	--debug
						end
					else
						respawnPoint = pt;		-- I found the first acceptable
--						System:Log("IsIntersectingPlayerOrVehicle b");	--debug
					end
				end
			end
		end	

		if(respawnPoint)then
			respawnPoint.lastUsedTime=_time;

			respawnPoint = new(respawnPoint);		-- create a copy because we will change the point
			respawnPoint.z=respawnPoint.z+dz;

			return respawnPoint;
		end

		if(isPointExisting==0)then
			return nil;				-- there is no spawnpoint for this team
		end
	
		-- all points full!  Raise up a bit
		dz=dz+2;
		
		if(dz>20)then
			System:Error("GameRules:GetFreeTeamRespawnPoint "..tostring(team).." failed");
			return nil;				-- to prevent endless loop
		end;	
	end

	return respawnPoint;
end

-------------------------------------------------------------------------------
-- returns the number of intersecting players at a particular point
function GameRules:IsIntersectingPlayerOrVehicle(x,y,z, ents, entityToIgnore)

	for i, entity in ents do
		if (entity~=entityToIgnore and entity.type=="Player") then

			local pos = entity:GetPos();
			local _dx=pos.x-x;
			local _dy=pos.y-y;
			local _dz=pos.z-z;
			-- if the entity is closer than 1 meter then consider it intersecting
			if (((_dx * _dx) + (_dy * _dy) + (_dz * _dz)) < 1) then
				return 1;
			end;
			
		elseif (entity.type=="Vehicle") then
			local bbox=entity:GetBBox();
			local min=bbox.min; local max=bbox.max;
			if (x > min.x and x < max.x and
					y > min.y and y < max.y and
					z > min.z and z < max.z) then
						return 1;

			end
		end
	end
	
	return 0;
end

-------------------------------------------------------------------------------
--
function GameRules:OnAfterSpawnEntity( server_slot )

	local newent=GetSlotPlayer(server_slot);
		
	if newent.type=="Player" then
	
		local locInitialPlayerProperties=self:GetInitialPlayerProperties(server_slot);
		
		--set the default stats
		if locInitialPlayerProperties then
			newent.cnt.health			= locInitialPlayerProperties.health;
			newent.cnt.max_health	= locInitialPlayerProperties.health;
			newent.cnt.armor			= locInitialPlayerProperties.armor;			

			if locInitialPlayerProperties.StaminaTable then
				newent.cnt:InitStaminaTable( locInitialPlayerProperties.StaminaTable );
			end	
			
			if locInitialPlayerProperties.move_params then
				newent.cnt:SetMoveParams( locInitialPlayerProperties.move_params );
			end	
			
			newent.Properties.equipEquipment = locInitialPlayerProperties.equipEquipment;
			-- have to make a copy of the table at this point, because otherwise when the
			-- initial player properties change (class change, but before spawn) these
			-- changes will alias to Properties.equipEquipment and we won't know what stuff
			-- the player has anymore
			newent.currentEquipment = new(EquipPacks[newent.Properties.equipEquipment]);
			newent.sCurrentPlayerClass=locInitialPlayerProperties.sPlayerClass;		-- to identify class name
			
			-- your class is now <MultiplayerClassDefiniton.PlayerClasses>
			if locInitialPlayerProperties.sPlayerClass then
				Server:BroadcastCommand("YCN "..server_slot:GetPlayerId().." "..locInitialPlayerProperties.sPlayerClass);
			else
				Server:BroadcastCommand("YCN "..server_slot:GetPlayerId().." DefaultMultiPlayer");
			end

			BasicPlayer.InitAllWeapons(newent, 1);	-- because equipEquipment has changed
		end
			
		if (gr_InvulnerabilityTimer~=nil and toNumberOrZero(gr_InvulnerabilityTimer)>0) then
--			System:Log("Player "..newent:GetName().." spawned, activated invulnerbility");
			newent.invulnerabilityTimer=toNumberOrZero(gr_InvulnerabilityTimer);
			-- turn on invulnerability shader
--			System:Log("FX: Setting invulnerability shader: "..tostring(newent.name));
			Server:BroadcastCommand("FX", g_Vectors.v000, g_Vectors.v000, newent.id, 2);
		end
	
		newent.cnt.alive=1;
		newent:EnablePhysics(1);

		-- we would like its viewdistratio to be max (== 255)
		newent:SetViewDistRatio(255);
		
		if self.bShowUnitHightlight then
			local highlightpos=new(newent:GetPos());
			local highlight = Server:SpawnEntity("UnitHighlight",highlightpos);
		
			newent.idUnitHighlight = highlight.id;		-- only done on the server
		
			-- send "follow this player" command
			Server:BroadcastCommand("FX "..tonumber(newent.id), g_Vectors.v000, g_Vectors.v000,highlight.id,0);
		end

		-- reset viewlayers
		server_slot:SendCommand("GI WS");				
		-- deactivate radar enemies
		server_slot:SendCommand("HUD RR");		
	end
end

function GameRules:GetPlayerTeamCount( team )
	local ret=0;
	local slots = Server:GetServerSlotMap();

	for i, slot in slots do
		local player_id=slot:GetPlayerId();

		if Game:GetEntityTeam(player_id)==team then
			ret=ret+1;
		end
	end

	return ret;
end

-------------------------------------------------------------------------------
-- called to change the team for a client, either because the mode has
-- changed, or the player hAs requested so manually (OnClientJoinTeamRequest).
-- the change may be denied if required
function GameRules:ChangeTeam(server_slot, new_team, force)
	
	--[GL punished players cannot change team]
	if GLcommands:requestSpawn(server_slot)==0 then
		return;
	end
	
	if toNumberOrZero(getglobal("gl_allow_spectators"))==0 then
		if new_team=="spectators" then
			server_slot:SendText(getglobal("gl_ssmod_id").." spectating is not allowed in this game");
			return;	
		end
	end
		
	--[end GL]
	
	local player_id = server_slot:GetPlayerId();
	local requested_classid = PLAYER_CLASS_ID;
	
	if player_id ~= 0 then
		local player = System:GetEntity(player_id);
		local old_team = Game:GetEntityTeam(player_id);
					
		if force or new_team ~= old_team then
			System:Log("player '"..tostring(server_slot:GetName()).."$1' changed team from "..tostring(old_team).." to "..tostring(new_team));
    
   		if new_team=="spectators" then
				requested_classid=SPECTATOR_CLASS_ID;
				
				if (self.respawnList) then
					self.respawnList[server_slot] = nil;
				end

				local newEntity=self:SpawnPlayer(server_slot,requested_classid,new_team);
				Server:AddToTeam("spectators",newEntity.id);

				return;
			end

			if (self.respawnList and  (self:GetGameState() ~= CGS_PREWAR)) then
				System:Log("changing team! "..new_team.." team!");
				self.respawnList[server_slot] = {classid = requested_classid, team = new_team,};
			else
				local newEntity=self:SpawnPlayer(server_slot,requested_classid,new_team);
				Server:AddToTeam(new_team, newEntity.id);
				
				if (self.SetTeamObjectivesOnePlayer) then
					self:SetTeamObjectivesOnePlayer(server_slot);
				end
					
				if (self.UpdateCaptureProgressOnePlayer) then
					self:UpdateCaptureProgressOnePlayer(server_slot);
				end				
			end
		end
	end
end

function GameRules:GetTeamMemberCountRL(team)	
	local iCount = 0;	
	
	if (self.respawnList) then
		local Slots = Server:GetServerSlotMap();
		
		for i,ServerSlot in Slots do
		
			local RespawnInfo = self.respawnList[ServerSlot];
			local szPlayerTeam;
			
			if (not RespawnInfo) then
				szPlayerTeam = Game:GetEntityTeam(ServerSlot:GetPlayerId());
			else
				szPlayerTeam = RespawnInfo.team;
				
				if (not szTeam) then
					szPlayerTeam = Game:GetEntityTeam(ServerSlot:GetPlayerId());
				end
			end
			
			if (team == szPlayerTeam) then
				iCount = iCount + 1;
			end
		end
	else
		iCount = iCount + Server:GetTeamMemberCount(team);
	end

	return iCount;
end


-------------------------------------------------------------------------------
function GameRules:HandleJoinTeamRequest(server_slot,new_team)
	--[GL start]
	
	--player got some kinda respawn punishment
	if GLcommands:requestSpawn(server_slot)==0 then
		return;
	end
	
	if toNumberOrZero(getglobal("gl_allow_spectators"))==0 then
		if new_team=="spectators" then
			server_slot:SendText(getglobal("gl_ssmod_id").." spectating is not allowed in this game");
			return;	
		end
	end
		
	--first, check if player is forced to 1 team
	local forcedTeam=GLplayerTrack:GetBySs(server_slot, "forcedteam");
	
	if forcedTeam ~="" then 
		if new_team~="spectators" then
			new_team=forcedTeam;
			Server:BroadcastText(getglobal("gl_ssmod_id").."$o player "..server_slot:GetName().."$o is not allowed to be in other team than "..new_team);
		end
	end
	
	--check for global teamforcing
	if forcedTeam == "" then
		if toNumberOrZero(getglobal("gl_forcebluejoin")) == 1 and GLcommands:ProtectedName(server_slot:GetName())~=1 then
			if new_team == "red" then
				new_team = "blue";
				Server:BroadcastText(getglobal("gl_ssmod_id").." player "..server_slot:GetName().."$o cannot join (red team is locked)");
			end  
		end
		if toNumberOrZero(getglobal("gl_forceredjoin")) == 1 and GLcommands:ProtectedName(server_slot:GetName())~=1 then
			if new_team == "blue" then
				new_team = "red";
				Server:BroadcastText(getglobal("gl_ssmod_id").." player "..server_slot:GetName().."$o cannot join (blue team is locked)");
			end		
		end
	end
	--[end GL]
	
	local maxplayers = toNumberOrZero(getglobal("gr_MaxTeamLimit"));
	
	if (maxplayers and maxplayers > 0 and self.bIsTeamBased) then
		if new_team~="spectators" and self:GetPlayerTeamCount(new_team)>=maxplayers then
	  	Server:BroadcastText("@ReachedMaxTeam "..GameRules.TeamText[new_team]);
			return;
		end
	end
	local player_id = server_slot:GetPlayerId();
	local old_team = Game:GetEntityTeam(player_id);
	local locInitialPlayerProperties=self:GetInitialPlayerProperties(server_slot);
	if locInitialPlayerProperties then
		local glClass=locInitialPlayerProperties.sPlayerClass;
		local activeClass=GameRules:CountClass(server_slot, glClass, new_team);
		if glClass=="Sniper" and activeClass>=tonumber(getglobal("gl_max_snipers")) then
			--server_slot:SendText(getglobal("gl_ssmod_id").." "..server_slot:GetName().."$4 NOT ALLOWED TO JOIN AS SNIPER: $6the maximum number of snipers is : "..tonumber(getglobal("gl_max_snipers")));
			new_team=old_team;
			GLcommands:SetPC(server_slot,"Grunt",1,1,1,3);
			Server:BroadcastText(getglobal("gl_ssmod_id").." "..server_slot:GetName().."$4 NOT ALLOWED TO JOIN AS SNIPER: $6the maximum number of snipers is : "..tonumber(getglobal("gl_max_snipers")));
			return;
		end
		if glClass=="Grunt" and activeClass>=tonumber(getglobal("gl_max_scouts")) then
			server_slot:SendText(getglobal("gl_ssmod_id").." "..server_slot:GetName().."$4 NOT ALLOWED TO JOIN AS SCOUT: $6the maximum number of scouts is : "..tonumber(getglobal("gl_max_scouts")));
			new_team="spectators";
			Server:BroadcastText(getglobal("gl_ssmod_id").." "..server_slot:GetName().."$o was moved to spectators. Reason : too many scouts in that team");
		end
		if glClass=="Support" and activeClass>=tonumber(getglobal("gl_max_engineers")) then
			server_slot:SendText(getglobal("gl_ssmod_id").." "..server_slot:GetName().."$4 NOT ALLOWED TO JOIN AS ENGINEER: $6the maximum number of engineers is : "..tonumber(getglobal("gl_max_engineers")));
			new_team="spectators";
			Server:BroadcastText(getglobal("gl_ssmod_id").." "..server_slot:GetName().."$o was moved to spectators. Reason : too many engineers in that team");
		end
	end
	----
	    -- Team Balancing --
	local team_balance = tonumber(getglobal("gr_team_balance"));
	local game_mode = tostring(getglobal("g_GameType"));
	local team1 = 0;
	local team2 = 0;  	

	if (team_balance==1) then
		if (game_mode ~= "FFA") then
			if (old_team~="spectators") then
				if (self.bIsTeamBased) then
					if (new_team~="spectators") then
						team1 = self:GetPlayerTeamCount(new_team);
				  		team2 = self:GetPlayerTeamCount(old_team);
				  		if (team1 >= team2) then
				  		    Server:BroadcastText("AutoBalance: "..server_slot:GetName().." cannot switch teams");
				  			return;
				  		end
					end
				end
			elseif (old_team=="spectators") then
				if (self.bIsTeamBased) then
					team1=self:GetPlayerTeamCount(new_team);
					if new_team=="blue" then
						team2=self:GetPlayerTeamCount("red");
					elseif new_team=="red" then
						team2=self:GetPlayerTeamCount("blue");
					end
					if (team1 > team2) then
						Server:BroadcastText("AutoBalance: "..server_slot:GetName().." cannot join team "..new_team);
			  			return;
					end
				end
			end
		end
	end
	
	----

	-- Team Locking --

	  local allowswitch = tonumber(getglobal("gr_allow_teamswitch"));
	  if (allowswitch==1 or old_team=="spectators" or new_team=="spectators" or self.bIsTeamBased==false) then
	  		self:ChangeTeam(server_slot,new_team,nil);
	  else
	     Server:BroadcastText(server_slot:GetName().."$o Cannot Switch Teams");
	     return;
	  end
	  -- END EDIT --

	if(new_team=="spectators" or new_team=="players")then
	  Server:BroadcastText(server_slot:GetName().."$o @HasJoinedThe "..GameRules.TeamText[new_team]);
	else
	  Server:BroadcastText(server_slot:GetName().."$o @HasJoinedTeam "..GameRules.TeamText[new_team]);
	end
	
  server_slot:Ready(nil);
  server_slot:ResetPlayTime();
end


-------------------------------------------------------------------------------
function GameRules:ResetReadyState(state)
	local slots = Server:GetServerSlotMap();
	for i, slot in slots do
		slot:Ready(state);
	end
end


-------------------------------------------------------------------------------
--return a non nil value if the slot has a "player entity" assigned to him
function IsPlayer(slot)
    return System:GetEntity(slot:GetPlayerId()).type=="Player";
end


-------------------------------------------------------------------------------
-- callback for when the player uses the "team" console command
function GameRules:OnClientJoinTeamRequest(server_slot,team_name)
	self:Invoke("OnClientJoinTeamRequest",server_slot,team_name);
end



-------------------------------------------------------------------------------
function GameRules:GetGameState()
	local curstate=self:GetState();
	for i,val in self.states_map do
		if(val==curstate)then
			return i
		end
	end
end


-------------------------------------------------------------------------------
-- clean up when a client leaves
function GameRules:OnClientDisconnect( server_slot )
	-- this must be called before the player is deleted,
	-- because we might want to do some processing on player data	
	
	--[GL store last used name]
	GLplayerTrack:SetBySs(server_slot,"name",server_slot:GetName(),0);
	--[end GL]
	
	self:Invoke("OnClientDisconnect",server_slot);

	Server:BroadcastText(server_slot:GetName().."$o @HasLeftGame");
	--remove the player from the team
	--remove the entity
	local player_id = server_slot:GetPlayerId();
	server_slot:SetPlayerId(0); -- set an invalid player id
	DeletePlayer(player_id);
	
	self:RemovePlayerFromScoreboard(server_slot:GetId());
	
end

-------------------------------------------------------------------------------
-- first time connect, mod can decide how to add the new client to the
-- game (spectator or not, etc). needs to spawn new entity.
function GameRules:OnClientConnect( server_slot, requested_classid )

	local reds =  GameRules:GetTeamMemberCountRL("red");
	local blues = GameRules:GetTeamMemberCountRL("blue");
	local tiimi="red";
	if reds and blues then
		if reds>blues then
			tiimi="blue";
		end
	end

	local newPlayer=self:SpawnPlayer(server_slot,SPECTATOR_CLASS_ID,"spectators");
	
	--inform player about current game state
	server_slot:SetGameState(self:GetGameState(), _time-self.mapstart);
	server_slot:Ready(nil);
	server_slot:ResetPlayTime();

	Server:BroadcastText(server_slot:GetName().."$o @HasConnected");
	    server_slot:SendText("This server is running GodLike & RenEvo SSM & Kerpo SSM mixed by KoRi");
    	if (tostring(getglobal("gr_motd"))) then
    		server_slot:SendText(tostring(getglobal("gr_motd")));
    	end

	Server:AddToTeam("spectators",newPlayer.id);
	
	local pt;
	pt=Server:GetRandomRespawnPoint("spectators")
	if(pt==nil)then
		pt = Server:GetFirstRespawnPoint();
		System:Log("ERROR: Spawn points for 'spectators' are missing, please check your map");
		if (pt==nil) then
			System:Log("ERROR: There are no spawn points");
			pt = {x=0, y=0, z=0, xA=0, yA=0, zA=0};
		end
	end
	
	newPlayer:SetPos(pt);	
	newPlayer:SetAngles({x=pt.xA, y=pt.yA, z=pt.zA});

	if self.bShowUnitHightlight then
		-- send "follow this player" command to the UnitHightlight objects
	  local slots = Server:GetServerSlotMap();
		for i, slot in slots do
	    local ent = System:GetEntity(slot:GetPlayerId());
	    
	    if ent and ent.idUnitHighlight then
				server_slot:SendCommand("FX "..tonumber(ent.id), g_Vectors.v000, g_Vectors.v000,ent.idUnitHighlight,0);
			end
	 	end
	end

	if floor(toNumberOrZero(getglobal("gr_RespawnTime")))~=0 and self.respawnCycle then
		server_slot:SendCommand("RTR "..tostring(self.respawnCycleTimer));
	end
	
	if (self.SetPlayerTimeLimit) then
		self:SetPlayerTimeLimit(server_slot);
	end
	
	-- update the teamcoloring/invulnerability shader for the player entities on the client that just connected
	local SlotMap = Server:GetServerSlotMap();
--	System:Log("Sending Team Color To "..server_slot:GetName().."...");
	
	for i, Slot in SlotMap do
		local Player = GetSlotPlayer(Slot);
		
		if (Player and Player.ApplyTeamColor) then
			if (not Player.invulnerabilityTimer or (Player.invulnerabilityTimer and Player.invulnerabilityTimer <= 0)) then
				-- this player is not invulnerable
				System:Log("Player "..Slot:GetName().." is teamcolored!")	;				
				server_slot:SendCommand("FX", g_Vectors.v000, g_Vectors.v000, Slot:GetPlayerId(), 1);
			else
				-- this player is invulnerable
				System:Log("Player "..Slot:GetName().." is invulnerable!");				
				server_slot:SendCommand("FX", g_Vectors.v000, g_Vectors.v000, Slot:GetPlayerId(), 2);
			end
		end
	end
	
	self:AddPlayerToScoreboard(server_slot:GetId());
	--[GL playertracking]
	--player login 
	GLplayerTrack:OnConnect(server_slot);  
	--[end GL]

	local autopick = tonumber(getglobal("gr_auto_pickteam"));
	if autopick == 1 then
		GameRules:HandleJoinTeamRequest(server_slot,tiimi);
	end	
end


-------------------------------------------------------------------------------
function GameRules:OnUpdate()
	if (toNumberOrZero(getglobal("gl_static_respawn")) > 0) then
		GLplayerTrack:CheckRespawn();
	end
	--[gl - high ping kick]
	if (toNumberOrZero(getglobal("gl_max_average_ping")) > 0) then
		local interval = toNumberOrZero(getglobal("gl_ping_check_interval"));
		local lastchecked = toNumberOrZero(getglobal("gl_last_checked"));
		--
		local tnow=_time;
		if (lastchecked > tnow) then 
			lastchecked=0;
		end
		if ((tnow-lastchecked) > interval) then
			--System:Log("checking "..tnow.." .. "..lastchecked); 
			lastchecked = tnow-0.01;
			GLplayerTrack:DoPingKick();
			setglobal("gl_last_checked",lastchecked);
		end
	end
	--[end gl]
	--statemachine update
	if (toNumberOrZero(getglobal("gr_TimeLimit")) > 999) then
		setglobal("gr_TimeLimit", 999);
	end
	if (toNumberOrZero(getglobal("gr_TimeLimit")) < 0) then
		setglobal("gr_TimeLimit", 0);
	end
	self:CheckPlayerCount();		-- might go to PREWAR or back when min player count is not ok

	if (self.restartbegin and (_time >= floor(self.restartend))) then
		self.restartbegin = nil;
		self.restartend = nil;
		self:DoRestart();
	end
	
	self:Update();
	MapCycle:Update();
	
	if GameRules.ScoreboardUpdate	then
		GameRules:ScoreboardUpdate();
	else
		System:Log("ERROR: GameRules:ScoreboardUpdate() not implemented");
	end
end


-------------------------------------------------------------------------------
-- player requests to be killed (typed in the "kill" console command)
function GameRules:OnKill(server_slot)
	self:Invoke("OnKill",server_slot);
end


-------------------------------------------------------------------------------
-- called for every damage on every player, so should be relatively efficient
-- most of a mods specific quirks are likely to go in here
function GameRules:OnDamage( hit )

--  System:Log("Lib GameRules:OnDamage");
  
  local theTarget = hit.target;
  local theShooter = hit.shooter;
   
  if (theTarget and theTarget.type == "Player" and theTarget.invulnerabilityTimer~=nil and  theTarget.invulnerabilityTimer>0) then
--	System:Log("Player "..theTarget:GetName().." is invulnerable, no damage is applied");
  	return -- Player is invulnerable, don't accept damage
  end
      
	self:Invoke("OnDamage",hit);
end


-------------------------------------------------------------------------------
-- called from the map reloading process
-- gamerules.lua is reloaded and thus reset before this is called
function GameRules:OnMapChange()

	System:Log("GameRules:OnMapChange");

	if tostring(getglobal("gr_PrewarOn")) ~= "0" then
		self:NewGameState(CGS_PREWAR);
	else
		self:NewGameState(CGS_INPROGRESS);
	end
	
	if (MapCycle) then
		MapCycle:OnMapChanged();
	end
	
	self:RecreateTableOfRespawnPoints();		-- update GameRules.RespawnPoints

	self:CreateScorebordEntity();
end


-------------------------------------------------------------------------------
function GameRules:CreateScorebordEntity()
	-- create only if needed
	if GameRules.idScoreboard then
		return
	end

	-- create Scoreboard
	local Scoreboard = Server:SpawnEntity({ classid=SYNCHED2DTABLE_CLASS_ID, name="Scoreboard" });

	if Scoreboard then
		GameRules.idScoreboard = Scoreboard.id;
	else
		GameRules.idScoreboard = nil;
	end

--	System:Log("create Scoreboard entity "..tostring(Scoreboard.id));
end

-------------------------------------------------------------------------------
function GameRules:COUNTDOWN_OnBeginState()
	self.countnext = 0;
end


-------------------------------------------------------------------------------
function GameRules:COUNTDOWN_OnUpdate()
	if _time>self.countnext+self.mapstart then
   	    if self.countnext==floor(gr_CountDown) then
    	    self.mapstart = _time;
        	self:NewGameState(CGS_INPROGRESS);
        	
	        local slots = Server:GetServerSlotMap();
        	for i, slot in slots do
--	        	    local ent = System:GetEntity(slot:GetPlayerId());
--	        	    if ent~=nil and ent.type=="Player" then
--	        	        self:ChangeTeam(slot, Game:GetEntityTeam(ent.id), 1);
--	        	    end
        	    slot:ResetPlayTime();
        	    
        	    -- set the default class for player
            end
            
        	GameRules:OnMapChange();	-- init map entites
   	    else
   	    	Server:BroadcastText("@CountdownNo "..(floor(gr_CountDown)-self.countnext), 0.95);
   	        self.countnext = self.countnext+1;
   	    end
    end
end




-------------------------------------------------------------------------------
-- called after player has died and presses fire to respawn
-- normal procedure is for this function to recreate an entity
function GameRules:OnClientRequestRespawn( server_slot, requested_classid )
	--[GL start player cannot spawn]
	if GLcommands:requestSpawn(server_slot)==0 then
		return;
	end
	--[GL end]
--	GLplayerTrack:OnRespawn(server_slot);
	
	local player;
	
	-- If this game mode supports respawning in groups then put that player in
	-- the requested respawn list rather than respawning them immediately.
	-- They will be respawned in DoOntimerRespawnCycleTimer defined in GameRulesTeamLib.lua
	if floor(toNumberOrZero(getglobal("gr_RespawnTime")))~=0 and self.respawnCycle then
--		System:Log("client request respawn! same team!");
		if (self.respawnList[server_slot]) then
			self.respawnList[server_slot] = { classid = requested_classid, team = self.respawnList[server_slot].team, };
		else			
			server_slot:SendText("@RespawningIn "..self.respawnCycleTimer.." @GameStartingInSeconds", min(self.respawnCycleTimer, 6));
			self.respawnList[server_slot] = { classid = requested_classid, };
			--[GL spawnmessage]
			GLcommands:DisplaySpawnMessage(server_slot);
			--[end gl]
		end	
		
		return 
	end;
	
	if(server_slot.last_death and (_time-server_slot.last_death<self.respawndelay))then
		return
	end
	
--	if(server_slot.last_death)then
--		System:Log("server_slot.last_death="..server_slot.last_death.." _time=".._time);
--	else
--		System:Log("server_slot.last_death=(nil)");
--	end
	
	self:RespawnPlayer(server_slot);

end




-------------------------------------------------------------------------------
function GameRules:RespawnPlayer( server_slot )
	GLplayerTrack:OnRespawn(server_slot);
	-- store old stats
	local prevPlayerEntity=GetSlotPlayer(server_slot);
	
	local oldscore;
	local olddeaths;
	
	if prevPlayerEntity~=nil and prevPlayerEntity.type=="Player" then
		oldscore=prevPlayerEntity.cnt.score;
		olddeaths=prevPlayerEntity.cnt.deaths;
	end

	-- create new player
	local oldteam=Game:GetEntityTeam(server_slot:GetPlayerId());
	local requested_classid = PLAYER_CLASS_ID;
	
	local player=self:SpawnPlayer(server_slot,requested_classid,oldteam);

	-- restore old stats
	if(oldscore or olddeaths)then
		player.cnt.score=oldscore;
		player.cnt.deaths = olddeaths;
	end

	Server:AddToTeam(oldteam,player.id);	
	
end


-------------------------------------------------------------------------------
function GameRules:OnSpectatorSwitchModeRequest(spect)
	if toNumberOrZero(getglobal("gl_allow_spectators"))==0 then
		return;
	end
	local curhost=spect.cnt:GetHost();
	local newhost=0;
	local players=GetPlayers();
--	System:LogToConsole("OnSpectatorSwitchModeRequest curr host="..curhost);
	if(count(players)==0)then 
--		System:LogToConsole("OnSpectatorSwitchModeRequest no players");
		return 
	end;
	
	for id,ent in players do
--		System:LogToConsole("pp");
		if(curhost==0)then
			spect.cnt:SetHost(id);
			return
		else
			if(curhost==id)then
				curhost=0;
			end
		end
	end
	
	spect.cnt:SetHost(0);
end




-------------------------------------------------------
-------------------------------------------------------
-- return 1=if interaction is accepted, otherwise nil
function GameRules:IsInteractionPossible(actor,entity)
	-- prevent a player who is in a vehicle from interacting with the entity
	if actor then
		if (actor.theVehicle) then
			return nil;
		end

		-- prevent corpses from interacting with something
		if(actor.cnt and actor.cnt.health and actor.cnt.health < 1)then
			return nil;
		end
	end

	local iCurstate=self:GetGameState();

	if(iCurstate~=CGS_INPROGRESS)then
		return nil;
	end
	
	return 1;
end


-------------------------------------------------------
-------------------------------------------------------
-- reset the state of all entities
function GameRules:ResetMapEntities()

	local entities=System:GetEntities();
	
	if(entities)then
		for i,ent in entities do
			if(ent)then
				if (ent.deleteOnGameReset) then
					Server:RemoveEntity(ent.id);
					ent = nil;
				elseif (ent.OnMultiplayerReset) then
					ent:OnMultiplayerReset();
				end

				-- store the initial position and angles of the entity, so they can be restored by the Phoenix
				if ent ~= nil and ent.GetPos then
					if not ent.PhoenixData then
						ent.PhoenixData = {};
						ent.PhoenixData.pos = new(ent:GetPos());
						ent.PhoenixData.angles = new(ent:GetAngles());	
					else
						-- we have phoenix data, so reset these guys
						local data = ent.PhoenixData;
						ent:SetPos(data.pos);
						ent:SetAngles(data.angles);
						if (ent.OnReset) then
							ent:OnReset();
						end
					end
				end
			end
		end
	end

	-- respawn all players
	local slots = Server:GetServerSlotMap();
	for i, slot in slots do
		local ent=System:GetEntity(slot:GetPlayerId());
		if ent and ent.type=="Player" then
			self:RespawnPlayer(slot);							-- was already a player so respawn
		end
	end
	self:CreateScorebordEntity();
end


-------------------------------------------------------
-- is invoked on server from SendCommand() call on client
function GameRules:OnClientCmd( server_slot ,cmd)
--	System:Log("RCV CLIENT CMD = ["..cmd.."]");

	local toktable=tokenize(cmd);
	
	if toktable~={} then
		local sCommand=toktable[1];
		
		local Function=self.ClientCommandTable[sCommand];
		
		if Function then
			Function(cmd,server_slot,toktable);
		end
	end
end



-------------------------------------------------------------------------------
-- count player that are in ready state (e.g. typed in /ready)
function GameRules:ReadyPlayersCount()
	local count=0;
	local slots = Server:GetServerSlotMap();
	for i, slot in slots do
		if(slot:IsReady())then
			local ent=System:GetEntity(slot:GetPlayerId());
			if(ent and ent.type=="Player")then
				count=count+1;
			end
		end
	end
	return count;
end

-------------------------------------------------------------------------------
-- callback from map cleanup
function GameRules:OnShutdown()
end






-------------------------------------------------------------------------------
--------- voting system -------------------------------------------------------
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-- callback for the "callvote" console command. votes can be called on
-- anything that we have a "game command" for (see below).
-- only 1 vote can be in progress at any one time, and votes timeout after 1 minute
function GameRules:OnCallVote(server_slot, command, arg1)
	if tonumber(getglobal("gr_allow_voting"))==1 then
		if(command=="ready")then
		    if IsPlayer(server_slot)==nil then
				server_slot:SendText("@VoteFailNoSpec");
				return
			end
		
			self:ReadyPlayer(server_slot);
	        return
		end
		self.voting_state:OnCallVote(server_slot, command, arg1);
	else
		server_slot:SendText("Voting is currently disabled.");
		return
	end
end


-------------------------------------------------------------------------------
-- callback for the "vote" console command, which casts an actual vote
-- if this vote causes a majority on "yes", execute the "game command"
-- that was voted on
function GameRules:OnVote(server_slot, vote)
	self.voting_state:OnVote(server_slot, vote);
end


-------------------------------------------------------------------------------
-- specific voting version for the "ready" console command
-- (needs unanimous votes, can happen during another vote)
function GameRules:ReadyPlayer(server_slot)
	if self:GetState()~="PREWAR" then
		server_slot:SendText("@ReadyOnlyInPrewar");
		return
	end

	Server:BroadcastText(server_slot:GetName().." @IsReady");

	server_slot:Ready(1);
end



-- \return team_name or nil 
function GameRules.ReturnTeamThatDoesDamage(shooter,hit)
	if hit then
		if hit.weapon and hit.weapon.LaunchedByTeam then
			return hit.weapon.LaunchedByTeam;
		else
			if shooter==nil then
				return;
			end

			return Game:GetEntityTeam(shooter.id);			
		end
	end
end


-- currrently only for explosive damage
-- \return 1 or nil 
function GameRules.ReturnIfPlayerDamagesHimself(target,shooter,hit)
	if hit and hit.weapon and hit.weapon.shooterSSID and target then
		return Server:GetServerSlotBySSId(hit.weapon.shooterSSID):GetPlayerId() == target.id;
	end
	
	if shooter and target==shooter then
		return 1
	end
end


-------------------------------------------------------------------------------
-- if this function is specified in the gamerules, the pickups fade away after a while
function GameRules:GetPickupFadeTime()
	local UserVal=toNumberOrZero(getglobal("gr_DropFadeTime"));
	
	if not UserVal then
		UserVal=10;
	end
	
	if UserVal<1 then 
		UserVal=1;
	end
	
	if UserVal>100 then 
		UserVal=100;
	end

	return UserVal;
end


-------------------------------------------------------------------------------
-- private function to get the same damage calculation in all mods
-- /return nil=player is still living or no player was hit
--         1=player was killed by enemy
--         2=player self kill
--         3=team kill
function GameRules:UsualDamageCalculation(hit)
	local target = hit.target;
	local shooter = hit.shooter;
	local damage = hit.damage;
	local seb = 0;
--	System:Log("\003 UsualDamageCalculation damage: "..hit.damage);

	-- usually used for friendly fire
	if self.IgnoreDamageBetween and self:IgnoreDamageBetween(target,shooter,hit)==1 then 
--		System:Log("UsualDamageCalculation IgnoreDamageBetween damage: "..hit.damage);		--debug
		return 
	end
	
	if not shooter then																								-- explosive damage (entity id might be reassigned)
		local ss_shooter=Server:GetServerSlotBySSId(hit.shooterSSID);
    		if ss_shooter then		-- might be nil e.g. vehicle destroyes itself
    			shooter = System:GetEntity(ss_shooter:GetPlayerId());
    		end
  	end

	--if is falling damage the shooter could be nil
	if(shooter==nil)then 
--		System:Log("GAMERULES>SHOOTER == TARGET")
		shooter=hit.target; 
	end

	if(hit.explosion ~= nil)then
		local expl=target:IsAffectedByExplosion();
		if(expl<=0)then return end
		damage = expl * damage;
	end

--	System:Log("\003 UsualDamageCalculation shooter: "..shooter.id);

	if target==nil or target.type ~= "Player" or target.cnt.health<=0 then 
--		System:Log("GAMERULES>TARGET IS NOT A PLAYER");
		return nil;
	end   -- don't care about damage in prewar etc.

	local zone = target.cnt:GetBoneHitZone( hit.ipart);
    if zone==0 then zone = 2 end;
    
--	System:Log("\003 UsualDamageCalculation zone: "..zone);

  local headshot;				-- nil/1

	if(hit.target_material and hit.target_material.type=="head") then		-- HEAD SHOT
	  	headshot=1;
     	damage=damage*tonumber(gr_HeadshotMultiplier);
	end
	
	damage = damage * tonumber(gr_DamageScale);
    	local cel=Server:GetServerSlotByEntityId(target.id); 
    	local celnev;
    	local lovonev;
	local lovo=Server:GetServerSlotByEntityId(shooter.id);
	if (cel~=nil) then celnev=cel:GetName();
	else celnev=target:GetName();
	end
	if (lovo~=nil) then lovonev=lovo:GetName();
	else lovonev=shooter:GetName();
	end
	local thealth=format("%.d", target.cnt.health);
	local tarmor=format("%.d", target.cnt.armor);
	if (damage > 0) then
		seb = format("%.d", damage);
		if (tonumber(gr_DisplayStat) > 1) and (tonumber(gr_DisplayStat) ~= 3) then 
			BroadcastConsolePrint("$4"..celnev.."$5 stats before: Health: $4"..thealth.." $5Armor: $8"..tarmor.."$6"); 
			if (tonumber(gr_DisplayStat) > 3) then 
          	 		Server:BroadcastText("$4"..celnev.."$5 stats before: Health: $4"..target.cnt.health.." $5Armor: $8"..target.cnt.armor.."$6"); 
          	 	end
        	end

		--- apply damage first to armor, if it is present
		if (hit.damage_type ~= "healthonly") then
			if (target.cnt.armor > 0) then
				target.cnt.armor= target.cnt.armor - (damage*0.5);
				-- clamp to zero
				if (target.cnt.armor < 0) then
					damage = -target.cnt.armor;
					target.cnt.armor = 0;
				else
					damage = 0;
				end
			end
		end
	end
	
	target.cnt.health = target.cnt.health - damage;
   	tarmor=format("%.d", target.cnt.armor);
   	thealth=format("%.d", target.cnt.health);
	if (seb ~= 0) then
		if (tonumber(gr_DisplayStat) > 0) then
			BroadcastConsolePrint("$4"..celnev.."$5 stats after: Health: $4"..thealth.." $5Armor: $8"..tarmor.." $5Damage: $8"..seb.."$4 from "..lovonev.."$6");
			if (tonumber(gr_DisplayStat) > 2) then
				Server:BroadcastText("$4"..celnev.."$5 stats after: Health: $4"..thealth.." $5Armor: $8"..tarmor.." $5Damage: $8"..seb.."$4 from "..lovonev.."$6");
			end
		end
		seb=0;
	end

 	-- negative damage (medic tool) is is bounded to max_health
	if(target.cnt.health>target.cnt.max_health) then
		target.cnt.health=target.cnt.max_health;
	end

	if target.cnt.health<=0 then
--		System:Log("\003 UsualDamageCalculation Killed");

		local shooterTeam = self.ReturnTeamThatDoesDamage(shooter,hit);

		target.cnt.health = 0;
		if shooter.id==target.id or shooter.type ~= "Player" then 
			return 2; -- shoot self or by neutral entity
		elseif Game:GetEntityTeam(target.id)==shooterTeam then
			return 3; -- player was killed by a team member
		else
			return 1; -- player was killed by enemy
		end
	end
	
	return nil;	-- player is still living
end

-------------------------------------------------------------------------------
-- this function sets the score of a player
function GameRules:SetPlayerScore(player, score, deaths)
	player.cnt.score = score;
	
	if (deaths) then
		player.cnt.deaths = deaths;
	end
end

-------------------------------------------------------------------------------
-- this function sets the scores of every player
function GameRules:ResetPlayerScores(score, deaths)

	local ServerSlotMap = Server:GetServerSlotMap();
	
--	for i, Slot in ServerSlotMap do
--		local Player = System:GetEntity(Slot:GetPlayerId());
	local ents=System:GetEntities();
	for i,Player in ents do	
		if (Player and (Player.type == "Player")) then
			self:SetPlayerScore(Player, score, deaths);
		end
	end
		
	if (MPStatistics) then
		MPStatistics:Reset();
	end
	--[GL playertracking]
	GLplayerTrack:Init();
	--[end GL]
end

-------------------------------------------------------------------------------
-- private function to get the same score calculation in all mods
-- most of a mods specific quirks are likely to go in here
-- /param hit={ target=<entity>, shooter=<entity> }
-- /param damage_ret typically from UsualDamageCalculation()
function GameRules:UsualScoreCalculation( hit, damage_ret )
	
	if damage_ret==nil then																											-- player is still alive
		return
	end
	
	local target = hit.target;
	local shooter = hit.shooter;
	local situation = 0;                                                        -- normal kill
	local delta = 1;

	target:GotoState("Dead");		

	local ss_target=Server:GetServerSlotByEntityId(target.id);					-- serverslot
	local ss_shooter;																										-- serverslot

	if shooter then																											-- non explosive damage
		ss_shooter=Server:GetServerSlotByEntityId(shooter.id);
	else																																-- explosive damage (entity id might be reassigned)
		ss_shooter=Server:GetServerSlotBySSId(hit.shooterSSID);
    		if ss_shooter then		-- might be nil e.g. vehicle destroyes itself
    			shooter = System:GetEntity(ss_shooter:GetPlayerId());
    		end
  	end
	
-- 	System:Log(">>>> damage_ret:"..damage_ret);

	
	local weapon = "World";
	local minstreak=toNumberOrZero(getglobal("gl_killing_spree"));
	local targetstreak = GLplayerTrack:GetById(target.id, "killstreak");
	
	if(hit.weapon)then
		if(hit.weapon.IsBoat)then
			weapon="Boat";
		elseif(hit.weapon.IsVehicle)then
			weapon="Vehicle";
		elseif(hit.weapon.name)then
			weapon = hit.weapon.name;
		else
			weapon = hit.weapon.classname;
		end
	end
	if damage_ret==2 then			-- shoot self or by neutral entity
		situation = 1;
		--MPStatistics:AddStatisticsDataEntity(target,"nSelfKill",1);
		GLplayerTrack:SetById(target.id, "selfkills", 1, 1);
		GLplayerTrack:SetById(target.id, "deaths", 1, 1);
		GLplayerTrack:SetStaticSpawn(target.id);
		delta = -1;
		local targetstreak = GLplayerTrack:GetById(target.id, "killstreak");
		if (minstreak~=0) and (targetstreak>=minstreak) then
			if (weapon=="Boat") then
				Server:BroadcastText(getglobal("gl_ssmod_id").."$6 killing spree of $4"..targetstreak.." KILLS $6("..target:GetName().."$6) was $4ended$6 by a BOAT",2);
			elseif (weapon=="Vehicle") then 
				Server:BroadcastText(getglobal("gl_ssmod_id").."$6 killing spree of $4"..targetstreak.." KILLS $6("..target:GetName().."$6) was $4ended$6 by a CAR",2);
			else
				Server:BroadcastText(getglobal("gl_ssmod_id").." player "..target:GetName().."$4 ended $6his own killing spree of $4"..targetstreak.." KILLS",2);
			end
			
		end
	else
		if(hit.target_material and hit.target_material.type=="head") then					-- HEAD SHOT
			if ss_shooter then
				MPStatistics:AddStatisticsDataSSId(ss_shooter:GetId(),"nHeadshot",1);	-- successfully killed by headshot
			else local id = shooter.Properties.bots+30;
			--	MPStatistics:AddStatisticsDataSSId(id,"nHeadshot",1);
			end
			GLplayerTrack:SetById(shooter.id,"headshots", 1, 1);
			--[gl] headshot message
			if toNumberOrZero(getglobal("gl_announce_headshot")) == 1 then
				if toNumberOrZero(getglobal("gl_headshot_message_private")) == 1 then
					if ss_shooter then
					ss_shooter:SendText(getglobal("gl_ssmod_id").."$6 you killed $o"..target:GetName().."$6 with a $4headshot",2);
					end
				else
					Server:BroadcastText(getglobal("gl_ssmod_id").." player "..shooter:GetName().."$6 killed $o"..target:GetName().."$6 with a $4headshot",2);
				end
			end
			--[end gl]
		end

		if damage_ret==1 then		
			-- player was killed by enemy
			if ss_shooter then
				MPStatistics:AddStatisticsDataSSId(ss_shooter:GetId(),"nKill",1);
			else local id = shooter.Properties.bots+30;
			--	MPStatistics:AddStatisticsDataSSId(id,"nKill",1);
			end
			GLplayerTrack:SetWeaponKill(shooter.id, weapon);
			GLplayerTrack:SetById(target.id, "deaths", 1, 1);
			GLplayerTrack:SetStaticSpawn(target.id);
			local iCurrState=tonumber(GameRules.CurrentProgressStep); 
			if iCurrState then
				if (iCurrState > 1) then
					GLplayerTrack:SetById(shooter.id, "flagactkills", 1, 1);  --kill when flag was activated
				end
			end
			GLplayerTrack:SetById(shooter.id, "kills", 1, 1);
		--	if toNumberOrZero(getglobal("gl_rm_needed_kills"))>0 then
		--		GLplayerTrack:RMAddKill(ss_shooter);
		--	end
			GLplayerTrack:SetById(shooter.id, "killstreak", 1, 1);
			local killstreak=GLplayerTrack:GetById(shooter.id,"killstreak");
			if (killstreak) and (killstreak>GLplayerTrack:GetById(shooter.id,"hkillstreak")) then
				GLplayerTrack:SetById(shooter.id,"hkillstreak",killstreak,0);
			end
			local killstreak= GLplayerTrack:GetById(shooter.id, "killstreak");
			if (minstreak~=0) and (killstreak>=minstreak) then
				if toNumberOrZero(getglobal("gl_killing_spree_display")) == 0 then  --display message at each kill
					Server:BroadcastText(getglobal("gl_ssmod_id").." player "..shooter:GetName().."$6 is on a killing spree! ($4"..killstreak.." KILLS$6) (last kill with $4"..weapon.."$6)",2);
				end
			end
			local targetstreak = GLplayerTrack:GetById(target.id, "killstreak");
			if (minstreak~=0) and (targetstreak>=minstreak) then
				Server:BroadcastText(getglobal("gl_ssmod_id").."$6 killing spree of $4"..targetstreak.." KILLS $6("..target:GetName().."$6) was $4ended$6 by $o"..shooter:GetName(),2);
			end
			
		elseif damage_ret==3 then																									-- player was killed by a team member
   	  		situation = 2;
   	  		if ss_shooter then
				MPStatistics:AddStatisticsDataSSId(ss_shooter:GetId(),"nTeamKill",1);
			else local id = shooter.Properties.bots+30;
			--	MPStatistics:AddStatisticsDataSSId(id,"nTeamKill",1);
			end
			
			delta = -1;		
			--[GL punish teamkillers, either by respawntime or by moving to spectators]
			GLplayerTrack:SetById(shooter.id, "tks", 1, 1);
			GLplayerTrack:SetById(target.id, "deaths", 1, 1);
			GLplayerTrack:SetStaticSpawn(target.id);
			local tks=toNumberOrZero(GLplayerTrack:GetById(shooter.id, "tks"));			
			local stTk=tks;
			local freeKills = toNumberOrZero(getglobal("gl_free_teamkills"));
			local allowtk = toNumberOrZero(getglobal("gl_allow_teamkilling"));	
		--	if allowtk == 0 then
		--		if GLcommands:ProtectedName(ss_shooter:GetName())==0 or toNumberOrZero(getglobal("gl_punish_protected"))==1 then
					stTk=tostring(stTk.."/"..freeKills);
		--			-- check if player has too many teamkills, and should be moved to spectors for the rest of this game
		--			if tks>=toNumberOrZero(getglobal("gl_teamkill_force_spectate")) then
		--				GLcommands:movePlayerToTeam(tostring(ss_shooter:GetId().." spectators 1"));  --force to spectators, cant change back (this map)
		--				Server:BroadcastText(getglobal("gl_ssmod_id").." player "..ss_shooter:GetName().."$o has been forced to spectate (too many teamkills) cant join untill next map",2);					
		--				GameRules:Invoke("OnKill",ss_shooter);
		--			elseif tks>freeKills then		
		--			-- check if player has too many teamkills, and should recieve a respawn punishment
		--				GameRules:Invoke("OnKill",ss_shooter);
		--				local PunishTime = (tks-freeKills)*toNumberOrZero(getglobal("gl_teamkill_extra_time"));
		--				if self.respawnCycleTimer then
		--					PunishTime = PunishTime + floor(self.respawnCycleTimer) - 1;  --add respawntime (-1, so that there is time to press mouse and spawn, when punishment is over, without having to wait another cycle)
		--				end
		--				if toNumberOrZero(getglobal("gl_static_respawn"))>0 then
		--					PunishTime = PunishTime + tonumber(getglobal("gl_static_respawn"));  --add respawntime
		--				end
		--				if PunishTime>0 then
		--					GLcommands:punishPlayer(ss_shooter:GetId().." "..PunishTime.." $4too many teamkills$o");
		--					Server:BroadcastText(getglobal("gl_ssmod_id").." player "..ss_shooter:GetName().."$o cannot join for $6"..PunishTime.."$o seconds. reason: Teamkilling",2);
		--				end
		--			end
		--		end
		--	end
			if ss_shooter then
				if ss_target then
				Server:BroadcastText(ss_shooter:GetName().." $4<teamkilled> $o"..ss_target:GetName().."  $4["..stTk.."]", 2);
				else
				Server:BroadcastText(ss_shooter:GetName().." $4<teamkilled> $o"..target:GetName().."  $4["..stTk.."]", 2);
				end
			else 	if ss_target then
				Server:BroadcastText(shooter:GetName().." $4<teamkilled> $o"..ss_target:GetName().."  $4["..stTk.."]", 2);
				else
				Server:BroadcastText(shooter:GetName().." $4<teamkilled> $o"..target:GetName().."  $4["..stTk.."]", 2);
				end
			end
			
			local targetstreak = GLplayerTrack:GetById(target.id, "killstreak");
			if (minstreak~=0) and (targetstreak>=minstreak) then
				Server:BroadcastText(getglobal("gl_ssmod_id").."$6 killing spree of $4"..targetstreak.."$6 ("..target:GetName().."$6) was $4ended$6 by his TEAMMATE $o"..shooter:GetName(),2);
			end			
			--[end GL]
		end
	end
	
	-- score changes?
	if ((shooter and shooter.type == "Player") or (damage_ret == 2)) then
		if (not shooter) then
			target.cnt.score=target.cnt.score+delta;
		else
			shooter.cnt.score=shooter.cnt.score+delta;
		end
	end
	


	if (target.type == "Player") then
		target.cnt.deaths=target.cnt.deaths+1;
	--	if toNumberOrZero(getglobal("gl_rm_needed_kills"))>0 then
	--		GLplayerTrack:RMResetOne(ss_target);
	--	end
	end

-- 	System:Log(">>>> delta:"..delta);

	-- send hud text message to all clients
	local slots = Server:GetServerSlotMap();
	for i, slot in slots do
		if ss_shooter then
--	    	System:Log(">>>> 2 PKP "..target.id.." "..ss_shooter:GetPlayerId().." "..situation);
			slot:SendCommand("PKP "..target.id.." "..ss_shooter:GetPlayerId().." "..situation.." "..weapon);				-- usual print for others
		elseif (shooter) then
			slot:SendCommand("PKP "..target.id.." "..shooter.id.." "..situation.." "..weapon);
		else
--	    	System:Log(">>>> 3 PKP "..target.id.." 1 "..situation);
			slot:SendCommand("PKP "..target.id.." 0 "..situation.." "..weapon);				-- self kill - instead of passing a dummy value could be done better
		end			
 	end	


	if ss_target then
		ss_target.last_death=_time;
	else
		System:Log("server slot is nil");
	end

	Game:ForceScoreBoard(target.id, 1);

	return delta;
end



function GameRules:GotoNextMap()
	local nextMap = getglobal("gr_NextMap");

	Game:LoadLevelMPServer(nextMap);
end


-- get the entries for MPStatistics
function GameRules:GetInitialPlayerStatistics()
	return new(self.InitialPlayerStatistics);
end


-------------------------------------------------------------------------------
--

function GameRules:ChangeMap(mapname, gametype, timelimit)
	--[gl extended function with params, for use with mapcycle]
	local szGameType = gametype;
	local szTimeLimit = timelimit;
	
	if (not mapname or strlen(mapname) < 1) then
		return nil;
	end

	if (not gametype or strlen(gametype) < 1) then
		szGameType = getglobal("g_GameType");
	end
	
	if (not timelimit or strlen(timelimit) < 1) then
		szTimeLimit = getglobal("gr_TimeLimit");
	end
	
	--[end gl]
	System:Log("\001Map: "..mapname.." ("..szGameType..")");
	
	if (not Game:CheckMap(mapname, szGameType)) then
	
		if (Game:CheckMap(mapname)) then
			local szDefaultGameType = Game:GetMapDefaultMission(mapname);
			
			if (not szDefaultGameType) then
				System:Warning( "Map '"..mapname.."' does not support '"..szGameType.."'. No default Game Type found! Skipping this map!");
				MapCycle:OnMapChanged();
				MapCycle:OnMapFinished();
			
				return nil;
			else
				System:Warning( "Map '"..mapname.."' does not support '"..szGameType.."'. Using '"..szDefaultGameType.."' instead!");				
				szGameType = szDefaultGameType;
			end
		else
			System:Warning( "Map '"..mapname.."' not found! Skipping this map!");
			MapCycle:OnMapChanged();
			MapCycle:OnMapFinished();

			return nil;
		end
	end

	Server:BroadcastText("@ChangeMapTo "..mapname.." ("..szGameType..")", 10);
	
	setglobal("g_GameType", szGameType);
	setglobal("gr_NextMap", mapname);
	--[gl] added these
	setglobal("gr_TimeLimit", szTimeLimit); 

	--[end gl]
	GameRules:NewGameState(CGS_INTERMISSION);
	GLplayerTrack:Init();
	return 1;
end

-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------

-------------------------------------------------------------------------------
-- below follow votable commands players can use for this mod.
-- to add a command, just add a similar function below: the name of the
-- function is what players type as first argument to "callvote", "arg"
-- is an optional second argument

GameCommands = {};


GameCommands.map=
{
	-- \return nil=call is not possible, otherwise the vote has started
	OnCallVote=function(self,serverslot,arg)
		self.nextmap=arg;
		return 1;
	end,

	OnExecute=function(self)
		GameRules:ChangeMap(self.nextmap);
	end,

	-- arguments
	nextmap=nil,
}



GameCommands.kick=
{
	-- \return nil=call is not possible, otherwise the vote has started
	OnCallVote=function(self,serverslot,arg)

		local KickSlot=Server:GetServerSlotBySSId(arg);						-- id was specified
		
		if(not KickSlot)then
			KickSlot=MultiplayerUtils:GetServerslotFromName(arg);		-- name was specified
		end
		
		if(not KickSlot)then
			System:Log("GameCommands.kick: Slot of '"..arg.."' not found");
			serverslot:SendText("@VoteUnknCommand",3);
			return;
		end
		
		if(_localplayer and KickSlot==Server:GetServerSlotByEntityId(_localplayer.id))then
			System:Log("GameCommands.kick: don't kick the server");
			serverslot:SendText("@VoteUnknCommand",3);
			return;
		end
		
		self.PlayerSSID=KickSlot:GetId();
		return 1;
	end,

	OnExecute=function(self)
--	  Server:BroadcastText("@KickingPlayer "..arg);
 		GameRules:KickID(self.PlayerSSID);
	end,

	-- arguments
	PlayerSSID=nil,
}



GameCommands.restart=
{
	-- \return nil=call is not possible, otherwise the vote has started
	OnCallVote=function(self,serverslot,arg)
		return 1;
	end,

	OnExecute=function(self)
	  Server:BroadcastText("@RestartingGame");
	  GameRules:Restart(5);
	end,
	
	-- arguments
}



-------------------------------------------------------------------------------
-- Kicks a player
function GameRules:Kick(player)

	if (not player or strlen(player) < 1) then
		return;
	end

	local KickSlot = MultiplayerUtils:GetServerslotFromName(player);

--	System:Log("GameRules:Kick "..player.." "..tostring(KickSlot));

	GameRules:KickSlot(KickSlot);	
end

-------------------------------------------------------------------------------
--
function GameRules:KickID(id)
	if (not id or strlen(id) < 1) then
		return;
	end

	local KickSlot = Server:GetServerSlotBySSId(id);
	
	GameRules:KickSlot(KickSlot);
end

-------------------------------------------------------------------------------
--
function GameRules:KickSlot(ServerSlot)
	if (_localplayer and ServerSlot) then
		if (_localplayer.id == ServerSlot:GetPlayerId()) then
			System:Log("don't kick the server");
			return;
		end
	end

	-- found nothing?
	if(not ServerSlot)then
		System:Log("failed to kick the player");
		return;
	end

--		printf("kicked player id %d...", KickIDServerSlot:GetPlayerId());
	System:Log("kicked player "..tostring(ServerSlot:GetName()));
	Server:BroadcastText("@KickingPlayer "..tostring(ServerSlot:GetName()));
	ServerSlot:Disconnect("@Kicked");
end

-------------------------------------------------------------------------------
--
function GameRules:Ban(player)
	if (not player or strlen(player) < 1) then
		return;
	end

	local BanSlot = MultiplayerUtils:GetServerslotFromName(player);
	
	GameRules:BanSlot(BanSlot);
end

-------------------------------------------------------------------------------
--
function GameRules:BanID(id)
	if (not id or strlen(id) < 1) then
		return;
	end

	local BanSlot = Server:GetServerSlotBySSId(id);
	
	GameRules:BanSlot(BanSlot);
end

-------------------------------------------------------------------------------
--
function GameRules:BanSlot(ServerSlot)
	if (_localplayer and ServerSlot) then
		if (_localplayer.id == ServerSlot:GetPlayerId()) then
			System:Log("don't ban the server");
			return;
		end
	end
	
	if (ServerSlot) then
		ServerSlot:BanByIP();
		GameRules:KickSlot(ServerSlot);
	end
end

-------------------------------------------------------------------------------
--
function GameRules:Unban(id)
	if (id) then
		Server:Unban(tonumber(id));
	end
end
-------------------------------------------------------------------------------
-- Does initilization for respawn cycles
function GameRules:StartGameRulesLibTimer()
	if floor(toNumberOrZero(getglobal("gr_RespawnTime")))~=0 and self.respawnCycle then
		self.respawnList={};	
		self.respawnCycleTimer = 1; -- Add this counter to the table.  1 second so we spawn all dead players from the last map immediately.
	end
	
	self:SetTimer(1000); -- Set the on timer event for respawns
end

-------------------------------------------------------------------------------
function GameRules:DoGameRulesLibTimer()

	-- TASKLIST START --
	local nTaskEnable = tonumber(getglobal("gr_task_enable"));
	if ( nTaskEnable == 1) then
		TaskList:OnUpdate();
	end
	-- TASKLIST END --

	local bPerformedRespawn = 0;
	
	local bDoCycle;
	
	local fRespawnTime=floor(toNumberOrZero(getglobal("gr_RespawnTime")));
	
	if fRespawnTime~=0 and self.respawnCycle then
		bDoCycle=1;
	end

	if self.respawnCycleTimer and not bDoCycle then				-- gr_RespawnTime was set to 0
		Server:BroadcastCommand("RTR");
		self.respawnCycleTimer=nil;
		self.respawnList=nil;
	end

	if self.respawnCycleTimer and self.respawnCycleTimer > fRespawnTime then					-- gr_RespawnTime was lowered
		self.respawnCycleTimer=fRespawnTime;
		Server:BroadcastCommand("RTR "..self.respawnCycleTimer);
	end

	-- Does respawn cycles
	if bDoCycle then
		if self.respawnCycleTimer then
			self.respawnCycleTimer=self.respawnCycleTimer-1;
		end
			
		if (not self.respawnCycleTimer or self.respawnCycleTimer<=0) then
			bPerformedRespawn = 1;
			if self.respawnList then
				for server_slot,respawn_info in self.respawnList do
					
					local prevPlayerEntity=GetSlotPlayer(server_slot);
					
					local requested_classid = respawn_info.classid;
					local newteam = respawn_info.team;
					
					-- if no newteam specified, the new team is the old team
					if (not newteam) then
						newteam=Game:GetEntityTeam(server_slot:GetPlayerId());
					end
					
					local oldscore;
					local olddeaths;
					
					if prevPlayerEntity~=nil and prevPlayerEntity.type=="Player" then
						oldscore=prevPlayerEntity.cnt.score;
						olddeaths=prevPlayerEntity.cnt.deaths;
					end
					--also restore score when went to spectators
					if toNumberOrZero(getglobal("gl_keep_score"))==1 then
						if prevPlayerEntity~=nil and prevPlayerEntity.type=="spectator" then
							oldscore=prevPlayerEntity.cnt.score;
							olddeaths=prevPlayerEntity.cnt.deaths;
						end
					end
					
					local player;
					
					----
					local locInitialPlayerProperties=self:GetInitialPlayerProperties(server_slot);
					if locInitialPlayerProperties then
						local oldteam=GLplayerTrack:GetBySs(server_slot, "lastteam");
						if oldteam==nil then 
							oldteam="blue";
						end
--						local oldclass=GLplayerTrack:GetBySs(server_slot, "lastclass");
						local glClass=locInitialPlayerProperties.sPlayerClass;
						local activeClass=GameRules:CountClass(server_slot, glClass, newteam);
						if glClass=="Sniper" and activeClass>=tonumber(getglobal("gl_max_snipers")) then
							--server_slot:SendText(getglobal("gl_ssmod_id").." "..server_slot:GetName().."$4 NOT ALLOWED TO JOIN AS SNIPER: $6the maximum number of snipers is : "..tonumber(getglobal("gl_max_snipers")));
							newteam=oldteam;
							GLcommands:SetPC(server_slot,"Grunt",1,1,1,3);
							requested_classid=PLAYER_CLASS_ID;
--							newteam="spectators";
--							requested_classid=SPECTATOR_CLASS_ID;
							Server:BroadcastText(getglobal("gl_ssmod_id").." "..server_slot:GetName().."$4 NOT ALLOWED TO JOIN AS SNIPER: $6the maximum number of snipers is : "..tonumber(getglobal("gl_max_snipers")));
						end
						if glClass=="Grunt" and activeClass>=tonumber(getglobal("gl_max_scouts")) then
							server_slot:SendText(getglobal("gl_ssmod_id").." "..server_slot:GetName().."$4 NOT ALLOWED TO JOIN AS SCOUT: $6the maximum number of scouts is : "..tonumber(getglobal("gl_max_scouts")));
							newteam="spectators";
							requested_classid=SPECTATOR_CLASS_ID;
							Server:BroadcastText(getglobal("gl_ssmod_id").." "..server_slot:GetName().."$o was moved to spectators. Reason : too many scouts in that team");
						end
						if glClass=="Support" and activeClass>=tonumber(getglobal("gl_max_engineers")) then
							server_slot:SendText(getglobal("gl_ssmod_id").." "..server_slot:GetName().."$4 NOT ALLOWED TO JOIN AS ENGINEER: $6the maximum number of engineers is : "..tonumber(getglobal("gl_max_engineers")));
							newteam="spectators";
							requested_classid=SPECTATOR_CLASS_ID;
							Server:BroadcastText(getglobal("gl_ssmod_id").." "..server_slot:GetName().."$o was moved to spectators. Reason : too many engineers in that team");
						end
					end
					----				
					
					System:Log("spawning player in "..newteam.." team!");
					local player=self:SpawnPlayer(server_slot,requested_classid,newteam);

					-- copy old stats
					if(oldscore or olddeaths)then
						player.cnt.score=oldscore;
						player.cnt.deaths=olddeaths;
					end

					Server:AddToTeam(newteam,player.id);

					if (self.SetTeamObjectivesOnePlayer) then
						self:SetTeamObjectivesOnePlayer(server_slot);
					end
					
					if (self.UpdateCaptureProgressOnePlayer) then
						self:UpdateCaptureProgressOnePlayer(server_slot);
					end
				end
			end
			self.respawnList={};
		------------------------------------------------	
		------------Bots respawn------------------------
		
			local ents=System:GetEntities();
			for i,entity in ents do
				if (entity.Properties and entity.Properties.bots) then
					if entity:GetState()=="Dead" then
						local sEntName=entity:GetName();
						local team=Game:GetEntityTeam(entity.id);
						local team_color;
						local iSpecies=0;
						if (team=="blue") then 							
							team_color={0,0,1};
							iSpecies=0;
						elseif (team=="red") then
							team_color={1,0,0};
							iSpecies=1;
						end			
						if (not entity.Properties.Statistics) then
							entity.Properties.Statistics = GameRules:GetInitialPlayerStatistics();
						end
						local iPlayerScore=0;
						if (entity.cnt.score) then
							iPlayerScore=entity.cnt.score;
						end
						local rp=self:GetTeamRespawnPoint(team,entity);
						if (rp==nil) then
							System:Log("GameRules:SpawnPlayer haven't found a TeamRespawnPoint")
							rp=Server:GetRandomRespawnPoint();
							if (rp==nil) then
								rp={x=0, y=0, z=0, xA=0, yA=0, zA=0};
							end
						end
						local pos = {x = rp.x, y = rp.y, z = rp.z};
						local ang = {x = rp.xA, y = rp.yA, z = rp.zA};					
						local pclass;
						local weapon1=1;
						local weapon2;
						local weapon3;
						local weapon4;
						local rnd=random(1,6); --pclass: 1:2-Grunt, 1:3-Support, 1:6-Sniper
						local clas;
						if rnd<=3 then pclass=MultiplayerClassDefiniton.PlayerClasses["Grunt"];
							weapon2=1;
							weapon3=random(1,2);
							weapon4=random(1,3);
							clas="Grunt";
						elseif rnd==6 then pclass=MultiplayerClassDefiniton.PlayerClasses["Sniper"];
							weapon2=1;
							if tonumber(getglobal("gr_Disable_SniperRL"))==1 then
								weapon3=1;
							else
								weapon3=random(1,2);
							end
							clas="Sniper";
						elseif (rnd==5 or rnd==4) then pclass=MultiplayerClassDefiniton.PlayerClasses["Support"];
							weapon2=random(1,2);
							weapon3=1;
							weapon4=1;
							clas="Support";
						end
						local mypack={};
						local no=4;	
							mypack[1]={ Type="Weapon", Name=pclass.weapon1[weapon1], };
							mypack[2]={ Type="Weapon", Name=pclass.weapon2[weapon2], };
							mypack[3]={ Type="Weapon", Name=pclass.weapon3[weapon3], };
							if weapon4 and pclass.weapon4[weapon4] then
								mypack[4]={ Type="Weapon", Name=pclass.weapon4[weapon4], };
								no=no+1;
							end
							-- assign a primary weapon if we have specified one
							if (pclass.primaryWeaponSlot ~= nil) then
								mypack[pclass.primaryWeaponSlot].Primary = 1;
							end
							mypack.Ammo=pclass.InitialAmmo;
							-- add items to equipment pack
							if (pclass.items ~= nil) then
								for i, item in pclass.items do
									mypack[no] = { Type="Item", Name = item, };
									no = no + 1;
								end
							end
							local packname  = "NetPlayerPack" .. count(EquipPacks);
							EquipPacks[ packname ] = mypack;
						local newt = --{bHasArmor = 0,bHelmetOnStart = 0,eye_height = 2.0,	species = iSpecies,bots=num,max_health = 255,groupid = iSpecies,bTrackable=1,}
							{
							KEYFRAME_TABLE = "BASE_HUMAN_MODEL",
							SOUND_TABLE = "MERC_SCOUT",
							bots = entity.Properties.bots,
							suppressedThrhld = 5.5,
							bSleepOnSpawn = 0,
							dropArmor = 0,
							aggression = 0.3,	-- 0 = passive, 1 = total aggression
							bAffectSOM = 1,
							bSmartMelee = 0,
						-- Original from player
							bHasArmor = 0,
							bHelmetOnStart = 0,
							eye_height = 2.0,
							species = iSpecies,
							max_health = 255,
							groupid = iSpecies,
							bTrackable=1,
							sCurrentPlayerClass=clas,
						-- Original from player end
							commrange = 30.0,
							attackrange = 70,
							horizontal_fov = 160,
							forward_speed = 1.27,
							back_speed = 1.27,
							accuracy = 0.6,
							responsiveness = 7,
							special = 0,
					
							fSpeciesHostility = 2,
							fGroupHostility = 0,
							fPersistence = 0.8,
							equipEquipment = packname,
							equipDropPack = "none",
							AnimPack = "Basic",
							SoundPack = "dialog_template",
					
							aicharacter_character = "Scout",
							pathname = "none",
							pathsteps = 0,
							pathstart = 0,
							ReinforcePoint = "none",
							fileModel = pclass.model,
							Statistics={
								nCaptureFinished = entity.Properties.Statistics["nCaptureFinished"],
								nCaptureStarted = entity.Properties.Statistics["nCaptureStarted"],
								nCaptureAverted = entity.Properties.Statistics["nCaptureAverted"],
								nBuildingBuilt = entity.Properties.Statistics["nBuildingBuilt"],
								nBuildingRepaired = entity.Properties.Statistics["nBuildingRepaired"],
								nBuildingDestroyed = entity.Properties.Statistics["nBuildingDestroyed"],
								nHealed = entity.Properties.Statistics["nHealed"],
								},
							}	 
						Server:RemoveEntity(entity.id);
						local newPlayer=Server:SpawnEntity({classid=85,name=sEntName,pos=pos,angles=ang,color=team_color,properties=newt,}); --85= Bot
						newPlayer.cnt.health		= pclass.health;
						newPlayer.cnt.max_health	= pclass.health;
						newPlayer.cnt.armor		= pclass.armor;			
						newPlayer.cnt:InitStaminaTable( pclass.StaminaTable );
						newPlayer.cnt:SetMoveParams( pclass.move_params );
						newPlayer.currentEquipment = new(EquipPacks[newPlayer.Properties.equipEquipment]);
						Server:BroadcastCommand("YCN "..newPlayer.id.." "..clas);
						newPlayer.cnt.score = iPlayerScore;
						BasicPlayer.InitAllWeapons(newPlayer, 1);
						Server:AddToTeam(team,newPlayer.id);														
					end
				end
			end
			
		------------Bots Respawn end------------
		----------------------------------------
			self.respawnCycleTimer = fRespawnTime;
			
--			printf("SENT RTR! " .. GameRules.respawnCycleTimer);
			Server:BroadcastCommand("RTR "..tostring(GameRules.respawnCycleTimer));
			
		else
			--for server_slot,requested_classid in self.respawnList do
			--	server_slot:SendText("@RespawningIn "..self.respawnCycleTimer, 1)
			--end
		end
	end

	local slots = Server:GetServerSlotMap();
	
	for i, slot in slots do
		local player = GetSlotPlayer(slot);
		if (player and player.invulnerabilityTimer~=nil and player.ApplyTeamColor~=nil) then
			player.invulnerabilityTimer = player.invulnerabilityTimer - 1;
			if (player.invulnerabilityTimer<=0) then
				--BasicPlayer.SecondShader_TeamColoring(self);
				-- turn off invulnerability shader
--				System:Log("Player "..player:GetName().." lost invulnerbility (timer)");
				Server:BroadcastCommand("FX", g_Vectors.v000, g_Vectors.v000, player.id, 1);
				player.invulnerabilityTimer=nil;
			else
				Server:BroadcastCommand("FX", g_Vectors.v000, g_Vectors.v000, player.id, 2);
				--slot:SendText("@InvEndsIn "..tostring(player.invulnerabilityTimer), 1);
			end
		end
	end
	
	-- process all entities - only done after a respawn wave :)
	if ((bPerformedRespawn == 1) or (fRespawnTime == 0)) then
		local ents=System:GetEntities();
		for i, entity in ents do
			if (entity.type == "Phoenix") then
				entity:RaiseFromAshes();
			end
		end
	end
	
	-- Keep the timer going	
	self:SetTimer(1000);	
end

function GameRules:CountClass(server_slot, class, team)	
	--gl, count 'active' players of class in this team
	--team= requested team
	
	local iCount = 0;	
	
	if (self.respawnList) then
		local Slots = Server:GetServerSlotMap();
		for i,ServerSlot in Slots do
			if server_slot~=ServerSlot then
				local Player = GetSlotPlayer(ServerSlot);
				local playerClass = "";
				if Player and Player.cnt then
					playerClass = ServerSlot.InitialPlayerProperties.sPlayerClass;
				end
				local RespawnInfo = self.respawnList[ServerSlot];
				local szPlayerTeam;
				
				if (not RespawnInfo) then
					szPlayerTeam = Game:GetEntityTeam(ServerSlot:GetPlayerId());
				else
					szPlayerTeam = RespawnInfo.team;
					
					if (not szTeam) then
						szPlayerTeam = Game:GetEntityTeam(ServerSlot:GetPlayerId());
					end
				end
				
				if (team == szPlayerTeam) and (playerClass == class) then
					iCount = iCount + 1;
				end
			end
		end
	else
		--not team based
		return 0;
	end
	return iCount;
end


--------------------------------------------------------
-- check the players counts (min players and max players
------------------------------------------------------
function GameRules:CheckPlayerCount()
	local iMinTeamLimit = toNumberOrZero(getglobal("gr_MinTeamLimit"));
	
	
	if (not self.bIsTeamBased) then
		if (tostring(getglobal("gr_PrewarOn")) ~= "0") then
			iMinTeamLimit = 1;
		else
			iMinTeamLimit = 0;
		end
	end
	
	local iCurrentState = self:GetGameState();
		
	if ((iMinTeamLimit < 1) and (iCurrentGameState == CGS_INPROGRESS)) then
		return;
	end

	local iTeam01Count = GameRules:GetTeamMemberCountRL("red");
	local iTeam02Count = GameRules:GetTeamMemberCountRL("blue");
	
	if(not self.bIsTeamBased)then
		iTeam01Count = GameRules:GetTeamMemberCountRL("players");
		iTeam02Count = iTeam01Count;
	end
	
	if (iTeam01Count < iMinTeamLimit or iTeam02Count < iMinTeamLimit) then
				
		if (iCurrentState == CGS_INPROGRESS) then
			self:NewGameState(CGS_PREWAR);
			if (self.bIsTeamBased) then
				Server:BroadcastText("@PlayerCountNotOk "..iMinTeamLimit.." @PlayerCountNotOk2", 2);
			else
				Server:BroadcastText("@PlayerCountNotOkNoTeam "..iMinTeamLimit.." @PlayerCountNotOk2NoTeam", 2);
			end
		end
	elseif ((iCurrentState == CGS_PREWAR) and ((not self.restartbegin) or self.restartbegin == 0)) then
		self:Restart(3, 1);
		Server:BroadcastText("@GameStartingIn "..(3).." @GameStartingInSeconds", 2);
	end
end



--------------------------------------------------------
-- update Synched2DTable scoreboard
------------------------------------------------------
function GameRules:RemovePlayerFromScoreboard( idClient )
--	System:Log("GameRules:RemovePlayerFromScoreboard");						--debug
	local Entity = System:GetEntity(GameRules.idScoreboard).cnt;
	
	local iY;
	local iLines=Entity:GetLineCount();
	
	for iY=0,iLines-1 do
		local idThisClient = Entity:GetEntryXY(ScoreboardTableColumns.ClientID,iY)-1;		-- first element is the clientid-1

		-- clear line
		if idThisClient==idClient then
--			System:Log("GameRules:RemovePlayerFromScoreboard idThisClient==idClient");						--debug
			Entity:SetEntriesY(iY,0);
			return;
		end
	end
	
--	System:Log("Error: GameRules:RemovePlayerFromScoreboard not found");
end


--------------------------------------------------------
-- update Synched2DTable scoreboard
------------------------------------------------------
function GameRules:AddPlayerToScoreboard( idClient )
	local Entity = System:GetEntity(GameRules.idScoreboard).cnt;
	
	if not Entity then
		System:LogError("GameRules:AddPlayerToScoreboard internal error");			-- better than script error but shouln't happen
		return;
	end
	
	local iY;
	local iLines=Entity:GetLineCount();
	
	for iY=0,iLines-1 do
		local idThisClient = Entity:GetEntryXY(ScoreboardTableColumns.ClientID,iY)-1;		-- first element is the clientid-1

		if idThisClient==-1 then
			-- set player (should be already cleared)
			Entity:SetEntryXY(ScoreboardTableColumns.ClientID,iY,idClient+1);
			System:Log("GameRules:AddPlayerToScoreboard line "..iY);		--debug
			return;
		end
	end

	Entity:SetEntryXY(ScoreboardTableColumns.ClientID,iLines,idClient+1);
	System:Log("GameRules:AddPlayerToScoreboard line "..iLines);		--debug
end


--------------------------------------------------------
-- update Synched2DTable scoreboard
------------------------------------------------------
function GameRules:SetScoreboardEntryXY( iX, idClient, Value )
--	System:Log("GameRules:SetScoreboardEntryXY "..iX.." "..idClient.." "..tostring(Value));		--debug
	local Entity = System:GetEntity(GameRules.idScoreboard).cnt;
	
	if iX==0 then
		return;					-- first element is the clientid-1
	end
	
	local iY;
	local iLines=Entity:GetLineCount();
	
	for iY=0,iLines-1 do
		local idThisClient = Entity:GetEntryXY(ScoreboardTableColumns.ClientID,iY)-1;		-- first element is the clientid-1

		if idThisClient==idClient then
--			System:Log("GameRules:SetScoreboardEntryXY line "..iY);		--debug
			Entity:SetEntryXY(iX,iY,Value);
			return;
		end
	end
end

