#include extreme\_ex_lin;

//******************************************************************************
// eXtreme+ launch main threads
//******************************************************************************
main()
{
	// get the map dimensions and playing field dimensions
	extreme\_ex_utils::GetMapDim();
	extreme\_ex_utils::GetFieldDim();

	// set up map rotation
	if(getCvar("ex_maprotdone") == "")
	{
		if(level.ex_pbrotate)
			extreme\_ex_maprotation::pbrotation();
		setCvar("ex_maprotdone","1");
	}

	// clear any camping players
	if(level.ex_campwarntime || level.ex_campsniper_warntime) thread extreme\_ex_camper::removeCampers();

	// fix the map rotation
	if(level.ex_fixmaprotation) level extreme\_ex_maprotation::fixMapRotation();

	// set a random map rotation
	if(level.ex_randommaprotation) level thread extreme\_ex_maprotation::randomMapRotation();

	// text logos for mod and server
	thread extreme\_ex_modinfo::main();
	
	// time announcer
	if(level.ex_timeannouncer) level thread extreme\_ex_timeannouncer::main();
	
	// rotate if empty
	if(level.ex_rotateifempty) level thread extreme\_ex_rotate::main();
	
	// rotate motd
	if(level.ex_motdrotate) level thread extreme\_ex_messages::motdrotate();

	// server messages
	if(level.ex_svrmsg) level thread extreme\_ex_messages::serverMessages();
	
	// flares
	if(level.ex_flares >= 1) level thread extreme\_ex_flares_ambient::init();

	// mortars
	if(level.ex_mortars >= 1) level thread extreme\_ex_mortar_ambient::init();

	// remove minefields triggers
	if(!level.ex_minefields) level thread extreme\_ex_utils::disableMinefields();
	
	// planes
	if(level.ex_planes >= 1) level thread extreme\_ex_airplanes::init();
	
	// artillery
	if(level.ex_artillery >= 1) level thread extreme\_ex_artillery_ambient::init();	
	
	// launch name checker
	if(level.ex_namechecker) level thread extreme\_ex_namecheck::init();

	// tracers
	if(level.ex_tracers) for(tracers=0;tracers<level.ex_tracers;tracers++) level thread extreme\_ex_skyeffects::tracers();

	// flak fx	
	if(level.ex_flakfx && !level.ex_planes_flak) for(flak=0;flak<level.ex_flakfx;flak++) level thread extreme\_ex_skyeffects::flakfx();

	// livestats
	//if(level.ex_livestats && level.ex_teamplay) level thread extreme\_ex_livestats::init();
	if(level.ex_livestats) level thread extreme\_ex_livestats::init();

	// eXtreme+ command monitor
	if(level.ex_cmdmonitor) level thread extreme\_ex_cmdmonitor::init();

	// ammo crates
	if(level.ex_amc_perteam) level thread extreme\_ex_ammocrates::spawnCrates();

	// turrets monitor
	if(level.ex_turrets) level thread extreme\_ex_unfix_turrets::unfix_turrets_init();

	// gametype start delay
	if(level.ex_gtsdelay) level thread gtsDelay();

	// monitor the world for potatoes!
	level thread potatoMonitor();

	// start bot system
	level notify("gobots");
	
	// launch kick monitor
	if(level.ex_inactive_plyr || level.ex_inactive_spec) level thread extreme\_ex_kick_monitor::main();

	// show custom clan logo (mylogo)
	level thread extreme\_ex_mylogo::start();

	// delay the availability of the Call Vote feature
	if(level.ex_cvdelay) level thread extreme\_ex_callvote_delay::init();

	// procedure to free up entities by removing unused spawnpoints (DEBUG)
	if(level.ex_entities) level thread extreme\_ex_entities::init();
}

//******************************************************************************
// eXtreme+ launch player threads
//******************************************************************************
playerThreads()
{
	level endon("ex_gameover");
	self endon("disconnect");
	self endon("ex_dead");

	// start the move monitor
	self thread moveMonitor();

	// range finder
	if(level.ex_rangefinder) self thread extreme\_ex_utils::rangef();

	// Tnic
	if(level.ex_intromusic || level.ex_specmusic || level.ex_deathmusic)
	{
		self.pers["spec_on"] = false; self.pers["intro_on"] = false; self.pers["dth_on"] = false;
		self playLocalSound("spec_music_null");
		self playLocalSound("spec_music_stop");
	}

	// monitor for knife
	self thread extreme\_ex_knife::knife();

	// monitor for flamethrower
	self thread extreme\_ex_flamethrower::init();

	// show logo
	if(level.ex_logopic) self thread extreme\_ex_logo::logogfx();

	// spawn protection
	if(level.ex_spwn_time) self thread extreme\_ex_spawnpro::spawnprotection();

	// parachutes
	if(level.ex_parachutes >= 1)
		self thread extreme\_ex_parachute::PlayerParachute();
	
	// display round number at spawn for roundbased gametypes
	if(level.ex_roundbased) self thread roundDisplay();
	
	// mobile mg monitor
	if(level.ex_turrets == 2) self thread extreme\_ex_unfix_turrets::mobile_mg();

	// display round number at spawn for roundbased gametypes
	if(level.ex_roundbased) self thread roundDisplay();

	// apply laser dot
	if(level.ex_laserdot && !isDefined(level.ex_gtsmsg))
	  self thread extreme\_ex_laserdot::init();

	// prone shoot delay monitor
	if(level.ex_stanceshoot) self thread stanceShootMonitor();

	// health bar
	if(level.ex_healthBar) self thread healthBar();

	// check names and show welcome messages 
	self thread handleWelcome();
	
	// welcome sound
	if(level.ex_goodluck)
	{
		if(isDefined(self.ex_team_changed))
		{
			self.ex_glplay = undefined;
			self.ex_team_changed = undefined;
		}

		if(!isDefined(self.ex_glplay) /*&& level.ex_teamplay*/) self thread extreme\_ex_welcome::announce();
	}

	// camper check
	if(level.ex_campwarntime || level.ex_campsniper_warntime) self thread extreme\_ex_camper::campercheck();

	// start the rank hud system
	if(level.ex_ranksystem && level.ex_rankhud) self thread extreme\_ex_ranksystem::rankhud();

	// start cold breath fx if wintermap
	if(level.ex_wintermap && level.ex_coldbreathfx) self thread coldbreathMonitor();

	// start the firstaid system monitor
	if(level.ex_callformedic == 1) self thread firstaidMonitor();

	// firstaid system
	if(level.ex_callformedic >= 1) self thread extreme\_ex_firstaid::firstaid();

	// if you're a bot, this is as far as you go buddy!
	if(getCvarInt("scr_testclients") > 0 && isDefined(self.pers["isbot"]) && self.pers["isbot"])
	{
		if(!isDefined(getCvarInt("scr_botfreeze"))) setCvar("scr_botfreeze", 0);
		else if(getCvarInt("scr_botfreeze") == 1) self extreme\_ex_utils::punishment("enable", "freeze");
		return;
	}

	// monitor use of binoculars
	self thread binocularMonitor();

	// tripwire monitor
	if(level.ex_tweapon) self thread extreme\_ex_tripwires::tripwiremon();

	// start the sprint system
	if(level.ex_sprint) self thread extreme\_ex_sprintsystem::sprintInit();

	// start the grenade warning monitor
	if(level.ex_grenadewarn && level.ex_teamplay) self thread grenadeMonitor();

	// force clientside dvars
	if(level.ex_forceclientdvars) self thread extreme\_ex_forcedvar::forceClientDvars();

	// Prepare in-game menu for adding this server to the favorites
	if(level.ex_addtofavorites)
	{
		self setClientCvar("ui_favoriteExtreme", "1");
		self setClientCvar("ui_favoriteName", getCvar("sv_hostname"));
		if(isDefined(level.ex_addtofavorites_ip) && level.ex_addtofavorites_ip != "")
			self setClientCvar("ui_favoriteAddress", level.ex_addtofavorites_ip + ":" + getCvar("net_port"));
		else
			self setClientCvar("ui_favoriteAddress", getCvar("net_ip") + ":" + getCvar("net_port"));
	}
}

//******************************************************************************
// eXtreme+ monitors
//******************************************************************************
coldbreathMonitor()
{
	level endon("ex_gameover");
	self endon("disconnect");
	self endon("ex_dead");

	wait (2 + randomint(3));

	while(isPlayer(self) && self.sessionstate == "playing")
	{
		playfxontag (level.ex_effect["coldbreathfx"], self, "TAG_EYE");
		
		if(self.ex_playsprint || self.ex_sprintreco) wait randomfloatrange(0.5,1.5);
			else wait randomfloatrange(1.5,3.5);
	}
}

