//************************************************************************************************************************************************************************
// WW2X launch main threads
//************************************************************************************************************************************************************************
main()
{
	// get the map dimensions and playing field dimensions
	ww2x\_wwx_utils::GetMapDim();
	ww2x\_wwx_utils::GetFieldDim();

	// set up map rotation
	if(getCvar("wwx_maprotdone") == "")
	{
		if(level.wwx_pbrotate)
			ww2x\_wwx_maprotation::pbrotation();
		else
		{
			setCvar("sv_maprotationcurrent", "");
			setCvar("sv_maprotationcurrent", getCvar("sv_maprotation"));
		}

		setCvar("wwx_maprotdone","1");
	}

	// clear any camping players
	if(level.wwx_campwarntime || level.wwx_campsniper_warntime) thread ww2x\_wwx_camper::removeCampers();

	// fix the map rotation
	if(level.wwx_fixmaprotation) level ww2x\_wwx_maprotation::fixMapRotation();

	// set a random map rotation
	if(level.wwx_randommaprotation) level thread ww2x\_wwx_maprotation::randomMapRotation();

	// text logos for mod and server
	thread ww2x\_wwx_modinfo::main();
	
	// time announcer
	if(level.wwx_timeannouncer) level thread ww2x\_wwx_timeannouncer::main();
	
	// rotate if empty
	if(level.wwx_rotateifempty) level thread ww2x\_wwx_rotate::main();
	
	// rotate motd
	if(level.wwx_motdrotate) level thread ww2x\_wwx_messages::motdrotate();

	// server messages
	if(level.wwx_svrmsg) level thread ww2x\_wwx_messages::serverMessages();

	// mortars
	if(level.wwx_mortars >= 1) level thread ww2x\_wwx_mortar_ambient::init();

	// disable minefields	
	if(level.wwx_disableminefields) level thread ww2x\_wwx_utils::disableMinefields();
	
	// planes
	if(level.wwx_planes >= 1) level thread ww2x\_wwx_airplanes::init();
	
	// launch name checker
	if(level.wwx_namechecker) level thread ww2x\_wwx_namecheck::init();

	// tracers
	if(level.wwx_tracers) for(tracers=0;tracers<level.wwx_tracers;tracers++) level thread ww2x\_wwx_skyeffects::tracers();

	// flak fx	
	if(level.wwx_flakfx && !level.wwx_planes_flak) for(flak=0;flak<level.wwx_flakfx;flak++) level thread ww2x\_wwx_skyeffects::flakfx();

	// livestats
	//if(level.wwx_livestats && level.wwx_teamplay) level thread ww2x\_wwx_livestats::init();
      if(level.wwx_livestats) level thread ww2x\_wwx_livestats::init();

	// WW2X command monitor
	if(level.wwx_cmdmonitor) level thread ww2x\_wwx_cmdmonitor::init();

	// fog
	if(level.wwx_efogstr != "none") level thread ww2x\_wwx_fog::init();

	// weapon limiter
	level.wwx_wepo_limiter = getCvarInt("wwx_weaponlimit", 1);
	if(level.wwx_wepo_limiter) level thread ww2x\_wwx_weaponlimit::limiter_init();

	// ammo crates
	if(level.wwx_amc_perteam) level thread ww2x\_wwx_ammocrates::spawnCrates();

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

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

	// start bot system
	level notify("gobots");

	// launch kick monitor
	if(level.wwx_inactive_plyr || level.wwx_inactive_spec) level thread ww2x\_wwx_spkick::main();
}	

//************************************************************************************************************************************************************************
// WW2X launch player threads
//************************************************************************************************************************************************************************
playerThreads()
{
	level endon("wwx_gameover");
	self endon("disconnect");
	self endon("wwx_dead");


       // range finder
       if(level.wwx_rangefinder) self thread ww2x\_wwx_utils::rangef();

	// monitor for weapon usage
	thread maps\mp\gametypes\_weapons::watchWeaponUsage();

	// show logo
	if(level.wwx_logopic) self thread ww2x\_wwx_logo::logogfx();

	// spawn protection
	if(level.wwx_spwn_time) self thread ww2x\_wwx_spawnpro::spawnprotection();

      // parachutes
       if(level.wwx_parachutes >= 1)
		self thread ww2x\_wwx_parachute::PlayerParachute();

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

	// apply laser dot
	if(level.wwx_laserdot && !isDefined(level.wwx_gtsmsg))
	{
		self createLaserdot();

		// start the laserdot monitor
		//self thread lasedotMonitor(); && !isDefined(level.wwx_gtsmsg))
	}

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

	// movie mode
	if(level.wwx_moviemode) self thread ww2x\_wwx_hud::movieMode();

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

      // check names and show welcome messages 
      self thread handleWelcome();

	// welcome sound
	if(level.wwx_goodluck)
	{
		if(isDefined(self.wwx_team_changed))
		{
			self.wwx_glplay = undefined;
			self.wwx_team_changed = undefined;
		}

		if(!isDefined(self.wwx_glplay) && level.wwx_teamplay) self thread ww2x\_wwx_welcome::announce();
	}

	// camper check
	if(level.wwx_campwarntime || level.wwx_campsniper_warntime) self thread ww2x\_wwx_camper::campercheck();

	// start player rainfx
	if(level.wwx_rainfx) self thread ww2x\_wwx_weather::player_launch();

	// start the rank hud system
	if(level.wwx_ranksystem && level.wwx_rankhud) self thread ww2x\_wwx_ranksystem::rankhud();

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

	// if sprint not active still load the sprint movememon, used by firstaid and bobfactor
	if(!level.wwx_sprint && (level.wwx_callformedic || level.wwx_bobfactor)) self thread ww2x\_wwx_sprintsystem::sprintmovemon();

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

	// firstaid system
	if(level.wwx_callformedic >= 1) self thread ww2x\_wwx_firstaid::firstaid();

	// start player bob factor
	if(level.wwx_bobfactor) self thread ww2x\_wwx_bobfactor::playerBobFactor();

	// 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 ww2x\_wwx_utils::punishment("enable", "freeze");
		return;
	}

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

	// tripwire monitor
	if(level.wwx_tweapon) self thread ww2x\_wwx_tripwires::tripwiremon();

	// start the sprint system
	if(level.wwx_sprint) self thread ww2x\_wwx_sprintsystem::sprintInit();

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

	// force clientside dvars
	if(level.wwx_forceclientdvars) self thread ww2x\_wwx_forcedvar::forceClientDvars();
        
       // Positioning shows you your exact position on a map (in devmap mode only)
       //self thread ww2x\_wwx_tools::ShowPlayerPosition();

      // Deployable turrets
	self thread ww2x\_wwx_turrets::RunOnSpawn();

}

