
cvardef(varname, vardefault, min, max, type)
{
	mapname = getcvar("mapname");
	gametype = getcvar("g_gametype");
	multigtmap = gametype + "_" + mapname;

	// check against gametype
	tempvar = varname + "_" + gametype;
	if(getcvar(tempvar) != "") varname = tempvar;

	// check against the map name
	tempvar = varname + "_" + mapname;
	if(getcvar(tempvar) != "") varname = tempvar;

	// check against the gametype and the map name
	tempvar = varname + "_" + multigtmap;
	if(getcvar(tempvar) != "") varname = tempvar;

	// get the variable's definition
	switch(type)
	{
		case "int":
		if(getcvar(varname) == "") definition = vardefault;
		else definition = getcvarint(varname);
		break;

		case "float":
		if(getcvar(varname) == "") definition = vardefault;
		else definition = getcvarfloat(varname);
		break;

		case "string":
		default:
		if(getcvar(varname) == "") definition = vardefault;
		else definition = getcvar(varname);
		break;
	}

	// check to see if the value is within the min & max parameters
	if(type != "string")
	{
		if(min != 0 && definition < min) definition = min;
		else if(max != 0 && definition > max) definition = max;
	}

	return definition;
}

vectorScale (vec, scale)
{
	vec = (vec[0] * scale, vec[1] * scale, vec[2] * scale);
	return vec;
}

vectorMulti(vec, size)
{
	x = vec[0] * size;
	y = vec[1] * size;
	z = vec[2] * size;
	vec = (x,y,z);
	return vec;
}

excllnbo(state,lines) // Function to clear LNBOLD statements
{
	for(count=0;count<lines;count++)
	{
		if(state == "all") iprintlnbold(&"MISC_BLANK_LINE_TXT");
		else	if(state == "self") self iprintlnbold(&"MISC_BLANK_LINE_TXT");
	}
}

punishment(weaponstatus, movestatus)
{
	if(!isDefined(weaponstatus)) weaponstatus = "keep";

	if(!isDefined(movestatus)) movestatus = "same";

	if(isDefined(self.ex_lock))
	{
		self unlink();
		self.ex_lock delete();
	}

	if(weaponstatus == "disable") self disableWeapon();
	else if(weaponstatus == "random" && randomInt(100) < 50)
	{
		if(randomInt(100) < 50)
		{
			self.ex_hasnoweapon = true;
			self disableWeapon();
		}
		else self extreme\_ex_weapons::dropcurrentweapon();
	}
	else if(weaponstatus == "drop") self extreme\_ex_weapons::dropcurrentweapon();
	else if(weaponstatus == "enable")
	{
		self enableWeapon();
		self.ex_hasnoweapon = false;
	}

	if(movestatus == "freeze")
	{
		// spawn a script origin, and lock the players in place and disable weapon
		self.ex_lock = spawn("script_origin", self.origin);
		self linkTo(self.ex_lock);
	}
	else	if(movestatus == "release" && isDefined(self.ex_lock))
	{
		self unlink();
		self.ex_lock delete();
	}		
}

playSoundLoc(sound, position, special)
{
	if(!isDefined(position))
		position = level.ex_playArea_Centre;
	
	soundloc = spawn( "script_model", position);
	wait 0.05;
	soundloc show();

	if(!isDefined(special)) soundloc playSound(sound);
	else
	{
		if(special == "death")
		{
			if(self.pers["team"] == "allies") soundloc playsound(sound+"_" + game["allies"] + "_" + randomInt(level.ex_voices[game["allies"]]));
			else soundloc playsound(sound+"_german_" + randomInt(level.ex_voices["german"]));
		}
	}

	wait 5;
	soundloc delete();
}

playSoundOnPlayer(sound, special)
{
	self notify("ex_soplayer");
	self endon("ex_soplayer");
	self endon("ex_spawned");
	self endon("ex_dead");

	if(!isDefined(special)) self playsound(sound);
	else
	{
		if(special == "pain")
		{
			if(self.pers["team"] == "allies") self playsound(sound+"_" + game["allies"] + "_" + randomInt(level.ex_voices[game["allies"]]));
			else self playsound(sound+"_german_" + randomInt(level.ex_voices["german"]));
		}
	}
}

playSoundOnPlayers(sound, team, spectators)
{
	if(!isDefined(spectators)) spectators = true;

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

	if(isDefined(team))
	{
		for(i = 0; i < players.size; i++)
		{
			wait 0.01;

			if(isPlayer(players[i]) && isDefined(players[i].pers) && isDefined(players[i].pers["team"]) && players[i].pers["team"] == team)
			{
				if(spectators) players[i] playLocalSound(sound);
				else if(players[i].sessionstate != "spectator") players[i] playLocalSound(sound);
			}
		}
	}
	else
	{
		for(i = 0; i < players.size; i++)
		{
			wait 0.01;

			if(isPlayer(players[i]) && spectators) players[i] playLocalSound(sound);
			else if(isPlayer(players[i]) && isDefined(players[i].sessionstate) && players[i].sessionstate != "spectator") players[i] playLocalSound(sound);
		}
	}

	wait 1;	
	level notify("psopdone");
}

ex_PrecacheShader(shader)
{
	if(isDefined(game["gamestarted"])) return;

	thread extreme\_ex_logfile::Log("exPrecacheShader");
	
	//Rank Precache Icons
	if(level.ex_ranksystem) thread extreme\_rank_precache::precache();

	if(!isDefined(level.ex_precachedshaders)) level.ex_precachedshaders = [];

	if(isInArray(level.ex_precachedshaders, shader)) return;

	level.ex_precachedshaders[level.ex_precachedshaders.size] = shader;
	precacheShader(shader);
}

ex_PrecacheHeadIcon(icon)
{
	if(isDefined(game["gamestarted"])) return;

	thread extreme\_ex_logfile::Log("exPrecacheHeadIcon");

	if(!isDefined(level.ex_precachedheadicons)) level.ex_precachedheadicons = [];

	if(isInArray(level.ex_precachedheadicons, icon)) return;

	level.ex_precachedheadicons[level.ex_precachedheadicons.size] = icon;
	precacheHeadIcon(icon);
}

ex_PrecacheStatusIcon(icon)
{
	if(isDefined(game["gamestarted"])) return;

	thread extreme\_ex_logfile::Log("exPrecacheStatusIcon");

	if(!isDefined(level.ex_precachedstatusicons)) level.ex_precachedstatusicons = [];

	if(isInArray(level.ex_precachedstatusicons, icon)) return;

	level.ex_precachedstatusicons[level.ex_precachedstatusicons.size] = icon;
	precacheStatusIcon(icon);
}

ex_PrecacheModel(model)
{
	if(isDefined(game["gamestarted"])) return;

	thread extreme\_ex_logfile::Log("exPrecacheModel");

	if(!isDefined(level.ex_precachedmodels)) level.ex_precachedmodels = [];

	if(isInArray(level.ex_precachedmodels, model)) return;

	level.ex_precachedmodels[level.ex_precachedmodels.size] = model;
	precacheModel(model);
}

ex_PrecacheItem(item)
{
	if(isDefined(game["gamestarted"])) return;

	thread extreme\_ex_logfile::Log("exPrecacheItem");

	if(!isDefined(level.ex_precacheditems)) level.ex_precacheditems = [];

	if(isInArray(level.ex_precacheditems, item)) return;

	level.ex_precacheditems[level.ex_precacheditems.size] = item;
	precacheItem(item);
}