firstaidMonitor()
{
	level endon("ex_gameover");
	self endon("disconnect");
	self endon("ex_dead");

	while(isPlayer(self) && self.sessionstate == "playing")
	{
		if(self.health < level.ex_medicall && level.ex_callformedic)
		{
			self thread extreme\_ex_firstaid::callformedic();
			wait 20;
		}
		
		wait 0.5;
	}
}

binocularMonitor()
{
	level endon("ex_gameover");
	self endon("disconnect");
	self endon("ex_dead");

	while(isPlayer(self) && self.sessionstate == "playing")
	{
		self waittill("binocular_enter");
		self.ex_binocuse = true;
		self waittill("binocular_exit");
		self.ex_binocuse = false;

		wait 0.1;
	}
}

grenadeMonitor()
{
	level endon("ex_gameover");
	self endon("disconnect");
	self endon("ex_dead");

	for(;;)
	{
		grenadetype = undefined;

		// how many of each grenade do they have?
		frags = self getammocount(self.pers["fragtype"]) + self getammocount(self.pers["enemy_fragtype"]);
		smokes = self getammocount(self.pers["smoketype"]) + self getammocount(self.pers["enemy_smoketype"]);

		while(isPlayer(self) && self.sessionstate == "playing" && !self.ex_plantwire)
		{
			wait 0.05;
	
			// how many of each grenade do they have now?
			frags_check = self getammocount(self.pers["fragtype"]) + self getammocount(self.pers["enemy_fragtype"]);
			smokes_check = self getammocount(self.pers["smoketype"]) + self getammocount(self.pers["enemy_smoketype"]);				
	
			// player may have thrown both!, so no else here....
			if(frags_check < frags && !self.ex_plantwire)
			{
				self thread maps\mp\gametypes\_quickmessages::quickwarning("frag", 480, true, true);
				frags = frags_check;
			}
			else frags = frags_check;
	
			if(smokes_check < smokes && !self.ex_plantwire)
			{
				self thread maps\mp\gametypes\_quickmessages::quickwarning("smoke", 480, true, true);
				smokes = smokes_check;
			}
			else smokes = smokes_check;
		}
		
		wait 0.05;
	}
}

moveMonitor()
{
	level endon("ex_gameover");
	self endon("disconnect");
	self endon("ex_dead");

	self.ex_stance = 0;
	self.ex_pace = false;
	self.ex_moving = false;
	if(level.ex_antirun) self.antirun_puninprog = false;

	while(isPlayer(self) && isAlive(self) && self.sessionstate=="playing")
	{
		// Get the stance
		self.ex_stance = [[level.ex_getStance]](false);

		if(isPlayer(self))
		{
			// Calculate current speed
			mark = self.origin;
			wait 0.1;

			if(isPlayer(self))
			{
				dist = distance(mark, self.origin);

				if(dist > 1) self.ex_moving = true;
					else self.ex_moving = false;

				if(dist > 10) self.ex_pace = true;
					else self.ex_pace = false;

				// Sniper anti-run
				if(level.ex_antirun && !self.ex_invulnerable)
				{
					if(self.ex_sprinting || (self.ex_stance == 0 && self.ex_moving))
					{
						if(isdefined(self.antirun_mark))
						{
							if(int(distance(self.antirun_mark, self.origin)) > level.ex_antirun_distance)
							{
								self thread antirunPunish();
								self.antirun_mark = undefined;
							}
						}
						else self.antirun_mark = mark;
					}
					else self.antirun_mark = undefined;
				}
			}
		}
	}
}

stanceShootMonitor()
{
	level endon("ex_gameover");
	self endon("disconnect");
	self endon("ex_dead");

	lastprone = 2;
	lastjump = 3;

	jumpcheck = false;
	jumpsensitivity = level.ex_jump_sensitivity;
	jumpsensor = 0;

	for(;;)
	{
		wait 0.05;

		if(self.ex_plantwire || self.ex_defusewire || self.handling_mine || isDefined(self.ex_isparachuting)) continue;

		stance = [[level.ex_getStance]](false);
		jump = [[level.ex_getStance]](true);
		doit = false;

		switch(level.ex_stanceshoot)
		{
			case 1:
				if(stance == 2 && lastprone != 2) doit = true;
				break;
			case 2:
				if(jump == 3 && lastjump != 3) jumpcheck = true;
				break;
			default: {
				if(stance == 2 && lastprone != 2) doit = true;
					else if(jump == 3 && lastjump != 3) jumpcheck = true;
			}
		}

		if(jumpcheck)
		{
			jumpsensor++;
			if(jumpsensor > jumpsensitivity)
			{
				jumpsensor = 0;
				doit = true;
			}
			jumpcheck = false;
		}
		else jumpsensor = 0;

		lastprone = stance;
		if(jumpsensor == 0) lastjump = jump;

		if(doit) self thread extreme\_ex_utils::weaponPause(0.4 + randomfloat(0.4));
	}
}

potatoMonitor()
{
	level endon("ex_gameover");
	
	while(!level.ex_gameover)
	{
		//Clean the "world" of dropped potatoes
		thread extreme\_ex_utils::deletePlacedEntity("weapon_sprint_mp");
		thread extreme\_ex_utils::deletePlacedEntity("weapon_dummy1_mp");
		thread extreme\_ex_utils::deletePlacedEntity("weapon_dummy2_mp");
		thread extreme\_ex_utils::deletePlacedEntity("weapon_dummy3_mp");
		thread extreme\_ex_utils::deletePlacedEntity("weapon_dummy4_mp");
		wait 0.25;
	}
}

//******************************************************************************
// eXtreme+ anti-run punishments
//******************************************************************************
antirunPunish()
{
	if(!isdefined(self.antirun_punlevel))
		self.antirun_punlevel = 0;

	if(!isdefined(self.antirun_puninprog))
		self.antirun_puninprog = false;

	if(self.antirun_puninprog) return;

	self.antirun_puninprog = true;
	self iprintlnbold(&"SPRINT_RUNWARNINGA");
	self iprintlnbold(&"SPRINT_RUNWARNINGB");

	switch(self.antirun_punlevel)
	{
		case 0:
			self.antirun_punlevel++;
			self iprintlnbold(&"SPRINT_FIRST_PLAYER");
			if(level.IsLinuxServer) iprintln(&"SPRINT_FIRST_ALL", self.name);
				else iprintln(&"SPRINT_FIRST_ALL", self);
			extreme\_ex_utils::forceto("crouch");
			self disableWeapon();
			self shellshock("default", 5);
			wait(5);
			self enableWeapon();
			break;

		case 1:
			self.antirun_punlevel++;
			self iprintlnbold(&"SPRINT_SECOND_PLAYER");
			if(level.IsLinuxServer) iprintln(&"SPRINT_SECOND_ALL", self.name);
				else iprintln(&"SPRINT_SECOND_ALL", self);
			extreme\_ex_utils::forceto("crouch");
			self.health = int(self.health / 2);
			self disableWeapon();
			self shellshock("default", 10);
			wait(10);
			self enableWeapon();
			break;

		case 2:
			self.antirun_punlevel++;
			self iprintlnbold(&"SPRINT_THIRD_PLAYER");
			if(level.IsLinuxServer) iprintln(&"SPRINT_THIRD_ALL", self.name);
				else iprintln(&"SPRINT_THIRD_ALL", self);
			extreme\_ex_utils::forceto("crouch");
			self thread extreme\_ex_punishments::doWarp(true);
			wait(30);
			break;

		case 3:
			self.antirun_punlevel = 0;
			self iprintlnbold(&"SPRINT_FOURTH_PLAYERA");
			extreme\_ex_utils::forceto("crouch");
			self disableWeapon();
			self shellshock("default", 5);
			wait(5);
			self iprintlnbold(&"SPRINT_FOURTH_PLAYERB");
			wait(3);
			if(level.IsLinuxServer) iprintln(&"SPRINT_FOURTH_ALL", self.name);
				else iprintln(&"SPRINT_FOURTH_ALL", self);
			kick(self getEntityNumber());
			break;
	}

	self.antirun_puninprog = false;
}