//************************************************************************************************************************************************************************
// WW2X monitors
//************************************************************************************************************************************************************************
coldbreathMonitor()
{
	level endon("wwx_gameover");
	self endon("disconnect");
	self endon("wwx_dead");

	wait (2 + randomint(3));

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

firstaidMonitor()
{
	level endon("wwx_gameover");
	self endon("disconnect");
	self endon("wwx_dead");

	while(isPlayer(self) && self.sessionstate == "playing")
	{
		if(self.health < level.wwx_medicall && level.wwx_callformedic)
		{
			self thread ww2x\_wwx_firstaid::callformedic();
			wait 20;
		}
		
		wait 0.5;
	}
}

binocularMonitor()
{
	level endon("wwx_gameover");
	self endon("disconnect");
	self endon("wwx_dead");

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

		wait 0.1;
	}
}

grenadeMonitor()
{
	level endon("wwx_gameover");
	self endon("disconnect");
	self endon("wwx_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.wwx_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.wwx_plantwire)
			{
				self thread maps\mp\gametypes\_quickmessages::quickwarning("frag", 480, true, true);
				frags = frags_check;
			}
			else frags = frags_check;
	
			if(smokes_check < smokes && !self.wwx_plantwire)
			{
				self thread maps\mp\gametypes\_quickmessages::quickwarning("smoke", 480, true, true);
				smokes = smokes_check;
			}
			else smokes = smokes_check;
		}
		
		wait 0.05;
	}
}