ex_PrecacheString(element)
{
	if(isDefined(game["gamestarted"])) return;

	thread extreme\_ex_logfile::Log("exPrecacheString");

	if(!isDefined(level.ex_precachedstrings)) level.ex_precachedstrings = [];

	if(isInArray(level.ex_precachedstrings, element)) return;

	level.ex_precachedstrings[level.ex_precachedstrings.size] = element;
	precacheString(element);
}

ex_PrecacheMenuItem(menutype)
{
	if(isDefined(game["gamestarted"])) return;

	thread extreme\_ex_logfile::Log("ex_PrecacheMenuItem");

	if(!isDefined(level.ex_precachedmenuitem))
		level.ex_precachedmenuitem = [];

	if(isInArray(level.ex_precachedmenuitem, menutype)) return;

	level.ex_precachedmenuitem[level.ex_precachedmenuitem.size] = menutype;
	precacheMenu(menutype);
}

ex_PrecacheShellShock(shocktype)
{
	if(isDefined(game["gamestarted"])) return;

	thread extreme\_ex_logfile::Log("ex_PrecacheShellShock");

	if(!isDefined(level.ex_precachedshellshock)) level.ex_precachedshellshock = [];

	if(isInArray(level.ex_precachedshellshock, shocktype)) return;

	level.ex_precachedshellshock[level.ex_precachedshellshock.size] = shocktype;
	precacheShellShock(shocktype);
}

ex_precacheForcedWeapon(weapon)
{
	if(isDefined(game["gamestarted"])) return;
	
	if( ![[level.ex_isWeaponType]]("german",weapon) && ![[level.ex_isWeaponType]]("american",weapon) && ![[level.ex_isWeaponType]]("british",weapon) && ![[level.ex_isWeaponType]]("russian",weapon) && ![[level.ex_isWeaponType]]("common",weapon) )
		return;

	if(weapon == "none" || weapon == "" || weapon == "select" || weapon == "selectother" || weapon == "random" || weapon == "randomother" || weapon == "disable")
		return;

	ex_PrecacheItem(weapon);
}

isInArray(array, element)
{
	thread extreme\_ex_logfile::Log("isInArray");

	if(!isDefined(array) || !array.size) return false;

	for(i=0;i<array.size;i++)
		if(array[i] == element) return true;

	return false;
}

monotone( str )
{
	if ( !isDefined( str ) || ( str == "" ) )
		return ( "" );

	_s = "";

	_colorCheck = false;
	for ( i = 0; i < str.size; i++ )
	{
		ch = str[ i ];
		if ( _colorCheck )
		{
			_colorCheck = false;

			switch ( ch )
			{
			  case "0":	// black
			  case "1":	// red
			  case "2":	// green
			  case "3":	// yellow
			  case "4":	// blue
			  case "5":	// cyan
			  case "6":	// pink
			  case "7":	// white
			  case "8":	// Olive
			  case "9":	// Grey
			  	break;
			  default:
			  	_s += ( "^" + ch );
			  	break;
			}
		}
		else if ( ch == "^" )
			_colorCheck = true;
		else
			_s += ch;
	}
	return ( _s );
}

isOutside(origin)
{
	if(!isDefined(origin)) return false;

	trace = bulletTrace(origin, origin+ (0,0,6000), false, false);

	if(distance(origin, trace["position"]) >= 1000) return true;
	else return false;
}

saveHeadIcon()
{
	if(isDefined(self.headicon)) self.oldheadicon = self.headicon;

	if(isDefined(self.headiconteam)) self.oldheadiconteam = self.headiconteam;
}

restoreHeadicon(oldicon)
{
	level endon("ex_gameover");
	self endon("disconnect");
	self endon("ex_dead");

	if(level.drawfriend && self.pers["team"] != "spectator" && level.ex_teamplay)
	{
		headicon = "headicon_" + self.pers["team"];
		self.headicon = game[headicon];
	
		if(isDefined(self.sessionteam) && self.sessionteam != "spectator") self.headiconteam = self.sessionteam;
		else self.headiconteam = self.pers["team"];

	}
	else self.headicon = "";
	
	if(isDefined(self.oldheadicon) && isDefined(oldicon))
	{
		if(self.oldheadicon == oldicon) self.oldheadicon = self.headicon;
	}
	else
	{
		if(isDefined(self.oldheadicon)) self.headicon = self.oldheadicon;
		if(isDefined(self.oldheadiconteam)) self.headiconteam = self.oldheadiconteam;
	}		
}

ex_hud_announce(message, predelay)
{
	self endon("kill_ex_hud_announce");
	self endon("disconnect");

	if(!isDefined(message)) return;

	if(!isDefined(self.ex_hud_announce))
	{
		self.ex_hud_announce = [];
		self.ex_hudwait = 1;
	}

	if(self.ex_hudwait < 1) self.ex_hudwait = 1;

	self.ex_hudwait++;
	
	wait self.ex_hudwait;

	for(i = 0; i < self.ex_hud_announce.size; i++)
	{
		if(isDefined(self.ex_hud_announce[i]))
		{
			self.ex_hud_announce[i] moveOverTime(0.25);
			self.ex_hud_announce[i].y = self.ex_hud_announce[i].y - 20;
		}
	}

	i = 0;
	while(isDefined(self.ex_hud_announce[i])) i++;

	self.ex_hud_announce[i] = newClientHudElem(self);
	self.ex_hud_announce[i].alignX = "center";
	self.ex_hud_announce[i].alignY = "middle";
	self.ex_hud_announce[i].x = 320;
	self.ex_hud_announce[i].y = 50;
	self.ex_hud_announce[i].alpha = 0;
	self.ex_hud_announce[i].fontscale = 1.5;
	wait 0.25;

	self.ex_hud_announce[i] settext(message);
	self.ex_hud_announce[i] fadeOverTime(0.5);
	self.ex_hud_announce[i].alpha = 1;
	wait 2.5;
	self.ex_hud_announce[i] fadeOverTime(0.5);
	self.ex_hud_announce[i].alpha = 0;
	wait 0.25;

	self.ex_hud_announce[i] destroy();
	self.ex_hudwait--;
}