//******************************************************************************
// eXtreme+ health system
//******************************************************************************
healthBar()
{
	level endon("ex_gameover");
	self endon("disconnect");
	self endon("ex_dead");

	if(isDefined(self.ex_healthbar)) return;

	self.ex_healthcross = newClientHudElem(self);
	self.ex_healthcross.x = 543;
	self.ex_healthcross.y = 455;
	self.ex_healthcross.alignX = "right";
	self.ex_healthcross.alignY = "top";
	self.ex_healthcross setShader("gfx/hud/hud@health_cross.tga", 10, 10);

	self.ex_healthback = newClientHudElem(self);
	self.ex_healthback.x = 547;
	self.ex_healthback.y = 455;
	self.ex_healthback.alignX = "left";
	self.ex_healthback.alignY = "top";
	self.ex_healthback setShader("gfx/hud/hud@health_back.tga", 90, 10);

	self.ex_healthbar = newClientHudElem(self);
	self.ex_healthbar.x = 548;
	self.ex_healthbar.y = 456;
	self.ex_healthbar.alignX = "left";
	self.ex_healthbar.alignY = "top";
	self.ex_healthbar.color = ( 0, 1, 0);
	self.ex_healthbar setShader("gfx/hud/hud@health_bar.tga", 88, 8);

	self thread healthUpdate();
}

healthDrop()
{
	self endon("disconnect");
	
	if(isDefined(game["ex_healthqueue"][game["ex_healthqueuecurrent"]])) game["ex_healthqueue"][game["ex_healthqueuecurrent"]] delete();

	health_nr = RandomInt(3);

	switch(health_nr)
	{
		case 1: modeltype = "xmodel/health_medium"; break;
		case 2: modeltype = "xmodel/health_large"; break;
		default: modeltype = "xmodel/health_small"; break;
	}

	item_health = spawn("script_model", (0,0,0));	
	item_health setModel(modeltype);
	item_health.targetname = "item_healths";
	item_health hide();
	item_health.origin = self.origin;
	item_health.angles = (0, randomint(360), 0);
	item_health show(); 

	rotation = (randomFloat(180), randomFloat(180), randomFloat(180));
	velocity = (randomInt(4) + 4, randomInt(4) + 4, randomInt(6) + 6);

	item_health extreme\_ex_utils::bounceObject(rotation, velocity, (0,0,0), (0,0,0), 5, 0.4, undefined, undefined, "health");
	item_health thread healthThink(health_nr);

	game["ex_healthqueue"][game["ex_healthqueuecurrent"]] = item_health;
	game["ex_healthqueuecurrent"]++;

	if(game["ex_healthqueuecurrent"] >= 16) game["ex_healthqueuecurrent"] = 0;
}

healthThink(health_nr)
{
	while(isDefined(self))
	{
		wait 0.2;

		if(!isDefined(self)) return;

		player = "";
		players = getentarray("player", "classname");

		for(i = 0; i < players.size; i++)
		{
			player = players[i];

			if(isPlayer(player) && isDefined(self) && player.sessionstate == "playing" && distance(self.origin,player.origin) < 50 && (player.health < player.maxhealth || level.ex_callformedic && level.ex_firstaidpickup && player.ex_firstaidkits < 9))
			{
				player endon("disconnect");

				if(player.health < player.maxhealth)
				{
					player.health += health_nr * 40;

					if(player.health > player.maxhealth) player.health = player.maxhealth;

					if(health_nr == 0) player playLocalSound("health_pickup_small");
					else if(health_nr == 1) player playLocalSound("health_pickup_medium");
					else if(health_nr == 2) player playLocalSound("health_pickup_large");
					else player playLocalSound("health_pickup_medium");

					if(isDefined(self)) self delete();
				}
				else if(level.ex_callformedic && level.ex_firstaidpickup && player.ex_firstaidkits < level.ex_firstaidpickup)
				{
					player.ex_firstaidkits++;
					player playLocalSound("health_pickup_medium");
					player iprintln(&"FIRSTAID_PICKEDUP");
					player.ex_canheal = true;
					if(isDefined(player.ex_firstaidval))
					{
						player.ex_firstaidval setValue(player.ex_firstaidkits);
						player.ex_firstaidval.color = (1, 1, 1);
					}

					if(isDefined(self)) self delete();
				}
			}
		}				
	}
}

healthUpdate()
{
	self endon("disconnect");
	self endon("ex_dead");
	
	oldhealth = self.health;
	width = undefined;
	
	for(;;)
	{
		wait 0.1;

		if(isPlayer(self) && isDefined(self.health) && self.health != oldhealth)
		{
			health = self.health / self.maxhealth;
		
			width = int(health * 88);
					
			if(width < 1) width = 1;

			if(isDefined(self.ex_healthbar))
			{					
				self.ex_healthbar setShader("gfx/hud/hud@health_bar.tga", width, 8);
				self.ex_healthbar.color = ( 1.0 - health, health, 0);
			}
			
			oldhealth = self.health;
		}
	}
}

//************************************************************************************************************************************************************************
// eXtreme+ gametype additional routines
//************************************************************************************************************************************************************************
gtsDelay()
{
	if(isDefined(game["gtsdone"])) return;
	
	if(level.ex_currentgt == "dom" || level.ex_currentgt == "ons" || level.ex_currentgt == "lms") return;

	level.ex_gtsmsg = newHudElem();
	level.ex_gtsmsg.x = 320;
	level.ex_gtsmsg.y = 20; //240;
	level.ex_gtsmsg.alignX = "center";
	level.ex_gtsmsg.alignY = "top"; //"middle";
	level.ex_gtsmsg.fontscale = 2;
	level.ex_gtsmsg.color = (0, 1, 0);
			
	level.ex_gtsmsg.label = &"GTSDELAY_WAITING_PLAYERS";

	// wait for 2 players to join
	for(;;)
	{
		wait 1;

		players = getentarray("player", "classname");

		// no players, so keep waiting
		if(players.size < 1) continue;

		playercount = 0;

		for (i = 0; i < players.size; i++)
		{
			if(isDefined(players[i].pers["team"]) && players[i].pers["team"] != "spectator") playercount++;
		}

		// enough players?
		if(playercount >= 2) break;
	}

	// enough players have joined so start the count down to spawn
	level.ex_gtsmsg.label = &"GTSDELAY_MATCH_BEGINS";
	level.ex_gtsmsg setTimer(level.ex_gtsdelay);

	wait level.ex_gtsdelay - 1;

	if(isDefined(level.ex_gtsmsg))
	{
		level.ex_gtsmsg fadeOverTime(1);
		level.ex_gtsmsg.alpha = 0;
		wait 1;
	}

	if(isDefined(level.ex_gtsmsg)) level.ex_gtsmsg destroy();
		
	// reset the game time and restart the map
	level.starttime = getTime();
	game["gtsdone"] = true;

	// restart the map here if not a roundbased gametype
	if(!level.ex_roundbased) map_restart(true);
	else
	{
		iprintln(&"MP_MATCHSTARTING");

		level notify("kill_endround");
		level.roundended = false;

		switch(level.ex_currentgt)
		{
			case "lts":
			level thread maps\mp\gametypes\lts::endRound("gtsreset");
			break;

			case "sd":
			level thread maps\mp\gametypes\sd::endRound("gtsreset");
			break;
			
			case "rbctf":
			level thread maps\mp\gametypes\rbctf::endRound("gtsreset");
			break;
			
			case "rbcnq":
			level thread maps\mp\gametypes\rbcnq::endRound("gtsreset");
			break;
			
			case "esd":
			level thread maps\mp\gametypes\esd::endRound("gtsreset");
			break;
		}
	}
}

swapTeams()
{
	level endon("ex_gameover");

	if(game["roundsplayed"] == 0 || !game["matchstarted"]) return;

	if(level.ex_swapteams == 2 && game["roundnumber"] > level.half_time) return;

	players = getentarray("player", "classname");
	for(i = 0; i < players.size; i++)
	{
		// don't do anything with spectators
		if(!isDefined(players[i].pers["team"]) || players[i].pers["team"] == "spectator") continue;

		newTeam = undefined;

		if(players[i].pers["team"] == "axis") newTeam = "allies";
		else if(players[i].pers["team"] == "allies") newTeam = "axis";

		if(level.ex_wepo_secondary) players[i].pers["first_time"] = true;

		players[i].pers["team"] = newTeam;
		players[i].pers["savedmodel"] = undefined;
		players[i] thread extreme\_ex_clientcontrol::clearWeapons();
		//players[i] maps\mp\gametypes\_spectating::setSpectatePermissions();
	}

	tempscore = game["alliedscore"];
	game["alliedscore"] = game["axisscore"];
	game["axisscore"] = tempscore;
	setTeamScore("allies", game["alliedscore"]);
	setTeamScore("axis", game["axisscore"]);
}