stanceShootMonitor()
{
	level endon("wwx_gameover");
	self endon("disconnect");
	self endon("wwx_dead");

	lastprone = 2;
	lastjump = 3;

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

	for(;;)
	{
		wait 0.05;

		if(self.wwx_plantwire || self.wwx_defusewire || self.handling_mine || isDefined(self.wwx_isparachuting)) continue;

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

		switch(level.wwx_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 ww2x\_wwx_utils::weaponPause(0.4 + randomfloat(0.4));
	}
}

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

/*lasedotMonitor()
{
	level endon("wwx_gameover");
	self endon("disconnect");
	self endon("wwx_dead");

	while(isPlayer(self) && self.sessionstate == "playing")
	{
		if(isDefined(self.wwx_laserdot))
		{
			if(![[level.wwx_isADS]]()) self.wwx_laserdot.alpha = level.wwx_laserdot;
			else self.wwx_laserdot.alpha = 0;
		}

		wait 0.5;
	}
}
*/

//************************************************************************************************************************************************************************
// WW2X health system
//************************************************************************************************************************************************************************
healthBar()
{
	level endon("wwx_gameover");
	self endon("disconnect");
	self endon("wwx_dead");

	if(isDefined(self.wwx_healthbar)) return;

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

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

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

	self thread healthUpdate();
}

healthDrop()
{
	self endon("disconnect");
	
	if(isDefined(game["wwx_healthqueue"][game["wwx_healthqueuecurrent"]])) game["wwx_healthqueue"][game["wwx_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 ww2x\_wwx_utils::bounceObject(rotation, velocity, (0,0,0), (0,0,0), 5, 0.4, undefined, undefined, "health");
	item_health thread healthThink(health_nr);

	game["wwx_healthqueue"][game["wwx_healthqueuecurrent"]] = item_health;
	game["wwx_healthqueuecurrent"]++;

	if(game["wwx_healthqueuecurrent"] >= 16) game["wwx_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.wwx_callformedic && level.wwx_firstaidpickup && player.wwx_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.wwx_callformedic && level.wwx_firstaidpickup && player.wwx_firstaidkits < level.wwx_firstaidpickup)
				{
					player.wwx_firstaidkits++;
					player playLocalSound("health_pickup_medium");
					player iprintln(&"FIRSTAID_PICKEDUP");
					player.wwx_canheal = true;
					if(isDefined(player.wwx_firstaidval))
					{
						player.wwx_firstaidval setValue(player.wwx_firstaidkits);
						player.wwx_firstaidval.color = (1, 1, 1);
					}

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

healthUpdate()
{
	self endon("disconnect");
	self endon("wwx_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.wwx_healthbar))
			{					
				self.wwx_healthbar setShader("gfx/hud/hud@health_bar.tga", width, 8);
				self.wwx_healthbar.color = ( 1.0 - health, health, 0);
			}
			
			oldhealth = self.health;
		}
	}
}

//************************************************************************************************************************************************************************
// WW2X gametype additional routines
//************************************************************************************************************************************************************************
gtsDelay()
{
	if(isDefined(game["gtsdone"])) return;
	
	if(level.wwx_currentgt == "dom") return;

	if(level.wwx_currentgt == "ons") return;

      if(level.wwx_currentgt == "lms") return;

	if(level.wwx_currentgt == "sd") return;

	if(level.wwx_currentgt == "esd") return;

	if(level.wwx_currentgt == "htf") return;

	if(level.wwx_currentgt == "lib") return;

	level.wwx_gtsmsg = newHudElem();
	level.wwx_gtsmsg.x = 320;
	level.wwx_gtsmsg.y = 240;
	level.wwx_gtsmsg.alignX = "center";
	level.wwx_gtsmsg.alignY = "middle";
	level.wwx_gtsmsg.fontscale = 2;
	level.wwx_gtsmsg.color = (0, 1, 0);
			
	level.wwx_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.wwx_gtsmsg.label = &"GTSDELAY_MATCH_BEGINS";
	level.wwx_gtsmsg setTimer(level.wwx_gtsdelay);

	wait level.wwx_gtsdelay - 1;

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

	if(isDefined(level.wwx_gtsmsg)) level.wwx_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.wwx_roundbased) map_restart(true);
	else
	{
		iprintln(&"MP_MATCHSTARTING");

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

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

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

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

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

	if(game["roundsplayed"] == 0 || !game["matchstarted"]) 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.wwx_wepo_secondary) players[i].pers["first_time"] = true;

		players[i].pers["team"] = newTeam;
		players[i].pers["savedmodel"] = undefined;
		players[i] thread ww2x\_wwx_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"]);
}

wwxPreSpawn()
{
	level endon("wwx_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.wwx_roundbased && level.wwx_swapteams)
		self maps\mp\gametypes\_teams::testClient(self.pers["team"]);

	// remove camper objective
	if(level.wwx_campwarntime || level.wwx_campsniper_warntime) self thread ww2x\_wwx_camper::removeCamper();

	// start rank monitor
	if(level.wwx_ranksystem) self thread ww2x\_wwx_ranksystem::playerRankMonitor();

}

wwxPostSpawn()
{
	level endon("wwx_gameover");
	self endon("disconnect");
	self notify("wwx_spawned");
	// wait for threads to die
	wait 0.01;

	if(isDefined(self.wwx_redirected)) return;

	if(isPlayer(self))
	{
		// Attach spinemarker, used by GetStance()
		if(!isDefined(self.wwx_spinemarker))
		{
			self.wwx_spinemarker = spawn("script_origin",(0,0,0));
			self.wwx_spinemarker linkto (self, "J_Spine4",(0,0,0),(0,0,0));	
		}
	
		// Attach headmark, used by sprint sound
		if(!isDefined(self.wwx_headmarker))
		{
			self.wwx_headmarker = spawn("script_origin",(0,0,0));
			self.wwx_headmarker linkto (self, "J_Head",(0,0,0),(0,0,0));
		}

		// Attach eye marker, used by ADS
		if(!isDefined(self.wwx_eyemarker))
		{
			self.wwx_eyemarker = spawn("script_origin",(0,0,0));
			self.wwx_eyemarker linkto (self, "tag_eye",(0,0,0),(0,0,0));
		}
	
	/*
		// Attach elbow marker, used by ADS
		if(!isDefined(self.wwx_elbowmarker))
		{
			self.wwx_elbowmarker = spawn("script_origin",(0,0,0));
			self.wwx_elbowmarker linkto (self, "J_Elbow_bulge_ri",(0,0,0),(0,0,0));
		}
	
		// Attach eye marker, used by ADS
		if(!isDefined(self.wwx_eyemarker))
		{
			self.wwx_eyemarker = spawn("script_origin",(0,0,0));
			self.wwx_eyemarker linkto (self, "tag_eye",(0,0,0),(0,0,0));
		}
	
		// Attach thumb marker, used by ADS
		if(!isDefined(self.wwx_thumbmarker))
		{
			self.wwx_thumbmarker = spawn("script_origin",(0,0,0));
			self.wwx_thumbmarker linkto (self, "J_Thumb_ri_1",(0,0,0),(0,0,0));
		}
	*/
	
		self thread playerThreads();

	}
}

wwxPlayerDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime)
{
	self endon("disconnect");
	if (!isDefined(vPoint))
		vPoint = self.origin + (0,0,11);
	// disable or drop weapon after a fall, for a bit anyway
	if(sMeansOfDeath == "MOD_FALLING") self thread weaponfall(1.5);

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

	fire = false;
	if(sMeansOfDeath == "MOD_EXPLOSIVE" && sWeapon == "smoke_grenade_american_fire_mp" || sWeapon == "smoke_grenade_german_fire_mp" || sWeapon == "smoke_grenade_british_fire_mp" || sWeapon == "smoke_grenade_russian_fire_mp") fire = true;

	panzer = false;
	if(sMeansOfDeath == "MOD_PROJECTILE" && sWeapon == "panzerschreck_mp") panzer = true;

	knife = false;
	if((sMeansOfDeath == "MOD_EXPLOSIVE" || sMeansOfDeath == "MOD_GRENADE" ||  sMeansOfDeath == "MOD_ARTILLERY") && sWeapon == "knife_mp") knife = true;

	knive = false;
	if((sMeansOfDeath == "MOD_EXPLOSIVE" || sMeansOfDeath == "MOD_GRENADE" ||  sMeansOfDeath == "MOD_ARTILLERY") && sWeapon == "knive_mp") knive = true;

	panzer = false;
	if(sMeansOfDeath == "MOD_PROJECTILE" && sWeapon == "panzerschreck_allies") panzer = true;

	gas = false;
	if(sMeansOfDeath == "MOD_EXPLOSIVE" && sWeapon == "smoke_grenade_american_gas_mp" || sWeapon == "smoke_grenade_german_gas_mp" || sWeapon == "smoke_grenade_british_gas_mp" || sWeapon == "smoke_grenade_russian_gas_mp") gas = true;

	sticky = false;
	if(sMeansOfDeath == "MOD_EXPLOSIVE" && sWeapon == "smoke_grenade_american_sticky_mp" || sWeapon == "smoke_grenade_german_sticky_mp" || sWeapon == "smoke_grenade_british_sticky_mp" || sWeapon == "smoke_grenade_russian_sticky_mp") sticky = true;

	satchel = false;
	if(sMeansOfDeath == "MOD_EXPLOSIVE" && sWeapon == "smoke_grenade_american_satchel_mp" || sWeapon == "smoke_grenade_german_satchel_mp" || sWeapon == "smoke_grenade_british_satchel_mp" || sWeapon == "smoke_grenade_russian_satchel_mp") satchel = true;


	// Damage modifiers, weapons
	if(level.wwx_wdmodon && sMeansOfDeath != "MOD_MELEE" && isDefined(sWeapon))
		iDamage = int((iDamage / 100) * [[level.drm_cvardef]]("wwx_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.wwx_bulletholes && (sMeansOfDeath == "MOD_PISTOL_BULLET" || sMeansOfDeath == "MOD_RIFLE_BULLET")) self thread ww2x\_wwx_bulletholes::bullethole();

	// punish attacking player for attacking spawn protected players
	if(level.wwx_spwn_punish_attacker && isPlayer(eAttacker) && eAttacker != self && self.usedweapons && self.wwx_invulnerable)
	{
		eAttacker thread ww2x\_wwx_spawnpro::punish("attacking");
		return;
	}

	// punish protected player for abusing spawn protection
	if(level.wwx_spwn_punish_self && isPlayer(eAttacker) && eAttacker != self && eAttacker.usedweapons && eAttacker.wwx_invulnerable)
	{
		eAttacker thread ww2x\_wwx_spawnpro::punish("abusing");
		return;
	}

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

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

		// Helmet pop
		switch(sHitLoc)
		{
			case "helmet":
			case "head":
			if(randomInt(100) < level.wwx_pophelmet && !self.wwx_helmetpopped)
			{
				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.wwx_callformedic >= 1 && level.wwx_teamplay && level.wwx_revokeonth)
		{
			if(isDefined(eAttacker))
			{
				eAttacker playlocalsound("friendlyfire");
				eAttacker thread ww2x\_wwx_firstaid::disablePlayerHealing();
			}
		}
	}

	if(isAlive(self))
	{	
		switch(sHitLoc)
		{
			case "right_hand":
			case "left_hand":
			case "gun":
			if(level.wwx_droponhandhit != 0 && randomInt(100) < level.wwx_droponarmhit) self thread ww2x\_wwx_weapons::dropcurrentweapon();
			break;
			
			case "right_arm_lower":
			case "left_arm_lower":
			if(level.wwx_droponarmhit != 0 && randomInt(100) < level.wwx_droponarmhit) self thread ww2x\_wwx_weapons::dropcurrentweapon();
			break;
	
			case "right_foot":
			case "left_foot":
			if(level.wwx_triponfoothit && randomInt(100) < level.wwx_triponfoothit) self thread spankme(1);
			break;

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

	iDamage = int(iDamage);

	if(!napalm && level.wwx_bleeding && self.health < level.wwx_startbleed)
		self thread ww2x\_wwx_bleeding::doPlayerBleed(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime);
	
	[[level.wwx_orignalPlayerDamage]](eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime);
}

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

	if(level.wwx_moviemode) self thread ww2x\_wwx_hud::movieRemove();
	if(level.wwx_drophealth) self thread healthDrop();

	// clean the hud
	self ww2x\_wwx_hud::cleanplayer();

	if(level.wwx_deathsound) self thread ww2x\_wwx_utils::playSoundLoc("generic_death",self.origin, "death");

	// turret abuse check
	if(level.wwx_turretabuse >= 1 && (ww2x\_wwx_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.wwx_turretabuse && randomInt(100) < level.wwx_turretabusepc)
		{
			if(sWeapon == "none")
			{
                        if(level.wwx_linux_server >= 1)
                        {
				attacker iprintlnbold(&"MISC_TURRET_ABUSER_PMSG");
				iprintln(&"CUSTOM_TURRET_ABUSER_OBITMSG_0", attacker.name);
				iprintln(&"MISC_TURRET_ABUSER_OBITMSG_2");
                        }
                        else
                        {
				attacker iprintlnbold(&"MISC_TURRET_ABUSER_PMSG");
				iprintln(&"CUSTOM_TURRET_ABUSER_OBITMSG_0", attacker);
				iprintln(&"MISC_TURRET_ABUSER_OBITMSG_2");
                        }
			}
			else
			{
                        if(level.wwx_linux_server >= 1)
                        {
				attacker iprintlnbold(&"CUSTOM_TURRET_ABUSERWEP_PMSG", wepname);
				iprintln(&"CUSTOM_TURRET_ABUSER_OBITMSG_0", attacker.name);
				iprintln(&"MISC_TURRET_ABUSER_OBITMSG_1", wepname);
                        }
                        else
                        {
				attacker iprintlnbold(&"CUSTOM_TURRET_ABUSERWEP_PMSG", wepname);
				iprintln(&"CUSTOM_TURRET_ABUSER_OBITMSG_0", attacker);
				iprintln(&"MISC_TURRET_ABUSER_OBITMSG_1", wepname);
                        }
			}

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

	if(level.wwx_fbannounce && !isDefined(self.switching_teams))
	{
		players = getentarray("player", "classname");
		if(level.wwx_firstblood && isplayer(attacker))
		{
			for(i=0;i < players.size;i++)
			{
				if(players[i] != self)
				{ 
                              if(level.wwx_linux_server == 1)
                              {
					players[i] iprintln(&"FIRSTBLOOD_REPORT_ALL");

 					if(attacker.pers["team"] != self.pers["team"] || !level.wwx_teamplay) players[i] iprintln(&"FIRSTBLOOD_VICTIM", self);
 					else if(attacker.pers["team"] == self.pers["team"] && level.wwx_teamplay && attacker != self) players[i] iprintln(&"FIRSTBLOOD_VICTIM_TEAM_MATE");
					else if(attacker == self) players[i] iprintln(&"FIRSTBLOOD_VICTIM_SELF");
                              }
                              else if(level.wwx_linux_server == 2)
                              {
					players[i] iprintln(&"FIRSTBLOOD_REPORT_ALL2", attacker.name);

 					if(attacker.pers["team"] != self.pers["team"] || !level.wwx_teamplay) players[i] iprintln(&"FIRSTBLOOD_VICTIM", self.name);
 					else if(attacker.pers["team"] == self.pers["team"] && level.wwx_teamplay && attacker != self) players[i] iprintln(&"FIRSTBLOOD_VICTIM_TEAM_MATE2", self.name);
					else if(attacker == self) players[i] iprintln(&"FIRSTBLOOD_VICTIM_SELF");
                              }
                              else
                              {
					players[i] iprintln(&"FIRSTBLOOD_REPORT_ALL2", attacker);

 					if(attacker.pers["team"] != self.pers["team"] || !level.wwx_teamplay) players[i] iprintln(&"FIRSTBLOOD_VICTIM", self);
 					else if(attacker.pers["team"] == self.pers["team"] && level.wwx_teamplay && attacker != self) players[i] iprintln(&"FIRSTBLOOD_VICTIM_TEAM_MATE2", self);
					else if(attacker == self) players[i] iprintln(&"FIRSTBLOOD_VICTIM_SELF");
                              }
					players[i] playlocalsound("firstblood");
				}
			}

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

		level.wwx_fbannounce = false;
	}

	// Check for headpopping
	dopop = false;

	switch(sHitLoc)
	{
		case "head":
		case "helmet":
		if(randomInt(100) < level.wwx_pophelmet && !self.wwx_helmetpopped) self popHelmet( vDir, iDamage );
		if(	level.wwx_popheadbullet && sMeansOfDeath != "MOD_MELEE" && (ww2x\_wwx_weapons::isWeaponType(sWeapon,"turret") ||
			ww2x\_wwx_weapons::isWeaponType(sWeapon,"sniper") || ww2x\_wwx_weapons::isWeaponType(sWeapon,"rifle"))) dopop = true;
		break;

		default:
		break;
	}

	switch(sMeansOfDeath)
	{
		case "MOD_MELEE":
		if(level.wwx_popheadmelee && iDamage>=100) dopop = true;
		break;

		case "MOD_PROJECTILE":
		case "MOD_PROJECTILE_SPLASH":
		case "MOD_GRENADE_SPLASH":
		case "MOD_EXPLOSIVE":
		case "MOD_PANZER":
		case "MOD_KNIFE":

		if(level.wwx_popheadexplosion && iDamage>=100) dopop = true;
		break;

		default:
		break;
	}

	// 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)
	{
		if(randomInt(100) < level.wwx_pophead && !self.wwx_headpopped) self popHead( vDir, iDamage);
		else if(randomInt(100) < level.wwx_pophelmet && !self.wwx_helmetpopped) self popHelmet( vDir, iDamage);
	}

	// add to stats
	//self.pers["death"]++;

	//if(isPlayer(attacker))
	//{
	//	if(attacker.pers["team"] != self.pers["team"] || !level.wwx_teamplay) attacker.pers["kill"]++;
	//	else attacker.pers["kill"]--;
	//}

	// attacker taunt
	if(level.wwx_taunts >= 2 && isPlayer(attacker))
	{
		if(level.wwx_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!
	}

	if(!level.wwx_teamplay || !isPlayer(attacker)) return;

	// team kill check
	if(isPlayer(attacker) && attacker != self && level.wwx_teamplay)
	{
		if(level.wwx_sinbin && isPlayer(attacker))
		{
			if(attacker.pers["team"] == self.pers["team"])
			{
				attacker.pers["teamkill"]++;
			
				if(attacker.pers["teamkill"] > level.wwx_sinbinmaxtk)
				{
					attacker thread ww2x\_wwx_sinbin::main();
					game[attacker.name]["conseckill"] = 0;
					attacker.pers["sinbin"]++;
				}
			}
		}
	}
}

wwxEndMap()
{
	level.wwx_gameover = true;
	level notify("wwx_gameover");
	wait 0.05;

	// announce result
	if(isDefined(level.wwx_resultsound)) level thread ww2x\_wwx_utils::playSoundOnPlayers(level.wwx_resultsound);

	// set players to spectate mode
	players = getentarray("player", "classname");
	for(mx=0;mx<players.size;mx++)
	{
		if(isPlayer(players[mx]))
		{
			players[mx] thread ww2x\_wwx_spawn::SpawnSpectator();
			if(level.wwx_moviemode) players[mx] thread ww2x\_wwx_hud::movieRemove();
			if(level.wwx_ranksystem) players[mx] thread setPlayerVariables();
			players[mx] thread ww2x\_wwx_hud::cleanplayerend();
		}
	}

	// clear hud elements
	level thread ww2x\_wwx_hud::cleanallhud();

	// play end music
	if(level.wwx_endmusic) level thread ww2xMusic();

	// launch statsboard
	if(level.wwx_stbd) 
	{
		ww2x\_wwx_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.wwx_pbrotate && !level.wwx_mapvote) ww2x\_wwx_maprotation::pbrotation();

	// launch mapvote
	//if(level.wwx_mapvote && !level.wwx_pbrotate) ww2x\_wwx_mapvote::main();
	if(level.wwx_mapvote) ww2x\_wwx_mapvote::main();

	// stop the music and load next map	
	level notify("endmusic");

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

ww2xMusic()
{
	time = level.wwx_endmusic_time;
	mn = randomInt(10);
	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");
}

//************************************************************************************************************************************************************************
// WW2X player additional routines
//************************************************************************************************************************************************************************
roundDisplay()
{
	level endon("wwx_gameover");
	self endon("disconnect");

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

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

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

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

	wait 7;

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

createLaserdot()
{
	level endon("wwx_gameover");
	self endon("disconnect");
	self endon("wwx_dead");

	if(!isDefined(self.wwx_laserdot))
	{
		self.wwx_laserdot = newClientHudElem(self);
		self.wwx_laserdot.x = 320;
		self.wwx_laserdot.y = 242;
		self.wwx_laserdot.alignX = "center";
		self.wwx_laserdot.alignY = "middle";
		self.wwx_laserdot.alpha = level.wwx_laserdot;
		self.wwx_laserdot.color = (level.wwx_laserdotred, level.wwx_laserdotgreen, level.wwx_laserdotblue);
		self.wwx_laserdot setShader("white", level.wwx_laserdotsize, level.wwx_laserdotsize );
	}
}

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] = [];
	if(!isDefined(game[self.name]["conseckill"])) game[self.name]["conseckill"] = 0;

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

	// 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] = [];
	game[self.name]["conseckill"] = 0;

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

	// reset the player rank
	if(level.wwx_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 ww2x\_wwx_weapons::replenishWeapons(true);
	self thread ww2x\_wwx_weapons::replenishGrenades(true);
	self thread ww2x\_wwx_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");

	// WW2X
	self.wwx_iscamper = false;
	self.wwx_freezeme = false;
	self.wwx_upndown = false;
	if(!isDefined(self.wwx_isunknown)) self.wwx_isunknown = false;
      if(!isDefined(self.wwx_isdupname)) self.wwx_isdupname = false;
      self.wwx_ispunished = false;
	self.wwx_hasnoweapon = false;
	self.wwx_sinbin = false;
	self.wwx_sbsplat = false;
	self.wwx_oldweapon = undefined;
	self.wwx_invulnerable = false;
	self.wwx_ishealing = undefined;
	self.wwx_helmetpopped = false;
	self.wwx_headpopped = false;
	self.wwx_artillery_strike = false;
	self.wwx_air_strike = false;
	self.wwx_mortar_strike = false;
	self.wwx_sprinttime = 0;
	self.wwx_playsprint = false;
	self.wwx_sprintreco = false;
	self.wwx_sprinting = false;
	self.wwx_binocuse = false;
	self.wwx_warningwire = undefined;
	self.wwx_plantwire = false;
	self.wwx_defusewire = false;
	self.wwx_stopwepmon = false;
	self.wwx_bleeding = false;
	self.wwx_bsoundinit = false;
	self.wwx_bphockinit = false;
	self.wwx_zombie = false;
	self.wwx_pace = false;
	self.wwx_checkingwmd = undefined;
	self.wwx_spwn_punish = undefined;
	self.wwx_firstaidkits = 0;
	self.wwx_inmenu = false;
	self.wwx_isparachuting = undefined;
      self.handling_mine = false;
	self.wwx_isonfire = undefined;
	self.wwx_puked = undefined;



	// 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
             if(isPlayer(self)) self thread maps\mp\gametypes\_quickmessages::quicktaunts(tauntno, true);
             if(isPlayer(self)) self thread maps\mp\gametypes\_quickmessages::quicktauntsb(tauntno, true);
          	
        }
}

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

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

	self detach(self.hatModel , "");

	self.wwx_stance = [[level.wwx_getStance]](false);

	if(isPlayer(self))
	{
		switch(self.wwx_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 = ww2x\_wwx_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 ww2x\_wwx_utils::bounceObject(rotation, velocity, offset, (0,0,0), radius, bounce, impactsound, undefined, "helmet");
}

popHead( damageDir, damage)
{
	self.wwx_headpopped = true;

	if(!self.wwx_helmetpopped) self popHelmet( damageDir, damage );

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

	self.wwx_stance = [[level.wwx_getStance]](false);

	self detach(self.wwx_headmodel , "");
	playfxontag(level.wwx_effect["pophead"] ,self,"J_Neck");

	if(isPlayer(self))
	{
		switch(self.wwx_stance)
		{
			case 2:	headoffset = (0,0,16);	break;
			case 1:	headoffset = (0,0,45);	break;
			default:	headoffset = (0,0,65);	break;
		}
	}
	else headoffset = (0,0,16);
	
	rotation = (randomFloat(540), randomFloat(540), randomFloat(540));
	offset = (-2,0,-13);
	radius = 6;

	velocity = ww2x\_wwx_utils::vectorScale(damageDir, (damage/20 + randomFloat(5)) ) + (0,0,(damage/20 + randomFloat(5)) );

	head = spawn("script_model", self.origin + headoffset );
	head setmodel( self.wwx_headmodel );
	head.angles = self.angles;
	head thread ww2x\_wwx_utils::bounceObject(rotation, velocity, offset, (-90,0,-90), radius, 0.75, "Land_", level.wwx_effect["pophead"] , "head");
}

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

	// Do an extra bloodfx for headpopped players
	if(level.wwx_deadbodyfx >= 2 && owner.wwx_headpopped) self thread delayedBloodFx();

	// sink body in to the ground
	if(level.wwx_deadbodyfx == 1 || level.wwx_deadbodyfx == 3) self thread bodySink();
}

delayedBloodFx()
{
	fx = 2 + randomint(6);

	for(i = 0; i< fx; i++)
	{
		wait 0.25 + randomfloat(i);
		if(isDefined(self)) playfxontag (level.wwx_effect["pophead"] ,self,"J_Neck");
	}
}

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("wwx_gameover");
	self endon("wwx_dead");

	if(!level.wwx_bloodonscreen) return;

	if(!isDefined(self.wwx_bloodonscreen))
	{
		self.wwx_bloodonscreen = newClientHudElem(self);
		self.wwx_bloodonscreen1 = newClientHudElem(self);
		self.wwx_bloodonscreen2 = newClientHudElem(self);
		self.wwx_bloodonscreen3 = newClientHudElem(self);

		self.wwx_bloodonscreen.alignX = "left";
		self.wwx_bloodonscreen.alignY = "top";
	
		self.wwx_bloodonscreen1.alignX = "left";
		self.wwx_bloodonscreen1.alignY = "top";

		self.wwx_bloodonscreen2.alignX = "left";
		self.wwx_bloodonscreen2.alignY = "top";
		
		self.wwx_bloodonscreen3.alignX = "left";
		self.wwx_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.wwx_bloodonscreen.x = bs1;
		self.wwx_bloodonscreen.y = bs2;

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

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

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

		bs3 = randomint(48);
		bs3a = randomint(48);
		bs3b = randomint(48);
		bs3c = randomint(48);
		self.wwx_bloodonscreen.color = (1,1,1);
		self.wwx_bloodonscreen1.color = (1,1,1);
		self.wwx_bloodonscreen2.color = (1,1,1);
		self.wwx_bloodonscreen3.color = (1,1,1);
		self.wwx_bloodonscreen.alpha = 1;
		self.wwx_bloodonscreen1.alpha = 1;
		self.wwx_bloodonscreen2.alpha = 1;
		self.wwx_bloodonscreen3.alpha = 1;

		self.wwx_bloodonscreen SetShader("gfx/impact/flesh_hit2",96 + bs3 , 96 + bs3);
		self.wwx_bloodonscreen1 SetShader("gfx/impact/flesh_hitgib",96 + bs3a , 96 + bs3a);
		self.wwx_bloodonscreen2 SetShader("gfx/impact/flesh_hit2",96 + bs3b , 96 + bs3b);
		self.wwx_bloodonscreen3 SetShader("gfx/impact/flesh_hitgib",96 + bs3c , 96 + bs3c);

		wait 4;

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

		self.wwx_bloodonscreen fadeOverTime (2); 
		self.wwx_bloodonscreen.alpha = 0;
		self.wwx_bloodonscreen1 fadeOverTime (2);
		self.wwx_bloodonscreen1.alpha = 0;
		self.wwx_bloodonscreen2 fadeOverTime (2);
		self.wwx_bloodonscreen2.alpha = 0;
		self.wwx_bloodonscreen3 fadeOverTime (2);
		self.wwx_bloodonscreen3.alpha = 0;

		wait 2;

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

		if(isDefined(self.wwx_bloodonscreen)) self.wwx_bloodonscreen destroy();
		if(isDefined(self.wwx_bloodonscreen1)) self.wwx_bloodonscreen1 destroy();
		if(isDefined(self.wwx_bloodonscreen2)) self.wwx_bloodonscreen2 destroy();
		if(isDefined(self.wwx_bloodonscreen3)) self.wwx_bloodonscreen3 destroy();
	}
}

distortPlayerView()
{
	level endon("wwx_gameover");
	level endon("intermission");
	self endon("disconnect");
	self endon("wwx_spawned");
	self endon("wwx_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("wwx_playerhit");
	self endon("wwx_playerhit");
	self endon("wwx_dead");

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

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

weaponfall(delay)
{
	self endon("disconnect");
	self endon("wwx_spawned");
	self endon("wwx_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 ww2x\_wwx_weapons::dropcurrentweapon();
	}
	else self thread ww2x\_wwx_weapons::dropcurrentweapon();
}

spankme(time)
{
	level endon("wwx_gameover");
	self notify("wwx_spankme");
	self endon("wwx_spankme");
	self endon("wwx_spawned");
	self endon("wwx_dead");

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

		wait 0.2;
	}
}

handleWelcome()
{
  level endon("wwx_gameover");
  self endon("disconnect");
  self endon("wwx_dead");
  self endon("wwx_freefall");

  if(isPlayer(self))
  {
    // Resetting the tag wwx_ispunished is done in resetFlagVars()
    
    // Did the Name Checker already tag the player for using an unacceptable name?
    if(isDefined(self.wwx_isunknown) && self.wwx_isunknown)
    {
      self thread handleUnknown(false);
    }
    else
    {
      // Did the Name Checker already tag the player for using a duplicate name?
      if(isDefined(self.wwx_isdupname) && self.wwx_isdupname)
      {
        self thread handleDupName();
      }
      else
      {
        // If Name Checker is disabled and Unknown Soldier handling is enabled,
        // check for unacceptable names ourselves
        if (level.wwx_uscheck && isUnknown(self))
        {
          self thread handleUnknown(false);
        }
        else self thread ww2x\_wwx_messages::welcomemsg();
      }
    }
  }
}

handleDupName()
{
  level endon("wwx_gameover");
  self endon("disconnect");
  self endon("wwx_dead");
  self endon("wwx_freefall");

  // Tag the player to prevent the Name Checker to kick in more than once
  self.wwx_isdupname = true;
  
  if(level.wwx_linux_server >= 1)
  {
  self iprintlnbold(&"NAMECHECK_DNCHECK_DUPNAME1", self.name);
  }
  else
  {
  self iprintlnbold(&"NAMECHECK_DNCHECK_DUPNAME1", self);
  }
  
  self setClientCvar("name", "Unknown Soldier");
  self iprintlnbold(&"NAMECHECK_DNCHECK_NEWUNKNOWN");
  
  if(level.wwx_ncskipwarning)
  {
    if(level.wwx_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.wwx_ncskipwarning);

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

handleUnknown(skipwarning)
{
  level endon("wwx_gameover");
  self endon("disconnect");
  self endon("wwx_dead");
  self endon("wwx_freefall");

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

	usname = [];

  if(!skipwarning)
  {
    if(isPlayer(self))
    {
      // Warn them first
      if(level.wwx_usclanguest)
      {
        if(level.wwx_linux_server >= 1)
        {
        self iprintlnbold(&"UNKNOWNSOLDIER_MSG_UNACCEPTABLE", self.name);
        }
        else
        {
        self iprintlnbold(&"UNKNOWNSOLDIER_MSG_UNACCEPTABLE", self);
        }
        self iprintlnbold(&"UNKNOWNSOLDIER_MSG_CHANGEIT");
        self iprintlnbold(&"UNKNOWNSOLDIER_MSG_CLANGUEST", level.wwx_uswarndelay1);	
      }
      else
      {
        if(level.wwx_linux_server >= 1)
        {
        self iprintlnbold(&"UNKNOWNSOLDIER_MSG_UNACCEPTABLE", self.name);
        }
        else
        {
        self iprintlnbold(&"UNKNOWNSOLDIER_MSG_UNACCEPTABLE", self);
        }
        self iprintlnbold(&"UNKNOWNSOLDIER_MSG_CHANGEIT");
        self iprintlnbold(&"UNKNOWNSOLDIER_MSG_GUEST", level.wwx_uswarndelay1);
      }
    }
    // Now give them some time to change their name
    waitWhileUnknown(level.wwx_uswarndelay1);
  }  

  if(isPlayer(self) && isUnknown(self))
  {
    // Get a free guest number (1 to sv_maxclients)
    level.wwx_usguestno = getFreeGuestSlot();
      
    if(level.wwx_usclanguest)
    {
      usname[0] = level.wwx_usclanguestname; // Clan Guest
      usname[1] = usname[0] + level.wwx_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.wwx_isunknown = false;
      wait 3;
      if(isPlayer(self)) self thread ww2x\_wwx_messages::welcomemsg();
      return;
    }
    else
    {
      // Only assign guest name if not already using an assigned guest name
      if(!isAssignedName(self))
      {
        usname[0] = level.wwx_usguestname; // Non-clan Guest
        usname[1] = usname[0] + level.wwx_usguestno;
        self setClientCvar("name", usname[1]);
        self iprintlnbold(&"UNKNOWNSOLDIER_NEWNAME_BYSERVER");
        self iprintlnbold(&"UNKNOWNSOLDIER_NEWNAME_GUEST", usname[1]);
        self iprintlnbold(&"UNKNOWNSOLDIER_NEWNAME_CHANGEIT", level.wwx_uswarndelay2);

        // After name assignment, non-clan guests get a second chance to change their name
        waitWhileUnknown(level.wwx_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.wwx_uspunishcount)
    {
      if(!self.wwx_upndown)
      {
        while(self.wwx_sinbin) wait 1;
        if(level.wwx_linux_server >= 1)
        {
        self iprintlnbold(&"UNKNOWNSOLDIER_MSG_TEMPORARY", self.name);
        }
        else
        {
        self iprintlnbold(&"UNKNOWNSOLDIER_MSG_TEMPORARY", self);
        }         
        self iprintlnbold(&"UNKNOWNSOLDIER_MSG_CHANGEIT");
        self iprintlnbold(&"UNKNOWNSOLDIER_STILL_PUNISH");
        self thread ww2x\_wwx_utils::punishment("drop", "freeze");
        waitWhileUnknown(10);
        if(isPlayer(self)) self thread ww2x\_wwx_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.wwx_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.wwx_ispunished && !isUnknown(self))
  {
    // Has the Name Checker tagged him because of using a duplicate name?
    if(isPlayer(self) && !self.wwx_isdupname)
    {
      // No, so thank them, and show the welcome messages
      if(level.wwx_linux_server >= 1)
      {
      self iprintlnbold(&"UNKNOWNSOLDIER_MSG_THANKS", self.name);
      }
      else
      {
      self iprintlnbold(&"UNKNOWNSOLDIER_MSG_THANKS", self);
      }
      wait 3;
      if(isPlayer(self)) self thread ww2x\_wwx_messages::welcomemsg();
    }
    else self thread handleDupName();
  }

  // Remove the tag; the player is either renamed, punished or dupname-tagged
  self.wwx_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("wwx_dead");

  playernorm = "";
  if(isPlayer(player)) playernorm = ww2x\_wwx_utils::monotone(player.name);
  playernorm = ww2x\_wwx_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("wwx_dead");

  chkname = [];
  maxguestno = getCvarInt("sv_maxclients");
  chkname[0] = level.wwx_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("wwx_dead");
	
	if(isUnknownSoldier(player)) return true;
	if(isAssignedName(player)) return true;
	return false;
}

getFreeGuestSlot()
{
  // Get a free guest number.
  
	self endon("disconnect");
	self endon("wwx_dead");

  chkname = [];
  maxguestno = getCvarInt("sv_maxclients");
  players = getentarray("player", "classname");
  maxplayers = players.size;
  
  if(level.wwx_usclanguest) chkname[0] = level.wwx_usclanguestname;
    else chkname[0] = level.wwx_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 "remotekill";
		case 17: return "landminekill";
		case 18: return "firenadekill";
		case 19: return "gasnadekill";
		case 20: return "stickynadekill";
		case 21: return "satchelkill";

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

		// other
		case 45: return "turretkill";
		case 46: return "conseckill";
		case 47: return "noobstreak";
		case 48: return "weaponstreak";
		case 49: return "sinbin";
		case 50: return "roundshown";

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