time_convert(value)
{
	switch(value)
	{
		case 1: return &"TIME_1_SECOND";
		case 2: return &"TIME_2_SECONDS";
		case 3: return &"TIME_3_SECONDS";
		case 4: return &"TIME_4_SECONDS";
		case 5: return &"TIME_5_SECONDS";
		case 6: return &"TIME_6_SECONDS";
		case 7: return &"TIME_7_SECONDS";
		case 8: return &"TIME_8_SECONDS";
		case 9: return &"TIME_9_SECONDS";
		case 10: return &"TIME_10_SECONDS";

		case 11: return &"TIME_11_SECONDS";
		case 12: return &"TIME_12_SECONDS";
		case 13: return &"TIME_13_SECONDS";
		case 14: return &"TIME_14_SECONDS";
		case 15: return &"TIME_15_SECONDS";
		case 16: return &"TIME_16_SECONDS";
		case 17: return &"TIME_17_SECONDS";
		case 18: return &"TIME_18_SECONDS";
		case 19: return &"TIME_19_SECONDS";
		case 20: return &"TIME_20_SECONDS";
		
		case 21: return &"TIME_21_SECONDS";
		case 22: return &"TIME_22_SECONDS";
		case 23: return &"TIME_23_SECONDS";
		case 24: return &"TIME_24_SECONDS";
		case 25: return &"TIME_25_SECONDS";
		case 26: return &"TIME_26_SECONDS";
		case 27: return &"TIME_27_SECONDS";
		case 28: return &"TIME_28_SECONDS";
		case 29: return &"TIME_29_SECONDS";
		case 30: return &"TIME_30_SECONDS";

		case 31: return &"TIME_31_SECONDS";
		case 32: return &"TIME_32_SECONDS";
		case 33: return &"TIME_33_SECONDS";
		case 34: return &"TIME_34_SECONDS";
		case 35: return &"TIME_35_SECONDS";
		case 36: return &"TIME_36_SECONDS";
		case 37: return &"TIME_37_SECONDS";
		case 38: return &"TIME_38_SECONDS";
		case 39: return &"TIME_39_SECONDS";
		case 40: return &"TIME_40_SECONDS";

		case 41: return &"TIME_41_SECONDS";
		case 42: return &"TIME_42_SECONDS";
		case 43: return &"TIME_43_SECONDS";
		case 44: return &"TIME_44_SECONDS";
		case 45: return &"TIME_45_SECONDS";
		case 46: return &"TIME_46_SECONDS";
		case 47: return &"TIME_47_SECONDS";
		case 48: return &"TIME_48_SECONDS";
		case 49: return &"TIME_49_SECONDS";
		case 50: return &"TIME_50_SECONDS";

		case 51: return &"TIME_51_SECONDS";
		case 52: return &"TIME_52_SECONDS";
		case 53: return &"TIME_53_SECONDS";
		case 54: return &"TIME_54_SECONDS";
		case 55: return &"TIME_55_SECONDS";
		case 56: return &"TIME_56_SECONDS";
		case 57: return &"TIME_57_SECONDS";
		case 58: return &"TIME_58_SECONDS";
		case 59: return &"TIME_59_SECONDS";
		case 60: return &"TIME_60_SECONDS";
	}
}

GetMapDim()
{
	entitytypes = getentarray();

	xMax = -30000;
	xMin = 30000;

	yMax = -30000;
	yMin = 30000;

	zMax = -30000;
	zMin = 30000;

	xMin_e[0] = xMax;
	yMin_e[1] = yMax;
	zMin_e[2] = zMax;

	xMax_e[0] = xMin;
	yMax_e[1] = yMin;
	zMax_e[2] = zMin;       

	for(i = 1; i < entitytypes.size; i++)
	{
		//edited by oraco: 	fix the crashing problem with too many entities taking too long
		//					and entity with higher number (entitytypes[number]} becomes undefined
		//					when the loop gets there...
		if(isdefined(entitytypes[i].origin)){
		
			trace = bulletTrace(entitytypes[i].origin, entitytypes[i].origin - (30000,0,0),false,undefined);
			if(trace["fraction"] != 1)  xMin_e  = trace["position"];
	
			trace = bulletTrace(entitytypes[i].origin, entitytypes[i].origin + (30000,0,0),false,undefined);
			if(trace["fraction"] != 1)  xMax_e  = trace["position"];
	
			trace = bulletTrace(entitytypes[i].origin, entitytypes[i].origin - (0,30000,0),false,undefined);
			if(trace["fraction"] != 1)  yMin_e  = trace["position"];
	
			trace = bulletTrace(entitytypes[i].origin, entitytypes[i].origin + (0,30000,0),false,undefined);
			if(trace["fraction"] != 1)  yMax_e  = trace["position"];
	
			trace = bulletTrace(entitytypes[i].origin, entitytypes[i].origin - (0,0,30000),false,undefined);
			if(trace["fraction"] != 1)  zMin_e  = trace["position"];
	
			trace = bulletTrace(entitytypes[i].origin, entitytypes[i].origin + (0,0,30000),false,undefined);
			if(trace["fraction"] != 1)  zMax_e  = trace["position"];

			if (xMin_e[0] < xMin)   xMin = xMin_e[0];
			if (yMin_e[1] < yMin)   yMin = yMin_e[1];
			if (zMin_e[2] < zMin)   zMin = zMin_e[2];
	
			if (xMax_e[0] > xMax)   xMax = xMax_e[0];
			if (yMax_e[1] > yMax)   yMax = yMax_e[1];
			if (zMax_e[2] > zMax)   zMax = zMax_e[2];       
		}
		wait 0.05;
	}

	level.ex_mapArea_CentreX = int(xMax + xMin)/2;
	level.ex_mapArea_CentreY = int(yMax + yMin)/2;
	level.ex_mapArea_CentreZ = int(zMax + zMin)/2;
	level.ex_mapArea_Centre = (level.ex_mapArea_CentreX, level.ex_mapArea_CentreY, level.ex_mapArea_CentreZ);

	level.ex_mapArea_Max = (xMax, yMax, zMax);
	level.ex_mapArea_Min = (xMin, yMin, zMin);

	level.ex_mapArea_Width = int(distance((xMin,yMin,zMax),(xMax,yMin,zMax)));
	level.ex_mapArea_Length = int(distance((xMin,yMin,zMax),(xMin,yMax,zMax)));

	// Special Z coords for mp_carentan, mp_decoy, mp_railyard, mp_toujane
	level.ex_mapArea_PlaneZ = int(zMax + zMin)/1.35;

	entitytypes = [];
	entitytypes = undefined;
}