exPreSpawn()
{
	level endon("ex_gameover");
	self endon("disconnect");

	// set spawn variables
	setPlayerVariables();

	// allow team change option on weapons menu
	self setClientCvar("ui_allow_teamchange", 0);

	if(getCvarInt("scr_testclients") > 0 && isDefined(self.pers["isbot"]) && !isDefined(self.pers["weapon"]) && level.ex_roundbased && level.ex_swapteams)
		self maps\mp\gametypes\_teams::testClient(self.pers["team"]);

	// remove camper objective
	if(level.ex_campwarntime || level.ex_campsniper_warntime) self thread extreme\_ex_camper::removeCamper();

	// start rank monitor
	if(level.ex_ranksystem) self thread extreme\_ex_ranksystem::playerRankMonitor();

}

exPostSpawn()
{
	level endon("ex_gameover");
	self endon("disconnect");
	self notify("ex_spawned");
	// wait for threads to die
	wait 0.01;
	
	if(isDefined(self.ex_redirected)) return;

	if(isPlayer(self))
	{
		// Attach head marker, used by Sprint System and LR Hitloc
		if(!isDefined(self.ex_headmarker))
		{
			self.ex_headmarker = spawn("script_origin",(0,0,0));
			//self.ex_headmarker linkto (self, "J_Head",(0,50,0),(0,0,0));
			self.ex_headmarker linkto (self, "J_Head",(0,0,0),(0,0,0));
		}
		// Attach spine marker, used by GetStance() and LR Hitloc
		if(!isDefined(self.ex_spinemarker))
		{
			self.ex_spinemarker = spawn("script_origin",(0,0,0));
			self.ex_spinemarker linkto (self, "J_Spine4",(0,0,0),(0,0,0));	
		}
		// Attach eye marker, used by Range Finder and LR Hitloc
		if(!isDefined(self.ex_eyemarker))
		{
			self.ex_eyemarker = spawn("script_origin",(0,0,0));
			self.ex_eyemarker linkto (self, "tag_eye",(0,0,0),(0,0,0));
		}
		// Attach thumb marker, used by Knife and Unfixed Turrets
		if(!isDefined(self.ex_thumbmarker))
		{
			self.ex_thumbmarker = spawn("script_origin",(0,0,0));
			self.ex_thumbmarker linkto (self, "J_Thumb_ri_1",(0,0,0),(0,0,0));
		}

		if(level.ex_lrhitloc)
		{
			// Attach left ankle marker, used by LR Hitloc
			if(!isDefined(self.ex_lankmarker))
			{
				self.ex_lankmarker = spawn("script_origin",(0,0,0));
				self.ex_lankmarker linkto (self, "j_ankle_le",(0,0,0),(0,0,0));
			}
			// Attach right ankle marker, used by LR Hitloc
			if(!isDefined(self.ex_rankmarker))
			{
				self.ex_rankmarker = spawn("script_origin",(0,0,0));
				self.ex_rankmarker linkto (self, "j_ankle_ri",(0,0,0),(0,0,0));
			}
			// Attach left wrist marker, used by LR Hitloc
			if(!isDefined(self.ex_lwristmarker))
			{
				self.ex_lwristmarker = spawn("script_origin",(0,0,0));
				self.ex_lwristmarker linkto (self, "j_wrist_le",(0,0,0),(0,0,0));
			}
			// Attach right wrist marker, used by LR Hitloc
			if(!isDefined(self.ex_rwristmarker))
			{
				self.ex_rwristmarker = spawn("script_origin",(0,0,0));
				self.ex_rwristmarker linkto (self, "j_wrist_ri",(0,0,0),(0,0,0));
			}
		}

		self thread playerThreads();
	}
}

exPlayerDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime)
{
	self endon("disconnect");

	if (!isDefined(vPoint))
		vPoint = self.origin + (0,0,11);

	// napalm?
	napalm = false;
	if(sMeansOfDeath == "MOD_PROJECTILE" && sWeapon == "planebomb_mp") napalm = true;

	// disable or drop weapon after a fall, for a bit anyway
	if(sMeansOfDeath == "MOD_FALLING") self thread weaponfall(1.5);

	// Damage modifiers, weapons
	if(level.ex_wdmodon && sMeansOfDeath != "MOD_MELEE" && isDefined(sWeapon))
		iDamage = int((iDamage / 100) * [[level.ex_drm]]("ex_wdm_" + sWeapon,100,0,500,"int"));

	// Splatter on attacker?
	if(isPlayer(eAttacker) && (sMeansOfDeath == "MOD_MELEE" || distance(eAttacker.origin , self.origin ) < 50)) eAttacker thread bloodonscreen();

	// bulletholes?
	if(level.ex_bulletholes && (sMeansOfDeath == "MOD_PISTOL_BULLET" || sMeansOfDeath == "MOD_RIFLE_BULLET")) self thread extreme\_ex_bulletholes::bullethole();

	// punish attacking player for attacking spawn protected players
	if(level.ex_spwn_punish_attacker && isPlayer(eAttacker) && eAttacker != self && self.usedweapons && self.ex_invulnerable)
	{
		eAttacker thread extreme\_ex_spawnpro::punish("attacking");
		return;
	}

	// punish protected player for abusing spawn protection
	if(level.ex_spwn_punish_self && isPlayer(eAttacker) && eAttacker != self && eAttacker.usedweapons && eAttacker.ex_invulnerable)
	{
		eAttacker thread extreme\_ex_spawnpro::punish("abusing");
		return;
	}

	if(isPlayer(eAttacker))
	{
		// show hit blip
		if(isPlayer(eAttacker) && eAttacker != self) eAttacker thread showPlayerHit();

		// Pains sound
		if(level.ex_painsound)
		{
			if(napalm && randomInt(100) < 25) self thread extreme\_ex_utils::playSoundOnPlayer("generic_pain", "pain");
				else if(!napalm) self thread extreme\_ex_utils::playSoundOnPlayer("generic_pain", "pain");
		}

		// Helmet pop
		if(!self.ex_helmetpopped)
		{
			switch(sHitLoc)
			{
				case "helmet":
				case "head":
					if(randomInt(100) < level.ex_pophelmet)
					{
						self popHelmet( vDir, iDamage );
						self thread bloodonscreen();
					}
					break;
			}
		}
	
		// firstaid disable if team mate
		if((isPlayer(eAttacker)) && (self.pers["team"] == eAttacker.pers["team"]) && (self != eAttacker) && level.ex_callformedic >= 1 && level.ex_teamplay && level.ex_revokeonth)
		{
			if(isDefined(eAttacker))
			{
				eAttacker playlocalsound("friendlyfire");
				eAttacker thread extreme\_ex_firstaid::disablePlayerHealing();
			}
		}
	}

	if(isAlive(self))
	{	
		switch(sHitLoc)
		{
			case "right_hand":
			case "left_hand":
			case "gun":
				if(level.ex_droponhandhit != 0 && randomInt(100) < level.ex_droponarmhit) self thread extreme\_ex_weapons::dropcurrentweapon();
				break;
			
			case "right_arm_lower":
			case "left_arm_lower":
				if(level.ex_droponarmhit != 0 && randomInt(100) < level.ex_droponarmhit) self thread extreme\_ex_weapons::dropcurrentweapon();
				break;
	
			case "right_foot":
			case "left_foot":
				if(level.ex_triponfoothit && randomInt(100) < level.ex_triponfoothit) self thread spankme(1);
				break;

			case "right_leg_lower":
			case "left_leg_lower":
				if(level.ex_triponleghit && randomInt(100)<level.ex_triponleghit) self thread spankme(1);
				break;

			case "torso_lower":
				if(isDefined(self.tankonback) && (randomInt(100) < level.ex_ft_tank_explode))
				{
					if(!level.ex_teamplay || (level.ex_teamplay && level.friendlyfire == "1" && isPlayer(eAttacker) && (self.pers["team"] == eAttacker.pers["team"])) )
					{
						level thread extreme\_ex_flamethrower::tankExplosion(self, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime);
						return;
					}
				}
				break;
		}
	}

	iDamage = int(iDamage);

	if(!napalm && level.ex_bleeding && self.health < level.ex_startbleed)
		self thread extreme\_ex_bleeding::doPlayerBleed(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime);
	
	[[level.ex_orignalPlayerDamage]](eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime);
}