GetFieldDim()
{
	spawnpoints = [];

	spawnpoints_s1 = getentarray("mp_dm_spawn", "classname");
	spawnpoints_s2 = getentarray("mp_tdm_spawn", "classname");
	spawnpoints_s3 = getentarray("mp_ctf_spawn_allied", "classname");
	spawnpoints_s4 = getentarray("mp_ctf_spawn_axis", "classname");
	spawnpoints_s5 = getentarray("mp_sd_spawn_attacker", "classname");
	spawnpoints_s6 = getentarray("mp_sd_spawn_defender", "classname");

	for(i=0;i<spawnpoints_s1.size;i++) spawnpoints = maps\mp\gametypes\_spawnlogic::add_to_array(spawnpoints, spawnpoints_s1[i]);
	for(i=0;i<spawnpoints_s2.size;i++) spawnpoints = maps\mp\gametypes\_spawnlogic::add_to_array(spawnpoints, spawnpoints_s2[i]);
	for(i=0;i<spawnpoints_s3.size;i++) spawnpoints = maps\mp\gametypes\_spawnlogic::add_to_array(spawnpoints, spawnpoints_s3[i]);
	for(i=0;i<spawnpoints_s4.size;i++) spawnpoints = maps\mp\gametypes\_spawnlogic::add_to_array(spawnpoints, spawnpoints_s4[i]);
	for(i=0;i<spawnpoints_s5.size;i++) spawnpoints = maps\mp\gametypes\_spawnlogic::add_to_array(spawnpoints, spawnpoints_s5[i]);
	for(i=0;i<spawnpoints_s6.size;i++) spawnpoints = maps\mp\gametypes\_spawnlogic::add_to_array(spawnpoints, spawnpoints_s6[i]);

	xMax = spawnpoints[0].origin[0];
	xMin = spawnpoints[0].origin[0];

	yMax = spawnpoints[0].origin[1];
	yMin = spawnpoints[0].origin[1];

	zMax = spawnpoints[0].origin[2];
	zMin = spawnpoints[0].origin[2];

	for(i=1;i<spawnpoints.size;i++)
	{
		if (spawnpoints[i].origin[0] > xMax)     xMax = spawnpoints[i].origin[0];
		if (spawnpoints[i].origin[1] > yMax)     yMax = spawnpoints[i].origin[1];
		if (spawnpoints[i].origin[2] > zMax)     zMax = spawnpoints[i].origin[2];
		if (spawnpoints[i].origin[0] < xMin)     xMin = spawnpoints[i].origin[0];
		if (spawnpoints[i].origin[1] < yMin)     yMin = spawnpoints[i].origin[1];
		if (spawnpoints[i].origin[2] < zMin)     zMin = spawnpoints[i].origin[2];
	}

	level.ex_playArea_CentreX = int(int(xMax + xMin)/2);
	level.ex_playArea_CentreY = int(int(yMax + yMin)/2);
	level.ex_playArea_CentreZ = int(int(zMax + zMin)/2);
	level.ex_playArea_Centre = (level.ex_playArea_CentreX, level.ex_playArea_CentreY, level.ex_playArea_CentreZ);

	level.ex_playArea_Min = (xMin, yMin, zMin);
	level.ex_playArea_Max = (xMax, yMax, zMax);

	level.ex_playArea_Width = int(distance((xMin, yMin, 800),(xMax, yMin, 800)));
	level.ex_playArea_Length = int(distance((xMin, yMin, 800),(xMin, yMax, 800)));
}

//Method to determine a player's current stance
GetStance(checkjump)
{
	if(isDefined(self.ex_newmodel)) return 0;
	
	if(checkjump && !self isOnGround()) return 3;		// jumping

	if(isDefined(self.ex_spinemarker))
	{
		distance = self.ex_spinemarker.origin[2] - self.origin[2];
		if(distance<18) return 2;			// prone
		else if(distance<43) return 1;	// crouch
		else return 0;							// standing
	}
	else return 0;
}

getMax( a, b, c, d )
{
	if( a > b ) ab = a;
	else ab = b;

	if( c > d ) cd = c;
	else cd = d;

	if( ab > cd ) m = ab;
	else m = cd;

	return m;
}

bounceObject(vRotation, vVelocity, vOffset, angles, radius, falloff, bouncesound, bouncefx, objecttype)
{
	level endon("ex_gameover");
	self endon("disconnect");
	self endon("ex_bounceobject");

	if(!isDefined(objecttype)) return;

	self thread putinQ(objecttype);

	// Hide until everthing is setup
	self hide();

	// Setup default values
	if(!isDefined(vRotation))	vRotation = (0,0,0);
	pitch = vRotation[0]*0.05;	// Pitch/frame
	yaw = vRotation[1]*0.05;	// Yaw/frame
	roll = vRotation[2]*0.05;		// Roll/frame

	if(!isDefined(vVelocity))	vVelocity = (0,0,0);
	if(!isDefined(vOffset)) vOffset = (0,0,0);
	if(!isDefined(falloff)) falloff = 0.5;

	// Spawn anchor (the object that we will rotate)
	self.bounce_anchor = spawn("script_origin", self.origin );
	self.bounce_anchor.angles = self.angles;

	// Link to anchor
	self linkto( self.bounce_anchor, "", vOffset, angles );
	self show();

	wait .05;	// Let it happen

	if(isDefined(level.ex_gravity)) gravity = level.ex_gravity;
	else gravity = 100;

	// Set gravity
	vGravity = (0,0,-0.02 * gravity);

	stopme = 0;
	notrace = 5;	// Avoid bullettrace for the first number of frames

	// Drop with gravity
	for(;;)
	{
		// Let gravity do, what gravity do best
		vVelocity +=vGravity;

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

		// Get destination origin
		neworigin = self.bounce_anchor.origin + vVelocity;

		// Check for impact, check for entities but not myself.
		if(!notrace)
		{
			trace=bulletTrace(self.bounce_anchor.origin,neworigin,false,undefined); 
			if(trace["fraction"] != 1)	// Hit something
			{
				// Place object at impact point - radius
				distance = distance(self.bounce_anchor.origin,trace["position"]);
				if(distance)
				{
					fraction = (distance - radius) / distance;
					delta = trace["position"] - self.bounce_anchor.origin;
					delta2 = maps\mp\_utility::vectorScale(delta,fraction);
					neworigin = self.bounce_anchor.origin + delta2;
				}
				else neworigin = self.bounce_anchor.origin;

				// Play sound if defined
				if(isDefined(bouncesound)) self.bounce_anchor playSound(bouncesound + trace["surfacetype"]);	

				// Test if we are hitting ground and if it's time to stop bouncing
				if(vVelocity[2] <= 0 && vVelocity[2] > -10) stopme++;
				if(stopme==5)
				{
					stopme=0;
					// Set origin to impactpoint	
					self.bounce_anchor.origin = neworigin;
					wait .05;
					// Delete anchor to save gamestate size
					self unlink();
					self.bounce_anchor delete();
					return;
				}
				// Play effect if defined and it's a hard hit
				if(isDefined(bouncefx) && length(vVelocity) > 20) playfx(bouncefx,trace["position"]);

				// Decrease speed for each bounce.
				vSpeed = length(vVelocity) * falloff;

				// Calculate new direction (Thanks to Hellspawn this is finally done correctly)
				vNormal = trace["normal"];
				vDir = maps\mp\_utility::vectorScale(vectorNormalize( vVelocity ),-1);
				vNewDir = ( maps\mp\_utility::vectorScale(maps\mp\_utility::vectorScale(vNormal,2),vectorDot( vDir, vNormal )) ) - vDir;

				// Scale vector
				vVelocity = maps\mp\_utility::vectorScale(vNewDir, vSpeed);
	
				// Add a small random distortion
				vVelocity += (randomFloat(1)-0.5,randomFloat(1)-0.5,randomFloat(1)-0.5);
			}
		}
		else notrace--;

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

		self.bounce_anchor.origin = neworigin;

		// Rotate pitch
		a0 = self.bounce_anchor.angles[0] + pitch;
		while(a0<0) a0 += 360;
		while(a0>359) a0 -=360;

		// Rotate yaw
		a1 = self.bounce_anchor.angles[1] + yaw;
		while(a1<0) a1 += 360;
		while(a1>359) a1 -=360;

		// Rotate roll
		a2 = self.bounce_anchor.angles[2] + roll;
		while(a2<0) a2 += 360;
		while(a2>359) a2 -=360;
		self.bounce_anchor.angles = (a0,a1,a2);
		
		// Wait one frame
		wait .05;
	}
}