exPlayerKilled(eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc)
{
	self endon("disconnect");
	self notify("ex_dead");

	if(level.ex_drophealth) self thread healthDrop();

	// clean the hud
	self extreme\_ex_hud::cleanplayer();

	if(level.ex_deathsound) self thread extreme\_ex_utils::playSoundLoc("generic_death",self.origin, "death");

	// Tnic
	if(level.ex_deathmusic && !level.ex_roundbased && !self.pers["spec_on"]) self playLocalSound("death_music");
	
	// turret abuse check
	if(level.ex_turretabuse && (extreme\_ex_weapons::isWeaponType(sWeapon,"turret") || (sWeapon == "none" && sMeansOfDeath == "MOD_RIFLE_BULLET")) && isDefined(attacker))
	{
		wepname = maps\mp\gametypes\_weapons::getWeaponName(sWeapon);
		attacker.pers["turretkill"]++;
		
		if(attacker.pers["turretkill"] == level.ex_turretabuse_warn)
		{
			attacker iprintlnbold(&"TURRET_ABUSER_WARN_PMSG_0");
			attacker iprintlnbold(&"TURRET_ABUSER_WARN_PMSG_1");
		}
		else if(attacker.pers["turretkill"] >= level.ex_turretabuse_kill)
		{
			if(sWeapon == "none")
			{
				attacker iprintlnbold(&"TURRET_ABUSER_PMSG");
				iprintlnFIXED(&"TURRET_ABUSER_OBITMSG_0", attacker);
				iprintln(&"TURRET_ABUSER_OBITMSG_2");
			}
			else
			{
				attacker iprintlnbold(&"TURRET_ABUSERWEP_PMSG", wepname);
				iprintlnFIXED(&"TURRET_ABUSER_OBITMSG_0", attacker);
				iprintln(&"TURRET_ABUSER_OBITMSG_1", wepname);
			}

			wait 2;
			playfx(level.ex_effect["blowthefag"], attacker.origin);
			attacker playsound("mortar_explosion1");
			wait 0.05;
			attacker.pers["turretkill"] = 0;
			attacker.ex_forcedsuicide = true;
			attacker suicide();
		}
	}

	if(level.ex_fbannounce && !isDefined(self.switching_teams))
	{
		players = getentarray("player", "classname");
		if(level.ex_firstblood && isplayer(attacker))
		{
			for(i=0;i < players.size;i++)
			{
				if(players[i] != self)
				{
					iprintlnboldFIXED(&"FIRSTBLOOD_REPORT_ALL", attacker, players[i]);
					if(attacker.pers["team"] != self.pers["team"] || !level.ex_teamplay) iprintlnboldFIXED(&"FIRSTBLOOD_VICTIM", self, players[i]);
						else if(attacker.pers["team"] == self.pers["team"] && level.ex_teamplay && attacker != self) iprintlnboldFIXED(&"FIRSTBLOOD_VICTIM_TEAM_MATE", self, players[i]);
							else if(attacker == self) players[i] iprintlnbold(&"FIRSTBLOOD_VICTIM_SELF");

					players[i] playlocalsound("firstblood");
				}
			}

			self iprintlnbold(&"FIRSTBLOOD_REPORT_SELF");
			self playlocalsound("whyami");
		}

		level.ex_fbannounce = false;
	}

	// Check for helmetpopping
	if(!self.ex_helmetpopped)
	{
		dopop = false;

		switch(sHitLoc)
		{
			case "head":
			case "helmet":
			if(randomInt(100) < level.ex_pophelmet) dopop = true;
		}

		// if attacker used a shotgun and was within 6ft of the player killed - pop!
		if(isPlayer(attacker) && sWeapon == "shotgun_mp" && distance(self.origin, attacker.origin) < 80) dopop = true;

		if(dopop) self popHelmet(vDir, iDamage);
	}

	// attacker taunt
	if(level.ex_taunts >= 2 && isPlayer(attacker))
	{
		if(level.ex_teamplay && attacker.pers["team"] != self.pers["team"]) attacker thread taunts(randomInt(10));
			else attacker thread taunts(4); // DM taunts set to Got one! and Got him!
	}

	// team kill check
	if(level.ex_sinbin && level.ex_teamplay && isPlayer(attacker) && attacker != self)
	{
		if(attacker.pers["team"] == self.pers["team"])
		{
			attacker.pers["teamkill"]++;
			if(attacker.pers["teamkill"] > level.ex_sinbinmaxtk)
			{
				attacker thread extreme\_ex_sinbin::main();
				attacker.pers["conseckill"] = 0;
			}
		}
	}
}

exEndMap()
{
	level.ex_gameover = true;
	level notify("ex_gameover");
	wait 0.05;

	// end-of-game music (Tnic)
	if(level.ex_endmusic || level.ex_mvmusic || level.ex_statsmusic)
		level thread extreme\_ex_utils::playSoundOnPlayers("spec_music_null");

	// announce result
	if(isDefined(level.ex_resultsound)) level thread extreme\_ex_utils::playSoundOnPlayers(level.ex_resultsound);

	// set players to spectate mode
	players = getentarray("player", "classname");
	for(mx=0;mx<players.size;mx++)
	{
		if(isPlayer(players[mx]))
		{
			players[mx] thread extreme\_ex_spawn::SpawnSpectator();
			if(level.ex_ranksystem) players[mx] thread setPlayerVariables();
			players[mx] thread extreme\_ex_hud::cleanplayerend();
		}
	}

	// clear hud elements
	level thread extreme\_ex_hud::cleanallhud();

	// play end music
	if(level.ex_endmusic) level thread extremeMusic();

	// launch statsboard
	if(level.ex_stbd) 
	{
		extreme\_ex_statsboard::main();
		wait 2;
	}

	// if playerbased map rotation is enabled and map voting is disabled change the rotation
	// now that the player size may have changed from the start of the game
	if(level.ex_pbrotate && !level.ex_mapvote) extreme\_ex_maprotation::pbrotation();

	// launch mapvote
	if(level.ex_mapvote) extreme\_ex_mapvote::main();
	
	// stop the music and load next map	
	level notify("endmusic");

	// stop any ambient effects
	level notify("ex_stop_ambient_fx");
}

extremeMusic()
{
	time = level.ex_endmusic_time;
	mn = randomInt(10) + 1;
	musicplay("gom_music_" + mn);

	// wait here till stats and mapvote are done
	level waittill("endmusic");
	
	musicstop(time);
	wait time;
	
	// trigger next map
	level notify("end_music_over");
}

//******************************************************************************
// eXtreme+ player additional routines
//******************************************************************************
roundDisplay()
{
	level endon("ex_gameover");
	self endon("disconnect");

	if(!game["roundnumber"] || game["roundnumber"] == self.pers["roundshown"] || isDefined(self.ex_roundnumber)) return;

	self.pers["roundshown"] = game["roundnumber"];

	// display the round number once each round for roundbased games
	self.ex_roundnumber = newClientHudElem(self);
	self.ex_roundnumber.x = 320;
	self.ex_roundnumber.y = 40;
	self.ex_roundnumber.alignX = "center";
	self.ex_roundnumber.alignY = "middle";
	self.ex_roundnumber.alpha = 0;
	self.ex_roundnumber.fontscale = 2.4;
	
	if(level.roundlimit == game["roundnumber"]) self.ex_roundnumber setText(&"WELCOME_LASTROUND");
	else
	{
		self.ex_roundnumber.label = &"WELCOME_ROUNDNUMBER";
		self.ex_roundnumber setValue(game["roundnumber"]);
	}

	self.ex_roundnumber fadeOverTime(2);
	self.ex_roundnumber.alpha = 1;

	wait 7;

	if(isPlayer(self))
	{
		if(isDefined(self.ex_roundnumber))
		{
			self.ex_roundnumber fadeOverTime(2);
			self.ex_roundnumber.alpha = 0;
		}
		
		wait 2;
			
		if(isDefined(self.ex_roundnumber)) self.ex_roundnumber destroy();
	}
}

setPlayerVariables()
{
	self thread resetFlagVars();

	count = 1;

	// apply these stats if not defined already
	for(;;)
	{
		stat = getPlayerVariable(count);

		if(stat == "GTSRESET" || stat == "") break;
			else if(isPlayer(self) && !isDefined(self.pers[stat])) self.pers[stat] = 0;

		count++;
	}

	// misc variables
	if(!isDefined(game[self.name])) game[self.name] = [];

	// set streak variables to 0
	self.pers["noobstreak"] = 0;
	self.pers["weaponstreak"] = 0;
	self.pers["weaponname"] = "";

	// clear the grenades
	if(isDefined(self.pers["fragtype"])) self setWeaponClipAmmo(self.pers["fragtype"], 0);
	if(isDefined(self.pers["smoketype"])) self setWeaponClipAmmo(self.pers["smoketype"], 0);
	if(isDefined(self.pers["enemy_fragtype"])) self setWeaponClipAmmo(self.pers["enemy_fragtype"], 0);
	if(isDefined(self.pers["enemy_smoketype"])) self setWeaponClipAmmo(self.pers["enemy_smoketype"], 0);	
}

resetPlayerVariables()
{
	self thread resetFlagVars();

	count = 0;

	// reset the stats
	for(;;)
	{
		count++;
		stat = getPlayerVariable(count);

		if(stat == "GTSRESET") continue;
			else if(stat == "") break;

		if(isPlayer(self)) self.pers[stat] = 0;
	}

	// misc variables
	self.pers["weaponname"] = "";

	if(isDefined(game[self.name])) game[self.name] = [];

	self.score = 0;
	self.deaths = 0;

	// reset the player rank
	if(level.ex_ranksystem)
	{
		self.pers["special"] = 0;
		self.pers["rank"] = self.pers["preset_rank"];
		self.pers["newrank"] = self.pers["rank"];
	}

	// reset all weapons and firstaid
	self thread extreme\_ex_weapons::replenishWeapons(true);
	self thread extreme\_ex_weapons::replenishGrenades(true);
	self thread extreme\_ex_weapons::replenishFirstaid(true);	
}

resetFlagVars()
{
	// stop binocular weapons
	self notify("binocular_exit");

	// stop artillery
	self notify("artillery_over");
	self notify("end_artillery");

	// stop mortars
	self notify("mortar_over");
	self notify("end_mortar");

	// stop airstrikes
	self notify("airstrike_over");
	self notify("end_airstrike");

	// eXtreme+
	self.ex_iscamper = false;
	self.ex_isonfire = undefined;
	self.ex_puked = undefined;
	if(!isDefined(self.ex_isunknown)) self.ex_isunknown = false;
	if(!isDefined(self.ex_isdupname)) self.ex_isdupname = false;
	self.ex_ispunished = false;
	self.ex_hasnoweapon = false;
	self.ex_sinbin = false;
	self.ex_oldweapon = undefined;
	self.ex_invulnerable = false;
	self.ex_ishealing = undefined;
	self.ex_helmetpopped = false;
	self.ex_artillery_strike = false;
	self.ex_air_strike = false;
	self.ex_mortar_strike = false;
	self.ex_sprinttime = 0;
	self.ex_playsprint = false;
	self.ex_sprintreco = false;
	self.ex_sprinting = false;
	self.ex_binocuse = false;
	self.ex_warningwire = undefined;
	self.ex_plantwire = false;
	self.ex_defusewire = false;
	self.ex_stopwepmon = false;
	self.ex_bleeding = false;
	self.ex_bsoundinit = false;
	self.ex_bphockinit = false;
	self.ex_pace = false;
	self.ex_checkingwmd = undefined;
	self.ex_spwn_punish = undefined;
	self.ex_firstaidkits = 0;
	self.ex_inmenu = false;
	self.ex_isparachuting = undefined;
	self.handling_mine = false;

	// stock
	self.usedweapons = false;
	self.spamdelay = undefined;
}

taunts(tauntno)
{
	self endon("disconnect");

	chance = randomInt(20);

	if(chance == 10)
	{
		// delay for death sound to finish
		wait 1.5;

		// if the attacker is still here, play the sound now
		switch(randomInt(2))
		{
			case 1: { if(isPlayer(self)) self thread maps\mp\gametypes\_quickmessages::quicktaunts(tauntno, true); break; }
			default: { if(isPlayer(self)) self thread maps\mp\gametypes\_quickmessages::quicktauntsb(tauntno, true); break; }
		}
	}
}


popHelmet( damageDir, damage)
{
	self.ex_helmetpopped = true;

	if(!isDefined(self.hatModel) || isDefined(self.ex_newmodel)) return;

	self detach(self.hatModel , "");

	self.ex_stance = [[level.ex_getStance]](false);

	if(isPlayer(self))
	{
		switch(self.ex_stance)
		{
			case 2:	helmetoffset = (0,0,15);	break;
			case 1:	helmetoffset = (0,0,44);	break;
			default:	helmetoffset = (0,0,64);	break;
		}
	}
	else helmetoffset = (0,0,15);

	switch(self.hatModel)
	{
		case "xmodel/helmet_russian_trench_a_hat":
		case "xmodel/helmet_russian_trench_b_hat":
		case "xmodel/helmet_russian_trench_c_hat":
		case "xmodel/helmet_russian_padded_a":
			bounce = 0.2;
			impactsound = undefined;
			break;
		default:
			bounce = 0.7;
			impactsound = "helmet_bounce_";
			break;
	}		

	rotation = (randomFloat(540), randomFloat(540), randomFloat(540));
	offset = (0,0,3);
	radius = 6;
	velocity = extreme\_ex_utils::vectorScale(damageDir, (damage/20 + randomFloat(5)) ) + (0,0,(damage/20 + randomFloat(5)));

	helmet = spawn("script_model", self.origin + helmetoffset );
	helmet setmodel( self.hatModel );
	helmet.angles = self.angles;
	helmet.targetname = "popped helmet";
	helmet thread extreme\_ex_utils::bounceObject(rotation, velocity, offset, (0,0,0), radius, bounce, impactsound, undefined, "helmet");
}

handleDeadBody(team, owner)
{
	//Give the body a model
	self setModel(owner.model);

	// sink body in to the ground
	if(level.ex_deadbodyfx) self thread bodySink();
}

bodySink()
{
	wait 15;
	
	for(i=0;i<(100);i++)
	{
		if(!isDefined(self)) return;
		self.origin = self.origin - (0,0,0.2);
		wait .05;
	}
	if(isdefined(self)) self delete();
}

bloodonscreen()
{
	level endon("ex_gameover");
	self endon("ex_dead");

	if(!level.ex_bloodonscreen) return;

	if(!isDefined(self.ex_bloodonscreen))
	{
		self.ex_bloodonscreen = newClientHudElem(self);
		self.ex_bloodonscreen1 = newClientHudElem(self);
		self.ex_bloodonscreen2 = newClientHudElem(self);
		self.ex_bloodonscreen3 = newClientHudElem(self);

		self.ex_bloodonscreen.alignX = "left";
		self.ex_bloodonscreen.alignY = "top";
	
		self.ex_bloodonscreen1.alignX = "left";
		self.ex_bloodonscreen1.alignY = "top";

		self.ex_bloodonscreen2.alignX = "left";
		self.ex_bloodonscreen2.alignY = "top";
		
		self.ex_bloodonscreen3.alignX = "left";
		self.ex_bloodonscreen3.alignY = "top";
		
		bs1 = (randomint(496));
		bs2 = (randomint(336));
		bs1a = (randomint(496));
		bs2a = (randomint(336));
		bs1b = (randomint(496));
		bs2b = (randomint(336));
		bs1c = (randomint(496));
		bs2c = (randomint(336));

		self.ex_bloodonscreen.x = bs1;
		self.ex_bloodonscreen.y = bs2;

		self.ex_bloodonscreen1.x = bs1a;
		self.ex_bloodonscreen1.y = bs2a;

		self.ex_bloodonscreen2.x = bs1b;
		self.ex_bloodonscreen2.y = bs2b;

		self.ex_bloodonscreen3.x = bs1c;
		self.ex_bloodonscreen3.y = bs2c;

		bs3 = randomint(48);
		bs3a = randomint(48);
		bs3b = randomint(48);
		bs3c = randomint(48);
		self.ex_bloodonscreen.color = (1,1,1);
		self.ex_bloodonscreen1.color = (1,1,1);
		self.ex_bloodonscreen2.color = (1,1,1);
		self.ex_bloodonscreen3.color = (1,1,1);
		self.ex_bloodonscreen.alpha = 1;
		self.ex_bloodonscreen1.alpha = 1;
		self.ex_bloodonscreen2.alpha = 1;
		self.ex_bloodonscreen3.alpha = 1;

		self.ex_bloodonscreen SetShader("gfx/impact/flesh_hit2",96 + bs3 , 96 + bs3);
		self.ex_bloodonscreen1 SetShader("gfx/impact/flesh_hitgib",96 + bs3a , 96 + bs3a);
		self.ex_bloodonscreen2 SetShader("gfx/impact/flesh_hit2",96 + bs3b , 96 + bs3b);
		self.ex_bloodonscreen3 SetShader("gfx/impact/flesh_hitgib",96 + bs3c , 96 + bs3c);

		wait 4;

		if(!isDefined(self.ex_bloodonscreen)) return;

		self.ex_bloodonscreen fadeOverTime (2); 
		self.ex_bloodonscreen.alpha = 0;
		self.ex_bloodonscreen1 fadeOverTime (2);
		self.ex_bloodonscreen1.alpha = 0;
		self.ex_bloodonscreen2 fadeOverTime (2);
		self.ex_bloodonscreen2.alpha = 0;
		self.ex_bloodonscreen3 fadeOverTime (2);
		self.ex_bloodonscreen3.alpha = 0;

		wait 2;

		if(!isDefined(self.ex_bloodonscreen)) return;

		if(isDefined(self.ex_bloodonscreen)) self.ex_bloodonscreen destroy();
		if(isDefined(self.ex_bloodonscreen1)) self.ex_bloodonscreen1 destroy();
		if(isDefined(self.ex_bloodonscreen2)) self.ex_bloodonscreen2 destroy();
		if(isDefined(self.ex_bloodonscreen3)) self.ex_bloodonscreen3 destroy();
	}
}