putinQ(type)
{
	if(!isDefined(type)) self notify("ex_bounceobject");
	else
	{
		index = level.ex_objectQcurrent[type];
	
		level.ex_objectQcurrent[type]++;
	
		if(level.ex_objectQcurrent[type] >= level.ex_objectQsize[type]) level.ex_objectQcurrent[type] = 0;
	
		if(isDefined(level.ex_objectQ[type][index]))
		{
			level.ex_objectQ[type][index] notify("ex_bounceobject");
			wait .05; //Let thread die
	
			if(isDefined(level.ex_objectQ[type][index].anchor))
			{
				level.ex_objectQ[type][index] unlink();
				level.ex_objectQ[type][index].anchor delete();
			}
	
			if(isDefined(level.ex_objectQ[type][index])) level.ex_objectQ[type][index] delete();
		}
		
		level.ex_objectQ[type][index] = self;
	}
}

triggerOff()
{
	if(!isDefined (self.realOrigin)) self.realOrigin = self.origin;
	if(self.origin == self.realorigin) self.origin += (0, 0, -10000);
}

triggerOn()
{
	if(isDefined (self.realOrigin)) self.origin = self.realOrigin;
}

error(msg)
{
	println("^c*ERROR* ", msg);
	wait .05;	// waitframe
/#
	if (getcvar("debug") != "1") assertmsg("This is a forced error - attach the log file");
#/
}

add_to_array(array, ent)
{
	if(!isDefined(ent)) return array;
		
	if(!isDefined(array)) array[0] = ent;
	else array[array.size] = ent;
	
	return array;	
}

exploder(num)
{
	num = int(num);
	ents = level._script_exploders;

	for(i = 0; i < ents.size; i++)
	{
		if(!isDefined(ents[i])) continue;

		if(ents[i].script_exploder != num) continue;

		if(isDefined(ents[i].script_fxid)) level thread cannon_effect(ents[i]);

		if(isDefined (ents[i].script_sound)) ents[i] thread exploder_sound();

		if(isDefined(ents[i].targetname))
		{
			if(ents[i].targetname == "exploder") ents[i] thread brush_show();
			else if((ents[i].targetname == "exploderchunk") || (ents[i].targetname == "exploderchunk visible")) ents[i] thread brush_throw();
			else if(!isDefined(ents[i].script_fxid)) ents[i] thread brush_delete();
		}
		else if (!isDefined(ents[i].script_fxid)) ents[i] thread brush_delete();
	}
}

exploder_sound()
{
	if(isDefined(self.script_delay)) wait self.script_delay;
	self playSound(level.scr_sound[self.script_sound]);
}

cannon_effect(source)
{
	if(!isDefined(source.script_delay)) source.script_delay = 0;

	if((isDefined(source.script_delay_min)) && (isDefined(source.script_delay_max))) source.script_delay = source.script_delay_min + randomfloat (source.script_delay_max - source.script_delay_min);

	org = undefined;
	if(isDefined(source.target)) org = (getent(source.target, "targetname")).origin;

	level thread maps\mp\_fx::OneShotfx(source.script_fxid, source.origin, source.script_delay, org);
}

brush_delete()
{
	if(isDefined(self.script_delay)) wait(self.script_delay);
	self delete();
}

brush_show()
{
	if(isDefined(self.script_delay)) wait(self.script_delay);
	self show();
	self solid();
}

brush_throw()
{
	if(isDefined(self.script_delay)) wait(self.script_delay);

	ent = undefined;
	if(isDefined(self.target)) ent = getent(self.target, "targetname");

	if(!isDefined(ent))
	{
		self delete();
		return;
	}

	self show();
	org = ent.origin;
	temp_vec = (org - self.origin);

	x = temp_vec[0];
	y = temp_vec[1];
	z = temp_vec[2];

	self rotateVelocity((x,y,z), 12);
	self moveGravity((x, y, z), 12);

	wait(6);
	self delete();
}

saveModel()
{
	info["model"] = self.model;
	info["viewmodel"] = self getViewModel();

	if(isDefined(self.awe_headmodel)) info["awe_headmodel"] = self.awe_headmodel;
	if(isDefined(self.hatModel)) info["awe_hatmodel"] = self.hatModel;

	attachSize = self getAttachSize();
	info["attach"] = [];
	
	for(i = 0; i < attachSize; i++)
	{
		info["attach"][i]["model"] = self getAttachModelName(i);
		info["attach"][i]["tag"] = self getAttachTagName(i);
		info["attach"][i]["ignoreCollision"] = self getAttachIgnoreCollision(i);
	}
	
	return info;
}

loadModel(info)
{
	self detachAll();
	self setModel(info["model"]);
	self setViewModel(info["viewmodel"]);

	if(isDefined(info["awe_headmodel"])) self.awe_headmodel = info["awe_headmodel"];
	if(isDefined(info["awe_hatmodel"])) self.hatModel = info["awe_hatmodel"];

	attachInfo = info["attach"];
	attachSize = attachInfo.size;
    
	for(i = 0; i < attachSize; i++)
		self attach(attachInfo[i]["model"], attachInfo[i]["tag"], attachInfo[i]["ignoreCollision"]);
}

getPlant()
{
	start = self.origin + (0, 0, 10);

	range = 11;
	forward = anglesToForward(self.angles);
	forward = maps\mp\_utility::vectorScale(forward, range);

	traceorigins[0] = start + forward;
	traceorigins[1] = start;

	trace = bulletTrace(traceorigins[0], (traceorigins[0] + (0, 0, -18)), false, undefined);
	if(trace["fraction"] < 1)
	{
		temp = spawnstruct();
		temp.origin = trace["position"];
		temp.angles = orientToNormal(trace["normal"]);
		return temp;
	}

	trace = bulletTrace(traceorigins[1], (traceorigins[1] + (0, 0, -18)), false, undefined);
	if(trace["fraction"] < 1)
	{
		temp = spawnstruct();
		temp.origin = trace["position"];
		temp.angles = orientToNormal(trace["normal"]);
		return temp;
	}

	traceorigins[2] = start + (16, 16, 0);
	traceorigins[3] = start + (16, -16, 0);
	traceorigins[4] = start + (-16, -16, 0);
	traceorigins[5] = start + (-16, 16, 0);

	besttracefraction = undefined;
	besttraceposition = undefined;
	for(i = 0; i < traceorigins.size; i++)
	{
		trace = bulletTrace(traceorigins[i], (traceorigins[i] + (0, 0, -1000)), false, undefined);

		if(!isDefined(besttracefraction) || (trace["fraction"] < besttracefraction))
		{
			besttracefraction = trace["fraction"];
			besttraceposition = trace["position"];
		}
	}
	
	if(besttracefraction == 1) besttraceposition = self.origin;
	
	temp = spawnstruct();
	temp.origin = besttraceposition;
	temp.angles = orientToNormal(trace["normal"]);
	return temp;
}

orientToNormal(normal)
{
	hor_normal = (normal[0], normal[1], 0);
	hor_length = length(hor_normal);

	if(!hor_length) return (0, 0, 0);
	
	hor_dir = vectornormalize(hor_normal);
	neg_height = normal[2] * -1;
	tangent = (hor_dir[0] * neg_height, hor_dir[1] * neg_height, hor_length);
	plant_angle = vectortoangles(tangent);
	return plant_angle;
}