distortPlayerView()
{
	level endon("ex_gameover");
	level endon("intermission");
	self endon("disconnect");
	self endon("ex_spawned");
	self endon("ex_dead");

	horiz[1] = .26;
	horiz[2] = .26;
	horiz[3] = .25;
	horiz[4] = .25;
	horiz[5] = .25;
	horiz[6] = .25;
	horiz[7] = .25;
	horiz[8] = .25;
	horiz[9] = .25;
	horiz[10] = .25;
	horiz[11] = .25;
	horiz[12] = .15;
	horiz[13] = .13;
	vert[1] = 0.0;
	vert[2] = 0.025;
	vert[3] = 0.036;
	vert[4] = 0.037;
	vert[5] = 0.053;
	vert[6] = 0.072;
	vert[7] = 0.080;
	vert[8] = 0.100;
	vert[9] = 0.11;
	vert[10] = 0.15;
	vert[11] = 0.244;
	vert[12] = 0.238;
	vert[13] = 0.085;
	
	wait 2;

	i = 1;
	idir = 0;
	pshift = 0;
	yshift = 0;

	if(isPlayer(self))
	{
		for(;;)
		{
			VMag = self.VaxisMag;
			YMag = self.YaxisMag;

			if(i >= 1 && i <= 13)
			{
				pShift = horiz[i]*VMag;
				yShift = (0 - vert[i])*YMag;
			}
			else if(i >= 14 && i <= 26)
			{
				j = 14 - (i -13);
				pShift = (0 - horiz[j])*VMag;
				yShift = (0 - vert[j])*YMag;
			}
			else if(i >= 27 && i <= 39)
			{
				pShift = (0-horiz[i-26])*VMag;
				yShift = (vert[i-26])*YMag;
			}
			else if(i >= 40 && i <= 52)
			{
				j = 14 - (i -39);
				pShift = (horiz[j])*VMag;
				yShift = (vert[j])*YMag;
			}

			angles = self getplayerangles();
			self setPlayerAngles(angles + (pShift, yShift, 0));

			if(randomInt(50) == 0)
			{
				if(idir == 0) idir = 1;
				else idir = 0;
				i = i + 26;
			}

			if(idir == 0) i++;
			if(idir == 1) i--;
			if( i > 52) i = i - 52;
			if( i < 0) i = 52 - i; 
			wait 0.05;
		}
	}
}

showPlayerHit()
{
	return; // shader is shite, will find beter one!

	self notify("ex_playerhit");
	self endon("ex_playerhit");
	self endon("ex_dead");

	if(!isDefined(self.ex_playerhit))
	{
		self.ex_playerhit = newClientHudElem(self);
		self.ex_playerhit.alignX = "center";
		self.ex_playerhit.alignY = "middle";
		self.ex_playerhit.x = 320;
		self.ex_playerhit.y = 240;
		self.ex_playerhit.alpha = 0.5;
		self.ex_playerhit setShader("gfx/reticle/mg42_cross.tga", 32, 32);
	}

	if(isPlayer(self) && isDefined(self.ex_playerhit)) self.ex_playerhit scaleOverTime(0.10, 75, 75);
	wait 0.15;
	if(isPlayer(self) && isDefined(self.ex_playerhit)) self.ex_playerhit scaleOverTime(0.05, 48, 48);
	wait 0.05;
	if(isPlayer(self) && isDefined(self.ex_playerhit)) self.ex_playerhit destroy();
}

weaponfall(delay)
{
	self endon("disconnect");
	self endon("ex_spawned");
	self endon("ex_dead");

	// good strong health boy?, they can hold there weapon!
	if(self.health > 80) return;
	else if(self.health > 50 && self.health < 80)
	{
		if(randomInt(100) < 50)
		{
			if(isPlayer(self)) self disableWeapon();
			wait delay;
			if(isPlayer(self) && self.sessionstate == "playing") self enableWeapon();
		}
		else self thread extreme\_ex_weapons::dropcurrentweapon();
	}
	else self thread extreme\_ex_weapons::dropcurrentweapon();
}

spankme(time)
{
	level endon("ex_gameover");
	self notify("ex_spankme");
	self endon("ex_spankme");
	self endon("ex_spawned");
	self endon("ex_dead");

	for(i=0;i<(time*5);i++)
	{
		if(isPlayer(self))
		{
			self setClientCvar("cl_stance", "2");
			self thread extreme\_ex_weapons::dropcurrentweapon();
		}

		wait 0.2;
	}
}

handleWelcome()
{
	level endon("ex_gameover");
	self endon("disconnect");
	self endon("ex_dead");
	self endon("ex_freefall");

	if(isPlayer(self))
	{
		// Resetting the tag ex_ispunished is done in resetFlagVars()

		// Did the Name Checker already tag the player for using an unacceptable name?
		if(isDefined(self.ex_isunknown) && self.ex_isunknown)
		{
			self thread handleUnknown(false);
		}
		else
		{
			// Did the Name Checker already tag the player for using a duplicate name?
			if(isDefined(self.ex_isdupname) && self.ex_isdupname)
			{
				self thread handleDupName();
			}
			else
			{
				// If Name Checker is disabled and Unknown Soldier handling is enabled,
				// check for unacceptable names ourselves
				if (level.ex_uscheck && isUnknown(self))
				{
					self thread handleUnknown(false);
				}
				else self thread extreme\_ex_messages::welcomemsg();
			}
		}
	}
}

handleDupName()
{
	level endon("ex_gameover");
	self endon("disconnect");
	self endon("ex_dead");
	self endon("ex_freefall");

	// Tag the player to prevent the Name Checker to kick in more than once
	self.ex_isdupname = true;

	iprintlnboldFIXED(&"NAMECHECK_DNCHECK_DUPNAME1", self, self);
	self setClientCvar("name", "Unknown Soldier");
	self iprintlnbold(&"NAMECHECK_DNCHECK_NEWUNKNOWN");

	if(level.ex_ncskipwarning)
	{
		if(level.ex_usclanguest) self iprintlnbold(&"NAMECHECK_DNCHECK_NEXTCLANGUEST");
			else self iprintlnbold(&"NAMECHECK_DNCHECK_NEXTGUEST");
	}
	else self iprintlnbold(&"NAMECHECK_DNCHECK_NEXTUNKNOWN");

	// Wait several seconds before starting the Unknown Soldier handling code
	wait 10;
	self thread handleUnknown(level.ex_ncskipwarning);

	// Remove the tag; the player is officially an Unknown Soldier now
	self.ex_isdupname = false;
}