array_levelthread (ents, process, var, excluders)
{
	exclude = [];
	for (i=0;i<ents.size;i++)
		exclude[i] = false;

	if(isDefined (excluders))
	{
		for (i=0;i<ents.size;i++)
		for (p=0;p<excluders.size;p++)
		if (ents[i] == excluders[p]) exclude[i] = true;
	}

	for(i=0;i<ents.size;i++)
	{
		if(!exclude[i])
		{
			if(isDefined (var)) level thread [[process]](ents[i], var);
			else level thread [[process]](ents[i]);
		}
	}
}

set_ambient (track)
{
	level.ambient = track;
	if((isDefined (level.ambient_track)) && (isDefined (level.ambient_track[track])))
	{
		ambientPlay (level.ambient_track[track], 2);
		println ("playing ambient track ", track);
	}
}

abs (num)
{
	if(num < 0) num*= -1;
	return num;
}

deletePlacedEntity(entity)
{
	entities = getentarray(entity, "classname");
	for(i = 0; i < entities.size; i++) entities[i] delete();
}

hotSpot(position, radius, sMeansOfDeath, sWeapon)
{
	self endon("endhotspot");

	if(!isDefined(radius)) radius = 60; // 5ft

	for(;;)
	{
		players = getentarray("player", "classname");
		for(i = 0; i < players.size; i++)
		{
			if(isPlayer(players[i])) player = players[i];
			else continue;

			if(distance(position, player.origin) > radius) continue;
			else player thread [[level.callbackPlayerDamage]](self, self, 5, 1, sMeansOfDeath, sWeapon, undefined, (0,0,0), "none",0);
		}

		wait 0.5;
	}
}

scriptedfxradiusdamage(eAttacker, vOffset, sMeansOfDeath, sWeapon, iRange, iMaxDamage, iMinDamage, effect, surfacetype, quake, entignore, zignore, special)
{
	level endon("ex_gameover");

	if(!isDefined(vOffset)) vOffset = (0,0,0);
	if(!isDefined(entignore)) entignore = true; // ignore giving damage to non player entities
	if(!isDefined(zignore)) zignore = false;
	if(!isDefined(special)) special = "false";
	
	iDFlags = 1;

	surfacefx = "dirt";

	if(isDefined(effect))
	{
		if(isDefined(surfacetype))
		{
			switch(surfacetype)
			{
				case "beach":
				case "sand":
				surfacefx = "beach";
				break;
		
				case "asphalt":
				case "metal":
				case "rock":
				case "gravel":
				case "plaster":
				case "default":
				surfacefx = "concrete";
				break;
		
				case "mud":
				case "dirt":
				case "grass":
				surfacefx = "dirt";
				break;
		
				case "snow":
				case "ice":
				surfacefx = "snow";
				break;
		
				case "wood":
				case "bark":
				surfacefx = "wood";
				break;
		
				case "water":
				surfacefx = "water";
				break;
			}
		}

		// set to snow fx on wintermaps
		if(level.ex_wintermap) surfacefx = "snow";

		// play the fx
		if(effect == "generic") playfx(level.ex_effect["explosion_" + surfacefx], self.origin);
		else if(special == "false") playfx(level.ex_effect[effect], self.origin);

		// napalm fx
		if(sWeapon == "planebomb_mp" && special == "napalm")
		{
			playfx(level.ex_effect["napalm_bomb"], self.origin);
			playfx(level.ex_effect["bodygroundfire"], self.origin);
		}

		if(level.ex_wintermap && sWeapon == "planebomb_mp") thread sciptedfxdelay("explosion_snow", 1.5, self.origin);
	}
	
	if(quake)
	{
		// * Earthquake *
		peqs = randomInt(100);
		strength = 0.5 + 0.5 * peqs /100;
		length = 1 + 3*peqs/100;;
		range = iRange + iRange * peqs/100;
		earthquake(strength, length, self.origin, range);		
	}
	
	if(iMaxDamage == 0 && iMinDamage == 0) return;
	
	// Loop through players and cause damage
	players = getentarray("player", "classname");
	for(i=0;i<players.size;i++)
	{
		if(isPlayer(players[i]))
		{			
			// Check that player is in range
			distance = distance((self.origin + vOffset), players[i].origin);
			//zdistance = distance((0,0, self.origin[2]), (0,0, players[i].origin[2]));
				
			if(distance >= iRange || players[i].sessionstate != "playing" || !isAlive(players[i])) continue;

			// ignore if above 5' height
			//if(zignore && distance < iRange && zdistance > 60) continue;
			
			// if player is inside, no damage
			//if(!extreme\_ex_utils::isOutside(players[i].origin)) continue;

			if(players[i] != self)
			{
				percent = (iRange - distance) / iRange;
				iDamage = iMinDamage + (iMaxDamage - iMinDamage)*percent;
	
				offset = 0;
				stance = players[i] [[level.ex_getStance]](false);
				switch(stance)
				{
					case 2:	offset = (0,0,5);	break;
					case 1:	offset = (0,0,35);	break;
					case 0:	offset = (0,0,55);	break;
				}
	
				traceorigin = players[i].origin + offset;
	
				trace = bullettrace(self.origin + vOffset, traceorigin, true, self);
	
				if(isDefined(trace["entity"]) && trace["entity"] != players[i]) iDamage = iDamage * .01;	//Was .6// Damage blocked by entity, remove 40%
				else if(!isDefined(trace["entity"])) iDamage = iDamage * .01;	//Was .2// Damage blocked by other stuff(walls etc...), remove 80%

				vDir = vectorNormalize(traceorigin - (self.origin + vOffset));
			}
			else
			{
				iDamage = iMaxDamage;
				vDir = (0,0,1);
			}

			switch(special)
			{
				case "napalm":
				players[i] thread napalmDamage(eAttacker);
				break;
	
				default:
				players[i] thread [[level.callbackPlayerDamage]](self, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, undefined, vDir, "none",0);
				break;
			}
		}
	}

	if(entignore) return;

	// Loop through entities except players and cause damage
	entities = getentarray();
	for(i=0;i<entities.size;i++)
	{
		// Is it defined and not a player?
		if(isDefined(entities[i]) && !isPlayer(entities[i]))
		{
			// Check that entity is in range
			distance = distance((self.origin + vOffset), entities[i].origin);

			if(distance <= iRange)
			{	
				// Calculate damage
				if(entities[i] != self)
				{
					// bullet trace
					traceorigin = entities[i].origin;
					trace = bullettrace(self.origin + vOffset, traceorigin, true, self);
						
					// Nothing blocked the damage
					if(isDefined(trace["entity"]) && trace["entity"] == entities[i])
					{
						// get new distance and new damage position if we hit the entity directly
						pos = trace["position"];
			
						distance = distance((self.origin + vOffset), pos);
							
						// Calculate damage falloff
						percent = (iRange-distance)/iRange;
						iDamage = iMinDamage + (iMaxDamage - iMinDamage)*percent;
			
						// Cause a small radiusdamage
						if(iDamage > 0)
						{
							// Do radius damage at traced point
							if(isDefined(entities[i].health)) oldhealth = entities[i].health;
							radiusDamage(pos, 5, iDamage, iDamage, eAttacker, self);
						}
					}
					else  // Something blocked the damage
					{
						distance = distance((self.origin + vOffset), entities[i].origin);
			
						// Calculate damage falloff
						percent = (iRange-distance)/iRange;
						iDamage = iMinDamage + (iMaxDamage - iMinDamage)*percent;
			
						if(isDefined(trace["entity"])) iDamage = iDamage * .6;				// Damage blocked by entity, remove 40%
						else iDamage = iDamage * .2;													// Damage blocked by other stuff(walls etc...), remove 80%
			
						// Cause a small radiusdamage
						if(iDamage > 0) radiusDamage(entities[i].origin, 5, iDamage, iDamage, eAttacker, self);
					}
				}
			}
		}
	}
}