handleUnknown(skipwarning)
{
	level endon("ex_gameover");
	self endon("disconnect");
	self endon("ex_dead");
	self endon("ex_freefall");

	// Tag the player to prevent the Name Checker to kick in more than once
	self.ex_isunknown = true;

	usname = [];

	if(!skipwarning)
	{
		if(isPlayer(self))
		{
			// Warn them first
			if(level.ex_usclanguest)
			{
				iprintlnboldFIXED(&"UNKNOWNSOLDIER_MSG_UNACCEPTABLE", self, self);
				self iprintlnbold(&"UNKNOWNSOLDIER_MSG_CHANGEIT");
				self iprintlnbold(&"UNKNOWNSOLDIER_MSG_CLANGUEST", level.ex_uswarndelay1);
			}
			else
			{
				iprintlnboldFIXED(&"UNKNOWNSOLDIER_MSG_UNACCEPTABLE", self, self);
				self iprintlnbold(&"UNKNOWNSOLDIER_MSG_CHANGEIT");
				self iprintlnbold(&"UNKNOWNSOLDIER_MSG_GUEST", level.ex_uswarndelay1);
			}
		}
		// Now give them some time to change their name
		waitWhileUnknown(level.ex_uswarndelay1);
	}

	if(isPlayer(self) && isUnknown(self))
	{
		// Get a free guest number (1 to sv_maxclients)
		level.ex_usguestno = getFreeGuestSlot();

		if(level.ex_usclanguest)
		{
			usname[0] = level.ex_usclanguestname; // Clan Guest
			usname[1] = usname[0] + level.ex_usguestno;
			self setClientCvar("name", usname[1]);
			self iprintlnbold(&"UNKNOWNSOLDIER_NEWNAME_BYSERVER");
			self iprintlnbold(&"UNKNOWNSOLDIER_NEWNAME_CLANGUEST", usname[1]);

			// Clan guests are now off the hook; show welcome messages and return
			self.ex_isunknown = false;
			wait 3;
			if(isPlayer(self)) self thread extreme\_ex_messages::welcomemsg();
			return;
		}
		else
		{
			// Only assign guest name if not already using an assigned guest name
			if(!isAssignedName(self))
			{
				usname[0] = level.ex_usguestname; // Non-clan Guest
				usname[1] = usname[0] + level.ex_usguestno;
				self setClientCvar("name", usname[1]);
				self iprintlnbold(&"UNKNOWNSOLDIER_NEWNAME_BYSERVER");
				self iprintlnbold(&"UNKNOWNSOLDIER_NEWNAME_GUEST", usname[1]);
				self iprintlnbold(&"UNKNOWNSOLDIER_NEWNAME_CHANGEIT", level.ex_uswarndelay2);

				// After name assignment, non-clan guests get a second chance to change their name
				waitWhileUnknown(level.ex_uswarndelay2);
			}
		}
	}

	if(isPlayer(self) && isUnknown(self))
	{
		// My god, don't they understand? ok, time to f*** around with them!
		count = 0;
		while(isPlayer(self) && isUnknown(self) && count < level.ex_uspunishcount)
		{
			if(!self.ex_sinbin)
			{
				while(self.ex_sinbin) wait 1;
				iprintlnboldFIXED(&"UNKNOWNSOLDIER_MSG_TEMPORARY", self, self);
				self iprintlnbold(&"UNKNOWNSOLDIER_MSG_CHANGEIT");
				self iprintlnbold(&"UNKNOWNSOLDIER_STILL_PUNISH");
				self thread extreme\_ex_utils::punishment("drop", "freeze");
				waitWhileUnknown(10);
				if(isPlayer(self)) self thread extreme\_ex_utils::punishment("enable", "release");
				waitWhileUnknown(20 + randomInt(20));
				count++;
			}
			else break;
		}

		// Now, if still using assigned name, allow them to play without punishment until they die
		if(isPlayer(self) && isAssignedName(self))
		{
			// Set punished-tag so Name Checker doesn't kick in again
			self.ex_ispunished = true;
			self iprintlnbold(&"UNKNOWNSOLDIER_STILL_RELIEF1");
			self iprintlnbold(&"UNKNOWNSOLDIER_STILL_RELIEF2");
			self iprintlnbold(&"UNKNOWNSOLDIER_MSG_CHANGEIT");
		}
	}

	// Allow the Name Checker to iterate once to catch duplicate names.
	// Keep this wait statement outside the following if-block to catch players
	// that would otherwise fall through by quickly changing their name from US
	// to a valid name and back to US again (highly unlikely, but possible with key bindings)
	wait 5;

	if(isPlayer(self) && !self.ex_ispunished && !isUnknown(self))
	{
		// Has the Name Checker tagged him because of using a duplicate name?
		if(isPlayer(self) && !self.ex_isdupname)
		{
			// No, so thank them, and show the welcome messages
			iprintlnboldFIXED(&"UNKNOWNSOLDIER_MSG_THANKS", self, self);
			wait 3;
			if(isPlayer(self)) self thread extreme\_ex_messages::welcomemsg();
		}
		else self thread handleDupName();
	}

	// Remove the tag; the player is either renamed, punished or dupname-tagged
	self.ex_isunknown = false;
}

waitWhileUnknown(seconds)
{
	// Wait for x seconds as long as player has unacceptable name
	for(i = 0; i < seconds; i++)
	{
		if(isPlayer(self) && !isUnknown(self)) return;
			else wait 1;
	}
}

isUnknownSoldier(player)
{
	// Check if player is Unknown Soldier
	// Color codes are removed. Name is lowercased, so it will reject any case combination

	self endon("disconnect");
	self endon("ex_dead");

	playernorm = "";
	if(isPlayer(player)) playernorm = extreme\_ex_utils::monotone(player.name);
	playernorm = extreme\_ex_utils::lowercase(playernorm);

	if(playernorm == "" || playernorm == "unknown soldier" || playernorm == "unknownsoldier") return true;
	return false;
}

isAssignedName(player)
{
	// Check if player has an assigned guest name
	// Do NOT check for assigned clan guest names!

	self endon("disconnect");
	self endon("ex_dead");

	chkname = [];
	maxguestno = getCvarInt("sv_maxclients");
	chkname[0] = level.ex_usguestname;

	for(i = 1; i <= maxguestno; i++)
	{
		chkname[1] = chkname[0] + i;
		if(player.name == chkname[1]) return true;
	}
	return false;
}

isUnknown(player)
{
	// Check if player has unacceptable name

	self endon("disconnect");
	self endon("ex_dead");
	
	if(isUnknownSoldier(player)) return true;
	if(isAssignedName(player)) return true;
	return false;
}

getFreeGuestSlot()
{
	// Get a free guest number.

	self endon("disconnect");
	self endon("ex_dead");

	chkname = [];
	maxguestno = getCvarInt("sv_maxclients");
	players = getentarray("player", "classname");
	maxplayers = players.size;

	if(level.ex_usclanguest) chkname[0] = level.ex_usclanguestname;
		else chkname[0] = level.ex_usguestname;

	i = 1;
	while(i <= maxguestno)
	{
		chkname[1] = chkname[0] + i;
		found = false;
		for(j = 0; j < maxplayers; j++)
		{
			if(players[j].name == chkname[1])
			{
				found = true;
				break;
			}
		}
		if(found) i++;
			else break;
	}
	return i;
}

getPlayerVariable(stat)
{
	switch(stat)
	{
		// kills
		case 1:  return "kill";
		case 2:  return "grenadekill";
		case 3:  return "tripwirekill";
		case 4:  return "headshotkill";
		case 5:  return "bashkill";
		case 6:  return "sniperkill";
		case 7:  return "knifekill";
		case 8:  return "mortarkill";
		case 9:  return "artillerykill";
		case 10: return "airstrikekill";
		case 11: return "napalmkill";
		case 12: return "panzerkill";
		case 13: return "spawnkill";
		case 14: return "spamkill";
		case 15: return "teamkill";
		case 16: return "flamethrowerkill";
		case 17: return "landminekill";
		case 18: return "firenadekill";
		case 19: return "gasnadekill";
		case 20: return "satchelchargekill";

		// deaths
		case 21: return "death";
		case 22: return "grenadedeath";
		case 23: return "tripwiredeath";
		case 24: return "headshotdeath";
		case 25: return "bashdeath";
		case 26: return "sniperdeath";
		case 27: return "knifedeath";
		case 28: return "mortardeath";
		case 29: return "artillerydeath";
		case 30: return "airstrikedeath";
		case 31: return "napalmdeath";
		case 32: return "panzerdeath";
		case 33: return "spawndeath";
		case 34: return "planedeath";
		case 35: return "flamethrowerdeath";
		case 36: return "fallingdeath";
		case 37: return "minefielddeath";
		case 38: return "suicide";
		case 39: return "landminedeath";
		case 40: return "firenadedeath";
		case 41: return "gasnadedeath";
		case 42: return "satchelchargedeath";

		// other
		case 43: return "turretkill";
		case 44: return "noobstreak";
		case 45: return "conseckill";
		case 46: return "weaponstreak";
		case 47: return "roundshown";
		case 48: return "longdist";
		case 49: return "longhead";
		case 50: return "longspree";
		case 51: return "flagpoints";
		case 52: return "bonus";

		// reset only when using gametype start delay
		case 53: return "GTSRESET";
		case 54: return "score";
		case 55: return "deaths";
		default: return "";
	}
}