sciptedfxdelay(effect, delay, pos)
{
	wait delay;
	playfx(level.ex_effect[effect], pos);
}

lowercase(str)
{
	return(convertChar(str, "U-L" ));
}

uppercase(str)
{
	return(convertChar(str, "L-U" ));
}

convertChar(str, conv)
{
	if(!isDefined(str) || str == "") return "";

	switch ( conv )
	{
		case "U-L":	case "U-l":	case "u-L":	case "u-l":
		from = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
		to   = "abcdefghijklmnopqrstuvwxyz";
		break;

		case "L-U":	case "L-u":	case "l-U":	case "l-u":
		from = "abcdefghijklmnopqrstuvwxyz";
		to   = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
		break;

		default:
		return str;
	}

	string = "";

	for(i = 0; i < str.size; i++)
	{
		chr = str[i];

		for(j = 0; j < from.size; j++)
		{
			if(chr == from[j])
			{
				chr = to[j];
				break;
			}
		}

		string += chr;
	}

	return string;
}

justalphabet(str)
{
	if(!isDefined(str) || str == "") return "";

	uppercase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
	lowercase   = "abcdefghijklmnopqrstuvwxyz";

	string = "";
	
	for(i = 0; i < str.size; i++)
	{
		chr = str[i];

		for(j = 0; j < uppercase.size; j++)
		{
			if(chr == uppercase[j]) string += uppercase[j];
			else if(chr == lowercase[j]) string += lowercase[j];
		}
	}

	return string;
}

friendlyInRange(range)
{
	if(!range) return true;

	// Get all players and pick out the ones that are playing and are in the same team
	players = [];
	aplayers = getentarray("player", "classname");
	for(i = 0; i < aplayers.size; i++)
	{
		if(isDefined(aplayers[i]) && aplayers[i].sessionstate == "playing" && aplayers[i].pers["team"] == self.pers["team"])
				players[players.size] = aplayers[i];
	}

	// Get the players that are in range
	sortedplayers = sortByDist(players, self);

	// Need at least 2 players (myself + one team mate)
	if(sortedplayers.size<2) return false;

	// First player will be myself so check against second player
	distance = distance(self.origin, sortedplayers[1].origin);

	if(distance <= range) return true;
	else return false;
}

friendlyInRangeView(range)
{
	if(!range) return false;

	targetpos = self getTargetPos(range);

	if(!isDefined(targetpos)) return false;

	// Get all players and pick out the ones that are playing and are in the same team
	players = getentarray("player", "classname");
	for(i = 0; i < players.size; i++)
	{
		if(isPlayer(self) && isPlayer(players[i]))
		{
			if(players[i].sessionstate == "playing" && players[i].pers["team"] == self.pers["team"])
			{
				if(distance(targetPos, players[i].origin) <= range * 4)
					return players[i];
			}
		}
	}

	return false;
}

enemyInRange(range)
{
	if(!range) return true;

	// Get all players and pick out the ones that are playing and are in the same team
	players = [];
	aplayers = getentarray("player", "classname");
	for(i = 0; i < aplayers.size; i++)
	{
		if(isDefined(aplayers[i]))
			if(aplayers[i].sessionstate == "playing" && aplayers[i].pers["team"] != self.pers["team"])
				players[players.size] = aplayers[i];
	}

	// Get the players that are in range
	sortedplayers = sortByDist(players, self);

	// Need at least 2 players (myself + one team mate)
	if(sortedplayers.size<2) return false;

	// First player will be myself so check against second player
	distance = distance(self.origin, sortedplayers[1].origin);

	self.ex_targetwarn = sortedplayers[1];

	if(distance <= range) return true;
	else return false;
}

enemyInRangeView(range)
{
	if(!range) return false;

	targetpos = self getTargetPos(range);

	if(!isDefined(targetpos)) return false;

	// Get all players and pick out the ones that are playing and are in the same team
	players = getentarray("player", "classname");
	for(i = 0; i < players.size; i++)
	{
		if(isPlayer(self) && isPlayer(players[i]))
		{
			if(players[i].sessionstate == "playing" && players[i].pers["team"] != self.pers["team"])
			{
				if(distance(targetPos, players[i].origin) <= range * 4)
					return players[i];
			}
		}
	}

	return false;
}

getTargetPos(range)
{
	startOrigin = self.origin;
	forward = anglesToForward( self getplayerangles() );
	forward = [[level.ex_vectorscale]]( forward, range * 5);
	endOrigin = startOrigin + forward;
	return endOrigin;
}

sortByDist(points, startpoint, maxdist, mindist)
{
	thread extreme\_ex_logfile::Log("sortByDist");

	if(!isDefined(points)) return undefined;
	if(!isDefined(startpoint)) return undefined;

	if(!isDefined(mindist)) mindist = -1000000;
	if(!isDefined(maxdist)) maxdist = 1000000; // almost 16 miles, should cover everything.

	sortedpoints = [];

	max = points.size-1;
	for(i = 0; i < max; i++)
	{
		nextdist = 1000000;
		next = undefined;

		for(j = 0; j < points.size; j++)
		{
			thisdist = distance(startpoint.origin, points[j].origin);
			if(thisdist <= nextdist && thisdist <= maxdist && thisdist >= mindist)
			{
				next = j;
				nextdist = thisdist;
			}
		}

		// didn't find one that fit the range, stop trying
		if(!isDefined(next)) break;

		sortedpoints[i] = points[next];

		// shorten the list, fewer compares
		points[next] = points[points.size-1]; // replace the closest point with the end of the list
		points[points.size-1] = undefined; // cut off the end of the list
	}

	sortedpoints[sortedpoints.size] = points[0]; // the last point in the list

	return sortedpoints;
}

printOnPlayersInRange(owner, msg1, msg2, targetpos)
{
	players = getentarray("player", "classname");
	for(i = 0; i < players.size; i++)
	{
		player = players[i];
		
		if(player != owner && player.pers["team"] == owner.pers["team"] && isalive(player)) 
		{
			dist = distance( player.origin, targetpos );
				
			// only play the warning if they are close to the strike area
			if(dist < 1000)
			{
				player iprintlnbold(msg1, owner);
				player iprintlnbold(msg2);
			}
		}
	}
}

// Strip blanks at start and end of string
strip(str)
{
	if(str=="") return "";

	str2="";
	str3="";

	i=0;
	while(i<str.size && str[i]==" ") i++;

	// String is just blanks?
	if(i==str.size) return "";
	
	for(;i<str.size;i++) str2 += str[i];

	i=str2.size-1;

	while(str2[i]==" " && i>0) i--;

	for(j=0;j<=i;j++) str3 += str2[j];
		
	return str3;
}

explode(str,delimiter)
{
	j=0;
	temp_array[j] = "";	

	for(i=0;i<str.size;i++)
	{
		if(str[i]==delimiter)
		{
			j++;
			temp_array[j] = "";
		}
		else temp_array[j] += str[i];
	}

	return temp_array;
}

convertMUJ(string)
{
	string = monotone(string);
	string = uppercase(string);
	string = justalphabet(string);
	return string;
}

weaponPause(time)
{
	level endon("ex_gameover");
	self endon("disconnect");

	self disableWeapon();
	wait time;
	if(isPlayer(self))
	{
		if(isDefined(self.ex_planting) || isDefined(self.ex_defusing) || self.ex_plantwire || self.ex_defusewire) return;
		else self enableWeapon();
	}
}

createBarGraphic(barsize,bartime)
{
	level endon("ex_gameover");
	self endon("disconnect");

	cleanBarGraphic();
	
	// Background
	self.ex_pbbgrd = newClientHudElem(self);
	self.ex_pbbgrd.alignX = "center";
	self.ex_pbbgrd.alignY = "middle";
	self.ex_pbbgrd.x = 320;
	self.ex_pbbgrd.y = 410;
	self.ex_pbbgrd.alpha = 0.5;
	self.ex_pbbgrd.color = (0,0,0);
	self.ex_pbbgrd setShader("white", (barsize + 4), 12);

	self.ex_pb = newClientHudElem(self);				
	self.ex_pb.alignX = "left";
	self.ex_pb.alignY = "middle";
	self.ex_pb.x = (320 - (barsize / 2.0));
	self.ex_pb.y = 410;
	self.ex_pb setShader("white", 0, 8);
	self.ex_pb scaleOverTime(bartime , barsize, 8);
}

cleanBarGraphic()
{
	if(isDefined(self.ex_pbbgrd)) self.ex_pbbgrd destroy();
	if(isDefined(self.ex_pb)) self.ex_pb destroy();
}

disableMinefields()
{
	minefields = getentarray( "minefield", "targetname" );

	if(minefields.size)
		for(i=0;i< minefields.size;i++)
			if(isDefined(minefields[i]))
				minefields[i] delete();
}

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

	while(!level.ex_gamepover)
	{
		entities = getentarray();
		for(i = 0; i < entities.size; i++)
		{
			if(isDefined(entities[i].classname)) logPrint(entities[i].classname + " = classname");
			if(isDefined(entities[i].name)) logPrint(entities[i].name + " = name");
			
			logprint("\n");
			wait 0.05;
		}
	
		wait 1;
	}
}

napalmDamage(eAttacker)
{
	level endon("ex_gameover");
	self endon("ex_dead");

	// burn them c/w damage
	if(isPlayer(self)) self extreme\_ex_punishments::doTorch(self.origin, true, eAttacker);

	// play flame on dead body & make sure they die!
	if(isPlayer(self))
	{
		playfx(level.ex_effect["bodygroundfire"], self.origin);
		self thread [[level.callbackPlayerDamage]](eAttacker, eAttacker, 1000, 1, "MOD_PROJECTILE", "planebomb_mp", undefined, (0,0,1), "none",0);
	}
}

/*
isADS()
{
	res = false;
	stance = GetStance(true);

	if(isdefined(self.ex_eyemarker) && isdefined(self.ex_thumbmarker))
	{
		forward = anglesToForward( self getplayerangles() );
		forwardN = (-1 * forward[1], forward[0], 0);

		distance = getNormalDistFromArea(self.ex_eyemarker.origin, normalVector(forward, forwardN), self.ex_thumbmarker.origin);
		distanceP = getNormalDistFromArea(self.ex_eyemarker.origin, normalVector(forward, forwardN), self.ex_elbowmarker.origin);

		switch(stance)
		{
			case 0: // standing
			if(extreme\_ex_weapons::isWeaponType(self getcurrentweapon(),"pistol"))
			{
				if(5 < distanceP && distanceP < 11.5) res = true; // standing, hip range: 10.8 < distanceP < 15
				else if (0 < distance && distance < 9) res = true; // standing, hip range: 16 < distance < 21
			}
			break;

			case 1: // crouching
			if(extreme\_ex_weapons::isWeaponType(self getcurrentweapon(),"pistol"))
			{
				if(4 < distanceP && distanceP < 11) res = true; // crouching, hip range: 12 < distanceP < 17
				else if (0 < distance && distance < 10) res = true; // crouching, hip range: 12 < distance < 17
			}
			break;

			case 2: // prone
			res = true; // in this position it's not predictible so set to true
			break;

			default:  // jumping and not def
			if(0 < distance && distance < 10) res = true; // jumping is like crouching
			break;
		}
	}

	return res;
}

normalVector(vec1, vec2)
{
	x = vec1[1] * vec2[2] - vec1[2] * vec2[1];
	y = -1 * (vec1[0] * vec2[2] - vec1[2] * vec2[0]);
	z = vec1[0] * vec2[1] - vec1[1] * vec2[0];
	nvec = (x,y,z);
	return nvec;
}

getNormalDistFromArea(eyes, areaNormVec, pointQ)
{
	eyesQ = (pointQ[0] - eyes[0], pointQ[1] - eyes[1], pointQ[2] - eyes[2]);
	scalar = eyesQ[0] * areaNormVec[0] + eyesQ[1] * areaNormVec[1] + eyesQ[2] * areaNormVec[2];
	dNormVec = length(areaNormVec); 
	distance = maps\mp\_utility::abs(scalar / dNormVec);

	return distance;
}
*/

rangef()
{
  self endon("disconnect");
  self endon("death");
  self endon("ex_dead");

  trace = [];
  endOrigin = [];
  startOrigin = [];
  ground = [];

  countt = 0;
  while(isAlive(self))
  {
    wait .5;
    startOrigin = self.ex_eyemarker.origin;
    forward = anglesToForward(self getplayerangles());
    forward = [[level.ex_vectorscale]](forward, 100000);

    if(isdefined(startOrigin) && isdefined(forward))
      endOrigin = startOrigin + forward;

    if(isdefined(self.ex_eyemarker.origin) && isdefined(endorigin))
    {
      if(self.ex_binocuse || (self useButtonPressed() && (self playerads() == 1) && extreme\_ex_weapons::isWeaponType(self getcurrentweapon(),"sniper")))
      {
        rangedist = undefined;
        trace = bulletTrace(startOrigin, endOrigin, true, self);
        range = int(distance(startOrigin, trace["position"]));

        if(isDefined(range))
        {
          if(level.ex_rfrange == 1)
            rangedist = int(range * 0.02778); // Range in Yards
          else
            rangedist = int(range * 0.0254);  // Range in Metres
        }

        if(!isDefined(self.ex_rangehud))
        {
          self.ex_rangehud = newClientHudElem(self);
          self.ex_rangehud.x = 320;
          self.ex_rangehud.y = 245;
          self.ex_rangehud.alignx = "center";
          self.ex_rangehud.aligny = "middle";
          self.ex_rangehud.alpha =0.8;
          self.ex_rangehud.fontScale = 1;
        }

        if(level.ex_rfrange == 1)
        {
          self.ex_rangehud.label = &"MISC_RANGE";
          self.ex_rangehud setvalue(rangedist);
        }
        else
        {
          self.ex_rangehud.label = &"MISC_RANGE2";
          self.ex_rangehud setvalue(rangedist);
        }
      }
      else
        if(isDefined(self.ex_rangehud)) self.ex_rangehud destroy();
    }
  }
}
