L4D2 Decrypted mutations

From Valve Developer Community
Jump to: navigation, search

Decrypted L4D2 Mutation Scripts

These are all official L4D2 mutation scripts and their included scripts. The scripts have been decrypted on February 26th 2021 with L4D2 VScript Editor Beta 0.5 by Cynick and may be updated at any time.


Valve Mutations

L4d1 - Left 4 Dead 1 Coop

//============ Copyright (c) Valve Corporation, All rights reserved. ==========
//
//
//=============================================================================
Msg("Activating Mutation L4D1\n");

if ( !IsModelPrecached( "models/infected/smoker_l4d1.mdl" ) )
	PrecacheModel( "models/infected/smoker_l4d1.mdl" );
if ( !IsModelPrecached( "models/infected/boomer_l4d1.mdl" ) )
	PrecacheModel( "models/infected/boomer_l4d1.mdl" );
if ( !IsModelPrecached( "models/infected/hunter_l4d1.mdl" ) )
	PrecacheModel( "models/infected/hunter_l4d1.mdl" );
if ( !IsModelPrecached( "models/infected/hulk_l4d1.mdl" ) )
	PrecacheModel( "models/infected/hulk_l4d1.mdl" );

DirectorOptions <-
{
	ActiveChallenge = 1

	SpitterLimit = 0
	JockeyLimit = 0
	ChargerLimit = 0

	EscapeSpawnTanks = true

	weaponsToConvert =
	{
		weapon_shotgun_spas				= "weapon_autoshotgun_spawn"
		weapon_defibrillator			= "weapon_first_aid_kit_spawn"
		weapon_ammo_pack				= "weapon_first_aid_kit_spawn"
		weapon_sniper_awp				= "weapon_hunting_rifle_spawn"
		weapon_sniper_military			= "weapon_hunting_rifle_spawn"
		weapon_sniper_scout				= "weapon_hunting_rifle_spawn"
		weapon_vomitjar					= "weapon_molotov_spawn"
		weapon_adrenaline				= "weapon_pain_pills_spawn"
		weapon_pistol_magnum			= "weapon_pistol_spawn"
		weapon_shotgun_chrome			= "weapon_pumpshotgun_spawn"
		weapon_rifle_ak47				= "weapon_rifle_spawn"
		weapon_rifle_desert				= "weapon_rifle_spawn"
		weapon_rifle_sg552				= "weapon_rifle_spawn"
		weapon_smg_mp5					= "weapon_smg_spawn"
		weapon_smg_silenced				= "weapon_smg_spawn"
	}

	function ConvertWeaponSpawn( classname )
	{
		if ( classname in weaponsToConvert )
		{
			return weaponsToConvert[classname];
		}
		return 0;
	}

	weaponsToRemove =
	{
		weapon_rifle_m60 = 0
		weapon_grenade_launcher = 0
		weapon_chainsaw = 0
		weapon_melee = 0
		weapon_upgradepack_explosive = 0
		weapon_upgradepack_incendiary = 0
		upgrade_item = 0
	}

	function AllowWeaponSpawn( classname )
	{
		if ( classname in weaponsToRemove )
		{
			return false;
		}
		return true;
	}

	function ShouldAvoidItem( classname )
	{
		if ( classname in weaponsToRemove )
		{
			return true;
		}
		return false;
	}
}

function OnGameEvent_round_start_post_nav( params )
{
	EntFire( "worldspawn", "AddOutput", "timeofday 0" );
	EntFire( "weapon_*", "AddOutput", "skin 0" );
	EntFire( "weapon_*", "AddOutput", "weaponskin -1" );
	EntFire( "trigger_upgrade_laser_sight", "Kill" );
	
	foreach( wep, val in DirectorOptions.weaponsToRemove )
		EntFire( wep + "_spawn", "Kill" );
	foreach( wep, val in DirectorOptions.weaponsToConvert )
	{
		local wep_spawner = null;
		while ( wep_spawner = Entities.FindByClassname( wep_spawner, wep + "_spawn" ) )
		{
			if ( wep_spawner.IsValid() )
			{
				local spawnTable =
				{
					origin = wep_spawner.GetOrigin(),
					angles = wep_spawner.GetAngles().ToKVString(),
					targetname = wep_spawner.GetName(),
					count = NetProps.GetPropInt( wep_spawner, "m_itemCount" ),
					spawnflags = NetProps.GetPropInt( wep_spawner, "m_spawnflags" )
				}
				wep_spawner.Kill();
				SpawnEntityFromTable(val, spawnTable);
			}
		}
	}
	
	if ( SessionState.ModeName == "l4d1coop" || SessionState.ModeName == "l4d1vs" )
	{
		if ( SessionState.MapName == "c8m5_rooftop" || SessionState.MapName == "c9m2_lots" || SessionState.MapName == "c10m5_houseboat"
			|| SessionState.MapName == "c11m5_runway" || SessionState.MapName == "c12m5_cornfield" )
		{
			local finale = Entities.FindByClassname( null, "trigger_finale" );
			if ( finale )
				NetProps.SetPropInt( finale, "m_type", 0 );
		}
		else if ( SessionState.MapName == "c10m4_mainstreet" )
		{
			local relay = Entities.FindByName( null, "forklift_relay" );
			if ( relay )
			{
				EntityOutputs.RemoveOutput( relay, "OnTrigger", "director", "BeginScript", "c10m4_onslaught" );
				EntityOutputs.AddOutput( relay, "OnTrigger", "director", "ForcePanicEvent", "", 9.0, -1 );
			}
			EntFire( "onslaught1", "Kill" );
		}
		else if ( SessionState.MapName == "c11m4_terminal" )
		{
			local van = Entities.FindByName( null, "van_button" );
			if ( van )
			{
				EntityOutputs.RemoveOutput( van, "OnPressed", "@director", "", "" );
				EntityOutputs.AddOutput( van, "OnPressed", "@director", "ForcePanicEvent", "", 3.0, -1 );
			}
			local relay = Entities.FindByName( null, "alarm_on_relay" );
			if ( relay )
			{
				EntityOutputs.RemoveOutput( relay, "OnTrigger", "@director", "", "" );
				EntityOutputs.RemoveOutput( relay, "OnTrigger", "alarm_safety_relay", "", "" );
				EntityOutputs.AddOutput( relay, "OnTrigger", "@director", "ForcePanicEvent", "", 0.0, -1 );
				EntityOutputs.AddOutput( relay, "OnTrigger", "alarm_off_relay", "Trigger", "", 15.0, -1 );
			}
			EntFire( "van_follow_trigger", "Kill" );
			EntFire( "van_endscript_relay", "Kill" );
		}
		else if ( SessionState.MapName == "c12m3_bridge" )
		{
			local relay = Entities.FindByName( null, "train_engine_relay" );
			if ( relay )
			{
				EntityOutputs.RemoveOutput( relay, "OnTrigger", "director", "BeginScript", "c12m3_onslaught" );
				EntityOutputs.AddOutput( relay, "OnTrigger", "director", "ForcePanicEvent", "", 2.0, -1 );
				EntFire( "zombie_spawn1", "Kill" );
			}
		}
		else if ( SessionState.MapName == "c12m4_barn" )
			EntFire( "window_trigger", "Kill" );
	}
}

function OnGameEvent_player_spawn( params )
{
	local player = GetPlayerFromUserID( params["userid"] );
	
	if ( ( !player ) || ( player.IsSurvivor() ) )
		return;
	
	local modelName = player.GetModelName();
	if ( ( modelName.find( "l4d1" ) != null ) || ( modelName == "models/infected/hulk_dlc3.mdl" ) )
		return;
	
	switch( player.GetZombieType() )
	{
		case 1:
		{
			player.SetModel( "models/infected/smoker_l4d1.mdl" );
			break;
		}
		case 2:
		{
			player.SetModel( "models/infected/boomer_l4d1.mdl" );
			break;
		}
		case 3:
		{
			player.SetModel( "models/infected/hunter_l4d1.mdl" );
			break;
		}
		case 8:
		{
			player.SetModel( "models/infected/hulk_l4d1.mdl" );
			break;
		}
		default:
			break;
	}
}

function OnGameEvent_player_death( params )
{
	if ( !("userid" in params) )
		return;
	
	local victim = GetPlayerFromUserID( params["userid"] );
	
	if ( ( !victim ) || ( !victim.IsSurvivor() ) )
		return;
	
	EntFire( "survivor_death_model", "BecomeRagdoll" );
}

L4d1vs - Left 4 Dead 1 Versus

//============ Copyright (c) Valve Corporation, All rights reserved. ==========
//
//
//=============================================================================

IncludeScript( "l4d1" )

L4d1survival - Left 4 Dead 1 Survival

//============ Copyright (c) Valve Corporation, All rights reserved. ==========
//
//
//=============================================================================

IncludeScript( "l4d1" )

Mutation1 - Last Man On Earth

//-----------------------------------------------------
Msg("Activating Mutation 1\n");


DirectorOptions <-
{
	ActiveChallenge = 1

	cm_NoSurvivorBots = 1
	cm_CommonLimit = 0
	cm_DominatorLimit = 1
	cm_MaxSpecials = 2
	cm_SpecialRespawnInterval = 60
	cm_AutoReviveFromSpecialIncap = 1
	cm_AllowPillConversion = 0

	BoomerLimit = 0
	MobMaxPending = 0
	SurvivorMaxIncapacitatedCount = 1
	SpecialInitialSpawnDelayMin = 5
	SpecialInitialSpawnDelayMax = 30
	TankHitDamageModifierCoop = 0.5
	
	// convert items that aren't useful
	weaponsToConvert =
	{
		weapon_pipe_bomb =	"weapon_molotov_spawn"
		weapon_vomitjar =	"weapon_molotov_spawn"
		weapon_defibrillator =	"weapon_first_aid_kit_spawn"

		weapon_smg =		"weapon_rifle_spawn"
		weapon_pumpshotgun =	"weapon_autoshotgun_spawn"
		weapon_smg_silenced =	"weapon_rifle_desert_spawn"
		weapon_shotgun_chrome =	"weapon_shotgun_spas_spawn"
		weapon_smg_mp5 =	"weapon_rifle_sg552_spawn"
	}

	function ConvertWeaponSpawn( classname )
	{
		if ( classname in weaponsToConvert )
		{
			return weaponsToConvert[classname];
		}
		return 0;
	}	
}

function OnGameEvent_round_start_post_nav( params )
{
	local spawner = null;
	while ( spawner = Entities.FindByClassname( spawner, "info_zombie_spawn" ) )
	{
		if ( spawner.IsValid() )
		{
			local population = NetProps.GetPropString( spawner, "m_szPopulation" );
			
			if ( population == "boomer" || population == "hunter" || population == "smoker" || population == "jockey"
				|| population == "charger" || population == "spitter" || population == "new_special" || population == "church"
					|| population == "tank" || population == "witch" || population == "witch_bride" || population == "river_docks_trap" )
				continue;
			else
				spawner.Kill();
		}
	}
	
	if ( Director.GetMapName() == "c5m5_bridge" || Director.GetMapName() == "c6m3_port" )
		DirectorOptions.cm_MaxSpecials = 0;
	
	foreach( wep, val in DirectorOptions.weaponsToConvert )
	{
		local wep_spawner = null;
		while ( wep_spawner = Entities.FindByClassname( wep_spawner, wep + "_spawn" ) )
		{
			if ( wep_spawner.IsValid() )
			{
				local spawnTable =
				{
					origin = wep_spawner.GetOrigin(),
					angles = wep_spawner.GetAngles().ToKVString(),
					targetname = wep_spawner.GetName(),
					count = NetProps.GetPropInt( wep_spawner, "m_itemCount" ),
					spawnflags = NetProps.GetPropInt( wep_spawner, "m_spawnflags" )
				}
				wep_spawner.Kill();
				SpawnEntityFromTable(val, spawnTable);
			}
		}
	}
}

function OnGameEvent_finale_start( params )
{
	if ( Director.GetMapName() == "c6m3_port" )
		DirectorOptions.cm_MaxSpecials = 2;
}

function OnGameEvent_gauntlet_finale_start( params )
{
	if ( Director.GetMapName() == "c5m5_bridge" )
		DirectorOptions.cm_MaxSpecials = 2;
}

function Update()
{
	if ( Director.GetCommonInfectedCount() > 0 )
	{
		local infected = null;
		while ( infected = Entities.FindByClassname( infected, "infected" ) )
		{
			if ( infected.IsValid() )
				infected.Kill();
		}
	}
}

Mutation2 - Headshot!

//-----------------------------------------------------
Msg("Activating Mutation 2\n");


DirectorOptions <-
{
	ActiveChallenge = 1

	cm_HeadshotOnly = 1
}

Mutation3 - Bleed Out

//-----------------------------------------------------
Msg("Activating Mutation 3\n");


DirectorOptions <-
{
	ActiveChallenge = 1

	weaponsToConvert =
	{
		weapon_first_aid_kit = "weapon_pain_pills_spawn"
	}

	function ConvertWeaponSpawn( classname )
	{
		if ( classname in weaponsToConvert )
		{
			return weaponsToConvert[classname];
		}
		return 0;
	}	

	// Challenge vars
	cm_TempHealthOnly = 1
	cm_AllowPillConversion = 0
	cm_ShouldHurry = 1
	cm_MaxSpecials = 4
	cm_SpecialRespawnInterval = 45
	cm_WanderingZombieDensityModifier = 0.003

	// Global vars
	MobSpawnMinTime = 5
	MobSpawnMaxTime = 10
	MobMinSize = 10
	MobMaxSize = 15
	MobMaxPending = 30
	SustainPeakMinTime = 10
	SustainPeakMaxTime = 15
	IntensityRelaxThreshold = 0.95
	RelaxMinInterval = 3
	RelaxMaxInterval = 5
	RelaxMaxFlowTravel = 50
	PreferredMobDirection = SPAWN_BEHIND_SURVIVORS

	TempHealthDecayRate = 0.001
	function RecalculateHealthDecay()
	{
		if ( Director.HasAnySurvivorLeftSafeArea() )
		{
			TempHealthDecayRate = 0.27 // pain_pills_decay_rate default
		}
	}
}

function Update()
{
	DirectorOptions.RecalculateHealthDecay();
}

Mutation4 - Hard Eight

//-----------------------------------------------------
Msg("Activating Mutation 4\n");


DirectorOptions <-
{
	ActiveChallenge = 1

	cm_SpecialRespawnInterval = 15
	cm_MaxSpecials = 8
	cm_BaseSpecialLimit = 2

	DominatorLimit = 8
}

Mutation5 - Four Swordsmen

//-----------------------------------------------------
Msg("Activating Mutation 5\n");


DirectorOptions <-
{
	ActiveChallenge = 1

	cm_CommonLimit = 0
	cm_DominatorLimit = 8
	cm_MaxSpecials = 8
	cm_SpecialRespawnInterval = 15

	SpecialInitialSpawnDelayMin = 5
	SpecialInitialSpawnDelayMax = 30
	
	// convert items that aren't useful
	weaponsToConvert =
	{
		weapon_pipe_bomb =	"weapon_molotov_spawn"
		weapon_vomitjar =	"weapon_molotov_spawn"
		weapon_defibrillator =	"weapon_first_aid_kit_spawn"
	}

	function ConvertWeaponSpawn( classname )
	{
		if ( classname in weaponsToConvert )
		{
			return weaponsToConvert[classname];
		}
		return 0;
	}	

	weaponsToRemove =
	{
		weapon_pistol = 0
		weapon_pistol_magnum = 0
		weapon_smg = 0
		weapon_pumpshotgun = 0
		weapon_autoshotgun = 0
		weapon_rifle = 0
		weapon_hunting_rifle = 0
		weapon_smg_silenced = 0
		weapon_shotgun_chrome = 0
		weapon_rifle_desert = 0
		weapon_sniper_military = 0
		weapon_shotgun_spas = 0
		weapon_grenade_launcher = 0
		weapon_rifle_ak47 = 0
		weapon_smg_mp5 = 0
		weapon_rifle_sg552 = 0
		weapon_sniper_awp = 0
		weapon_sniper_scout = 0
		weapon_rifle_m60 = 0
		weapon_melee = 0
		weapon_chainsaw = 0
		weapon_upgradepack_incendiary = 0
		weapon_upgradepack_explosive = 0
		ammo = 0
		upgrade_item = 0
	}

	function AllowWeaponSpawn( classname )
	{
		if ( classname in weaponsToRemove )
		{
			return false;
		}
		return true;
	}

	function ShouldAvoidItem( classname )
	{
		if ( ( classname != "weapon_melee" ) && ( classname in weaponsToRemove ) )
		{
			return true;
		}
		return false;
	}

	DefaultItems =
	[
		"katana",
	]

	function GetDefaultItem( idx )
	{
		if ( idx < DefaultItems.len() )
		{
			return DefaultItems[idx];
		}
		return 0;
	}
}

function OnGameEvent_round_start_post_nav( params )
{
	local spawner = null;
	while ( spawner = Entities.FindByClassname( spawner, "info_zombie_spawn" ) )
	{
		if ( spawner.IsValid() )
		{
			local population = NetProps.GetPropString( spawner, "m_szPopulation" );
			
			if ( population == "boomer" || population == "hunter" || population == "smoker" || population == "jockey"
				|| population == "charger" || population == "spitter" || population == "new_special" || population == "church"
					|| population == "tank" || population == "witch" || population == "witch_bride" || population == "river_docks_trap" )
				continue;
			else
				spawner.Kill();
		}
	}
	
	if ( Director.GetMapName() == "c5m5_bridge" || Director.GetMapName() == "c6m3_port" )
		DirectorOptions.cm_MaxSpecials = 0;
	
	EntFire( "weapon_spawn", "Kill" );
	foreach( wep, val in DirectorOptions.weaponsToRemove )
		EntFire( wep + "_spawn", "Kill" );
}

function OnGameEvent_finale_start( params )
{
	if ( Director.GetMapName() == "c6m3_port" )
		DirectorOptions.cm_MaxSpecials = 8;
}

function OnGameEvent_gauntlet_finale_start( params )
{
	if ( Director.GetMapName() == "c5m5_bridge" )
		DirectorOptions.cm_MaxSpecials = 8;
}

function Update()
{
	if ( Director.GetCommonInfectedCount() > 0 )
	{
		local infected = null;
		while ( infected = Entities.FindByClassname( infected, "infected" ) )
		{
			if ( infected.IsValid() )
				infected.Kill();
		}
	}
}

Mutation7 - Chainsaw Massacre

//-----------------------------------------------------
Msg("Activating Mutation 7\n");


DirectorOptions <-
{
	ActiveChallenge = 1

	cm_InfiniteFuel = 1
	cm_ShouldHurry = 1

	weaponsToRemove =
	{
		weapon_pistol = 0
		weapon_pistol_magnum = 0
		weapon_smg = 0
		weapon_pumpshotgun = 0
		weapon_autoshotgun = 0
		weapon_rifle = 0
		weapon_hunting_rifle = 0
		weapon_smg_silenced = 0
		weapon_shotgun_chrome = 0
		weapon_rifle_desert = 0
		weapon_sniper_military = 0
		weapon_shotgun_spas = 0
		weapon_grenade_launcher = 0
		weapon_rifle_ak47 = 0
		weapon_smg_mp5 = 0
		weapon_rifle_sg552 = 0
		weapon_sniper_awp = 0
		weapon_sniper_scout = 0
		weapon_rifle_m60 = 0
		weapon_melee = 0
		weapon_chainsaw = 0
		weapon_upgradepack_incendiary = 0
		weapon_upgradepack_explosive = 0
		ammo = 0
		upgrade_item = 0
	}

	function AllowWeaponSpawn( classname )
	{
		if ( classname in weaponsToRemove )
		{
			return false;
		}
		return true;
	}

	function ShouldAvoidItem( classname )
	{
		if ( ( classname != "weapon_chainsaw" ) && ( classname in weaponsToRemove ) )
		{
			return true;
		}
		return false;
	}

	DefaultItems =
	[
		"weapon_chainsaw",
		"weapon_pistol",
	]

	function GetDefaultItem( idx )
	{
		if ( idx < DefaultItems.len() )
		{
			return DefaultItems[idx];
		}
		return 0;
	}
}

function OnGameEvent_round_start_post_nav( params )
{
	EntFire( "weapon_spawn", "Kill" );
	foreach( wep, val in DirectorOptions.weaponsToRemove )
		EntFire( wep + "_spawn", "Kill" );
}

Mutation8 - Ironman

//-----------------------------------------------------
Msg("Activating Mutation 8\n");


DirectorOptions <- 
{
	ActiveChallenge = 1

	cm_AllowSurvivorRescue = 0

	weaponsToRemove =
	{
		ammo = 0
	}

	function AllowWeaponSpawn( classname )
	{
		if ( classname in weaponsToRemove )
		{
			return false;
		}
		return true;
	}	
}

Mutation9 - Last Gnome On Earth

//-----------------------------------------------------
Msg("Activating Mutation 9\n");


DirectorOptions <- 
{
	ActiveChallenge = 1

	cm_VIPTarget = 1
	cm_ShouldHurry = 1
}

Mutation10 - Room For One

//-----------------------------------------------------
Msg("Activating Mutation 10\n");


DirectorOptions <-
{
	ActiveChallenge = 1

	cm_FirstManOut = 1
}

Mutation11 - Healthpackalypse!

//-----------------------------------------------------
Msg("Activating Mutation 11\n");

DirectorOptions <-
{
	ActiveChallenge = 1

	weaponsToRemove =
	{
		weapon_first_aid_kit = 0
		weapon_pain_pills = 0
		weapon_adrenaline = 0
		weapon_defibrillator = 0
	}

	function AllowWeaponSpawn( classname )
	{
		if ( classname in weaponsToRemove )
		{
			return false;
		}
		return true;
	}		
}

Mutation12 - Realism Versus

//-----------------------------------------------------
Msg("Activating Mutation 12\n");


DirectorOptions <-
{
	ActiveChallenge = 1

	cm_AllowPillConversion = 0

	ZombieGhostDelayMin = 15
	ZombieGhostDelayMax = 25

	weaponsToRemove =
	{
		weapon_first_aid_kit = 0
		weapon_defibrillator = 0
	}

	function AllowWeaponSpawn( classname )
	{
		if ( classname in weaponsToRemove )
		{
			return false;
		}
		return true;
	}		

	DefaultItems =
	[
		"weapon_first_aid_kit",
		"weapon_pistol",
	]

	function GetDefaultItem( idx )
	{
		if ( idx < DefaultItems.len() )
		{
			return DefaultItems[idx];
		}
		return 0;
	}	
}

Mutation13 - Follow the Liter

//-----------------------------------------------------
Msg("Activating Mutation 13\n");


DirectorOptions <- 
{
	ActiveChallenge = 1

	cm_SingleScavengeCluster = 1

	ScavengeScoreBonusTime = 30
}

Mutation14 - Gib Fest

//-----------------------------------------------------
Msg("Activating Mutation 14\n");


DirectorOptions <-
{
	ActiveChallenge = 1

	weaponsToRemove =
	{
		weapon_pistol = 0
		weapon_pistol_magnum = 0
		weapon_smg = 0
		weapon_pumpshotgun = 0
		weapon_autoshotgun = 0
		weapon_rifle = 0
		weapon_hunting_rifle = 0
		weapon_smg_silenced = 0
		weapon_shotgun_chrome = 0
		weapon_rifle_desert = 0
		weapon_sniper_military = 0
		weapon_shotgun_spas = 0
		weapon_grenade_launcher = 0
		weapon_rifle_ak47 = 0
		weapon_smg_mp5 = 0
		weapon_rifle_sg552 = 0
		weapon_sniper_awp = 0
		weapon_sniper_scout = 0
		weapon_rifle_m60 = 0
		weapon_melee = 0
		weapon_chainsaw = 0
		ammo = 0
	}

	function AllowWeaponSpawn( classname )
	{
		if ( classname in weaponsToRemove )
		{
			return false;
		}
		return true;
	}

	function ShouldAvoidItem( classname )
	{
		if ( ( classname != "weapon_rifle_m60" && classname != "weapon_pistol_magnum" ) && ( classname in weaponsToRemove ) )
		{
			return true;
		}
		return false;
	}

	DefaultItems =
	[
		"weapon_rifle_m60",
		"weapon_pistol_magnum",
	]

	function GetDefaultItem( idx )
	{
		if ( idx < DefaultItems.len() )
		{
			return DefaultItems[idx];
		}
		return 0;
	}
}

function OnGameEvent_round_start_post_nav( params )
{
	EntFire( "weapon_spawn", "Kill" );
	foreach( wep, val in DirectorOptions.weaponsToRemove )
		EntFire( wep + "_spawn", "Kill" );
}

Mutation15 - Versus Survival

//-----------------------------------------------------
Msg("Activating Mutation 15\n");


DirectorOptions <- 
{
	ActiveChallenge = 1

	cm_ProhibitBosses = 0
	cm_CommonLimit = 25
	cm_TankLimit = 1
	ZombieTankHealth = 2667

	SurvivalSetupTime = 90

	weaponsToRemove =
	{
		weapon_upgradepack_explosive = 0
	}

	function AllowWeaponSpawn( classname )
	{
		if ( classname in weaponsToRemove )
		{
			return false;
		}
		return true;
	}
}

function OnGameEvent_survival_round_start( params )
{
	EntFire( "info_director", "PanicEvent" );
	
	if ( SessionState.MapName == "c1m2_steets" )
		EntFire( "store_doors", "Open" );
	else if ( SessionState.MapName == "c7m1_docks" )
	{
		EntFire( "tankdoorin", "Open" );
		EntFire( "tankdoorin_button", "Kill" );
		EntFire( "tank_sound_timer", "Disable" );
		EntFire( "doorsound", "PlaySound" );
		EntFire( "tank_fog", "Enable" );
		EntFire( "tank_fog", "Disable", "", 0.5 );
		EntFire( "big_splash", "Start" );
		EntFire( "big_splash", "Stop", "", 2 );
		EntFire( "coop_tank", "Trigger" );
		EntFire( "radio_game_event", "Kill" );
		EntFire( "tank_door_clip", "Kill" );
		EntFire( "director", "EnableTankFrustration" );
		EntFire( "battlefield_cleared", "UnblockNav", "", 60 );
		EntFire( "tank_car_camera_clip", "Kill" );
	}
	else if ( SessionState.MapName == "c11m5_runway" )
		EntFire( "planecrash_trigger", "Trigger", "", 16 );
	else if ( SessionState.MapName == "c12m2_traintunnel" )
		EntFire( "emergency_door", "Open" );
}

Mutation16 - Hunting Party

//-----------------------------------------------------
Msg("Activating Mutation 16\n");


DirectorOptions <-
{
	ActiveChallenge = 1

	cm_SpecialSlotCountdownTime = 15
	cm_SpecialRespawnInterval = 1
	cm_MaxSpecials = 4

	HunterLimit = 4
	BoomerLimit = 0
	SmokerLimit = 0
	SpitterLimit = 0
	ChargerLimit = 0
	JockeyLimit = 0
	
	DominatorLimit = 4
}

Mutation17 - Lone Gunman

//-----------------------------------------------------
Msg("Activating Mutation 17\n");


DirectorOptions <-
{
	ActiveChallenge = 1

	cm_NoSurvivorBots = 1

	cm_CommonLimit = 10
	cm_BaseCommonAttackDamage = 15
	cm_WanderingZombieDensityModifier = 0.015

	HunterLimit = 0
	SmokerLimit = 0
	ChargerLimit = 0
	JockeyLimit = 0
	SpitterLimit = 0

	weaponsToRemove =
	{
		weapon_pistol = 0
		weapon_smg = 0
		weapon_pumpshotgun = 0
		weapon_autoshotgun = 0
		weapon_rifle = 0
		weapon_hunting_rifle = 0
		weapon_smg_silenced = 0
		weapon_shotgun_chrome = 0
		weapon_rifle_desert = 0
		weapon_sniper_military = 0
		weapon_shotgun_spas = 0
		weapon_grenade_launcher = 0
		weapon_rifle_ak47 = 0
		weapon_smg_mp5 = 0
		weapon_rifle_sg552 = 0
		weapon_sniper_awp = 0
		weapon_sniper_scout = 0
		weapon_rifle_m60 = 0
		weapon_melee = 0
		weapon_chainsaw = 0
		weapon_rifle_m60 = 0
		ammo = 0
		upgrade_item = 0
	}

	function AllowWeaponSpawn( classname )
	{
		if ( classname in weaponsToRemove )
		{
			return false;
		}
		return true;
	}

	DefaultItems =
	[
		"weapon_pistol_magnum",
	]

	function GetDefaultItem( idx )
	{
		if ( idx < DefaultItems.len() )
		{
			return DefaultItems[idx];
		}
		return 0;
	}
}

function OnGameEvent_round_start_post_nav( params )
{
	EntFire( "weapon_spawn", "Kill" );
	foreach( wep, val in DirectorOptions.weaponsToRemove )
		EntFire( wep + "_spawn", "Kill" );
}

Mutation18 - Bleed Out Versus

//-----------------------------------------------------
Msg("Activating Mutation 18\n");


DirectorOptions <-
{
	ActiveChallenge = 1

	weaponsToConvert =
	{
		weapon_first_aid_kit = "weapon_pain_pills_spawn"
	}

	function ConvertWeaponSpawn( classname )
	{
		if ( classname in weaponsToConvert )
		{
			return weaponsToConvert[classname];
		}
		return 0;
	}	

	// Challenge vars
	cm_TempHealthOnly = 1
	cm_AllowPillConversion = 0
	cm_ShouldHurry = 1

	TempHealthDecayRate = 0.001
	function RecalculateHealthDecay()
	{
		if ( Director.HasAnySurvivorLeftSafeArea() )
		{
			TempHealthDecayRate = 0.27 // pain_pills_decay_rate default
		}
	}
}

function Update()
{
	DirectorOptions.RecalculateHealthDecay();
}

Mutation19 - Taaannnkk!

//-----------------------------------------------------
Msg("Activating Mutation 19\n");


DirectorOptions <-
{
	ActiveChallenge = 1

//	cm_frustrationTimer = 0
	cm_TankRun = 1
	cm_ShouldHurry = 1
	cm_AutoSpawnInfectedGhosts = 1

	PreferredSpecialDirection = SPAWN_BEHIND_SURVIVORS
	BehindSurvivorsSpawnDistance = 2000
	TankRunSpawnDelay = 15

	MobMaxPending = 0

	// Always convert to the TANK!!!
	function ConvertZombieClass( iClass )
	{
		return 8;
	}	

	weaponsToConvert =
	{
		weapon_first_aid_kit = "weapon_pain_pills_spawn"
	}

	function ConvertWeaponSpawn( classname )
	{
		if ( classname in weaponsToConvert )
		{
			return weaponsToConvert[classname];
		}
		return 0;
	}	

}

Mutation20 - Healing Gnome

//-----------------------------------------------------
Msg("Activating Mutation 20\n");


DirectorOptions <-
{
	ActiveChallenge = 1

	weaponsToRemove =
	{
		weapon_first_aid_kit = 0
		weapon_pain_pills = 0
		weapon_adrenaline = 0
	}

	function AllowWeaponSpawn( classname )
	{
		if ( classname in weaponsToRemove )
		{
			return false;
		}
		return true;
	}

	// Challenge vars
	cm_TempHealthOnly = 1
	cm_AllowPillConversion = 0
	cm_HealingGnome = 1
	
	TempHealthDecayRate = 0.001
	function RecalculateHealthDecay()
	{
		if ( Director.HasAnySurvivorLeftSafeArea() )
		{
			TempHealthDecayRate = 0.27 // pain_pills_decay_rate default
		}
	}
}

function Update()
{
	DirectorOptions.RecalculateHealthDecay();
}

Rocketdude

Technically, Rocketdude was made by community members for the "The Last Stand" update, but listed as official because it is an official mutation.

//****************************************************************************************
//																						//
//									rocketDude.nut (mainfile)							//
//																						//
//****************************************************************************************

Msg("Activating RocketDude By ReneTM \n")

::rocketdude_version	<- "v1.7.7 build: 17:07:07 Feb 07 2021"
::mapName				<- Director.GetMapName().tolower()
::survivorSet			<- Director.GetSurvivorSet()
local grenadeData = {}




// Different scripts 2 include
// ----------------------------------------------------------------------------------------------------------------------------

IncludeScript("rocketdude/rd_debug")
IncludeScript("rocketdude/rd_utils")
IncludeScript("rocketdude/rd_melee_getter")
IncludeScript("rocketdude/rd_director")
IncludeScript("rocketdude/rd_damage_controll")
IncludeScript("rocketdude/rd_detonation_analysis")
IncludeScript("rocketdude/rd_meds")
IncludeScript("rocketdude/rd_events")
IncludeScript("rocketdude/rd_last_chance")
IncludeScript("rocketdude/rd_decals")
IncludeScript("rocketdude/rd_map_specifics")
IncludeScript("rocketdude/rd_custom_map_support")
IncludeScript("rocketdude/rd_saferoom_timer")
IncludeScript("rocketdude/rd_speedrun_mode")
IncludeScript("rocketdude/rd_mode_description")
IncludeScript("rocketdude/rd_hud_controller")




// Precache models and sounds
// ----------------------------------------------------------------------------------------------------------------------------

precacheSurvivorModels()

precacheRocketDudeModels()

precacheSounds()




// Creates a bullet time when the previous one is atleast 32 seconds ago.
// When this condition is met bullet time will occur with a probability of 3% 
// ----------------------------------------------------------------------------------------------------------------------------

local lastBulletTime = Time()
local lastDiceTime = Time()

::GLOBALS <-
{
	allowBulletTime = false
}

function bulletTime(){
	if(GLOBALS.allowBulletTime){
		if(Time() > lastBulletTime + 32){
			if(Time() >= lastDiceTime + 2){
				if(rollDice(3)){
					lastBulletTime = Time()
					DoEntFire( "!self", "Start", "", 0, timeScaler, timeScaler )
					DoEntFire( "!self", "Stop", "", 1, timeScaler, timeScaler )
				}
				lastDiceTime = Time()
			}
		}
	}
}

function saveGlobals(){
	SaveTable("GLOBAL_SAVINGS", GLOBALS)
}

function restoreGlobals(){
	RestoreTable("GLOBAL_SAVINGS", GLOBALS)
}




// When an instance of a grenade launcher projectile stops existing "doRocketJump" gets called
// ----------------------------------------------------------------------------------------------------------------------------

function grenadeExplodeEvent( impact ){
	foreach( player in getSurvivorsInRange( impact ) ){
		if(player.IsValid()){
			doRocketJump(impact, player)
		}
	}
}




// Returns an array of players within the blast radius
// ----------------------------------------------------------------------------------------------------------------------------

function getSurvivorsInRange(pos){
	local player = null
	while(player = Entities.FindByClassnameWithin(player, "player", pos, 160)){
		if(!player.IsDead()){
			if(!player.IsIncapacitated()){
				yield player
			}
		}
	}
}




// Compares current position of a projectile with the previous one. Force it to explode when it got stuck on prop_dynamic
// ----------------------------------------------------------------------------------------------------------------------------

function dynamicPropCheck(grenade){
	if(grenade in grenadeData){
		if(grenade.IsValid()){
			if((grenadeData[grenade] - grenade.GetOrigin()).Length() < 1){
				NetProps.SetPropInt(grenade, "m_takedamage", 1)
				grenade.TakeDamage(1337, 2, null)
			}	
		}
	}
}




// Get's fired 'OnGameplayStart' (usually after every single loadingscreen)
// ----------------------------------------------------------------------------------------------------------------------------

function OnGameplayStart(){
	
	/* CREATE MUSHROOMS */
	if(IsValveMap()){
		spawnMushrooms()
	}
	
	/* SPAWN MAP SIDED MUSHROOMS */
	spawnMapSidedMushrooms()
	
	/* SET PLAYERS MAX HEALTH AND CURRENT HEALTH TO 200 */
	setPlayersHealth()
	
	/* GENERAL SETTINGS */
	checkCvars()

	/* CREATE BULLET TIME */
	createBulletTimerEntity()

	/* KILL ALL DEATH CAMS */
	removeDeathFallCameras()
	
	/* CREATE THINK TIMER */
	createThinkTimer()

	/* RESTORE SETTINGS LIKE BULLET TIME 1/0 */
	restoreGlobals()
}

::fixEntitiesRemoved <- false

::killFixEntities <- function(){
	if(!fixEntitiesRemoved){
		EntFire( "anv_mapfixes*", "Kill" )
		EntFire( "env_player_blocker", "Kill" )
		EntFire( "rene_relay", "Trigger" )
		fixEntitiesRemoved = true
	}
}




// Called for every player within the detonation radius, it will launch the player in the calculated direction
// ----------------------------------------------------------------------------------------------------------------------------

function doRocketJump(detonationPos, player){

	local hitSurface = getSurfaceValue(detonationPos, player)
	local ignoreDistance = 160
	local playerEyes = player.EyePosition()
	local finalVector = null
	
	local detonationDistance = (detonationPos - playerEyes).Length()
	local playerIsMidAir = NetProps.GetPropInt(player, "m_hGroundEntity") & 1
	local midAirFactor = 0
	local boostdirection = (detonationPos - playerEyes) * - 1
	local eyeToSurface = ((detonationPos - playerEyes).Length())
	local distanceFactor = (160 - eyeToSurface) / 10
	
	// WALL OR FLOOR ?
	if(hitSurface == "WALL"){
		distanceFactor *= 1.5
	}else{
		distanceFactor *= 1.4
	}
	
	if(detonationDistance <= ignoreDistance){
		if(playerIsMidAir){
			midAirFactor = 1
			finalVector = (player.GetVelocity() + boostdirection * midAirFactor * distanceFactor) 
		}else{
			midAirFactor = 0.5
			finalVector = (player.GetVelocity() + boostdirection * midAirFactor * distanceFactor)
		}
		player.SetVelocity(finalVector)
	}
}




// 1. Save current origin of projectiles
// 2. Check if projectile is stuck to prop_dynamic
// 3. Projectiles from bots are ignored ( just in case )
// 4. If the player enabled auto bhop ( by using green mushroom ) use another projectile model and set it to glowing
// 5. Get rockets which travel in a straight line
// 6. Check for invalid rockets and fire grenadeExplodeEvent
// ----------------------------------------------------------------------------------------------------------------------------

local grenadeColor = GetColorInt(Vector(64,64,64))
local grenadeGlowColor = GetColorInt(Vector(255,16,16))

function ProjectileGenerator(){
	local proj = null
	while(proj = Entities.FindByClassname(proj, "grenade_launcher_projectile")){
		yield proj
	}
}

function GrenadeListener(){
	local ent = null
	foreach(ent in ProjectileGenerator()){
		dynamicPropCheck(ent)
		if(!IsPlayerABot(NetProps.GetPropEntity(ent, "m_hThrower"))){
			grenadeData[ent] <- ent.GetOrigin()
		}
	
		local scope = GetValidatedScriptScope(ent)
	
		// Projectile model and color
		if(!("modelChanged" in scope)){
			if(NetProps.GetPropEntity(ent, "m_hThrower") in bunnyPlayers){
				ent.SetModel("models/w_models/weapons/w_rd_grenade_scale_x4_burn.mdl")
			}else{
				ent.SetModel("models/w_models/weapons/w_rd_grenade_scale_x4.mdl")
			}
			NetProps.SetPropInt(ent, "m_clrRender", grenadeColor)
			scope["modelChanged"] <- true
		}

		if(!("creationTimestamp" in scope)){
			scope["creationTimestamp"] <- Time()
		}
	
		// Projectile Glow
		if(Time() > scope["creationTimestamp"] + 0.12){
			if(!("glowEnabled" in scope)){
				if(NetProps.GetPropEntity(ent, "m_hThrower") in bunnyPlayers){
					NetProps.SetPropInt(ent, "m_Glow.m_glowColorOverride", grenadeGlowColor)
					NetProps.SetPropInt(ent, "m_Glow.m_nGlowRangeMin", 32)
					NetProps.SetPropInt(ent, "m_Glow.m_nGlowRange", 8192)
					NetProps.SetPropInt(ent, "m_Glow.m_iGlowType", 3)
					NetProps.SetPropInt(ent, "m_Glow.m_bFlashing", 1)
					scope["glowEnabled"] <- true
				}
			}
		}
		
		// Let rockets travel in a straight line like in TF2
		// Another method would be to change the ent´s gravity.
		// But this would result in reflecting projectiles
		// NetProps.SetPropFloat(nade, "m_flGravity", 0.00000001)
		
		if(!("first_dir_vec" in scope)){
			scope["first_dir_vec"] <- ent.GetVelocity()
			ent.SetVelocity(scope["first_dir_vec"])
		}else{
			ent.SetVelocity(scope["first_dir_vec"])
		}
	}

	// Check if saved instances are still valid. Fire "grenadeExplodeEvent" when this is not the case anymore 
	foreach(grenade, origin in grenadeData){
		if(!grenade.IsValid()){
			grenadeExplodeEvent(origin)
			grenadeData.rawdelete(grenade)
		}
	}
}




// Disable player´s ledge hang, set his max health to 200 and disable fall damage crack
// ----------------------------------------------------------------------------------------------------------------------------

local PlayerSettingsCheckTime = Time()
function PlayerSettings(){
	if(Time() > PlayerSettingsCheckTime + 4){
		foreach(player in GetSurvivors()){
			DoEntFire("!self", "DisableLedgeHang", "", 0.0, player, player)
			DoEntFire("!self", "ignorefalldamagewithoutreset", "99999", 0.0, player, player)
			//
			if(NetProps.GetPropInt(player, "m_iMaxHealth") != 200){
				NetProps.SetPropInt(player, "m_iMaxHealth", 200)
			}
			PlayerSettingsCheckTime = Time()
		}
	}
}


function setPlayersHealth(){
	foreach(player in GetSurvivors()){
		NetProps.SetPropInt(player, "m_iMaxHealth", 200)
		NetProps.SetPropInt(player, "m_iHealth", 200)
	}
}




// While the player´s primary and secondary weapon still have infinite ammo we want to remove used items from player inventory
// Since players are able to receive throwables from mushrooms we need to check if any survivor is standing right on a mushroom
// ----------------------------------------------------------------------------------------------------------------------------

function projectileListener(){
	local projectiles = [ "vomitjar_projectile", "molotov_projectile", "pipe_bomb_projectile" ]
	foreach(projectile in projectiles){
		local throwable = null
		while(throwable = Entities.FindByClassname(throwable, projectile)){
			if(throwable.IsValid()){
				local scope = GetValidatedScriptScope(throwable)
				if(!("inv_edited" in scope)){
					local invTable = {}
					local player = NetProps.GetPropEntity(throwable, "m_hThrower")
					// Special case for c4m3_sugarmill_b ( mapsided projectiles which destroys stuff )
					if(player == null || player.GetClassname() == "pipe_bomb_projectile"){
						return
					}
					GetInvTable(player, invTable)
					if("slot2" in invTable){
						invTable.slot2.Kill()
						scope["inv_edited"] <- true
					}

					foreach(trigger in medkit_triggers){
						DoEntFire("!self", "TouchTest", "", 0.03, trigger, trigger)
					}
				}
			}
		}
	}
}




// Check if mushrooms can be reactivated
// ----------------------------------------------------------------------------------------------------------------------------

function updateMushroomTrigger(){
	foreach(trigger in medkit_triggers){
		
		local scope = GetValidatedScriptScope(trigger)

		if(Time() >= scope.usetime + scope.restoreTime){
			if(scope.usable == false){
				scope.usetime = Time() - scope.restoreTime
				setMedVisibility(1, scope.model)
				scope.usable = true
				DoEntFire("!self", "TouchTest", "", 0, trigger, trigger)
			}
		}
	}
}




// Hold space for auto-bhop ( if player used bhop mushroom )
// ----------------------------------------------------------------------------------------------------------------------------

::bunnyPlayers <- {}

function autobhop(){
	foreach(player,ent in bunnyPlayers){
		if(ent.IsValid()){
			if(!(NetProps.GetPropInt(ent, "m_fFlags") & 1) && NetProps.GetPropInt(ent, "movetype") == 2){
				if(ent.GetButtonMask() & 2){
					ent.OverrideFriction(0.033, 0)
				}
				NetProps.SetPropInt(ent, "m_afButtonDisabled", NetProps.GetPropInt(ent, "m_afButtonDisabled") | 2)
			}else{
				NetProps.SetPropInt(ent, "m_afButtonDisabled", NetProps.GetPropInt(ent, "m_afButtonDisabled") & ~2)
			}
		}else{
			bunnyPlayers.rawdelete(player)
		}
	}
}








// Get's fired every tick from a timer
// ----------------------------------------------------------------------------------------------------------------------------

function Think(){
	checkCvars()
	GrenadeListener()
	PlayerSettings()
	projectileListener()
	updateMushroomTrigger()
	autobhop()
	PlayerFunctions()
	lastChanceCountDown()
	tankrockListener()
	safeRoomTimer()
}

Rocketdude Additional Scripts

These scripts are all included in the main Rocket dude script.

rd_custom_map_support
//****************************************************************************************
//																						//
//								rd_custom_map_support.nut								//
//																						//
//****************************************************************************************




// Spawn mushrooms from map entities 
// ----------------------------------------------------------------------------------------------------------------------------

spawnMapSidedMushrooms <- function(){
	
	local mushroomTarget = null

	local types = [ "tiny", "small", "medium", "large", "bh", "exp", "item" ]
	
	foreach(type in types){
		while(mushroomTarget = Entities.FindByName(mushroomTarget, "rd_mushroom_" + type)){
			createRD_Medkit(mushroomTarget.GetOrigin(), mushroomTarget.GetAngles().ToKVString(), false, shroomProperties[type])
		}
	}
	
	foreach(type in types){
		while(mushroomTarget = Entities.FindByName(mushroomTarget, "rd_mushroom_" + type + "_rotating")){
			createRD_Medkit(mushroomTarget.GetOrigin(), mushroomTarget.GetAngles().ToKVString(), true, shroomProperties[type])
		}
	}
}




// Timers and stats
// ----------------------------------------------------------------------------------------------------------------------------

::resetPlayerStats <- function(){
	if(activator.IsValid()){
		if(activator in PlayerTimeData){
			if(activator.GetZombieType() == ZombieTypes.SURVIVOR){
				local prevBest = PlayerTimeData[activator].time_best
				PlayerTimeData[activator] <- { timerActive = false, finished = false, startTime = Time(), endTime = Time(), time_best = prevBest, ticks = 0, seconds = 0 }
				ClientPrint(activator, 5, BLUE + activator.GetPlayerName() + " | 00:00")
				EmitAmbientSoundOn("ui/littlereward.wav", 0.5, 100, 110, activator)
			}
		}
	}
}

::startTimer <- function(){
	if(activator.IsValid()){
		if(activator in PlayerTimeData){
			if(activator.GetZombieType() == ZombieTypes.SURVIVOR){
				resetPlayerStats()
				PlayerTimeData[activator].timerActive = true
			}
		}
	}
}

::mapFinished <- function(){
	
	if(PlayerTimeData[activator].finished == true){
		return
	}
	
	printFinalGroundTime(activator)
	PlayerTimeData[activator].finished = true
	resetPlayerStats()
	
	local playerSpawn = Entities.FindByName(null, "rd_player_start")
	if(playerSpawn != null){
		activator.SetOrigin(playerSpawn.GetOrigin())
	}else{
		ClientPrint(null, 5, "ERROR: ENTITY 'rd_player_start' NOT FOUND")
	}
}
rd_damage_controll
//****************************************************************************************
//																						//
//									rd_damage_controll.nut								//
//																						//
//****************************************************************************************



// Fix grenade launcher applying damage to the player on multiple ticks 
// ----------------------------------------------------------------------------------------------------------------------------

lastDamageTimes <- {}

function AllowGrenadeLauncherDamage(player){
	
	if(!(player in lastDamageTimes)){
		lastDamageTimes[player] <- Time() - 1
	}
	
	if( Time() > lastDamageTimes[player] + 0.06 ){
			lastDamageTimes[player] <- Time()
			return true
	}else{
		lastDamageTimes[player] <- Time()
		return false
	}
}




// When to allow damage
// ----------------------------------------------------------------------------------------------------------------------------

function AllowTakeDamage(damageTable){
	local damageType = damageTable["DamageType"]
	local attacker = damageTable["Attacker"]
	local victim = damageTable["Victim"]
	local damageDone = damageTable["DamageDone"]
	
	// TANK ROCK
	if(damageTable.Victim.GetClassname() == "tank_rock"){
		if(attacker != null && attacker.IsPlayer() && attacker.GetZombieType() == 9){
			
			local victimScope = GetValidatedScriptScope(victim)

			if(!("dealtDamage" in victim.GetScriptScope())){
				victimScope["dealtDamage"] <- damageDone
			}else{
				victimScope["dealtDamage"] += damageDone
			}
			if(victimScope["dealtDamage"] >= 50){
				victimScope["dealtDamage"] <- -500
				if(attacker.IsIncapacitated()){
					if(!missionFailed){
						if(last_chance_active){
							stopLastChanceMode()
							attacker.ReviveFromIncap()
						}else{
							if(!allSurvivorsIncap()){
								attacker.ReviveFromIncap()
							}else{
								ClientPrint(null, 5, BLUE + "Time to say goodbye")
							}
						}
						EmitAmbientSoundOn("player/orch_hit_csharp_short", 1, 100, 100, attacker);	
					}
				}else{
					if((attacker.GetHealth() + 5) <= 200){
							attacker.SetHealth(attacker.GetHealth() + 5)
					}else{
						attacker.SetHealth(200)
					}
					attacker.UseAdrenaline(1)
				}
			}
		}
	return true
	}


	// DISABLE FALL DAMAGE
	if(damageType == 32){
		return false
	}
	
	// DISABLE FF
	if(attacker.IsPlayer()){
		if(victim.IsPlayer()){
			if(attacker.GetZombieType() == 9 && victim.GetZombieType() == 9){
				if(IsPlayerABot(attacker)){
					return false
				}
				
				if(damageTable.Weapon == null){
					if(!AllowGrenadeLauncherDamage(victim)){
						return false
					}else{
						damageTable.DamageDone = 1
						return true
					}
				}
			}
		}
	}
	return true
}
rd_debug
//****************************************************************************************
//																						//
//										rd_debug.nut									//
//																						//
//****************************************************************************************

// Check if dev mode is active
// ----------------------------------------------------------------------------------------------------------------------------

::DevModeActive <- function(){
	return Convars.GetFloat("developer")
}




// Switch from mushroom to mushroom
// ----------------------------------------------------------------------------------------------------------------------------

::debugMushroom <- null;

::GoToNextMushroom <- function(ent){
	if(!DevModeActive()){
		return
	}
	
	if(debugMushroom = Entities.FindByModel(debugMushroom, "models/props_collectables/mushrooms_glowing.mdl")){
		NetProps.SetPropInt(ent, "m_MoveType", 8)
		ent.SetOrigin(debugMushroom.GetOrigin() + Vector(0,0,64))
	}else{
		NetProps.SetPropInt(ent, "m_MoveType", 2)
	}
}
rd_decals
//****************************************************************************************
//																						//
//										rd_decals.nut									//
//																						//
//****************************************************************************************




// Will place decals depending on map
// ----------------------------------------------------------------------------------------------------------------------------

::placeRocketDudeDecals <- function(){
	local texture = "decals/rocketdude/rd_logo_glow"
	
	if(mapName in RocketDudeDecals){
		foreach(pos in RocketDudeDecals[mapName]){
			if(mapName == "c14m1_junkyard" || mapName == "c14m2_lighthouse"){
				texture = "decals/rocketdude/rd_logo_glow_big"
			}
			applyDecalAt(pos, texture)
		}
	}
}




// Applies a decal with texture X on position Y
// ----------------------------------------------------------------------------------------------------------------------------

::applyDecalAt <- function (pos, tex){
	local decal = SpawnEntityFromTable( "infodecal", { targetname = "rd_decal", texture = tex, LowPriority = 0, origin = pos } )
	DoEntFire( "!self", "Activate", "", 0.0, decal, decal )
}




// Decal data for maps c1 - c14
// ----------------------------------------------------------------------------------------------------------------------------

::RocketDudeDecals <-
{
	c1m1_hotel			= [ Vector(2064.64,6783.97,2882) ]
	c1m2_streets		= [ Vector(-1174.98,728.091,912.947) ]
	c1m3_mall			= [ Vector(1776.31,-2015.99,642.575) ]
	c1m4_atrium			= [ Vector(-3927.97,-3403.11,856.501) ]

	c2m1_highway		= [ Vector(1338.82,-1736.51,-1087.55) ]
	c2m2_fairgrounds	= [ Vector(-495.969,-1238.1,187.207) ]
	c2m3_coaster		= [ Vector(303.034,3328.03,349.983) ]
	c2m4_barns 			= [ Vector(3717.22,576.031,-127.915) ]
	c2m5_concert 		= [ Vector(-4368.03,2687,236.19) ]

	c3m1_plankcountry 	= [ Vector(-3712,4349.86,-74.6688) ]
	c3m2_swamp 			= [ Vector(-3923.97,4650.4,85.4511) ]
	c3m3_shantytown 	= [ Vector(-5602.41,-1635.03,258.268) ]
	c3m4_plantation 	= [ Vector(1853.52,76.6953,600.031) ]

	c4m1_milltown_a 	= [ Vector(1643.17,4338.16,433.805) ]
	c4m2_sugarmill_a 	= [ Vector(2966.24,-3271.43,1200.15) ]
	c4m3_sugarmill_b 	= [ Vector(1424.04,-6088.03,416.549) ]
	c4m4_milltown_b 	= [ Vector(1437.01,6364.88,334.595) ]
	c4m5_milltown_escape = [ Vector(-5874.37,8072.03,377.766) ]

	c5m1_waterfront		= [ Vector(-2144.03,-2508.57,-334.026) ]
	c5m2_park			= [ Vector(-10102.6,-5538.72,48.0313) ]
	c5m3_cemetery		= [ Vector(3511.2,3503.97,258.566) ]
	c5m4_quarter		= [ Vector(-2060.97,3392.03,132.305) ]
	c5m5_bridge			= [ Vector(14288.3,6326.82,790.031) ]

	c6m1_riverbank		= [ Vector(1301.35,1616.03,578.07) ]
	c6m2_bedlam			= [ Vector(3964.55,4096.03,-549.714) ]
	c6m3_port			= [ Vector(-1258.88,-307.692,480.031) ]

	c7m1_docks			= [ Vector(2835.56,901.987,448.031) ]
	c7m2_barge 			= [ Vector(-10349.8,415.969,341.246) ]
	c7m3_port 			= [ Vector(-2223.97,-537.898,-32.6826) ]

	c8m1_apartment 		= [ Vector(1710.64,4568.03,264.95) ]
	c8m2_subway 		= [ Vector(9519.12,3772.03,71.1739) ]
	c8m3_sewers 		= [ Vector(15280,15322.9,72.8583) ]
	c8m4_interior 		= [ Vector(13064,14173,632.224) ]
	c8m5_rooftop		= [ Vector(7291.74,8976.03,470.528) ]

	c9m1_alleys			= [ Vector(1466.98,-1663.97,-161.193) ]
	c9m2_lots			= [ Vector(4135.19,1263.97,252.13) ]

	c10m1_caves			= [ Vector(-12608,-9505.11,27.5156) ]
	c10m2_drainage		= [ Vector(-9007.97,-9040.36,-377.031) ]
	c10m3_ranchhouse	= [ Vector(-9690.9,-7808.03,545.451) ]
	c10m4_mainstreet	= [ Vector(-1181.2,-4732.03,253.471) ]
	c10m5_houseboat		= [ Vector(2239.22,4086.55,72.0313) ]

	c11m1_greenhouse	= [ Vector(3248.03,961.976,69.2323) ]
	c11m2_offices		= [ Vector(5168.03,137.882,69.3103) ]
	c11m3_garage		= [ Vector(-2448.03,1375.55,217.825) ]
	c11m4_terminal		= [ Vector(1992.03,3128.09,513.913) ]
	c11m5_runway		= [ Vector(-7215.97,14444.1,623.558) ]

	c12m1_hilltop		= [ Vector(-7670.79,-7375.85,1478.86) ] 
	c12m2_traintunnel	= [ Vector(-7996.38,-7685.76,1329.54) ] 
	c12m3_bridge		= [ Vector(1828.55,-12166.7,484.031) ]
	c12m4_barn			= [ Vector(9825.65,-6235.72,861.689) ] 
	c12m5_cornfield		= [ Vector(4736,1515.6,362.583) ]

	c13m1_alpinecreek	= [ Vector(-3956.75,3989.21,461.736) ]
	c13m2_southpinestream = [ Vector(1329.92,993.69,170.069) ]
	c13m3_memorialbridge = [ Vector(6804.03,-4099.88,1720.87) ] 
	c13m4_cutthroatcreek = [ Vector(-731.623,1533.44,-48.6967) ] 

	c14m1_junkyard 		= [ Vector(-3616.64,-9215.38,165.2) ]
	c14m2_lighthouse	= [ Vector(340.733,-240.909,394.865) ] 

}
rd_detonation_analysis
//****************************************************************************************
//																						//
//								rd_detonation_analysis.nut								//
//																						//
//****************************************************************************************




// Distance between player eyepos and wall < distance between player eyepos to floor/ceiling...
// Thats why we want to have a higher value when the hit surface was a wall.
// ----------------------------------------------------------------------------------------------------------------------------

function getSurfaceValue(detonationPos,player){
	
	local traceTableX1 = { start = detonationPos+Vector(2,0,0), end = detonationPos+Vector(-2,0,0), ignore = player, mask = TRACE_MASK_PLAYER_SOLID }
	local traceTableX2 = { start = detonationPos+Vector(-2,0,0), end = detonationPos+Vector(2,0,0), ignore = player, mask = TRACE_MASK_PLAYER_SOLID }

	local traceTableY1 = { start = detonationPos+Vector(0,2,0), end = detonationPos+Vector(0,-2,0), ignore = player, mask = TRACE_MASK_PLAYER_SOLID }
	local traceTableY2 = { start = detonationPos+Vector(0,-2,0), end = detonationPos+Vector(0,2,0), ignore = player, mask = TRACE_MASK_PLAYER_SOLID }

	local traceTableZ1 = { start = detonationPos+Vector(0,0,2), end = detonationPos+Vector(0,0,-2), ignore = player, mask = TRACE_MASK_PLAYER_SOLID }
	local traceTableZ2 = { start = detonationPos+Vector(0,0,-2), end = detonationPos+Vector(0,0,2), ignore = player, mask = TRACE_MASK_PLAYER_SOLID }

	TraceLine(traceTableX1)
	TraceLine(traceTableX2)
	TraceLine(traceTableY1)
	TraceLine(traceTableY2)
	TraceLine(traceTableZ1)
	TraceLine(traceTableZ2)

	// WALL
	if("hit" in traceTableX1 && traceTableX1.hit == true ||  "hit" in traceTableX2 && traceTableX2.hit == true ){
		return "WALL"
	}
	// WALL
	else if("hit" in traceTableY1 && traceTableY1.hit == true ||  "hit" in traceTableY2 && traceTableY2.hit == true ){
		return "WALL"
	}
	// CEILING/FLOOR
	else if("hit" in traceTableZ1 && traceTableZ1.hit == true ||  "hit" in traceTableZ2 && traceTableZ2.hit == true ){
		return "CLOOR"
	}else{
		return "WALL" // Just in case...
	}
}
rd_director
//****************************************************************************************
//																						//
//										rd_director.nut									//
//																						//
//****************************************************************************************




MutationOptions <-
{
	// General
	cm_NoSurvivorBots	= 1
	
	// Special Infected
	MaxSpecials			= 6

	// Convert items
	weaponsToConvert =
	{
		weapon_first_aid_kit =	"weapon_pain_pills_spawn"
	}

	function ConvertWeaponSpawn(classname){
		if (classname in weaponsToConvert){
			return weaponsToConvert[classname]
		}
		return 0
	}	
	
	
	// Controll which weapons are allowed to be spawned
	weaponsToPreserve =
	{
		weapon_pain_pills		= 0
		weapon_adrenaline		= 0
		weapon_melee			= 0
		weapon_first_aid_kit	= 0
		weapon_gascan			= 0
		weapon_pistol_magnum	= 0
		weapon_grenade_launcher = 0
	}

	function AllowWeaponSpawn(classname){
		if(!IsValveMap()){
			if(classname in weaponsToPreserve){
				return true;
			}
		}
		
		if (classname in weaponsToPreserve){
			return true;
		}
		return false;
	}
	
	// Avoid fallen survivors carrying items
	function AllowFallenSurvivorItem(item){
		return false
	}
	
	// Get default items for survivors
	DefaultItems =
	[
		"weapon_grenade_launcher",
		RandomInt(0, 1) ? getAvailableMelee("Sharp") : "weapon_pistol_magnum"
	]

	function GetDefaultItem( idx ){
		if ( idx < DefaultItems.len() ){
			return DefaultItems[idx]
		}
		return 0
	}
}
rd_events
//****************************************************************************************
//																						//
//										rd_events.nut									//
//																						//
//****************************************************************************************




::devs <- {}
	
devs["STEAM_1:0:26359107"] <- { name = "ReneTM", role = "creator" }
devs["STEAM_1:0:16327272"] <- { name = "Derdoron", role = "Beta Tester" }




// Chat commands
// ----------------------------------------------------------------------------------------------------------------------------

function OnGameEvent_player_say(params){

	local text,ent = null
	
	if("userid" in params && params.userid == 0){
		return
	}
	
	text = strip(params["text"].tolower())
	ent = GetPlayerFromUserID(params["userid"])

	if(text.len() < 1){
		return
	}
	
	local steamID = ent.GetNetworkIDString()
	local scope = GetValidatedScriptScope(ent)

	switch(text){

		case "!version" :
		if(steamID in devs){
			ClientPrint(null, 5, BLUE + "RocketDude " + rocketdude_version)
		}
		break
		
		case "!countdown" :
		startSafeRoomTimer(ent)
		break
		
		case "!r" :
		restartFromSaferoom(ent)
		break
		
		case "!saveangles" :
		savePlayerEyeAngles(ent)
		break
		
		case "!speedrunmode" :
		speedrunModeToggle(ent)
		break
		
		case "!hud" :
		ChangeHudState(ent)
		break
		
		case "!stats" :
		outputStats(ent)
		break
		
		case "!info" :
		printMutationInfo(ent)
		break
		
		case "!g2mushroom" :
		GoToNextMushroom(ent)
		break
	}
}




function OnGameEvent_player_first_spawn(params){
	
	local orangestar = ORANGE + "★"

	if(params["isbot"] == 0){
		local player = GetPlayerFromUserID(params.userid)
		local steamID = player.GetNetworkIDString()

		if(steamID in devs){
			local invTable = {}
			GetInvTable(player, invTable)
			if("slot1" in invTable && invTable.slot1.GetClassname() == "weapon_melee"){
				if(NetProps.GetPropString(invTable.slot1, "m_strMapSetScriptName") != "crowbar"){
					invTable.slot1.Kill()
					player.GiveItemWithSkin("crowbar", 1)
				}else{
					NetProps.SetPropInt(invTable.slot1, "m_nSkin", 1)
				}
			}
			ClientPrint(null, 5, orangestar + GREEN + " RocketDude " + devs[steamID].role + BLUE + " " + player.GetPlayerName() + WHITE + " joined the game.")
		}
	}
}




// "The right man in the wrong place can make all the difference in the world"
// ----------------------------------------------------------------------------------------------------------------------------

function OnGameEvent_player_changename(params){
	local player = GetPlayerFromUserID(params["userid"])
	local invTable = {}
	GetInvTable(player, invTable)
	
	if("newname" in params && params["newname"] == "Dr. Gordon Freeman"){
		if("slot1" in invTable && invTable.slot1.GetClassname() == "weapon_melee"){
			if(NetProps.GetPropString(invTable.slot1, "m_strMapSetScriptName") == "crowbar"){
				NetProps.SetPropInt(invTable.slot1, "m_nSkin", 1)
			}
		}
	}
}




function OnGameEvent_player_spawn(params){

	local player = GetPlayerFromUserID(params["userid"])
	
	if(player.GetZombieType() == 9 && !IsPlayerABot(player)){
		placeRocketDudeDecals()
		teleportToSurvivor(player)
		
		DoEntFire("!self", "DisableLedgeHang", "", 0.0, player, player)
		DoEntFire("!self", "ignorefalldamagewithoutreset", "99999", 0.0, player, player)

		if(NetProps.GetPropInt(player, "m_iMaxHealth") != 200){
			NetProps.SetPropInt(player, "m_iMaxHealth", 200)
		}
	}
}




function teleportToSurvivor(player){
	local target = getClosestSurvivorTo(player)
	
	if(target == null){
		return
	}
	
	if((player.GetOrigin() - target.GetOrigin()).Length() < 256){
		return
	}
	
	if(target.IsValid()){
		if(!target.IsDead() && !target.IsDying()){
			if(Director.HasAnySurvivorLeftSafeArea()){
				player.SetOrigin(target.GetOrigin())
			}
		}
	}
}




function OnGameEvent_player_incapacitated(params){
	
	if("attackerentid" in params){
		if(EntIndexToHScript(params.attackerentid).GetClassname() == "trigger_hurt"){
			return
		}
	}

	if(!lastChanceUsed){
		lastChanceSwitch(params)
	}
}




// When a survivor stands full hp in a mushroom trigger volume the function is unlocked
// again and the survivor gets hurt a touchtest should be done
// ----------------------------------------------------------------------------------------------------------------------------

function OnGameEvent_player_hurt(params){
	if(GetPlayerFromUserID(params.userid).GetZombieType() == 9){
		foreach(trigger in medkit_triggers){
			DoEntFire("!self", "TouchTest", "", 0, trigger, trigger)
		}
	}
}




// Disable glows when survivors enter "last chance mode" but fail
// ----------------------------------------------------------------------------------------------------------------------------

function OnGameEvent_mission_lost(params){
	disableInfectedGlows()
}




// Called when any player dies. Purpose mostly for bullet time, reviving survivors from being incap and "last chance"
// ----------------------------------------------------------------------------------------------------------------------------

function OnGameEvent_player_death(params){
	
	local victim = null
	local attacker = null
	local victimClass = null
	
	if("userid" in params){
		victim = GetPlayerFromUserID(params["userid"])
	} else if("entityid" in params){
		victim = EntIndexToHScript(params["entityid"])
	}
	if("attacker" in params){
		attacker = GetPlayerFromUserID(params["attacker"])
	} else if("attackerentid" in params){
		attacker = EntIndexToHScript(params["attackerentid"])
	}
	
	if(!lastChanceUsed){
		lastChanceSwitch(params)	
	}
	
	if(victim.IsPlayer() && victim.GetZombieType() == 9){
		ClientPrint(null, 5, BLUE + victim.GetPlayerName() + WHITE + " did not finish this map. The map finished them.")
	}
	

	if(victim.GetClassname() != "infected"){
		if(victim.GetClassname() == "witch" || victim.GetZombieType() != 9){	// Killed witch or any Special infected or tank
			if(attacker != null && attacker.IsPlayer()){						// Dont do anything when the map is the killer
				if(attacker.GetZombieType() == 9){
					if(attacker.IsIncapacitated()){
						if(!missionFailed){
							if(last_chance_active){
								stopLastChanceMode()
								attacker.ReviveFromIncap()
							}else{
								if(!allSurvivorsIncap()){
									attacker.ReviveFromIncap()
								}else{
									ClientPrint(null, 5, BLUE + "Time to say goodbye")
								}
							}
							EmitAmbientSoundOn("player/orch_hit_csharp_short", 1, 100, 100, attacker)
						}
					}
				}
			}
		}
	}else{
		if(attacker != null && attacker.GetClassname() == "player" && attacker.GetZombieType() == 9){
			bulletTime()
		}
	}
}




// Set tank's health in relation to the current difficulty
// ----------------------------------------------------------------------------------------------------------------------------

function OnGameEvent_tank_spawn(params){
	local tank = EntIndexToHScript(params.tankid)
	local health = 0
	switch(Convars.GetStr("z_difficulty").tolower()){
		case "easy" :
			health = 8000;	break
		case "normal" :
			health = 16000;	break
		case "hard" :
			health = 32000;	break
		case "impossible" :
			health = 64000;	break
	}
	tank.SetMaxHealth(health)
	tank.SetHealth(health)
}




// Set witch health in relation to the current difficulty
// ----------------------------------------------------------------------------------------------------------------------------

function OnGameEvent_witch_spawn(params){
	local witch = EntIndexToHScript(params.witchid)
	local health = 0
	
	switch(Convars.GetStr("z_difficulty").tolower()){
		case "easy" :
			health = 2048;	break
		case "normal" :
			health = 4096;	break
		case "hard" :
			health = 8192;	break
		case "impossible" :
			health = 16384; break
	}
	witch.SetMaxHealth(health)
	witch.SetHealth(health)
}




// Avoid multiple rocketlaunchers on the ground 
// ----------------------------------------------------------------------------------------------------------------------------

function OnGameEvent_item_pickup(params){
	local player = GetPlayerFromUserID(params["userid"])
	local playerInv = {}
	if(player.GetZombieType() == 9 && !IsPlayerABot(player)){
		GetInvTable(player, playerInv)
		if("slot0" in playerInv){
			if(playerInv["slot0"].GetClassname() != "weapon_grenade_launcher"){
				playerInv["slot0"].Kill()
				player.GiveItem("weapon_grenade_launcher")
			}
		}
		
		// Gives devs a golden crowbar
		if(player.GetNetworkIDString() in devs){
			if("slot1" in playerInv){
				if(NetProps.GetPropString(playerInv.slot1, "m_strMapSetScriptName") == "crowbar"){
					NetProps.SetPropInt(playerInv.slot1, "m_nSkin", 1)
				}
			}
		}
	}
}




function OnGameEvent_weapon_drop(params){
	if("propid" in params){
		local droppedItem = EntIndexToHScript(params.propid)
		if(droppedItem.GetClassname() == "weapon_grenade_launcher"){
			droppedItem.Kill()
		}
	}
}

::entityChangesDone <- false

function OnGameEvent_player_left_checkpoint(params){
	if("userid" in params){
		local player = GetPlayerFromUserID(params["userid"])
		if(player.GetZombieType() == 9 && !IsPlayerABot(player) && !player.IsDead()){
			if(player in PlayerTimeData){
				if(!PlayerTimeData[player].finished){
					PlayerTimeData[player].timerActive = true
					PlayerTimeData[player].startTime = Time()
					PlayerTimeData[player].seconds = 0
					PlayerTimeData[player].ticks = 0
					EmitAmbientSoundOn("ui/beep07.wav", 0.5, 100, 107, player)
					ClientPrint(player, 5, BLUE + player.GetPlayerName() + " | 00:00")
				}
			}
		
			if(!IsPlayerABot(player)){
				if(!entityChangesDone){
					DoEntFire("worldspawn", "RunScriptCode", "killFixEntities()", 2, player, player)
					DoEntFire("worldspawn", "RunScriptCode", "mapSpecifics()", 4, player, player)
					entityChangesDone = true
				}
			}

			DoEntFire("!self", "DisableLedgeHang", "", 0.0, player, player)
			DoEntFire("!self", "ignorefalldamagewithoutreset", "99999", 0.0, player, player)

			if(NetProps.GetPropInt(player, "m_iMaxHealth") != 200){
				NetProps.SetPropInt(player, "m_iMaxHealth", 200)
			}
		}
	}
}




// Like in Portal 1 we want to challange the player to do as less steps as possible or atleast to spend as less time on the 
// ground as possible. Event "player_footstep" is non-functional atm...Valve please fix
// ----------------------------------------------------------------------------------------------------------------------------

function OnGameEvent_finale_vehicle_leaving(param){
	finalGroundTimeOutput()
}




function OnGameEvent_finale_win(param){
	finalGroundTimeOutput()
}




function finalGroundTimeOutput(){
	foreach(player,datatable in PlayerTimeData){
		if(player.IsValid()){
			if(!player.IsDead() && !player.IsDying() && !player.IsIncapacitated()){
				if(!PlayerTimeData[player].finished){
					printFinalGroundTime(player)
					ProcessSurvivorTime(player)
				}
			}
		}
	}
}




function ProcessSurvivorTime(ent){
	
	local tTable = PlayerTimeData[ent]
	tTable.endTime = Time()
	
	if(tTable.time_best == 0){
		tTable.time_best = tTable.endTime - tTable.startTime
	}else{
		if((tTable.endTime - tTable.startTime) < tTable.time_best){
			tTable.time_best = (tTable.endTime - tTable.startTime)
		}
	}
	tTable.finished = true
}




::printFinalGroundTime <- function(ent){
	if(!ent.IsDead() && !ent.IsDying() && !ent.IsIncapacitated()){
		local sec = PlayerTimeData[ent].seconds
		local fracs = PlayerTimeData[ent].ticks.tofloat()
		if(fracs > 0){
			fracs = (fracs / 30)
		}
		local groundTime = sec + fracs
		local time_curr = limitDecimalPlaces( Time() - (PlayerTimeData[ent].startTime).tofloat() )
		local midAirPercent = getMidAirPercentage(groundTime, time_curr)
		
		ClientPrint(null, 5, BLUE + ent.GetPlayerName() + WHITE + " finished this map in " + BLUE + time_curr + WHITE + " seconds and spent " + BLUE + midAirPercent + WHITE + " % midair")
		EmitAmbientSoundOn("ui/menu_invalid.wav", 0.75, 100, 110, ent)
		
		local diff = GetSpeedrunStats(ent, time_curr)

		// Speedrun tracker
		
		if( diff != null ){
			if( diff < 0){
				ClientPrint(null, 5, WHITE + "Thats a new personal record for " + mapName + GREEN + " ( "  + diff.tostring() + " seconds )")
				return
			}else if(diff > 0){
				ClientPrint(null, 5, ORANGE + "( +" + ( diff.tostring()) + " seconds )" )
				return
			}
		}
	}
}




::getMidAirPercentage <- function(groundTime, time_curr){
	local airTime = time_curr - groundTime
	local airPercentage = (( airTime / time_curr ) * 100).tofloat()
	return limitDecimalPlaces(airPercentage)
}




::limitDecimalPlaces <- function(var){
	return (var * 100).tointeger() / 100.0
}




// Check if the text from the file is a "number"
// ----------------------------------------------------------------------------------------------------------------------------

::isNumeric <- function(value){
	local newValue
	local dots = 0
	local numbers = ["0","1","2","3","4","5","6","7","8","9","."]
	for(local i=0; i < value.len(); i++){
		local checkChar = value.slice(i, i+1)
		if(checkChar == "."){
			dots++
		}
		if(dots > 1){
			return false
		}
		if(numbers.find(checkChar) == null){
			return false
		}
	}
	return true
}




// Record output for the local player
// ----------------------------------------------------------------------------------------------------------------------------

::GetSpeedrunStats <- function(player, newTime){
	
	// Restrict saving for the local player
	if(!(player == GetListenServerHost())){
		return null
	}
	
	local filePath = "rocketdude/speedrun/"
	local fileName = mapName + ".txt"
	
	local savedTime = FileToString(filePath + fileName)
	
	// Save a file when there is none
	if(savedTime == null || savedTime.len() == 0 || !isNumeric(savedTime)){
		StringToFile(filePath + fileName, newTime.tostring() )
		return null
	}
	
	try{
		savedTime = savedTime.tofloat()
	}catch(exception){
		return null
	}
	
	if(newTime < savedTime){
		StringToFile(filePath + fileName, newTime.tostring() )
	}
	
	return (newTime - savedTime)
}




// Typing sv_cheats 1 on local server would result in every cheat flagged variable reset
// ----------------------------------------------------------------------------------------------------------------------------

function OnGameEvent_server_cvar(param){
	if("cvarname" in param){
		local cvar = param.cvarname
		if(cvar == "sv_cheats"){
			checkCvars()
		}
	}
}




__CollectEventCallbacks(this, "OnGameEvent_", "GameEventCallbacks", RegisterScriptGameEventListener)
rd_hud_controller
//****************************************************************************************
//																						//
//									rd_hud_controller.nut								//
//																						//
//****************************************************************************************




// Flag and position tables
// ----------------------------------------------------------------------------------------------------------------------------

::HUDFlags <- {
	PRESTR = 1
	POSTSTR = 2
	BEEP = 4
	BLINK = 8
	AS_TIME = 16
	COUNTDOWN_WARN = 32
	NOBG = 64
	ALLOWNEGTIMER = 128
	ALIGN_LEFT = 256
	ALIGN_CENTER = 512
	ALIGN_RIGHT = 768
	TEAM_SURVIVORS = 1024
	TEAM_INFECTED = 2048
	TEAM_MASK = 3072
	NOTVISIBLE = 16384
}

::HUDPositions <- {
	LEFT_TOP = 0
	LEFT_BOT = 1
	MID_TOP = 2
	MID_BOT = 3
	RIGHT_TOP = 4
	RIGHT_BOT = 5
	TICKER = 6
	FAR_LEFT = 7
	FAR_RIGHT = 8
	MID_BOX = 9
	SCORE_TITLE = 10
	SCORE_1 = 11
	SCORE_2 = 12
	SCORE_3 = 13
	SCORE_4 = 14
}




// Main hud definition
// ----------------------------------------------------------------------------------------------------------------------------

RD_HUD <-
{
	Fields = 
	{
		timer = { slot = HUDPositions.MID_BOX, flags = HUDFlags.ALIGN_CENTER | HUDFlags.NOBG, name = "timer", datafunc = @()GetPlayerTimes() }
	}
}




// Returns one string for all survivors
// ----------------------------------------------------------------------------------------------------------------------------

::GetPlayerTimes <- function(){
	local str = ""
	
	foreach(ent in GetHumanSurvivors()){
		str += (GetCharacterDisplayName(ent) + ": ")
		if(ent in PlayerTimeData){
			if(!ent.IsDead() && !ent.IsDying()){
				if(PlayerTimeData[ent].timerActive){
					if(!PlayerTimeData[ent].finished){
						str += g_MapScript.TimeToDisplayString(Time() - PlayerTimeData[ent].startTime)
					}else{
						str += "Finished(" + g_MapScript.TimeToDisplayString(PlayerTimeData[ent].endTime - PlayerTimeData[ent].startTime) + ")"
					}
				}else{
					str += "0:00" 
				}
			}else{
				str += "(Dead)"
			}
		}
		str += "  "
	}
	return str
}




// Voting to disable the timer hud
// ----------------------------------------------------------------------------------------------------------------------------

function ChangeHudState(ent){
	local scope = GetValidatedScriptScope(ent)
	scope["speedrun_timer"] <- true
	
	foreach(player in GetHumanSurvivors()){
		local playerscope = GetValidatedScriptScope(player)
		if(!("speedrun_timer" in playerscope)){
			ClientPrint(null, 5, GREEN + ent.GetPlayerName() + WHITE + " voted to " + ( IsTimerHudActive() ? "disable" : "enable" ) + " the timer hud")
			return
		}
	}
	
	if(IsTimerHudActive()){
		DisableTimerHud()
	}else{
		EnableTimerHud()
	}
	
	foreach(player in GetHumanSurvivors()){
		local scope = GetValidatedScriptScope(player)
		if("speedrun_timer" in scope){
			player.GetScriptScope().rawdelete("speedrun_timer")
		}
	}
	
	ClientPrint(null, 5, WHITE + "Hud has been " + GREEN + ( IsTimerHudActive() ? "enabled" : "disabled") )
}




// Enable / disable the hud
// ----------------------------------------------------------------------------------------------------------------------------

function EnableTimerHud(){
	RD_HUD.Fields.timer.flags <- RD_HUD.Fields.timer.flags & ~HUDFlags.NOTVISIBLE
	HUDPlace(HUDPositions.MID_BOX, 0.0, 0.0, 1.0, 0.05)
}




function EnableTickerHud(){
	Ticker_AddToHud(RD_HUD, "!info in chat to print mutation details to your console", true)
	HUDPlace(HUDPositions.TICKER, 0.0, 0.0, 0.99, 0.25)
	Ticker_SetTimeout(16)
	Ticker_SetBlinkTime(16)
	RD_HUD.Fields.ticker.flags <- HUDFlags.ALIGN_CENTER | HUDFlags.NOBG | HUDFlags.BLINK
}




function DisableTimerHud(){
	RD_HUD.Fields.timer.flags <- RD_HUD.Fields.timer.flags | HUDFlags.NOTVISIBLE
}




// Checks if the timer hud is currently visible
// ----------------------------------------------------------------------------------------------------------------------------

function IsTimerHudActive(){
	return !(RD_HUD.Fields.timer.flags & HUDFlags.NOTVISIBLE)
}


EnableTimerHud()

EnableTickerHud()

HUDSetLayout(RD_HUD)
rd_last_chance
//****************************************************************************************
//																						//
//									rd_last_chance.nut									//
//																						//
//****************************************************************************************




// Basic functions to check if survivors are dead / capped
// ----------------------------------------------------------------------------------------------------------------------------

function allSurvivorsIncap(){
	foreach(survivor in GetSurvivors()){
		//if(!survivor.IsIncapacitated() && !survivor.IsHangingFromLedge() && !survivor.IsDead() && !survivor.IsDying() && !playerIsCapped(survivor)){  // 20200913
		if(!survivor.IsIncapacitated() && !survivor.IsDead() && !survivor.IsDying()){
			return false
		}
	}
	return true
}

function allSurvivorsDead(){
	foreach(survivor in GetSurvivors()){
		if(!survivor.IsDead() && !survivor.IsHangingFromLedge()){
			return false
		}
	}
	return true
}

function playerIsCapped(player){
	local infected = null;
	local keys = ["m_pummelVictim","m_carryVictim","m_pounceVictim","m_jockeyVictim","m_tongueVictim"]
	while(infected = Entities.FindByClassname(infected, "player")){
		if(infected.GetZombieType() != 9){
			foreach(key in keys){
				if(NetProps.GetPropEntity(infected, key) == player){
					return true
				}
			}
		}
	}
	return false
}




// When all survivors are incapacitated, give them 10 seconds to kill any special infected or boss infected ( BL-Style )
// ----------------------------------------------------------------------------------------------------------------------------

::last_chance_active	<- false
lastChanceCheckStamp	<- Time()
remainingReviveTime		<- 10
::missionFailed			<- false
::lastChanceUsed		<- false



// Called on player_death and player_incapacitated it will call enableLastChanceVision()
// when all survivors are incap but not all are dead
// ----------------------------------------------------------------------------------------------------------------------------

function lastChanceSwitch(params){
	if(!allSurvivorsDead()){
		if("userid" in params && GetPlayerFromUserID(params.userid).GetZombieType() == 9){
			if(allSurvivorsIncap() && !last_chance_active){
				enableLastChanceVision();
				lastChanceUsed = true
				last_chance_active = true
				remainingReviveTime = 10
				Convars.SetValue("director_no_death_check", 1)
				lastChanceCheckStamp = Time()
			}
		}
	}
}




// Called every tick, it will outout a countdown to timeout the survivors
// ----------------------------------------------------------------------------------------------------------------------------

function lastChanceCountDown(){
	if(last_chance_active){
		if(Time() >= lastChanceCheckStamp + 1 ){
			lastChanceCheckStamp = Time()
			if(allSurvivorsIncap()){
				if(remainingReviveTime == 0){
					last_chance_active = false
					Convars.SetValue("director_no_death_check", 0)
					ClientPrint(null, 5, BLUE + "Sorry, you don't have any time left to repopulate the world.")
					missionFailed = true
				}else{
					ClientPrint(null, 5, BLUE + remainingReviveTime)
				}
				remainingReviveTime --
			}else{
				last_chance_active = false
				Convars.SetValue("director_no_death_check", 0)
			}
		}
	}
}




// Will switch survivors vision to "black and white" and enables outline glows for special and boss infected
// ----------------------------------------------------------------------------------------------------------------------------

function enableLastChanceVision(){
	whiteScreen();
	local player = null
	local witch = null
	local rock = null
	
	while(player = Entities.FindByClassname(player, "player")){
		if(player.GetZombieType() != 9 && !player.IsDead() && !player.IsDying()){
			NetProps.SetPropInt(player, "m_Glow.m_iGlowType", 3)
			setInfectedGlowColor(player)
		}
		if(player.GetZombieType() == 9){
			player.SetReviveCount(2)
		}
	}
	while(witch = Entities.FindByClassname(witch, "witch")){
		if(witch.IsValid()){
			NetProps.SetPropInt(witch, "m_Glow.m_iGlowType", 3)
			setInfectedGlowColor(witch)
		}
	}
	while(rock = Entities.FindByClassname(rock, "tank_rock")){
		if(rock.IsValid()){
			NetProps.SetPropInt(rock, "m_Glow.m_iGlowType", 3)
			setInfectedGlowColor(rock)
		}
	}
}




// After a survivor gets a kill on any special or boss infected this should get called also
// ----------------------------------------------------------------------------------------------------------------------------

function stopLastChanceMode(){
	local player = null;
	local witch = null;
	local rock = null;
	while(player = Entities.FindByClassname(player, "player")){
		NetProps.SetPropInt(player, "m_Glow.m_iGlowType", 0)
		if(player.GetZombieType() == 9){
			player.SetReviveCount(1)
		}
	}
	while(witch = Entities.FindByClassname(witch, "witch")){
		NetProps.SetPropInt(witch, "m_Glow.m_iGlowType", 0)
	}
	while(rock = Entities.FindByClassname(rock, "tank_rock")){
		if(rock.IsValid()){
			NetProps.SetPropInt(rock, "m_Glow.m_iGlowType", 0)
		}
	}
}




// Called on mission_fail it will disable all infected glows of "last chance"
// ----------------------------------------------------------------------------------------------------------------------------

function disableInfectedGlows(){
	local player = null;
	local witch = null;
	while(player = Entities.FindByClassname(player, "player")){
		NetProps.SetPropInt(player, "m_Glow.m_iGlowType", 0)
	}
	while(witch = Entities.FindByClassname(witch, "witch")){
		NetProps.SetPropInt(witch, "m_Glow.m_iGlowType", 0)
	}
}




// Fade effect for entering the last chance vision
// ----------------------------------------------------------------------------------------------------------------------------

function whiteScreen(){
	foreach(player in GetHumanSurvivors()){
		ScreenFade(player, 200, 200, 200, 255, 1, 0.5, 1)
	}
}




// Change infected glows in "last_chance_mode" depending on difficulty
// ----------------------------------------------------------------------------------------------------------------------------

function setInfectedGlowColor(inf){
	local vector = null;
	switch(Convars.GetStr("z_difficulty").tolower()){
		case "easy"			: vector = Vector(255,45,180);	break;
		case "normal"		: vector = Vector(0,255,0);		break;
		case "hard"			: vector = Vector(200,0,0);		break;
		case "impossible"	: vector = Vector(120,120,120);	break;
		default				: vector = Vector(165,0,255);	break;
	}
	local color = vector.x
	color += 256 * vector.y
	color += 65536 * vector.z
	NetProps.SetPropInt(inf, "m_Glow.m_glowColorOverride", color)
}




// Returns vector color as int
// ----------------------------------------------------------------------------------------------------------------------------

function GetColorInt(col){
	
	if(typeof(col) == "Vector"){
		local color = col.x
		color += 256 * col.y
		color += 65536 * col.z
		return color
	}else if(typeof(col) == "string"){
		local colorArray = split(col, " ")
		local r = colorArray[0].tointeger()
		local g = colorArray[1].tointeger()
		local b = colorArray[2].tointeger()
		local color = r
		color += 256 * g
		color += 65536 * b
		return color
	}
}
rd_map_specifics
//****************************************************************************************
//																						//
//									rd_map_specifics.nut								//
//																						//
//****************************************************************************************




// Some maps require minor adjustments to improve gameplay. 
// ----------------------------------------------------------------------------------------------------------------------------

::mapSpecifics <- function(){
	switch(mapName){
		case "c1m1_hotel"				:	rd_specifics_c1m1();	break
		case "c1m2_streets"				:	rd_specifics_c1m2();	break
		case "c1m3_mall"				:	rd_specifics_c1m3();	break
		case "c5m5_bridge"				:	rd_specifics_c5m5();	break
		case "c7m3_port"				: 	rd_specifics_c7m3();	break
		case "c8m4_interior"			:	rd_specifics_c8m4();	break
		case "c8m5_rooftop"				:	rd_specifics_c8m5();	break
		case "c13m4_cutthroatcreek"		:	rd_specifics_c13m4();	break
		case "c14m1_junkyard"			:	rd_specifics_c14m1();	break
		case "c14m2_lighthouse"			:	rd_specifics_c14m2();	break
		default							:							break
	}
}




// Since we dont have any fall damage at all we should not stop players from just dropping down the hotel
// ----------------------------------------------------------------------------------------------------------------------------

::rd_specifics_c1m1 <- function(){
	local deathTriggers =
	[
		Vector(3200.000,5312.000,1648.000),
		Vector(2944.000,5888.000,1648.000),
		Vector(2936.000,6932.000,1648.000),
		Vector(1516.800,8000.000,1520.000),
		Vector(632.000,6944.000,1648.000),
		Vector(1600.000,4608.000,1648.000),
		Vector(0.000,5632.000,1648.000)
	]
	foreach(triggerPos in deathTriggers){
		local ent = null
		if(ent = Entities.FindByClassnameNearest( "trigger_hurt", triggerPos, 4 )){
			ent.Kill()
		}
	}
}




// Force start of parish finale
// ----------------------------------------------------------------------------------------------------------------------------

::rd_specifics_c5m5 <- function (){
	local ent = null
	if(ent = Entities.FindByClassname(null, "player")){
		DoEntFire( "radio_fake_button", "use", "", 1, ent, ent)
		DoEntFire( "finale", "use", "", 8, ent, ent)
		DoEntFire( "relay_init_heli", "Trigger", "", 12, ent, ent)
		DoEntFire( "relay_start_heli", "Trigger", "", 16, ent, ent)
	}
}




// Remove liftshaft_portal3_brush and rooftop_opening_clip
// ----------------------------------------------------------------------------------------------------------------------------

::rd_specifics_c8m5 <- function(){
	local ent = null
	if(ent = Entities.FindByClassnameNearest( "func_brush", Vector(7248.000000, 9168.000000, 7144.000000), 4)){
		ent.Kill()
	}
}




// Force start of cold stream finale
// ----------------------------------------------------------------------------------------------------------------------------

::rd_specifics_c13m4 <- function (){
	local ent = null
	if(ent = Entities.FindByClassname(null, "player")){
		DoEntFire( "startbldg_door_button", "use", "", 1, ent, ent)
		DoEntFire( "finale", "use", "", 12, ent, ent)
	}
}




// Change damage type of trigger_hurt ( water area in front of the end saferoom )
// ----------------------------------------------------------------------------------------------------------------------------

::rd_specifics_c14m1 <- function(){
	local ent = null
	if(ent = Entities.FindByClassnameNearest( "trigger_hurt_ghost", Vector(-4580,9352,-732), 4)){
		TriggerSetDamageType(ent, damageTypes.GENERIC)
	}
}




// Kill trigger which re-enables the ledgehang
// Change damage type of trigger_hurt ( water of rescue vehicle zone )
// ----------------------------------------------------------------------------------------------------------------------------

::rd_specifics_c14m2 <- function(){
	local ent = null
	
	if(ent = Entities.FindByClassnameNearest( "trigger_multiple", Vector(-4352,3928,1096), 4)){
		ent.Kill()
	}
	if(ent = Entities.FindByClassnameNearest( "trigger_hurt", Vector(-4608,7168,-256), 4)){
		TriggerSetDamageType(ent, damageTypes.GENERIC)
	}
	if(ent = Entities.FindByClassnameNearest("func_brush", Vector(275.000000, 930.000000, 1360.000000), 4)){
		ent.Kill()
	}
}




// Re-execute the elevator fix once
// ----------------------------------------------------------------------------------------------------------------------------


::rd_specifics_c8m4 <- function(){
	EntFire( "worldspawn", "RunScriptFile", "c8m4_elevatorfix" )
}




// Utils to manipulate entities
// ----------------------------------------------------------------------------------------------------------------------------

::TriggerSetDamageType <- function(ent, type){
	if(ent.GetClassname() == "trigger_hurt" || ent.GetClassname() == "trigger_hurt_ghost"){
		NetProps.SetPropInt(ent, "m_bitsDamageInflict", type)
	}
}




// Lets make c7m3 playable alone
// ----------------------------------------------------------------------------------------------------------------------------

::KillOldFinaleButton <- function(){
	local triggerFinale = null
		if(triggerFinale = Entities.FindByClassname(triggerFinale, "trigger_finale")){
			triggerFinale.Kill()
		}
	
	local br = null
	if(br = Entities.FindByClassname(null,"trigger_escape")){
		br.Kill()
	}
}




::rd_specifics_c7m3 <- function(){
	KillOldFinaleButton()
	EntFire("worldspawn", "RunScriptCode", "SpawnNewFinaleButton()", 1.0)
	EntFire("worldspawn", "RunScriptCode", "SpawnNewFinaleButton()", 1.0)
	EntFire("generator_start_model", "Disable", "", 1.5)
	EntFire("generator_start_model", "Enable", "", 2.0)
}




::SpawnNewFinaleButton <- function(){
	
	local keyvalues =
	{
		targetname = "generator_start_model",
		StartDisabled = "1",
		model = "models/props_vehicles/floodlight_generator_switch01.mdl",
		disableshadows = "1",
		UseDelay = "0",
		type = "2",
		origin = Vector(-456.187, -571.935, 2.57401),
		connections =
		{
			FinaleStart =
			{
				cmd1 = "bridge_ragdoll_fader�Enable��5�-1"
				cmd2 = "bridge_ragdoll_fader�Disable��8�-1"
				cmd3 = "bridge_move_sound�PlaySound��5�-1"
				cmd4 = "bridge_elevator�MoveToFloor�Bottom�5�-1"
				cmd5 = "store_brush�Enable��0�-1"
				cmd6 = "generator_brush�Enable��0�-1"
			}
			FinaleEscapeStarted =
			{
				cmd1 = "relay_escape�Trigger��0�-1"
			}
		}
	}
	
	local ent = SpawnEntityFromTable("trigger_finale", keyvalues)
	NetProps.SetPropEntity(Entities.FindByName(null, "finale_start_button"), "m_sGlowEntity", ent)
}




// Convert all weapons in shop ( except deagle ) to weapon_grenade_launcher_spawn
// ----------------------------------------------------------------------------------------------------------------------------

::rd_specifics_c1m2 <- function(){

	local ent = null
	local weapons =
	[
		"weapon_pistol_spawn",
		"weapon_autoshotgun_spawn",
		"weapon_chainsaw_spawn",
		"weapon_hunting_rifle_spawn",
		"weapon_pumpshotgun_spawn",
		"weapon_rifle_ak47_spawn",
		"weapon_rifle_desert_spawn",
		"weapon_rifle_m60_spawn",
		"weapon_rifle_sg552_spawn",
		"weapon_rifle_spawn",
		"weapon_shotgun_chrome_spawn",
		"weapon_shotgun_spas_spawn",
		"weapon_smg_mp5_spawn",
		"weapon_smg_silenced_spawn",
		"weapon_smg_spawn",
		"weapon_sniper_awp_spawn",
		"weapon_sniper_military_spawn",
		"weapon_sniper_scout_spawn"
	]
	
	foreach(weapon in weapons){
		while(ent = Entities.FindByClassname(ent, weapon)){
			SpawnEntityFromTable("weapon_grenade_launcher_spawn",
			{
				weaponskin = -1
				angles = ent.GetAngles().ToKVString()
				origin = ent.GetOrigin()
				spawnflags = 2
				body = 0
				solid = 0
				count = 1
				disableshadows = 0
				skin = 0
				glowrange = 0
				model = "models/w_models/weapons/w_grenade_launcher.mdl"
			})
			ent.Kill()
		}
	}
}




// Makes event glass also breakable by the rocket launcher's projectiles
// ----------------------------------------------------------------------------------------------------------------------------

::rd_specifics_c1m3 <- function(){
	local multiFilterOld = Entities.FindByName(null, "filter_breakglass")
	
	if(multiFilterOld && multiFilterOld.IsValid()){
		multiFilterOld.Kill()
	}
	

	SpawnEntityFromTable("filter_activator_class",
	{
			targetname = "filter_grenade_launcher_projectile"
			Negated = 0
			filterclass = "grenade_launcher_projectile"
	})
	
	SpawnEntityFromTable("filter_multi",
	{
		targetname = "filter_breakglass"
		Filter01 = "filter_bullet"
		Filter02 = "filter_melee"
		Filter03 = "filter_grenade_launcher_projectile"
		//
		filtertype = 1
		Negated = 0
	})
}
Note.png Note: The � symbols show "ESC" in notepad++. Unsure what they are meant to be
rd_meds
//****************************************************************************************
//																						//
//										rd_meds.nut										//
//																						//
//****************************************************************************************



// All mushroom triggers to iterate over
// ----------------------------------------------------------------------------------------------------------------------------

::medkit_triggers	<- []




// Returns handle of the created prop_dynamic with a 'mushroom model'
// ----------------------------------------------------------------------------------------------------------------------------

::RD_healtkit_model <- function(pos, ang, shroomdata){
	local model = SpawnEntityFromTable("prop_dynamic_override",
	{
		// General
		targetname = "RD_HP_MODEL", origin = pos, angles = ang, body = 0, DefaultAnim = "idle", DisableBoneFollowers = 1,
		// Shadows and fade
		disablereceiveshadows = 1, disableshadows = 1, disableX360 = 0, ExplodeDamage = 0, ExplodeRadius = 0, fademaxdist = 0, fademindist = -1, fadescale = 0,
		// Glows
		glowbackfacemult = 1.0, glowcolor = shroomdata.glowColor, glowrange = shroomdata.glowRange, glowrangemin = 0, glowstate = shroomdata.glowstate, health = 0,
		// Model & Animation
		LagCompensate = 0, MaxAnimTime = 10, maxcpulevel = 0, maxgpulevel = 0, MinAnimTime = 5,
		mincpulevel = 0, mingpulevel = 0, model = "models/props_collectables/mushrooms_glowing.mdl", PerformanceMode = 0, pressuredelay = 0,
		RandomAnimation = 0, renderamt = 255, rendercolor = shroomdata.modelColor, renderfx = 0, rendermode = 0, SetBodyGroup = 0, skin = 0,
		solid = 0, spawnflags = 0, updatechildren = 0
	})
	model.SetModelScale(shroomdata.modelScaleMax, 0)
	return model
}




// Creates a survivor only filter for the mushrooms
// ----------------------------------------------------------------------------------------------------------------------------

SpawnEntityFromTable("filter_activator_team", { targetname = "RD_FILTER_SURVIVOR", origin = Vector(0,0,0), Negated = 0, filterteam = 2 } )
::worldspawn <- Entities.FindByClassname(null, "worldspawn")
::worldspawn.ValidateScriptScope()




// Returns handle of trigger to execute the healing function
// ----------------------------------------------------------------------------------------------------------------------------

::RD_healthkit_trigger <- function(pos, shroomdata){
	local triggerMin = shroomdata.triggerSize[0]
	local triggerMax = shroomdata.triggerSize[1]
	local zOffset = triggerMax.z
	local HP_Value = shroomdata.hp
	
	local triggerName = "RD_HP_TRIGGER"
	//
	local triggerTable =
	{
		targetname    = triggerName
		StartDisabled = 0
		spawnflags    = 1
		allowincap    = 1
		entireteam    = 0
		filtername    = "RD_FILTER_SURVIVOR"
		origin        = pos + Vector(0,0,zOffset)
	}

	local trigger = SpawnEntityFromTable( "trigger_multiple", triggerTable)
	//
	setTriggerSize(trigger,triggerMin,triggerMax)
	NetProps.SetPropInt(trigger, "m_Collision.m_nSolidType", 2)
	//
	EntFire( triggerName, "AddOutput", "OnStartTouch worldspawn:RunScriptCode:survivorMedKitTouch(activator):0:-1" )
	EntFire( triggerName, "AddOutput", "OnTouching worldspawn:RunScriptCode:survivorMedKitTouch(activator):0:-1" )
	//
	if(DevModeActive()){
		DebugDrawBox( triggerTable.origin, Vector(32,32,32), Vector(-32,-32,-32), 255, 255, 255, 0, 16)
	}
	return trigger
}




// Sets the trigger size in relation to the mushroom size
// ----------------------------------------------------------------------------------------------------------------------------

function setTriggerSize(trigger, vectorMins, vectorMaxs){
	if(trigger.IsValid()){
		if(typeof(vectorMins) == "Vector"){
			if(typeof(vectorMaxs) == "Vector"){
				NetProps.SetPropVector(trigger, "m_Collision.m_vecMins", vectorMins)
				NetProps.SetPropVector(trigger, "m_Collision.m_vecMaxs", vectorMaxs)
			}else{
				error("setTriggerSize error: vectorMaxs ment to be datatype vector")
			}
		}else{
			error("setTriggerSize error: vectorMins ment to be datatype vector")
		}
	}
}




// Creates a set of trigger and prop_dynamic_override ( healing mushroom )
// ----------------------------------------------------------------------------------------------------------------------------

::createRD_Medkit <- function(pos, ang, rotating, shroomdata){
	local HP_Val = shroomdata.hp

	local trigger = RD_healthkit_trigger(pos, shroomdata);
	local model = RD_healtkit_model(pos, ang, shroomdata);
	
	local mushroomTable =
	{
		model = model
		trigger = trigger
		restoreTime = shroomdata.restoreTime
		usetime = Time() - shroomdata.restoreTime
		usable = true
		hp = HP_Val
		modelScaleMin = shroomdata.modelScaleMin
		modelScaleMax = shroomdata.modelScaleMax
		glowstate = shroomdata.glowstate
		action = shroomdata.action
		flashColor = shroomdata.flashColor
	}
	medkit_triggers.append(trigger)
	
	addTableToEntityScope(trigger, mushroomTable)
	addTableToEntityScope(model, mushroomTable)
	
	if(rotating){
		AttachRotatorTo(model)
	}
}

::addTableToEntityScope <- function(ent, table){
	
	local scope = GetValidatedScriptScope(ent)
	
	foreach(key, value in table){
		scope[key] <- value
	}
}




// Mushrooms properties
// ----------------------------------------------------------------------------------------------------------------------------
::shroomProperties <-
{
	large 	= 	{ hp = 75, action = "HP", restoreTime = 16, modelColor = "255 185 0", flashColor = "255 185 0", glowRange = 256, glowstate = 3, glowColor = "255 185 0", GlowColorRelation = true, modelScaleMax = 7.0, modelScaleMin = 1.0, triggerSize = [ Vector(-28,-28,-28), Vector(28,28,28) ] }
	medium 	=	{ hp = 50, action = "HP", restoreTime = 16, modelColor = "220 0 255", flashColor = "220 0 255", glowRange = 256, glowstate = 3, glowColor = "220 0 255", GlowColorRelation = true, modelScaleMax = 5.0, modelScaleMin = 1.0, triggerSize = [ Vector(-16,-16,-16), Vector(16,16,16) ] }
	small	=	{ hp = 25, action = "HP", restoreTime = 16, modelColor = "0 105 255", flashColor = "0 105 255", glowRange = 256, glowstate = 3, glowColor = "0 105 255", GlowColorRelation = true, modelScaleMax = 3.0, modelScaleMin = 1.0, triggerSize = [ Vector(-16,-16,-16), Vector(16,16,16) ] }
	tiny	=	{ hp = 10, action = "HP", restoreTime = 16, modelColor = "255 255 255", flashColor = "255 255 255", glowRange = 256, glowstate = 3, glowColor = "255 255 255", GlowColorRelation = true, modelScaleMax = 2.0, modelScaleMin = 1.0, triggerSize = [ Vector(-16,-16,-16), Vector(16,16,16) ] }
	exp		=	{ hp = 0, action = "EXP", restoreTime = 8, modelColor = "0 0 0", flashColor = "0 0 0", glowRange = 128, glowstate = 3, glowColor = "255 0 0", GlowColorRelation = false, modelScaleMax = 4.0, modelScaleMin = 1.0, triggerSize = [ Vector(-16,-16,-16), Vector(16,16,16) ] }
	item	=	{ hp = 0, action = "ITEM", restoreTime = 16, modelColor = "0 0 255", flashColor = "0 0 255", glowRange = 512, glowstate = 3, glowColor = "0 0 255", GlowColorRelation = true, modelScaleMax = 4.0, modelScaleMin = 1.0, triggerSize = [ Vector(-16,-16,-16), Vector(16,16,16) ] }
	bh		=	{ hp = 0, action = "BH", restoreTime = 1, modelColor = "0 255 0", flashColor = "0 255 0", glowRange = 1024, glowstate = 3, glowColor = "0 255 0", GlowColorRelation = true, modelScaleMax = 4.0, modelScaleMin = 1.0, triggerSize = [ Vector(-16,-16,-16), Vector(16,16,16) ] }
}

foreach(dataset in shroomProperties){
	if(dataset.glowstate == 3 && dataset.GlowColorRelation){
		dataset.glowColor = getColorWithIntensity(dataset.modelColor, 77)
	}
}




// After a mushroom heals a player it should be invisible for 10 seconds
// ----------------------------------------------------------------------------------------------------------------------------

::setMedVisibility <- function(x, ent){
	
	local scope = GetValidatedScriptScope(ent)
	
	if(x == 0){
		NetProps.SetPropInt(ent, "m_Glow.m_iGlowType", 0)
		NetProps.SetPropInt(ent, "m_fEffects", NetProps.GetPropInt(ent, "m_fEffects") | (1 << 5))
		mushroomSizer(ent,scope.modelScaleMin, 0)
	}else{
		NetProps.SetPropInt(ent, "m_fEffects", 0)
		NetProps.SetPropInt(ent, "m_Glow.m_iGlowType", scope.glowstate)
		mushroomSizer(ent,scope.modelScaleMax, 0.1)
		EmitAmbientSoundOn("level/popup.wav", 1, 100, 100, ent)
	}
}




// Let's the mushroom "grow"
// ----------------------------------------------------------------------------------------------------------------------------

::mushroomSizer <- function(ent, scale, time){
	ent.SetModelScale(scale, time)
}




// Survivor touches a mushroom trigger
// ----------------------------------------------------------------------------------------------------------------------------

::survivorMedKitTouch <- function(player){
	
	// Has to be set when this fuction gets called via OnTouching because the activator is the trigger itself
	
	if(player.GetClassname() == "trigger_multiple"){
		player = Entities.FindByClassnameNearest("player", player.GetOrigin(), 256)
	}
	
	local playerPos = player.GetOrigin()
	local medkit_trigger = Entities.FindByNameNearest("RD_HP_TRIGGER", playerPos, 256)
	
	local scope = GetValidatedScriptScope(medkit_trigger)
	
	local HP_Val = scope.hp
	local action = scope.action
	local flashColor = split(scope.flashColor," ")
	
	local medkit_model = scope.model
	local mushroomUsed = false
	
	if(!IsPlayerABot(player)){
		if(!missionFailed){
			if(Time() >= scope.usetime + scope.restoreTime){
				if(action == "HP"){
					if(player.GetHealth() < player.GetMaxHealth() || player.IsIncapacitated()){
						healPlayer(player, HP_Val)
						mushroomUsed = true
					}
				}else if(action == "BH"){
					if(!(player in bunnyPlayers)){
						playerBecomesBunny(player)
						player.UseAdrenaline(7)
						mushroomUsed = true
					}
				}else if(action == "EXP"){
					executeExplosion(medkit_trigger)
					mushroomUsed = true
				}else if(action == "ITEM"){
					if(!player.IsIncapacitated()){
						local invTable = {}
						GetInvTable(player, invTable)
						if(!("slot2" in invTable)){
							giveRandomItem(player, medkit_trigger)
							mushroomUsed = true
						}
					}
				}
				if(mushroomUsed){
					scope.usetime = Time()
					scope.usable = false
					setMedVisibility(0, medkit_model)
					ScreenFade(player, flashColor[0].tointeger(), flashColor[1].tointeger(), flashColor[2].tointeger(), 128, 1.0, 0, 1)
				}
			}
		}
	}
}




// Sounds used by the mushrooms 
// ----------------------------------------------------------------------------------------------------------------------------

::mushroomSounds <-
{
	explosions =
	[
		"player/boomer/explode/explo_medium_09.wav",
		"player/boomer/explode/explo_medium_10.wav",
		"player/boomer/explode/explo_medium_14.wav"
	]
	misc =
	[
		"level/gnomeftw.wav",
		"player/laser_on.wav",
		"ui/menu_invalid.wav"
	]
}





::precacheSounds <- function(){
	foreach(collection in mushroomSounds){
		foreach(sound in collection){
			PrecacheSound(sound)
		}
	}
}




// Mushroom action ( explosion )
// ----------------------------------------------------------------------------------------------------------------------------

::executeExplosion <- function(ent){
	local location = ent.GetOrigin()
	local expTable =
	{
		origin = location
		fireballsprite = "sprites/zerogxplode.spr"
		ignoredClass = 0
		targetname = "exp"
		iMagnitude = 128
		spawnflags = 0
		iRadiusOverride = 128
		rendermode = 5
	}
	
	local exp = SpawnEntityFromTable("env_explosion", expTable)
	DoEntFire("!self", "explode", "", 0, exp, exp)
	local sounds = mushroomSounds.explosions
	EmitAmbientSoundOn(sounds[RandomInt(0, sounds.len() - 1)], 0.8, 100, 147, ent)
}




// Mushroom action ( bunny-hop )
// ----------------------------------------------------------------------------------------------------------------------------

::playerBecomesBunny <- function(player){
	if(!(player in bunnyPlayers)){
		if(player.IsValid()){
			bunnyPlayers[player] <- player
			ClientPrint(null, 5, "\x03" + player.GetPlayerName() + "\x01" + " is a bunny now.")
			EmitAmbientSoundOn("player/laser_on.wav", 1, 100, 180, player)
		}
	}
}




// Mushroom action ( random item )
// ----------------------------------------------------------------------------------------------------------------------------

::giveRandomItem <- function(player, shroom){
	local throwables = [ "weapon_pipe_bomb", "weapon_vomitjar", "weapon_molotov" ]
	player.GiveItem(throwables[RandomInt(0, throwables.len() - 1)])
	EmitAmbientSoundOn(mushroomSounds.misc[0], 0.5, 100, 170, shroom)
}




// Mushroom action ( healing )
// ----------------------------------------------------------------------------------------------------------------------------

::healPlayer <- function(player, val){
	
	local sndPitch = 100;
	switch(GetCharacterDisplayName(player)){
		case "Rochelle"	:	sndPitch = 115; break;
		case "Zoey"		: 	sndPitch = 135; break;
		default			: 	sndPitch = 100; break;
	}
	
	EmitAmbientSoundOn("player/items/pain_pills/pills_use_1.wav", 1, 100, sndPitch, player)
	StopAmbientSoundOn("player/heartbeatloop.wav", player) 
	player.UseAdrenaline(7)
	local newHP = player.GetHealth() + val
	local playerMaxHealth = player.GetMaxHealth()
	
	if(player.IsIncapacitated())
	{
		player.ReviveFromIncap()
		player.SetReviveCount(0)
		player.SetHealthBuffer(0)
		player.SetHealth(val)
	}
	else
	{
		if(newHP >= playerMaxHealth)
		{
			//NetProps.SetPropInt(player,"m_isGoingToDie",0)
			//NetProps.SetPropInt(player,"m_isIncapacitated",0)
			player.ReviveFromIncap()
			player.SetReviveCount(0)
			player.SetHealthBuffer(0)
			player.SetHealth(playerMaxHealth)
		}
		else
		{
			//NetProps.SetPropInt(player,"m_isGoingToDie",0)
			//NetProps.SetPropInt(player,"m_isIncapacitated",0)
			player.ReviveFromIncap()
			player.SetReviveCount(0)
			player.SetHealthBuffer(0)
			player.SetHealth(newHP)
		}
	}
}




// Called "OnGameplayStart" it will spawn mushrooms for the current map 
// ----------------------------------------------------------------------------------------------------------------------------

spawnMushrooms <- function(){
	if(mapName in mushroomPositions){
		foreach(DS in mushroomPositions[mapName]){
			createRD_Medkit(DS.origin, DS.angles, DS.rotating, shroomProperties[DS.type])
		}
	}
}




// When specified in mushroom definition the passed entity gets attached to a rotation entity
// ----------------------------------------------------------------------------------------------------------------------------

AttachRotatorTo <- function(ent){
	local pos = ent.GetOrigin()
	local rotName = UniqueString("_mushroom_rot")
	local rotator = SpawnEntityFromTable("func_rotating", { targetname = rotName, origin = pos, spawnflags = 67})
	NetProps.SetPropVector(rotator, "m_Collision.m_vecMins", Vector(1,1,1))
	NetProps.SetPropVector(rotator, "m_Collision.m_vecMaxs", Vector(-1,-1,-1))
	DoEntFire("!self", "SetParent", "!activator", 0.00, rotator, ent);

}
rd_melee_getter
//****************************************************************************************
//																						//
//									rd_melee_getter.nut									//
//																						//
//****************************************************************************************

sharpMeleeData <-
[
	{ model = "models/v_models/weapons/v_knife_t.mdl", itemName = "knife", alias = "knife" },
	{ model = "models/weapons/melee/v_crowbar.mdl", itemName = "crowbar", alias = "crowbar" },
	{ model = "models/weapons/melee/v_fireaxe.mdl", itemName = "fireaxe", alias = "fireaxe" },
	{ model = "models/weapons/melee/v_katana.mdl", itemName = "katana", alias = "katana" },
	{ model = "models/weapons/melee/v_machete.mdl", itemName = "machete", alias = "machete" },
	{ model = "models/weapons/melee/v_pitchfork.mdl", itemName = "pitchfork", alias = "pitchfork" }
]

bluntMeleeData <-
[
	{ model = "models/weapons/melee/v_riotshield.mdl", itemName = "riotshield" , alias = "riotshield" },
	{ model = "models/weapons/melee/v_shovel.mdl", itemName = "shovel", alias = "shovel" },
	{ model = "models/weapons/melee/v_bat.mdl", itemName = "baseball_bat", alias = "bat" },
	{ model = "models/weapons/melee/v_cricket_bat.mdl", itemName = "cricket_bat", alias = "cricket" },
	{ model = "models/weapons/melee/v_golfclub.mdl", itemName = "golfclub", alias = "golfclub" },
	{ model = "models/weapons/melee/v_tonfa.mdl", itemName = "tonfa", alias = "tonfa" },
	{ model = "models/weapons/melee/v_electric_guitar.mdl", itemName = "electric_guitar", alias = "guitar" },
	{ model = "models/weapons/melee/v_frying_pan.mdl", itemName = "frying_pan", alias = "pan" }
]




// Before giving the melee check if it's available for the current map
// ----------------------------------------------------------------------------------------------------------------------------

function GiveMelee(player, melee){

	if(meleeAliases == null){
		meleeAliases = getAvailableMeleeAliases()
	}

	local sharps = GetAvailableSharpMelees()
	local blunts = GetAvailableBluntMelees()

	if(sharps.find(melee) != null || blunts.find(melee) != null){
		player.GiveItem(melee)
	}else{
		ClientPrint(null, 5, "The " + melee + " is not available on current map.")
		ClientPrint(null, 5, "Take one of those: " + meleeAliases)
	}
}

meleeAliases <- null;
function getAvailableMeleeAliases(){
	local aliases = "";
	foreach(dataSet in sharpMeleeData){
		if(IsModelPrecached(dataSet.model)){
			aliases += dataSet.alias + ", "
		}
	}
		foreach(dataSet in bluntMeleeData){
		if(IsModelPrecached(dataSet.model)){
			aliases += dataSet.alias + ", "
		}
	}
	return aliases;
}

function GetAvailableSharpMelees(){
	local sharps = []
	foreach(dataSet in sharpMeleeData){
		if(IsModelPrecached(dataSet.model)){
			sharps.append(dataSet.itemName)
		}
	}
	return sharps;
}

function GetAvailableBluntMelees(){
	local blunts = []
	foreach(dataSet in bluntMeleeData){
		if(IsModelPrecached(dataSet.model)){
			blunts.append(dataSet.itemName)
		}
	}
	return blunts;
}




// Will give the player a random sharp/blunt weapon depending which melee is available on the current map
// ----------------------------------------------------------------------------------------------------------------------------

function getAvailableMelee(attribute){
	local sharps = GetAvailableSharpMelees()
	local blunts = GetAvailableBluntMelees()
	local melee = null;
	if(sharps.len() > 0 && blunts.len() > 0){
		if(attribute == "Sharp"){
			melee = sharps[RandomInt(0, sharps.len() - 1)]
		}else{
			melee = blunts[RandomInt(0, blunts.len() - 1)]
		}
		return melee;
	}else{
		return "bat"
	}
}
rd_mode_description
//****************************************************************************************
//																						//
//								rd_mode_description.nut									//
//																						//
//****************************************************************************************




// Output the most important facts about this mode to the players console
// ----------------------------------------------------------------------------------------------------------------------------

::printMutationInfo <- function(ent){
	
	local line = "_______________________________________________________________________________________________"
	local txt = [
	
		"-------------------------------- RocketDude by ReneTM --------------------------------"
		" "
		"In this mutation, there are no bot survivors and the human Survivors start with 200 health."
		"They are equipped with a Grenade Launcher with infinite ammo and clip size, and either a sharp melee weapon or a Magnum."
		"The Grenade Launcher's grenades do full damage to infected but only 1 to the survivors."
		"They travel in a straight line like in TF2. There are only pills and adrenaline shots to pickup."
		"All medkits and defibs are gone. There are different kinds of mushrooms spread over the map."
		"They can be picked up by walking over them. Some may be hard to reach."
		" "
		line
		" "
		" "
		" "
		" "
		"Green       : Server sided autobhop"
		"Dark Blue   : Random throwable if the players inventory allows it"
		"Black       : Explosive! I would not touch them"
		" "
		"Yellow      : +75 HP"
		"Pink        : +50 HP"
		"Blue        : +25 HP"
		"White       : +10 HP"
		" "
		"! Touching those health mushrooms also removes any tempoary health and \"black and white\" state !"
		line
		" "
		" "
		"Specialties:"
		" "
		"When all survivors are on the ground and the round would restart \"the last chance\" mode gets activated."
		"This mode being active, the survivors will have the chance, to get revived by killing any special or boss infected."
		"This mode can only be active once. Survivors will be black and white after this mode."
		"Destroying \"Skeeting\" tank-rocks mid-air will give you +5hp."
		"When you are incapped, but other survivors are still up, you can get revived by killing any special or boss infected."
		"Survivors are able to crawl while being incapped. Keep your eyes open for mushrooms."
		" "
		line
		" "
		" "
		"Chatcommands:"
		" "
		"!hud           -> Vote to enable/disable the timer hud"
		"!countdown     -> Vote to start a countdown while being in saferoom"
		" "
		"Local usage only:"
		"----------------"
		"!speedrunmode  -> Vote to toggle between speedrunmode which disables all infected and normal mode"
		"!saveangles    -> Annoyed by survivors looking the wrong direction? Save your eye angles :)"
		"!stats         -> Print players besttimes to console"
		"!r             -> Vote to jump back to the saferoom to retry"
	]
	
	foreach(line in txt){
		ClientPrint(ent, 5, GREEN + line)
	}
	for(local i = 0; i < 10; i++){
		ClientPrint(ent, 5, " ")
	}
	ClientPrint(ent, 5, GREEN + "Check the console for information!")
}
rd_saferoom_timer
//****************************************************************************************
//																						//
//									rd_saferoom_timer.nut								//
//																						//
//****************************************************************************************



::countDownActive		<- false
::countDownStamp		<- Time()
::countdownTime			<- 3

if(!IsSoundPrecached("ui/beep07.wav")){
	PrecacheSound("ui/beep07.wav")
}

if(!IsSoundPrecached("ui/beep22.wav")){
	PrecacheSound("ui/beep22.wav")
}




// Executed by the chatcommand !countdown it will check of everybody wants to make a countdown
// ----------------------------------------------------------------------------------------------------------------------------

::saferoomTimerUseStamp <- Time()

::startSafeRoomTimer <- function(ent){
	
	if(!AllInStartSafeRoom()){
		return
	}
	
	if(!(mapName in survivorSpawnPoints)){
		ClientPrint(null, 5, "This function is only available for maps of c1-c14.")
		return
	}
	
	if(NetProps.GetPropInt(Entities.FindByClassname(null, "terror_gamerules"), "m_bInIntro")){
		return
	}
	
	if(!(Time() > saferoomTimerUseStamp + 5)){
		ClientPrint(null, 5, "This function can only be used every 5 seconds.")
		return
	}

	local playerscope = GetValidatedScriptScope(ent)
	if(!("countdown_vote" in playerscope)){
		playerscope["countdown_vote"] <- true
		foreach(player in GetHumanSurvivors()){
			local playerscope = GetValidatedScriptScope(player)
			if(!("countdown_vote" in playerscope)){
				ClientPrint(null, 5, GREEN + ent.GetPlayerName() + WHITE + " voted to start a countdown.")
				return
			}
		}
	}
	
	if(!countDownActive){
		countDownActive = true
		countDownStamp = Time()
		FreezePlayers()
		SetPlayersStartPositions()
		saferoomTimerUseStamp = Time()
		foreach(player in GetHumanSurvivors()){
			player.ValidateScriptScope()
			player.GetScriptScope().rawdelete("countdown_vote")
		}
	}
}




// Gets called from Think() function
// ----------------------------------------------------------------------------------------------------------------------------

::safeRoomTimer <- function(){
	if(countDownActive){
		if(Time() > countDownStamp + 1){
			if(countdownTime > 0){
				printTimeToChat()
				countdownTime--
				countDownStamp = Time()
			}else{
				printGoToChat()
				countDownActive = false
				countdownTime = 3
			}
		}
	}
}




// Prints timer to chat and outputs sound to all players
// ----------------------------------------------------------------------------------------------------------------------------

::printTimeToChat <- function(){
	
	local players = GetHumanSurvivors()
	
	ClientPrint(null, 5,  GREEN + countdownTime)
	foreach(player in players){
		EmitAmbientSoundOn("ui/beep07.wav", 0.75, 100, 100, player)	
	}
}

::printGoToChat <- function(){

	local players = GetHumanSurvivors()

	ClientPrint(null, 5,  ORANGE + "GO!")
	UnfreezePlayers()
	foreach(player in players){
		EmitAmbientSoundOn("ui/beep22.wav", 0.75, 100, 100, player)
	}
}




// Check if all survivors are in the beginning saferoom ( Re-entering possible )
// ----------------------------------------------------------------------------------------------------------------------------

::AllInStartSafeRoom <- function(){
	foreach(player in GetHumanSurvivors()){
		if(ResponseCriteria.GetValue(player, "instartarea" ) == "0"){
			return false
		}
	}
	return true
}


::FreezePlayers <- function(){
	foreach(player in GetHumanSurvivors()){
		NetProps.SetPropInt(player, "movetype", 0)
	}
}

::UnfreezePlayers <- function(){
	foreach(player in GetHumanSurvivors()){
		NetProps.SetPropInt(player, "movetype", 2)
	}	
}




// Sets the origin of all present players to one of in the saferoom positioned info_survivor_position entities.
// This method is only ment to be used when all survivors are in the saferoom!
// ----------------------------------------------------------------------------------------------------------------------------

::SetPlayersStartPositions <- function(){

	local offsets = [ Vector(16,16,0), Vector(16,-16,0), Vector(-16,-16,0), Vector(-16,16,0) ]
	
	local counter = 0
	
	foreach(player in GetHumanSurvivors()){
		if(counter == 4){
			counter = 0
		}
		player.SetOrigin(survivorSpawnPoints[ mapName ] + offsets[counter])
		counter++
	}
}




// These are player origins for all beginning saferooms ( c1-c14 ). We spawn every player with an offset
// ----------------------------------------------------------------------------------------------------------------------------

::survivorSpawnPoints <-
{
	c1m1_hotel				= Vector(599.989,5630.53,2851.46)
	c1m2_streets			= Vector(2360.92,5124.73,452.031)
	c1m3_mall				= Vector(6628.92,-1433.91,28.0313)
	c1m4_atrium				= Vector(-2090.7,-4624.16,540.031)

	c2m1_highway			= Vector(10895.7,7873.87,-548.574)
	c2m2_fairgrounds		= Vector(1636.83,2719.81,8.03125)
	c2m3_coaster			= Vector(4343.23,2048.8,-59.9688)
 	c2m4_barns				= Vector(3116.18,3330.29,-183.969)
	c2m5_concert			= Vector(-825.861,2292.01,-251.969)

	c3m1_plankcountry		= Vector(-12546.3,10456.1,248.893)
	c3m2_swamp 				= Vector(-8160.61,7615.23,16.0313)
	c3m3_shantytown			= Vector(-5709.34,2143.82,140.031)
	c3m4_plantation			= Vector(-5011.12,-1672.01,-92.8118)
	
	c4m1_milltown_a			= Vector(-7073.8,7719.17,117.924)
	c4m2_sugarmill_a		= Vector(3628.58,-1679.04,236.531)
	c4m3_sugarmill_b		= Vector(-1803.96,-13698.4,134.031)
	c4m4_milltown_b			= Vector(3910.57,-1569.59,236.281)
	c4m5_milltown_escape 	= Vector(-3367.6,7853.06,124.031)
	
	c5m1_waterfront			= Vector(798.114,676.213,-477.969)
	c5m2_park				= Vector(-3966.59,-1261.61,-339.969)
	c5m3_cemetery			= Vector(6402.67,8410.15,4.03125)
	c5m4_quarter			= Vector(-3291.22,4889.5,72.0313)
	c5m5_bridge				= Vector(-12039.5,5833.28,132.031)

	c6m1_riverbank			= Vector(911.249,3774.63,98.0062)
	c6m2_bedlam				= Vector(3157.03,-1218.18,-291.969)
	c6m3_port				= Vector(-2410.05,-456.402,-251.969)
	
	c7m1_docks				= Vector(13838.7,2583.8,36.2599)
	c7m2_barge				= Vector(10726.2,2445.25,180.031)
	c7m3_port				= Vector(1123.67,3230.13,174.531)
	
	c8m1_apartment			= Vector(2004.74,913.544,436.031)
	c8m2_subway				= Vector(2907.06,3063.35,20.0313)
	c8m3_sewers				= Vector(10948.2,4729.64,20.0313)
	c8m4_interior			= Vector(12376.7,12567.9,20.0313)
	c8m5_rooftop			= Vector(5405.11,8388.72,5540.03)

	c9m1_alleys				= Vector(-9951.59,-8578.78,-2.66612)
	c9m2_lots				= Vector(278.24,-1301.6,-171.969)
	
	c10m1_caves				= Vector(-11745.4,-14862.1,-214.225)
	c10m2_drainage 			= Vector(-11014.6,-9103.6,-587.969)
	c10m3_ranchhouse		= Vector(-8263.9,-5550.87,-20.9688)
	c10m4_mainstreet		= Vector(-3082.28,-23.326,164.031)
	c10m5_houseboat			= Vector(2120.59,4742.14,-59.9688)
	
	c11m1_greenhouse		= Vector(6815.1,-672.996,772.031)
	c11m2_offices			= Vector(5278.98,2785.43,52.0313)
	c11m3_garage			= Vector(-5368.26,-3069.3,20.0313)
	c11m4_terminal			= Vector(-447.003,3577.39,300.031)
	c11m5_runway			= Vector(-6716.97,12066.8,156.031)
	
	c12m1_hilltop			= Vector(-8073.41,-15118.7,283.495)
	c12m2_traintunnel		= Vector(-6611.49,-6712.19,352.031)
	c12m3_bridge			= Vector(-927.759,-10382.7,-59.9688)
	c12m4_barn				= Vector(7754.14,-11374,444.031)
	c12m5_cornfield			= Vector(10480.9,-566.041,-24.9688)
	
	c13m1_alpinecreek		= Vector(-3012.43,-589.132,68.0313)
	c13m2_southpinestream	= Vector(8566.48,7505.06,500.031)
	c13m3_memorialbridge	= Vector(-4372.79,-5150.93,100.031)
	c13m4_cutthroatcreek	= Vector(-3387.78,-9284.08,364.031)
	
	c14m1_junkyard			= Vector(-4189.09,-10697.9,-296.294)
	c14m2_lighthouse		= Vector(2220.41,-1103.17,452.031)
}




// Prevent players from using restartFromSaferoom on a finale
// ----------------------------------------------------------------------------------------------------------------------------

::valveFinaleMaps <-
[
	"c1m4_atrium"
	"c2m5_concert"
	"c3m4_plantation"
	"c4m5_milltown_escape"
	"c5m5_bridge"
	"c6m3_port"
	"c7m3_port"
	"c8m5_rooftop"
	"c9m2_lots"
	"c10m5_houseboat"
	"c11m5_runway"
	"c12m5_cornfield"
	"c13m4_cutthroatcreek"
	"c14m2_lighthouse"
]




//insafespot
//incheckpoint
rd_speedrun_mode
//****************************************************************************************
//																						//
//										rd_speedrun_mode.nut							//
//																						//
//****************************************************************************************




// Playing locally allows the player to enable the speedrun mode.
// !speedrunmode to toggle it. !r to respawn in the saferoom
// ----------------------------------------------------------------------------------------------------------------------------

::speedrunModeEnabled <- false

::speedrunModeToggle <- function(ent){
	
	if(!OnLocalServer()){
		return
	}
	
	ent.ValidateScriptScope()
	local scope = ent.GetScriptScope()
	scope["speedrun_mode"] <- true
	
	foreach(player in GetHumanSurvivors()){
		local playerscope = player.GetScriptScope()
		if(!("speedrun_mode" in playerscope)){
			ClientPrint(null, 5, GREEN + ent.GetPlayerName() + WHITE + " voted to " + ( speedrunModeEnabled ? "disable" : "enable" ) + " the speedrun mode!")
			return
		}
	}
	
	speedrunModeEnabled = !speedrunModeEnabled
	
	local speedrunDirector =
	{
		ProhibitBosses	= [true, false]
		MobMaxSize		= [0, 30]
		CommonLimit		= [0, 30]
		MobSpawnMinTime	= [9999, 90]
		MobSpawnMaxTime	= [9999, 180]
		MaxSpecials		= [0, 6]
		SpecialRespawnInterval = [9999, 45]
	}
	
	foreach(var,valarr in speedrunDirector){
		if(speedrunModeEnabled){
			SessionOptions[var.tostring()] <- valarr[0]
			removeAllInfected()
		}else{
			SessionOptions[var.tostring()] <- valarr[1]
		}
	}
	
	foreach(player in GetHumanSurvivors()){
		player.GetScriptScope().rawdelete("speedrun_mode")
	}
	
	ClientPrint(null, 5, WHITE + "Speedrun mode " + GREEN + ( speedrunModeEnabled ? "enabled" : "disabled") )
}




// While being in speedrun mode the survivor can jump back to the saferoom to restart his run
// ----------------------------------------------------------------------------------------------------------------------------

::restartFromSaferoom <- function(ent){
	
	if(!speedrunModeEnabled){
		ClientPrint(null, 5, WHITE + "Jumping back to the saferoom is restricted to " + GREEN + "speedrunmode!")
		return
	}
	
	if(!(Director.GetMapName() in survivorSpawnPoints)){
		return
	}
	
	if(valveFinaleMaps.find(Director.GetMapName()) != null){
		ClientPrint(null, 5, WHITE + "Jumping back to the saferoom is disabled for finales")
		return
	}
	
	local scope = GetValidatedScriptScope(ent)
	
	scope["go_back_2_safe"] <- true
	
	foreach(player in GetHumanSurvivors()){
		local playerscope = player.GetScriptScope()
		if(!("go_back_2_safe" in playerscope)){
			ClientPrint(null, 5, GREEN + ent.GetPlayerName() + WHITE + " voted to go back to the saferoom!")
			return
		}
	}
	
	foreach(player in GetHumanSurvivors()){
		local playerscope = GetValidatedScriptScope(player)
		playerscope.rawdelete("go_back_2_safe")
		local prevBest = PlayerTimeData[player].time_best
		PlayerTimeData[player] <- { timerActive = false, finished = false, startTime = Time(), endTime = Time(), time_best = prevBest, ticks = 0, seconds = 0 }
		bunnyPlayers.rawdelete(player)
		NetProps.SetPropInt(player, "m_afButtonDisabled", NetProps.GetPropInt(player, "m_afButtonDisabled") & ~2)
		GLOBALS.allowBulletTime = false
		
		// Set player eye angles when there are any saved
		if(player in SavedplayerEyeAngles){
			player.SnapEyeAngles(SavedplayerEyeAngles[player].EyeAngles)
		}
		player.SetVelocity(Vector(0,0,256))
	}
	
	SetPlayersStartPositions()
	setPlayersHealth()
}




// Prints all times of known maps to the console
// ----------------------------------------------------------------------------------------------------------------------------

::outputStats <- function(ent){
	
	if(ent != GetListenServerHost()){
		return
	}
	
	foreach(mapname in valveMaps){
		local str = null
		str = FileToString("rocketdude/speedrun/" + mapname + ".txt")
		if(str != null){
			str = strip(str)
			if(isNumeric(str)){
				ClientPrint(ent, 5, GREEN + mapname + GetCharChain(" ", 32 - mapname.len()) + ": " + str)
			}else{
				ClientPrint(ent, 5, ORANGE + mapname + GetCharChain(" ", 32 - mapname.len()) + ": INVALID DATA IN TEXT FILE")
			}
		}else{
			ClientPrint(ent, 5, BLUE + mapname + GetCharChain(" ", 32 - mapname.len()) + ": NO RECORD YET")
		}
	}
	for(local i = 0; i < 10; i++){
		ClientPrint(ent, 5, " ")
	}
	ClientPrint(ent, 5, GREEN + "Check the console for your stats!")
}




// Removes all infected like nb_delete_all does
// ----------------------------------------------------------------------------------------------------------------------------

::removeAllInfected <- function(){
	local player = null
	local witch = null
	local common = null
	
	while(player = Entities.FindByClassname(player, "player")){
		if(IsPlayerABot(player) && player.GetZombieType() != 9){
			player.Kill()
		}
	}
	
	while(witch = Entities.FindByClassname(witch, "witch")){
		if(witch.IsValid()){
			witch.Kill()
		}
	}
	
	while(common = Entities.FindByClassname(common, "infected")){
		if(common.IsValid()){
			common.Kill()
		}
	}
}




// Save player angles for speedrunning
// ----------------------------------------------------------------------------------------------------------------------------

::SavedplayerEyeAngles <- {}

function savePlayerEyeAngles(ent){
	
	if(!OnLocalServer()){
		ClientPrint(null, 5, WHITE + "Saving player angles is restricted to local servers!")
		return
	}
	
	if(!speedrunModeEnabled){
		ClientPrint(null, 5, WHITE + "Enable speedrunmode first ( " + GREEN + "!speedrunmode" + WHITE + " in chat )!")
		return
	}
	
	if(ResponseCriteria.GetValue(ent, "instartarea" ) == "0"){
		ClientPrint(null, 5, WHITE + "Move to the start area to save your eye angles!")
		return
	}
	
	if(ent.IsValid()){
		SavedplayerEyeAngles[ent] <- { EyeAngles = ent.EyeAngles() }
		ClientPrint(null, 5, WHITE + "Your eye angles got " + GREEN + "saved!")
	}
}
rd_utils
//****************************************************************************************
//																						//
//										rd_utils.nut									//
//																						//
//****************************************************************************************


getroottable()["TRACE_MASK_ALL"] <- -1
getroottable()["TRACE_MASK_SHOT"] <- 1174421507
getroottable()["TRACE_MASK_VISION"] <- 33579073
getroottable()["TRACE_MASK_NPC_SOLID"] <- 33701899
getroottable()["TRACE_MASK_PLAYER_SOLID"] <- 33636363
getroottable()["TRACE_MASK_VISIBLE_AND_NPCS"] <- 33579137


getroottable()["WHITE"]		<- "\x01"
getroottable()["BLUE"]		<- "\x03"
getroottable()["ORANGE"]	<- "\x04"
getroottable()["GREEN"]		<- "\x05"


::ZombieTypes <-
{
	COMMON		= 0
	SMOKER		= 1
	BOOMER		= 2
	HUNTER		= 3
	SPITTER		= 4
	JOCKEY		= 5
	CHARGER		= 6
	WITCH		= 7
	TANK		= 8
	SURVIVOR	= 9
	MOB			= 10
	WITCHBRIDE	= 11
	MUDMEN		= 12
}

::TEAMS <-
{ 
	SPECTATOR	= 1
	SURVIVOR	= 2
	INFECTED	= 3
}

::damageTypes <-
{
	GENERIC			= 0
	CRUSH			= 1
	BULLET			= 2
	SLASH			= 4
	BURN			= 8
	VEHICLE			= 16
	FALL			= 32
	BLAST			= 64
	CLUB			= 128
	SHOCK			= 256
	SONIC			= 512
	ENERGYBEAM		= 1024
	DROWN			= 16384
	PARALYSE		= 32768
	NERVEGAS		= 65536
	POISON			= 131072
	RADIATION		= 262144
	DROWNRECOVER	= 524288
	ACID			= 1048576
	SLOWBURN		= 2097152
	REMOVENORAGDOLL	= 4194304
}


// Creates the think timer which calls "Think()" every tick
// ----------------------------------------------------------------------------------------------------------------------------

function createThinkTimer(){
	local timer = null
	while (timer = Entities.FindByName(null, "thinkTimer")){
		timer.Kill()
	}
	timer = SpawnEntityFromTable("logic_timer", { targetname = "thinkTimer", RefireTime = 0.01 })
	timer.ValidateScriptScope()
	timer.GetScriptScope()["scope"] <- this

	timer.GetScriptScope()["func"] <- function (){
		scope.Think()
	}
	timer.ConnectOutput("OnTimer", "func")
	EntFire("!self", "Enable", null, 0, timer)
}




// Remove all deathcams e.g on c8m5_rooftop
// ----------------------------------------------------------------------------------------------------------------------------

function removeDeathFallCameras(){
	local deathCam = null
	while (deathCam = Entities.FindByClassname(deathCam, "point_deathfall_camera")){
		deathCam.Kill()
	}
}




// All needed cvars
// ----------------------------------------------------------------------------------------------------------------------------

::cvars <-
{
	//Survivor settings
	sv_infinite_ammo = 1
	survivor_allow_crawling = 1
	survivor_crawl_speed = 45
	first_aid_kit_max_heal = 200
	survivor_respawn_with_guns = 0
	first_aid_heal_percent = 0.8
	z_grab_ledges_solo = 1
	z_tank_incapacitated_decay_rate = 5
	//Grenadelauncher settings
	grenadelauncher_velocity = 1100
	grenadelauncher_startpos_right = 0
	grenadelauncher_startpos_forward = 16
	grenadelauncher_vel_variance = 0
	grenadelauncher_vel_up = 0
	//Force settings
	phys_explosion_force = 4096
	melee_force_scalar = 16
	melee_force_scalar_combat_character = 512
	phys_pushscale = 512
	//Infected settings
	z_force_attack_from_sound_range = 512
	z_brawl_chance = 1
	//Medicals
	pain_pills_health_threshold = 199
	pain_pills_health_value = 100
	//Items
	sv_infected_riot_control_tonfa_probability = 0
	sv_infected_ceda_vomitjar_probability = 0
	//Votes
	sv_vote_creation_timer = 8
	sv_vote_plr_map_limit = 128
	//Misc
	z_spawn_flow_limit = 99999
	director_afk_timeout = 99999
	mp_allowspectators = 0
}




local cvarChangeTime = Time()

function checkCvars(){
	if(Time() > cvarChangeTime + 4){
		foreach(var, value in cvars){
			if(Convars.GetFloat(var) != value.tofloat()){
				Convars.SetValue(var, value)
			}
		}
		cvarChangeTime = Time()
	}
}




// Create a func_timescale entity for the "bullet time"
// ----------------------------------------------------------------------------------------------------------------------------

timeScaler <- null
function createBulletTimerEntity(){
	while (timeScaler = Entities.FindByName(null, "timeScaler")){
		timeScaler.Kill()
	}
	timeScaler = SpawnEntityFromTable("func_timescale",
		{
			targetname = "timeScaler"
			acceleration = 0.05
			angles = "0 0 0"
			origin = Vector(0, 0, 0)
			blendDataMultiplier = 3.0
			minBlendRate = 0.1
			desiredTimescale = 0.25
		}
	)
}




// We roll a dice with probability of X to decide if event Y will occur 
// ----------------------------------------------------------------------------------------------------------------------------

function rollDice(probability){
	local roll = RandomInt(1, 100)
	
	if(probability == 100){
		return true
	}else if(roll <= probability){
		return true
	}
	return false
}




// Returns the closest survivor in any radius
// ----------------------------------------------------------------------------------------------------------------------------

function getClosestSurvivorTo(ent){
	local survivor = null
	local previousDistance = 0.0
	local closest = null
	local currentDistance = null
	
	foreach(survivor in GetSurvivors()){
		if(survivor != ent){
			if(previousDistance == 0.0){
				previousDistance = (ent.GetOrigin() - survivor.GetOrigin()).Length()
				closest = survivor
			}else{
				currentDistance = (ent.GetOrigin() - survivor.GetOrigin()).Length()
				if(currentDistance < previousDistance){
					previousDistance = currentDistance
					closest = survivor
				}
			}
		}
	}
	return closest
}




// Returns array of all players (bots included)
// ----------------------------------------------------------------------------------------------------------------------------

function GetSurvivors(){
	local ent = null
	while (ent = Entities.FindByClassname(ent, "player")){
		if (ent.GetZombieType() == 9){
			yield ent
		}
	}
}


// Returns array of all survivors (bots excluded)
// ----------------------------------------------------------------------------------------------------------------------------

::GetHumanSurvivors <- function(){
	local ent = null
	while (ent = Entities.FindByClassname(ent, "player")){
		if (ent.GetZombieType() == 9 && !IsPlayerABot(ent)){
			yield ent
		}
	}
}




// Validates script scope and returns it
// ----------------------------------------------------------------------------------------------------------------------------

::GetValidatedScriptScope <- function(ent){
	ent.ValidateScriptScope()
	return ent.GetScriptScope()
}




// Precache survivor models so game wont crash due to the "cm_NoSurvivorBots = 1" bug...Valve please fix
// ----------------------------------------------------------------------------------------------------------------------------

function precacheSurvivorModels(){
	
	local path = "models/survivors/"
	local models =
	[
		"survivor_coach.mdl", "survivor_gambler.mdl", "survivor_manager.mdl", "survivor_mechanic.mdl",
		"survivor_namvet.mdl", "survivor_biker.mdl", "survivor_producer.mdl", "survivor_teenangst.mdl"
	]

	foreach(model in models){
		if (!IsModelPrecached( path + model)){
			PrecacheModel(path + model)
		}
	}
}




// Precache projectile models and mushroom
// ----------------------------------------------------------------------------------------------------------------------------

function precacheRocketDudeModels(){
	
	local models =
	[
		"models/props_collectables/mushrooms_glowing.mdl",
		"models/w_models/weapons/w_rd_grenade_scale_x4_burn.mdl",
		"models/w_models/weapons/w_rd_grenade_scale_x4.mdl"
	]
	
	foreach(model in models){
		if(!IsModelPrecached(model)){
			PrecacheModel(model)
		}
	}
}




// Check if the current map is a valve map
// ----------------------------------------------------------------------------------------------------------------------------

::IsValveMap <- function(){
	if (valveMaps.find(mapName) == null){
		return false
	}
	return true
}




// Array of maps c1 - c14
// ----------------------------------------------------------------------------------------------------------------------------

::valveMaps <- [
	// DEAD CENTER
	"c1m1_hotel"
	"c1m2_streets"
	"c1m3_mall"
	"c1m4_atrium"
	// DARK CARNIVAL
	"c2m1_highway"
	"c2m2_fairgrounds"
	"c2m3_coaster"
	"c2m4_barns"
	"c2m5_concert"
	// SWAMP FEVER
	"c3m1_plankcountry"
	"c3m2_swamp"
	"c3m3_shantytown"
	"c3m4_plantation"
	// HARD RAIN
	"c4m1_milltown_a"
	"c4m2_sugarmill_a"
	"c4m3_sugarmill_b"
	"c4m4_milltown_b"
	"c4m5_milltown_escape"
	// THE PARISH
	"c5m1_waterfront"
	"c5m1_waterfront_sndscape"
	"c5m2_park"
	"c5m3_cemetery"
	"c5m4_quarter"
	"c5m5_bridge"
	// THE PASSING
	"c6m1_riverbank"
	"c6m2_bedlam"
	"c6m3_port"
	// THE SACRIFICE
	"c7m1_docks"
	"c7m2_barge"
	"c7m3_port"
	// NO MERCY
	"c8m1_apartment"
	"c8m2_subway"
	"c8m3_sewers"
	"c8m4_interior"
	"c8m5_rooftop"
	// CRASH COURSE
	"c9m1_alleys"
	"c9m2_lots"
	// DEATH TOLL
	"c10m1_caves"
	"c10m2_drainage"
	"c10m3_ranchhouse"
	"c10m4_mainstreet"
	"c10m5_houseboat"
	// DEAD AIR
	"c11m1_greenhouse"
	"c11m2_offices"
	"c11m3_garage"
	"c11m4_terminal"
	"c11m5_runway"
	// BLOOD HARVEST
	"c12m1_hilltop"
	"c12m2_traintunnel"
	"c12m3_bridge"
	"c12m4_barn"
	"c12m5_cornfield"
	// COLD STREAM
	"c13m1_alpinecreek"
	"c13m2_southpinestream"
	"c13m3_memorialbridge"
	"c13m4_cutthroatcreek"
	// THE LAST STAND
	"c14m1_junkyard"
	"c14m2_lighthouse"
]




// Mushroom positions of maps ( c1 -14 )
// ----------------------------------------------------------------------------------------------------------------------------

::mushroomPositions <-
{
	c1m1_hotel =
	[
		{ origin = Vector(910.302,5475.81,2656.03), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(3430.85,7487.11,1664.03), angles = "0 90 0", rotating = false, type = "item" }
		{ origin = Vector(2168.26,5703.24,2464.03), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(2480.38,6217.6,2656.03), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(2311.68,7656.3,2464.03), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(1924.48,5762.97,1336.03), angles = "0 90 0", rotating = false, type = "medium" }
		{ origin = Vector(540.982,4832.06,1320.03), angles = "0 0 0", rotating = false, type = "large" }
	]
	c1m2_streets =
	[
		{ origin = Vector(1059.62,4854.1,704.031), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(-6132.59,-1135.09,472.031), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-3897.52,2238.46,320.031), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-1253.71,777.393,811.381), angles = "0 90 0", rotating = false, type = "tiny" }
		{ origin = Vector(-5343.9,-2082.83,456.031), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-8619.01,-2111.07,963.138), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-7171.31,-4490.4,1224.7), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(1598.73,4225.02,521.433), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-2223.24,982.305,41.804), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-4588.86,1475.42,440.031), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-8635.88,-4498.73,440.031), angles = "0 0 0", rotating = false, type = "large" }
	]
	c1m3_mall =
	[
		{ origin = Vector(6447.99,-2648.58,288.031), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(1294.77,-2343.76,325.803), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(675.635,-4834.9,536.031), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(3600.03,-2384.03,825.031), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(4008.45,-290.606,0.03125), angles = "0 90 0", rotating = false, type = "tiny" }
		{ origin = Vector(2266.34,-1561.81,536.031), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-198.189,-5201.84,415.031), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-1891.39,-4127.36,574.433), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(6997.87,-1362.59,152.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(3896.59,-2873.28,318.958), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(2754.07,-1866.65,280.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(2414.66,-2412.11,536.031), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(1584.63,-5446.67,364.031), angles = "0 90 0", rotating = false, type = "medium" }
		{ origin = Vector(-1239.21,-4472.65,318.958), angles = "0 0 0", rotating = false, type = "large" }
	]
	c1m4_atrium =
	[
		{ origin = Vector(-1806.74,-4825.39,621.031), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(-2388.27,-3878.41,824.031), angles = "0 90 0", rotating = false, type = "item" }
		{ origin = Vector(-3856.95,-3123.88,536.031), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-2572.11,-5303.06,553.52), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-6031.35,-3306.58,792.031), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-3340.16,-4001.26,744.781), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-5103.33,-3918.44,408.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-4451.52,-3207.95,106.031), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-3209.12,-3865.85,107.617), angles = "0 90 0", rotating = false, type = "large" }
		{ origin = Vector(-5343.23,-4186.55,1080.03), angles = "0 0 0", rotating = false, type = "large" }
	]
	c2m1_highway =
	[
		{ origin = Vector(9530.42,8430.98,-176.474), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(2273.79,4287.25,-936.719), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(2188.58,3274.16,-807.969), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(3462.18,8450.17,-843.079), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-1168.83,2091.84,-1738.7), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(6915.73,7505.84,-675.791), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(3015.77,6931.89,-899.608), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(2914.95,4895.79,-507.969), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(1163.45,2193.78,-1213.73), angles = "0 0 0", rotating = false, type = "large" }
		{ origin = Vector(-1334.14,-2035.05,-510.97), angles = "0 0 0", rotating = false, type = "large" }
	]
	c2m2_fairgrounds =
	[
		{ origin = Vector(3966.348633,-465.165253,48.42), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(-1985.2,-3877.99,32.0313), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-3616.02,-721.635,0.03125), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(1576.99,1513.79,8.03125), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-3329.16,-4239.81,352.595), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(2710.8,516.643,200.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-2126.08,318.905,128.031), angles = "0 90 0", rotating = false, type = "medium" }
		{ origin = Vector(-874.256,-1539.82,128.031), angles = "0 90 0", rotating = false, type = "medium" }
		{ origin = Vector(-1493.91,-4415.5,-1.77925), angles = "0 0 0", rotating = false, type = "large" }
	]
	c2m3_coaster =
	[
		{ origin = Vector(2048.36,3296.04,196.031), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(-1582.4,1617.42,128.281), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-87.7301,3973.93,208.031), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(2797.84,1638.93,-35.5456), angles = "0 90 0", rotating = false, type = "tiny" }
		{ origin = Vector(-3968.39,1550.79,413.031), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-66.8414,3611.93,208.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-2754.75,1143.2,620.031), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-3743.81,3732.83,544.031), angles = "0 0 0", rotating = false, type = "large" }
	]
	c2m4_barns =
	[
		{ origin = Vector(3138.24,3629.02,-3.96875), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(-2756.8,1308.9,-43.9688), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(1081.55,1024.68,-147.969), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(2166.15,1419.64,18.4938), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-226.231,886.084,387.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-1918.83,95.4541,32.0313), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-3042.29,1694.38,-255.969), angles = "0 0 0", rotating = false, type = "large" }
		{ origin = Vector(3715.38,662.404,-191.969), angles = "0 0 0", rotating = false, type = "tiny" }
	]
	c2m5_concert =
	[
		{ origin = Vector(-1096.98,2354.9,-255.969), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(-2308.02,3211.99,140.031), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-2637.91,3340.46,312.678), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-448.975,2760.9,-255.969), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-1979.98,2505.26,191.031), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-2679.04,2500.67,191.031), angles = "0 0 0", rotating = false, type = "large" }
	]
	c3m1_plankcountry =
	[
		{ origin = Vector(-10586.4,10170.8,571.008), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(-5590.18,6843.73,220.031), angles = "0 90 0", rotating = false, type = "item" }
		{ origin = Vector(-980.031,4902.7,144.16), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-1067.97,4902.7,144.16), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-1022.58,4902.7,144.16), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-8121.03,7213.5,266.604), angles = "0 90 0", rotating = false, type = "tiny" }
		{ origin = Vector(-5392.27,5891.52,256.034), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(-6212.27,7861.63,48.0313), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-3386.78,6069.38,698.369), angles = "0 90 0", rotating = false, type = "medium" }
		{ origin = Vector(-2159.44,8640.77,253.1), angles = "0 0 0", rotating = false, type = "large" }
		{ origin = Vector(-1022.87,4946.13,332.031), angles = "0 0 0", rotating = false, type = "large" }
	]
	c3m2_swamp =
	[
		{ origin = Vector(-8114.13,5131.93,295.391), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(-1801.48,2818.93,47.6229), angles = "0 90 0", rotating = false, type = "item" }
		{ origin = Vector(-1884.05,3218.16,36.4844), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(1936.43,1250.89,19.2292), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(7614.55,3192.79,122.329), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-1895.61,3104.54,226.169), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(4779.86,1102.69,41.265), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(8702.53,512.321,569.088), angles = "0 0 0", rotating = false, type = "large" }
	]
	c3m3_shantytown =
	[
		{ origin = Vector(-5293.03,1199.51,871.913), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(-4088.94,-3094.7,189.711), angles = "0 90 0", rotating = false, type = "item" }
		{ origin = Vector(-659.526,-2485.02,4.4949), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-4605.58,-236.376,181.425), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(1532.24,-4869.25,24.0313), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-2651.48,-931.619,74.849), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-5504.8,-3256.66,308.68), angles = "0 90 0", rotating = false, type = "medium" }
		{ origin = Vector(3013.28,-4477.09,86.1035), angles = "0 90 0", rotating = false, type = "large" }
	]
	c3m4_plantation =
	[
		{ origin = Vector(-3650.61,-1527.09,542.849), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(1665.36,900.156,127.237), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(1788.93,-415.403,224.031), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(2591.66,57.0649,224.031), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(2032.64,252.058,416.031), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(1664.07,314.551,416.031), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(1663.35,291.383,224.031), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(1320.5,251.85,416.031), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(2743.47,-3207.78,65.3052), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(3040.81,2016.1,133.507), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-1426.21,-3443.73,187.907), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(1665.04,536.081,640.031), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(1867.68,-138.64,600.031), angles = "0 0 0", rotating = false, type = "large" }
	]
	c4m1_milltown_a =
	[
		{ origin = Vector(-5801.77,7494.31,1009.75), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(383.878,3381.04,368.031), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-6356.01,7456.24,104.031), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-1547.58,6908.92,200.773), angles = "0 90 0", rotating = false, type = "tiny" }
		{ origin = Vector(4152.82,1222.7,184.031), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-609.475,6187.33,296.031), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(1482.28,4149.37,435.32), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(3617.5,2161.08,368.155), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(3338.04,179.84,586.974), angles = "0 0 0", rotating = false, type = "large" }
	]
	c4m2_sugarmill_a =
	[
		{ origin = Vector(4260,-3671.71,406.515), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(-719.081,-7283.16,441.979), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-921.116,-8917.79,295.392), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(2429.61,-5696.4,124.659), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(4315.07,-4528.41,97.6315), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-10.6827,-12670.2,113.272), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(3212.06,-3036.58,1164.28), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(2725.17,-4272.82,329.469), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(2581.79,-6098.72,100.829), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-345.992,-8559.92,624.281), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(-1507.19,-13161,1141.23), angles = "0 0 0", rotating = false, type = "large" }
	]
	c4m3_sugarmill_b =
	[
		{ origin = Vector(-925.624,-13530.8,432.031), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(-53.2867,-6479.09,441.729), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(2434.5,-5315.34,194.031), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(2587.19,-6096.47,100.157), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(4114.54,-3217.8,406.265), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-1106.24,-8463.68,624.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(936.826,-6244.9,638.807), angles = "0 90 0", rotating = false, type = "medium" }
		{ origin = Vector(430.224,-4582.72,310.428), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(1741.85,-3929.74,737.02), angles = "0 0 0", rotating = false, type = "large" }
	]
	c4m4_milltown_b =
	[
		{ origin = Vector(3331.17,-847.188,586.974), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(665.963,2783.55,227.927), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(387.227,3489.37,336.38), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(4327.79,1780.41,363.761), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(673.109,4776.85,131.802), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-1486.32,7441.23,366.277), angles = "0 0 0", rotating = false, type = "large" }
	]
	c4m5_milltown_escape =
	[
		{ origin = Vector(-5868.91,8159.17,348.031), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(-4641.69,7626.45,479.597), angles = "0 90 0", rotating = false, type = "item" }
		{ origin = Vector(-6338.14,7169.93,104.031), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-5432.06,7068.65,100.536), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-5315.37,8567.07,584.038), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-7088.42,7698.42,113.924), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(-5815.44,6623.25,126.972), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-5801.8,7496.52,1009.75), angles = "0 0 0", rotating = false, type = "large" }
	]
	c5m1_waterfront =
	[
		{ origin = Vector(-335.683,64.8597,-52.3949), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(-1177.66,-2398.49,144.031), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-2347.78,-553.647,-367.969), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-2720.07,-1612.63,-13.3263), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-1904.66,-1864.33,-71.9454), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-3068.68,-2336.01,-157.992), angles = "0 0 0", rotating = false, type = "medium" }
	]
	c5m2_park =
	[
		{ origin = Vector(-5054.91,-2216.52,-127.982), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(-9802.61,-5213.03,-79.9688), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-8046.08,-6670.04,-247.969), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-7940.64,-6671.62,-247.969), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-7551.83,-416.524,-127.969), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-7166.92,-3491.44,44.5587), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-8789.87,-5193.33,89.7595), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-8113.72,-5774.25,485.766), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-6815.91,-8437.09,250.906), angles = "0 0 0", rotating = false, type = "large" }
	]
	c5m3_cemetery =
	[
		{ origin = Vector(6127.99,7700.52,208.031), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(5907.05,1012.93,155.243), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(2837.89,2634.4,176.031), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(4728.95,4570.35,131.378), angles = "0 90 0", rotating = false, type = "tiny" }
		{ origin = Vector(4432.79,3202.69,154.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(6189.01,1337.86,-159.969), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(8768.77,-6591.84,756.031), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(7417.2,-8926.28,264.031), angles = "0 0 0", rotating = false, type = "large" }
	]
	c5m4_quarter =
	[
		{ origin = Vector(-2368.29,3616.46,64.0313), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(-639.981,1607.46,224.031), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-569.655,1031.48,96.0313), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-3650.66,3888.94,384.031), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(29.7625,-1607.78,287.837), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-1193.22,1488.05,452.401), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-813.288,-2031.78,422.465), angles = "0 0 0", rotating = false, type = "large" }
	]
	c5m5_bridge =
	[
		{ origin = Vector(-6151.38,6420.53,765.699), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(-1954.61,6463.8,852.885), angles = "0 90 0", rotating = false, type = "item" }
		{ origin = Vector(2792.36,6336.46,790.031), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(2792.36,6450.22,790.031), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(2792.36,6211.1,790.031), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-7031.1,6298.27,456.031), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-12334.9,6552.2,453.565), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-3862.72,6225.25,897.47), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(2290.68,6425.58,790.031), angles = "0 90 0", rotating = false, type = "medium" }
		{ origin = Vector(14201,6326.86,790.031), angles = "0 90 0", rotating = false, type = "large" }
	]
	c6m1_riverbank =
	[
		{ origin = Vector(669.412,3116.3,640.031), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(1168,4459.88,510.031), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(2095.14,1641.12,352.031), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(4520.78,2670.4,555.536), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(4461.7,2397.54,224.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-4004.19,554.621,864.031), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-4223.99,1571.49,727.091), angles = "0 0 0", rotating = false, type = "large" }
	]
	c6m2_bedlam =
	[
		{ origin = Vector(2148.07,-1201.23,288.031), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(1750.27,3709.59,-275.002), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(1498.32,4839.88,-159.969), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-224.862,1354.13,28.093), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(1213.93,1966.51,336.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(998.689,2701.23,96.0313), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(1589.86,4615.77,32.0313), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(2868.05,5702.03,-1063.97), angles = "0 90 0", rotating = false, type = "large" }
		{ origin = Vector(5883.71,4207.33,-890.867), angles = "0 0 0", rotating = false, type = "large" }
	]
	c6m3_port =
	[
		{ origin = Vector(-2248.68,-646.404,320.031), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(-1761.71,256.046,156.841), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(583.696,1765.25,160.031), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-1184.55,-952.523,0.03125), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-788.678,-952.523,0.03125), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-1379.67,-952.523,0.03125), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-374.426,-889.011,0.0760522), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(447.796,1824.08,160.031), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-901.026,2123.37,320.031), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(345.633,-358.962,184.031), angles = "0 0 0", rotating = false, type = "small" }
	]
	c7m1_docks =
	[
		{ origin = Vector(14063.8,2301.94,16.2378), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(10153.7,449.507,129.809), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(11410.8,-229.048,-63.9688), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(12001.8,-787.905,-35.1111), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(5208.64,552.395,382.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(4679.74,764.071,303.384), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(2516.95,-188.518,138.451), angles = "0 90 0", rotating = false, type = "medium" }
		{ origin = Vector(3404.88,1676.66,336.031), angles = "0 90 0", rotating = false, type = "large" }
	]
	c7m2_barge =
	[
		{ origin = Vector(9920.64,608.323,322.374), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(-3827.21,671.672,342.031), angles = "0 90 0", rotating = false, type = "item" }
		{ origin = Vector(2140.39,1436.81,132.031), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(5435.93,755.192,256.282), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(10062.7,2095.96,305.796), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-593.486,2559.1,756.694), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-2894.79,725.009,576.031), angles = "0 90 0", rotating = false, type = "large" }
	]
	c7m3_port =
	[
		{ origin = Vector(1052.7,2623.67,544.031), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(-2137.34,-539.338,-95.9688), angles = "0 90 0", rotating = false, type = "item" }
		{ origin = Vector(680.945,2047.86,160.031), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(583.284,1770.19,160.758), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-938.757,931.624,352.031), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(736.739,2264.6,640.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(1560.46,168.305,345.56), angles = "0 90 0", rotating = false, type = "large" }
	]
	c8m1_apartment =
	[
		{ origin = Vector(1811.17,1797.96,640.031), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(896.289,3030.33,637.176), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(2797.04,4191.32,15.8693), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(334.88,767.923,957.29), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(2624.62,2212.92,945.241), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(1976.26,3944.68,608.031), angles = "0 0 0", rotating = false, type = "large" }
		{ origin = Vector(3493.26,4004.85,1436.03), angles = "0 0 0", rotating = false, type = "tiny" }
	]
	c8m2_subway =
	[
		{ origin = Vector(2937.46,4155.97,-178.67), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(8467.56,3832.97,376.031), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(4526.2,3801.29,-287.969), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(2204.75,3968.89,-335.969), angles = "0 90 0", rotating = false, type = "tiny" }
		{ origin = Vector(6775.15,2899.15,-178.67), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(7567.9,3417.3,424.031), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(8328.45,4596.1,1216.03), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(8805.87,5685.34,768.031), angles = "0 0 0", rotating = false, type = "large" }
	]
	c8m3_sewers =
	[
		{ origin = Vector(12631.2,5360.45,957.54), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(12712.9,6696.47,800.031), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(13137.6,7424.46,16.0313), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(11203.7,5091.59,712.031), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(11852.1,7923.79,276.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(13711,10120.7,-358.28), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(13068.6,10985.1,-191.969), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(13070.8,11448,-277.217), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(13788.6,11101.2,746.031), angles = "0 0 0", rotating = false, type = "large" }
		{ origin = Vector(13198.7,13927.2,5624.03), angles = "0 0 0", rotating = false, type = "large" }
	]
	c8m4_interior =
	[
		{ origin = Vector(12162.3,13317.7,42.4583), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(13687.1,14624.8,576.031), angles = "0 90 0", rotating = false, type = "item" }
		{ origin = Vector(12718.3,14651.8,424.772), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(13695.3,13991.1,5641.54), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(12314.1,13388.4,152.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(13439.6,15006.3,624.031), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(14045.5,14862.1,5920.03), angles = "0 0 0", rotating = false, type = "large" }
	]
	c8m5_rooftop =
	[
		{ origin = Vector(7487.75,9322.88,6314.71), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(5986.62,7845.9,6210.03), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(7303.02,8911.17,6092.24), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(7048.08,9024.08,6096.03), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(6960.91,9464.52,5644.03), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(7022.35,7746.25,16.0313), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(7714.38,9340.58,5952.03), angles = "0 0 0", rotating = false, type = "large" }
	]
	c9m1_alleys =
	[
		{ origin = Vector(-8671.74,-9921.56,384.031), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(205.191,-6577.32,-45.685), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-2334.43,-8371.1,0.03125), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-7876.03,-10448,192.031), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-6194.29,-10207.2,348.124), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(-2692.39,-9362.13,362.682), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-1326.74,-3292.39,445.932), angles = "0 0 0", rotating = false, type = "large" }
	]
	c9m2_lots =
	[
		{ origin = Vector(1621.53,-1232.15,186.989), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(6858.532,6943.091,223.031), angles = "0 90 0", rotating = false, type = "item" }
		{ origin = Vector(7559.25,6343.99,48.0313), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(7559.25,6680.94,48.0313), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(7535.98,6160.13,427.767), angles = "0 90 0", rotating = false, type = "tiny" }
		{ origin = Vector(3568.14,-492.286,35.807), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(1761.86,218.214,45.0175), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(7844.706,6643.726,375.036), angles = "0 90 0", rotating = false, type = "large" }
	]
	c10m1_caves =
	[
		{ origin = Vector(-11659.9,-13385.5,554.054), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(-12974.9,-6724.85,176.031), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-12244.6,-5599.09,-73.601), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-13089.6,-5241.27,-287.969), angles = "0 90 0", rotating = false, type = "tiny" }
		{ origin = Vector(-12349.5,-9805.34,496.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-12974.9,-5860.65,176.031), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-10690.6,-4991.48,688.028), angles = "0 90 0", rotating = false, type = "large" }
	]
	c10m2_drainage =
	[
		{ origin = Vector(-11215.6,-8447.19,-273.116), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(-8220.77,-8322.58,-452.1), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-9873.86,-7689.84,-376.396), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-10213.5,-8154.52,-162.483), angles = "0 90 0", rotating = false, type = "tiny" }
		{ origin = Vector(-9872.4,-6733.67,-307.969), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-7846.5,-6987.36,-457.214), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-8826.14,-7633.1,953.457), angles = "0 0 0", rotating = false, type = "large" }
	]
	c10m3_ranchhouse =
	[
		{ origin = Vector(-9839.35,-6342.99,89.6114), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(-4811.54,-1131.53,506.244), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-9440.1,-2750.96,-38.9688), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-6958,-1886.3,8.03125), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-8059.92,-5811.83,400.031), angles = "0 90 0", rotating = false, type = "tiny" }
		{ origin = Vector(-12539.413,-6515.756,408.296), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-9092.79,-3946.72,356.642), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-5081.95,-1693.59,626.119), angles = "0 0 0", rotating = false, type = "large" }
	]
	c10m4_mainstreet =
	[
		{ origin = Vector(-3068.25,-57.6381,1039.03), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(2790.76,-1699.64,336.031), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-1375.73,-4670.05,-55.9688), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-1425.11,-4670.65,-55.9688), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(137.127,-1897.99,112.031), angles = "0 90 0", rotating = false, type = "tiny" }
		{ origin = Vector(2756.35,-2412.78,336.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(1617.19,-4384.35,96.0313), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-673.274,-4583.52,176.031), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-1399.49,-4674.15,192.031), angles = "0 0 0", rotating = false, type = "large" }
	]
	c10m5_houseboat =
	[
		{ origin = Vector(2123.12,2634.09,326.222), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(4225.13,-5089.24,-170.896), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(2874.1,2413.57,-39.9688), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(3065.88,2759.15,-39.9688), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(3952.22,256.841,324.537), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(3854.66,4218.71,320.031), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(4264.85,-4686.83,231.018), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(2195.19,-4720.26,-35.1493), angles = "0 0 0", rotating = false, type = "large" }
	]
	c11m1_greenhouse =
	[
		{ origin = Vector(6418.19,-690.905,831.396), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(3427.550,1374.021,819.144), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(3518.21,2217.03,183.338), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(3370.21,731.601,554.228), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(2910.57,2108.9,416.535), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(4351.72,-300.349,1116.8), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(3394.22,-871.949,1029.47), angles = "0 0 0", rotating = false, type = "large" }
	]
	c11m2_offices =
	[
		{ origin = Vector(5488.19,4064.33,434.733), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(7419.72,3330.88,1212.23), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(7513.56,5654.95,16.0313), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(5839.04,3168.24,523.664), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(6272.13,1022.59,16.0313), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(5447.53,3558.41,303.983), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(6623.48,4828.47,600.482), angles = "0 0 0", rotating = false, type = "large" }
	]
	c11m3_garage =
	[
		{ origin = Vector(-3838.76,-3139.08,936.54), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(-5872.75,-1719.78,512.031), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-3488.1,2854.93,32.0313), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-3647.99,2854.92,31.0313), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-3565.8,2854.83,32.0313), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-4952.03,-2623.2,352.031), angles = "0 90 0", rotating = false, type = "tiny" }
		{ origin = Vector(-6805.34,-1273.95,550.072), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-5239.47,124.543,1301.27), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-2881.81,3153.91,160.031), angles = "0 90 0", rotating = false, type = "large" }
	]
	c11m4_terminal =
	[
		{ origin = Vector(-93.3845,4457.05,144.031), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(98.4939,3620.52,16.0313), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(1163.08,4456.62,296.031), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(710.154,5631.1,296.031), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(541.737,3785.52,536.031), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(471.801,2959.14,348.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(2130.92,1586.85,448.031), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(2780.61,6941.86,313.031), angles = "0 0 0", rotating = false, type = "large" }
	]
	c11m5_runway =
	[
		{ origin = Vector(-6838.83,9651.14,568.031), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(-5431.83,11812.2,62.8417), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-5418.15,10341.3,60.0313), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-6900.6,11531.3,-191.969), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-6891.96,10827.7,-191.969), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-5795.82,9010.43,176.974), angles = "0 0 0", rotating = false, type = "large" }
	]
	c12m1_hilltop =
	[
		{ origin = Vector(-8089.921,-15117.432,277.785), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(-6446.82,-8719.63,1015.15), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-6553.63,-7570.19,384.031), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-6418.22,-7523.43,365.19), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-6367.87,-7414.88,377.974), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-10427.594,-13344.635,746.753), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-10980.1,-10901.5,938.975), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-8990.35,-8981.87,1062.03), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-7808.35,-9486.53,992.031), angles = "0 0 0", rotating = false, type = "large" }
	]
	c12m2_traintunnel =
	[
		{ origin = Vector(-8229.1,-7517.71,160.353), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(-7719.68,-8901.63,304.031), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-4604.06,-8322.47,-63.5097), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-4175.46,-8323.85,-63.9688), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-3711.27,-8392.14,-63.1514), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-8740.09,-7214.79,200.031), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-8547.49,-8900.32,304.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-7563.7,-8612.52,826.025), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-4185.79,-8717.72,232.031), angles = "0 90 0", rotating = false, type = "large" }
	]
	c12m3_bridge =
	[
		{ origin = Vector(-1137.81,-10944.3,160.031), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(1827.29,-12229.5,484.031), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(1913.03,-12602.3,-31.9688), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(2030.97,-12602.3,-31.9688), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(1970.48,-12602.3,-31.9688), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-952.414,-10439.4,72.0312), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(3342.01,-14299.5,169.088), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(4914.05,-13113.9,1141.33), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(5932.44,-13851.9,272.05), angles = "0 0 0", rotating = false, type = "large" }
	]
	c12m4_barn =
	[
		{ origin = Vector(9334.13,-9355.23,932.537), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(11022.1,-4584.55,324.481), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(10382,-2600.35,-63.9688), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(10526,-2600.35,-63.9688), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(10453.7,-2600.35,-63.9688), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(7488.13,-10687,897.565), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(10616.6,-7429.15,274.641), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(9688.78,-4243.98,722.381), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(10453.7,-1712.4,268.031), angles = "0 0 0", rotating = false, type = "large" }
	]
	c12m5_cornfield =
	[
		{ origin = Vector(10059.6,821.12,462.47), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(7138.16,270.204,596.031), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(8960.78,3370.82,201.741), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(9060.78,3370.82,204.697), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(9272.22,3547.45,961.057), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(8446.37,422.173,590.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(6823.95,1191.26,794.031), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(7186.65,2650.19,1034.12), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(7536.12,2649.97,1034.12), angles = "0 0 0", rotating = false, type = "large" }
	]
	c13m1_alpinecreek =
	[
		{ origin = Vector(-2778.1,1140.71,255.406), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(1200.27,1406.95,1601.38), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(992.23,183.899,585.485), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(1120.63,183.726,585.398), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-2876.72,2797.21,1273), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-2332,3247.16,976.031), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(869.342,2451.07,805.031), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(880.042,-464,476.011), angles = "0 90 0", rotating = false, type = "large" }
	]
	c13m2_southpinestream =
	[
		{ origin = Vector(7961.18,6390.28,585.92), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(6728,2925.41,1216.03), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(78.6595,8679.27,-404.969), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(8107.63,4172.91,649.555), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(5677.56,2211.83,1090), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(4905.09,2577.37,1120.03), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-356.712,4977.07,272.031), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-348.279,6151.25,302.031), angles = "0 0 0", rotating = false, type = "large" }
	]
	c13m3_memorialbridge =
	[
		{ origin = Vector(-2169.22,-4092.02,1758.03), angles = "0 90 0", rotating = true, type = "bh" }
		{ origin = Vector(-2169.54,-4092.02,2201.03), angles = "0 90 0", rotating = false, type = "item" }
		{ origin = Vector(1003.74,-4472.38,590.031), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-3844.47,-4095.34,896.031), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(6777.83,-4114.74,2201.03), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(3687.47,-4074.24,2201.03), angles = "0 90 0", rotating = false, type = "medium" }
		{ origin = Vector(3686.79,-4095.17,896.031), angles = "0 90 0", rotating = false, type = "large" }
	]
	c13m4_cutthroatcreek =
	[
		{ origin = Vector(-3872.6,-8143.45,723.031), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(-772.295,3198.8,-101.969), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-1174.01,4688.39,180.031), angles = "0 0 0", rotating = false, type = "exp" }
		{ origin = Vector(-3622.38,-5926.86,623.746), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-3916.44,-3227.55,360.898), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-653.084,1568.25,18.0313), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-391.136,3752.91,88.0313), angles = "0 0 0", rotating = false, type = "large" }
	]
	c14m1_junkyard =
	[
		{ origin = Vector(-4227.94,-8819.65,103), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(-2505.24,1649.99,182.772), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(-4450.59,2204.69,-58.7463), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-1669.74,-5730.32,-300.582), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(-2676.9,2190.81,-50.752), angles = "0 0 0", rotating = false, type = "small" }
		{ origin = Vector(-4814.390,2711.914,100.450), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-2407,7408.93,168.031), angles = "0 0 0", rotating = false, type = "large" }
	]
	c14m2_lighthouse =
	[
		{ origin = Vector(1319.83,344.463,830.588), angles = "0 0 0", rotating = true, type = "bh" }
		{ origin = Vector(-2479.24,5569.72,-101.291), angles = "0 0 0", rotating = false, type = "item" }
		{ origin = Vector(320.403,923.429,560.905), angles = "0 90 0", rotating = false, type = "exp" }
		{ origin = Vector(-4567.81,3575.97,1400.03), angles = "0 0 0", rotating = false, type = "tiny" }
		{ origin = Vector(479.378,946.381,696.031), angles = "0 90 0", rotating = false, type = "small" }
		{ origin = Vector(-981.672,2524.87,701.112), angles = "0 0 0", rotating = false, type = "medium" }
		{ origin = Vector(-4725,5825,-92.968), angles = "0 0 0", rotating = false, type = "large" }
	]
}




// Output count of mushroom statistics
// ----------------------------------------------------------------------------------------------------------------------------

::map_mushroom_stats <- {}

::outputMushroomData <- function(){
	
	map_mushroom_stats.clear()
	local count_total = 0
	local nl = "\n"
	
	foreach(mapname, mushroomArray in mushroomPositions){
		foreach(DS in mushroomArray){
			local type = DS.type
			if(!(mapname in map_mushroom_stats)){
				map_mushroom_stats[mapname] <- { bh = 0, exp = 0, item = 0, tiny = 0, small = 0, medium = 0, large = 0 }
			}
			map_mushroom_stats[mapname][type] += 1
			count_total += 1
		}
	}
	
	printl("- - - - Mushroom stats - - - -" + nl)
	//
	printl("Total count of placed mushrooms: " + count_total + " ( " + mushroomPositions[mapName].len() + " in this map )" + nl)
	//
	foreach(mapname, datatable in map_mushroom_stats){
		printl("================== " + mapname + " " + GetCharChain("=", 32 - mapname.len() ) + nl)
		printl("BunnyHop: " + datatable.bh + "  Explosive: " + datatable.exp + "  Item: " + datatable.item)
		printl("Tiny: " + datatable.tiny + "  Small: " + datatable.small + "  Medium: " + datatable.medium + "  Large: " + datatable.large + nl)
	}
}




// Returns a concatenated string of the passed string with length of num
// ----------------------------------------------------------------------------------------------------------------------------

::GetCharChain <- function(str, num){
	local txt = ""
	for(local i = 0; i < num; i++){
		txt += str
	}
	return txt
}




// Returns the slot the weapon belongs to
// ----------------------------------------------------------------------------------------------------------------------------

function getItemSlot(item){
	local className = item.GetClassname()
	
	local slot0 =
	[
		"weapon_grenade_launcher","weapon_rifle_m60",
		"weapon_rifle","weapon_rifle_desert","weapon_rifle_ak47",
		"weapon_rifle_sg552","weapon_smg_mp5",
		"weapon_shotgun_chrome","weapon_pumpshotgun",
		"weapon_shotgun_spas","weapon_autoshotgun",
		"weapon_smg","weapon_smg_silenced",
		"weapon_hunting_rifle","weapon_sniper_military",
		"weapon_sniper_scout","weapon_sniper_awp"
	]
	local slot1 = 
	[
		"weapon_melee","weapon_chainsaw",
		"weapon_pistol","weapon_pistol_magnum"
	]
	local slot2 =
	[
		"weapon_molotov","weapon_pipe_bomb","weapon_vomitjar"
	]
	local slot3 =
	[
		"weapon_first_aid_kit","weapon_defibrillator",
		"weapon_upgradepack_explosive","weapon_upgradepack_incendiary"
	]
	local slot4 =
	[
		"weapon_adrenaline","weapon_pain_pills"
	]
	local slot5 = 
	[
		"weapon_oxygentank","weapon_propanetank","weapon_gascan",
		"weapon_gnome","weapon_cola_bottles","weapon_fireworkcrate"
	]

	if(slot0.find(className) != null){
		return "slot0"
	}else if(slot1.find(className) != null){
		return "slot1"
	}else if(slot2.find(className) != null){
		return "slot2"
	}else if(slot3.find(className) != null){
		return "slot3"
	}else if(slot4.find(className) != null){
		return "slot4"
	}else if(slot5.find(className) != null){
		return "slot5"
	}else{
		return null
	}
}




// This will track timings of the survivor
// ----------------------------------------------------------------------------------------------------------------------------

::PlayerTimeData <- {}

function PlayerTimer(ent){

	if(!(ent in PlayerTimeData)){
		PlayerTimeData[ent] <- { timerActive = false, finished = false, startTime = Time(), endTime = Time(), time_best = 0, ticks = 0, seconds = 0 }
	}
	
	if(PlayerIsOnGround(ent)){
		if(ent in PlayerTimeData){
			if(!PlayerTimeData[ent].finished){
				if(PlayerTimeData[ent].ticks < 30){
					PlayerTimeData[ent].ticks += 1
				}else{
					PlayerTimeData[ent].ticks = 0
					PlayerTimeData[ent].seconds += 1
				}
			}
		}
	}
}




// Check if survivor reached the saferoom
// ----------------------------------------------------------------------------------------------------------------------------

function survivorSaferoomCheck(ent){
	if(!PlayerTimeData[ent].finished){
		if( ResponseCriteria.GetValue(ent, "incheckpoint" ) == "1" ){
			printFinalGroundTime(ent)
			ProcessSurvivorTime(ent)
		}
	}
}




// Iteration over all human player and pass them to different methods
// ----------------------------------------------------------------------------------------------------------------------------

function PlayerFunctions(){
	foreach(ent in GetHumanSurvivors()){
		if(ent.IsValid()){
			PlayerTimer(ent)
			survivorSaferoomCheck(ent)
		}
	}
}




// Returns true when player is on the ground
// ----------------------------------------------------------------------------------------------------------------------------

function PlayerIsOnGround(player){
	if(NetProps.GetPropInt(player, "m_fFlags") & 1){
		return true
	}
	return false
}




// Print a chat message in color x 
// ----------------------------------------------------------------------------------------------------------------------------

function toChat(color, message, sound){
	local player = Entities.FindByClassname(null, "player")
	switch(color)
	{
		case "white"	: color = "\x01" ; break
		case "blue"		: color = "\x03" ; break
		case "orange"	: color = "\x04" ; break
		case "green"	: color = "\x05" ; break
	}
	switch(sound)
	{
		case "reward"	: sound = "ui/littlereward.wav" ; break
		case "error"	: sound = "ui/beep_error01.wav" ; break
		case "click"	: sound = "ui/menu_click01.wav" ; break
	}
	ClientPrint(null, 5, color + message)
	if(sound != null){
		EmitAmbientSoundOn( sound, 1, 100, 100, player)
	}
}




// Will change the model of "tank_rock" to a log when it is a L4D1 map
// When "last chance mode" is active, the rock will start glowing
// ----------------------------------------------------------------------------------------------------------------------------

function tankrockListener(){
	local rock = null

	while(rock = Entities.FindByClassname(rock, "tank_rock")){
		if(rock.IsValid()){
			local scope = GetValidatedScriptScope(rock)
			
			if(survivorSet == 1){
				if(!("usesLog" in scope)){
					rock.SetModel("models/props_foliage/tree_trunk.mdl")
					scope["usesLog"] <- true
				}
			}
			
			// When last change mode is active we need to enable the glow on all new rocks
			if(last_chance_active){
				if(!("glowing" in scope)){
					NetProps.SetPropInt(rock, "m_Glow.m_iGlowType", 3)
					rock.GetScriptScope()["glowing"] <- true
				}
			}
		}
	}
}




// Returns the given color with changed intesity as string 
// ----------------------------------------------------------------------------------------------------------------------------

function getColorWithIntensity(color, intensity){	

	local values = split(color, " ")
	
	local rNew = values[0].tofloat()
	local gNew = values[1].tofloat()
	local bNew = values[2].tofloat()

	rNew	= (rNew / 100 * intensity).tointeger()
	gNew	= (gNew / 100 * intensity).tointeger()
	bNew	= (bNew / 100 * intensity).tointeger()

	return "" + rNew + " " + gNew + " " + bNew
}




// Is the current server a local one
// ----------------------------------------------------------------------------------------------------------------------------

function OnLocalServer(){
	if(GetListenServerHost() == null){
		return false
	}
	return true
}


Community Mutations

These mutations were made by the community, eventually added to the base game by valve.

Community1 - Special Delivery

//-----------------------------------------------------
Msg("Activating Special Delivery\n");
Msg("Made by Rayman1103\n");

if ( !IsModelPrecached( "models/infected/smoker.mdl" ) )
	PrecacheModel( "models/infected/smoker.mdl" );
if ( !IsModelPrecached( "models/infected/smoker_l4d1.mdl" ) )
	PrecacheModel( "models/infected/smoker_l4d1.mdl" );
if ( !IsModelPrecached( "models/infected/boomer.mdl" ) )
	PrecacheModel( "models/infected/boomer.mdl" );
if ( !IsModelPrecached( "models/infected/boomer_l4d1.mdl" ) )
	PrecacheModel( "models/infected/boomer_l4d1.mdl" );
if ( !IsModelPrecached( "models/infected/boomette.mdl" ) )
	PrecacheModel( "models/infected/boomette.mdl" );
if ( !IsModelPrecached( "models/infected/hunter.mdl" ) )
	PrecacheModel( "models/infected/hunter.mdl" );
if ( !IsModelPrecached( "models/infected/hunter_l4d1.mdl" ) )
	PrecacheModel( "models/infected/hunter_l4d1.mdl" );
if ( !IsModelPrecached( "models/infected/limbs/exploded_boomette.mdl" ) )
{
	PrecacheModel( "models/infected/limbs/exploded_boomette.mdl" );
	::community1_no_female_boomers <- true;
}
if ( !IsModelPrecached( "models/infected/spitter.mdl" ) )
	PrecacheModel( "models/infected/spitter.mdl" );
if ( !IsModelPrecached( "models/infected/jockey.mdl" ) )
	PrecacheModel( "models/infected/jockey.mdl" );
if ( !IsModelPrecached( "models/infected/charger.mdl" ) )
	PrecacheModel( "models/infected/charger.mdl" );

MutationOptions <-
{
	ActiveChallenge = 1

	cm_CommonLimit = 0
	cm_DominatorLimit = 8
	cm_MaxSpecials = 8
	cm_ProhibitBosses = false
	cm_SpecialRespawnInterval = 0
	cm_AggressiveSpecials = false

	SpecialInitialSpawnDelayMin = 0
	SpecialInitialSpawnDelayMax = 5
	ShouldAllowSpecialsWithTank = true
	EscapeSpawnTanks = true
	MobMinSize = 0
	MobMaxSize = 0
	NoMobSpawns = true

	SmokerLimit = 2
	BoomerLimit = 2
	HunterLimit = 2
	SpitterLimit = 2
	JockeyLimit = 2
	ChargerLimit = 2

	// convert items that aren't useful
	weaponsToConvert =
	{
		weapon_pipe_bomb =	"weapon_molotov_spawn"
	}

	function ConvertWeaponSpawn( classname )
	{
		if ( classname in weaponsToConvert )
		{
			return weaponsToConvert[classname];
		}
		return 0;
	}
	
	DefaultItems =
	[
		"weapon_pistol_magnum",
	]

	function GetDefaultItem( idx )
	{
		if ( idx < DefaultItems.len() )
		{
			return DefaultItems[idx];
		}
		return 0;
	}
}

MutationState <-
{
	SIModelsBase = [ [ "models/infected/smoker.mdl", "models/infected/smoker_l4d1.mdl" ],
					[ "models/infected/boomer.mdl", "models/infected/boomer_l4d1.mdl", "models/infected/boomette.mdl" ],
						[ "models/infected/hunter.mdl", "models/infected/hunter_l4d1.mdl" ],
							[ "models/infected/spitter.mdl" ],
								[ "models/infected/jockey.mdl" ],
									[ "models/infected/charger.mdl" ] ]
	SIModels = [ [ "models/infected/smoker.mdl", "models/infected/smoker_l4d1.mdl" ],
				[ "models/infected/boomer.mdl", "models/infected/boomer_l4d1.mdl", "models/infected/boomette.mdl" ],
					[ "models/infected/hunter.mdl", "models/infected/hunter_l4d1.mdl" ],
						[ "models/infected/spitter.mdl" ],
							[ "models/infected/jockey.mdl" ],
								[ "models/infected/charger.mdl" ] ]
	ModelCheck = [ false, false, false, false, false, false ]
	LastBoomerModel = ""
	BoomersChecked = 0
	LeftSafeAreaThink = false
}

function LeftSafeAreaThink()
{
	local player = null;
	while ( player = Entities.FindByClassname( player, "player" ) )
	{
		if ( ( !player.IsValid() ) || ( NetProps.GetPropInt( player, "m_iTeamNum" ) != 2 ) )
			continue;
		
		if ( ResponseCriteria.GetValue( player, "instartarea" ) == "0" )
		{
			SessionOptions.cm_MaxSpecials = 8;
			SessionState.LeftSafeAreaThink = false;
			break;
		}
		else
			continue;
	}
}

function OnGameEvent_round_start_post_nav( params )
{
	local spawner = null;
	while ( spawner = Entities.FindByClassname( spawner, "info_zombie_spawn" ) )
	{
		if ( spawner.IsValid() )
		{
			local population = NetProps.GetPropString( spawner, "m_szPopulation" );
			
			if ( population == "boomer" || population == "hunter" || population == "smoker" || population == "jockey"
				|| population == "charger" || population == "spitter" || population == "new_special" || population == "church"
					|| population == "tank" || population == "witch" || population == "witch_bride" || population == "river_docks_trap" )
				continue;
			else
				spawner.Kill();
		}
	}
	
	if ( Director.GetMapName() == "c1m1_hotel" )
		DirectorOptions.cm_TankLimit <- 0;
	else if ( Director.GetMapName() == "c5m5_bridge" || Director.GetMapName() == "c6m3_port" )
		DirectorOptions.cm_MaxSpecials = 0;
	else if ( Director.GetMapName() == "c7m1_docks" )
		DirectorOptions.cm_ProhibitBosses = true;
}

function OnGameEvent_player_left_safe_area( params )
{
	local player = GetPlayerFromUserID( params["userid"] );
	if ( !player )
		return;
	
	local instartarea = ResponseCriteria.GetValue( player, "instartarea" );
	if ( instartarea == "1" )
	{
		SessionOptions.cm_MaxSpecials = 0;
		SessionState.LeftSafeAreaThink = true;
	}
}

function OnGameEvent_triggered_car_alarm( params )
{
	if ( !Director.IsTankInPlay() )
	{
		DirectorOptions.cm_AggressiveSpecials = true;
		ZSpawn( { type = 8 } );
		DirectorOptions.cm_AggressiveSpecials = false;
	}
	
	StartAssault();
}

function OnGameEvent_finale_start( params )
{
	if ( Director.GetMapName() == "c6m3_port" )
		DirectorOptions.cm_MaxSpecials = 8;
}

function OnGameEvent_gauntlet_finale_start( params )
{
	if ( Director.GetMapName() == "c5m5_bridge" )
		DirectorOptions.cm_MaxSpecials = 8;
}

function OnGameEvent_player_spawn( params )
{
	local player = GetPlayerFromUserID( params["userid"] );
	
	if ( ( !player ) || ( player.IsSurvivor() ) )
		return;
	
	local zombieType = player.GetZombieType();
	if ( zombieType > 6 )
		return;
	
	local modelName = player.GetModelName();
	
	if ( !SessionState.ModelCheck[ zombieType - 1 ] )
	{
		if ( (zombieType == 2) && !("community1_no_female_boomers" in getroottable()) )
		{
			if ( SessionState.LastBoomerModel != modelName )
			{
				SessionState.LastBoomerModel = modelName;
				SessionState.BoomersChecked++;
			}
			if ( SessionState.BoomersChecked > 1 )
				SessionState.ModelCheck[ zombieType - 1 ] = true;
		}
		else
			SessionState.ModelCheck[ zombieType - 1 ] = true;
		
		if ( SessionState.SIModelsBase[zombieType - 1].find( modelName ) == null )
		{
			SessionState.SIModelsBase[zombieType - 1].append( modelName );
			SessionState.SIModels[zombieType - 1].append( modelName );
		}
	}
	
	if ( SessionState.SIModelsBase[zombieType - 1].len() == 1 )
		return;
	
	local zombieModels = SessionState.SIModels[zombieType - 1];
	if ( zombieModels.len() == 0 )
		SessionState.SIModels[zombieType - 1].extend( SessionState.SIModelsBase[zombieType - 1] );
	local foundModel = zombieModels.find( modelName );
	if ( foundModel != null )
	{
		zombieModels.remove( foundModel );
		return;
	}
	
	local randomElement = RandomInt( 0, zombieModels.len() - 1 );
	local randomModel = zombieModels[ randomElement ];
	zombieModels.remove( randomElement );
	
	player.SetModel( randomModel );
}

function Update()
{
	if ( SessionState.LeftSafeAreaThink )
		LeftSafeAreaThink();
	if ( Director.GetCommonInfectedCount() > 0 )
	{
		local infected = null;
		while ( infected = Entities.FindByClassname( infected, "infected" ) )
		{
			if ( infected.IsValid() )
				infected.Kill();
		}
	}
}

Community2 -Flu Season

Msg("Activating Community Mutation 2\n");

DirectorOptions <-
{
	ActiveChallenge = 1
	RelaxMinInterval = 5
	RelaxMaxInterval = 10
	cm_SpecialRespawnInterval = 8
	SpecialInitialSpawnDelayMin = 1
	SpecialInitialSpawnDelayMax = 5

	cm_MaxSpecials = 8
	BoomerLimit = 4
	SpitterLimit = 4
	SmokerLimit = 0
	HunterLimit = 0
	ChargerLimit = 0
	JockeyLimit = 0

	ProhibitBosses = 1 //tanks still spawn at finales though
	MegaMobSize = 0

	// convert items that aren't useful
	weaponsToConvert =
	{
		weapon_vomitjar = 	"weapon_molotov_spawn"
	}

	function ConvertWeaponSpawn( classname )
	{
		if ( classname in weaponsToConvert )
		{
			return weaponsToConvert[classname];
		}
		return 0;
	}
}

function OnGameEvent_round_start_post_nav( params )
{
	local spawner = null;
	while ( spawner = Entities.FindByClassname( spawner, "info_zombie_spawn" ) )
	{
		if ( spawner.IsValid() )
		{
			local population = NetProps.GetPropString( spawner, "m_szPopulation" );
			
			if ( population == "boomer" || population == "spitter" || population == "church" || population == "river_docks_trap" )
				continue;
			else
				spawner.Kill();
		}
	}
}

Community3 - Riding My Survivor

Msg("Activating community mutation 3.\n");

DirectorOptions <-
{
	ActiveChallenge = 1
	
	cm_CommonLimit = 0
	
	BoomerLimit = 0
	ChargerLimit = 0
	HunterLimit = 0
	JockeyLimit = 4
	SmokerLimit = 0
	SpitterLimit = 0
	cm_MaxSpecials = 4
	
	cm_SpecialRespawnInterval = 20
	
	function ConvertZombieClass(id)
	{
		return 5;
	}
}

Community4 - Nightmare

//-----------------------------------------------------
Msg("Activating Nightmare\n");


DirectorOptions <-
{
	ActiveChallenge = 1

	cm_DominatorLimit = 8
	cm_MaxSpecials = 8
	cm_SpecialRespawnInterval = 30
	cm_AutoReviveFromSpecialIncap = 1
	cm_AllowPillConversion = 0
	cm_TankLimit = 4
	ProhibitBosses = 0

	BoomerLimit = 0
	SurvivorMaxIncapacitatedCount = 3
	SpecialInitialSpawnDelayMin = 5
	SpecialInitialSpawnDelayMax = 30
	TankHitDamageModifierCoop = 0.25	
}

Community5 - Death's Door

//-----------------------------------------------------
Msg("Activating Death's Door\n");
Msg("Made by Rayman1103\n");


DirectorOptions <-
{
	ActiveChallenge = 1

	cm_ShouldHurry = 1
	cm_AllowPillConversion = 0
	cm_AllowSurvivorRescue = 0
	SurvivorMaxIncapacitatedCount = 0

	weaponsToConvert =
	{
		weapon_first_aid_kit =	"weapon_pain_pills_spawn"
		weapon_adrenaline =	"weapon_pain_pills_spawn"
	}

	function ConvertWeaponSpawn( classname )
	{
		if ( classname in weaponsToConvert )
		{
			return weaponsToConvert[classname];
		}
		return 0;
	}

	DefaultItems =
	[
		"weapon_pistol",
		"weapon_pistol",
	]

	function GetDefaultItem( idx )
	{
		if ( idx < DefaultItems.len() )
		{
			return DefaultItems[idx];
		}
		return 0;
	}

	TempHealthDecayRate = 0.001
	function RecalculateHealthDecay()
	{
		if ( Director.HasAnySurvivorLeftSafeArea() )
		{
			TempHealthDecayRate = 0.27 // pain_pills_decay_rate default
		}
	}
}

function Update()
{
	DirectorOptions.RecalculateHealthDecay();
}

Community6 - Confogl

// vim: set ts=4
// CompLite.nut (Confogl Mutation)
// Copyright (C) 2012 ProdigySim
// All rights reserved.
// =============================================================================

IncludeScript("globals", this);

CompLite = InitializeCompLite();

// Don't need to do anything else if we're not first load
if(CompLite.Globals.GetCurrentRound() > 0)
{
	Msg("CompLite Starting Round "+CompLite.Globals.GetCurrentRound()+" on ");
	local mi = CompLite.Globals.MapInfo;
	if(mi.isIntro) Msg("an intro map.\n");
	else Msg("a non-intro map.\n");

	Msg("Found "+mi.saferoomPoints.len()+" saferoom points.\n");
	Msg("Map has a scavenge event? " + mi.hasScavengeEvent + "\n");
	Msg("MapName: "+mi.mapname+"\n");

	return;
}

Msg("Activating Mutation CompLite v3.6\n");

DirectorOptions.ActiveChallenge <- 1
DirectorOptions.cm_ProhibitBosses <- 0
DirectorOptions.cm_AllowPillConversion <- 0

// Name shortening references
local g_Timer = CompLite.Globals.Timer;
local g_FrameTimer = CompLite.Globals.FrameTimer;
local g_MapInfo = CompLite.Globals.MapInfo;
local g_GSC = CompLite.Globals.GSC;
local g_GSM = CompLite.Globals.GSM;
local g_MobResetti = CompLite.Globals.MobResetti;
local Modules = CompLite.Modules;

// Uncomment to add a debug event listener
//g_GSC.AddListener(Modules.MsgGSL());

g_GSC.AddListener(Modules.SpitterControl(Director, DirectorOptions, Entities));
g_GSC.AddListener(Modules.MobControl(g_MobResetti));


// Give out hunting rifles on non-intro maps.
// But limit them to 1 of each.
g_GSC.AddListener(Modules.HRControl(Entities, CompLite.Globals, Director));


g_GSC.AddListener(
	Modules.BasicItemSystems(
		// AllowWeaponSpawn Limits
		// 0: Always remove
		// >0: Keep the first n instances, delete others
		// <-1: Delete the first n instances, keep others.
		{
			weapon_defibrillator = 0
			weapon_grenade_launcher = 0
			weapon_upgradepack_incendiary = 0
			weapon_upgradepack_explosive = 0
			weapon_chainsaw = 0
			//weapon_molotov = 1
			//weapon_pipe_bomb = 2
			//weapon_vomitjar = 1
			weapon_propanetank = 0
			weapon_oxygentank = 0
			weapon_rifle_m60 = 0
			//weapon_first_aid_kit = -5
			upgrade_item = 0
		},
		// Conversion Rules
		{
		weapon_autoshotgun	  = "weapon_pumpshotgun_spawn"
		weapon_shotgun_spas	 = "weapon_shotgun_chrome_spawn"
		weapon_rifle			= "weapon_smg_spawn"
		weapon_rifle_desert	 = "weapon_smg_spawn"
		weapon_rifle_sg552	  = "weapon_smg_mp5_spawn"
		weapon_rifle_ak47	   = "weapon_smg_silenced_spawn"
		weapon_hunting_rifle	= "weapon_smg_silenced_spawn"
		weapon_sniper_military  = "weapon_shotgun_chrome_spawn"
		weapon_sniper_awp	   = "weapon_shotgun_chrome_spawn"
		weapon_sniper_scout	 = "weapon_pumpshotgun_spawn"
		weapon_first_aid_kit	= "weapon_pain_pills_spawn"
		weapon_molotov = "weapon_molotov_spawn"
		weapon_pipe_bomb = "weapon_pipe_bomb_spawn"
		weapon_vomitjar = "weapon_vomitjar_spawn"
		},
		// Default item list
		[
			"weapon_pain_pills",
			"weapon_pistol",
			"weapon_hunting_rifle"
		]
	)
);

// Enforce various item spawns to be single pickup.
g_GSC.AddListener(
	Modules.EntKVEnforcer(Entities,
		// classnames
		[
			"weapon_adrenaline_spawn",
			"weapon_pain_pills_spawn",
			"weapon_melee_spawn",
			"weapon_molotov_spawn",
			"weapon_vomitjar_spawn",
			"weapon_pipebomb_spawn"
		],
		// models
		[],
		// key to enforce
		"count",
		// value to set it to
		1
	)
);

// Entity tracking/removal/limiting
g_GSC.AddListener(
	Modules.ItemControl(Entities, 
	// Limit to value by classname
		{
			weapon_adrenaline_spawn = 1
			weapon_pain_pills_spawn = 4
			witch = 1
			func_playerinfected_clip = 0
			weapon_molotov_spawn = 1
			weapon_pipe_bomb_spawn = 1
			weapon_vomitjar_spawn = 1
		},
	// Limit to value by model name
		{
			["models/props_junk/propanecanister001a.mdl"] = 0,
			["models/props_equipment/oxygentank01.mdl"] = 0,
			["models/props_junk/explosive_box001.mdl"] = 1
		},
	// Remove these items from all saferooms
		[
			"weapon_adrenaline_spawn",
			"weapon_pain_pills_spawn",
			//"weapon_melee_spawn",
			"weapon_molotov_spawn",
			"weapon_pipe_bomb_spawn",
			"weapon_vomitjar_spawn"
		],
		g_MapInfo
	)
);

// Limit melee weapons to 4
g_GSC.AddListener(Modules.MeleeWeaponControl(Entities, 4));

// Remove all gascans on non-scavenge maps
g_GSC.AddListener(Modules.GasCanControl(Entities, g_MapInfo));


Msg("GSC/M/L Script run.\n");


EMS Mutations

These have been introduced with the L4D2 EMS Update

Dash

////////////////////////////////
// "Dash" is another extended mutation demo mode to show some of the capabilities available in script
//
// the players must run near a sequence of waypoints (well, ok, lampposts) in order
// As they reach each one, we light up the next one, and also summon a new panic wave from the next locale
// Designers use the Entity Placement tool to lay down a sequence of waypoints
// Waypoint naming:
// If you name your waypoints sequentially, e.g., waypoint_001, waypoint_002 flagpole waypoints will be used.
// See scripted_c5m2_park_dash.nut, c5m2_park_entities_dash.txt for an example.
//
// If you prefix your waypoints with "short_" e.g., short_waypoint_001, short_waypoint_002, then shorter
// waypoints will be used.
// See scripted_c5m4_quarter_dash.nut, c5m4_quarter_entities_dash.txt for an example.
//
// When survivors reach the final waypoint, we do a TANK wave, and they get a final time based on when they finish
// 
// The waypoint itself has a fairly big script to detect nearby players, and track which ones have reached it
//   as well as to trigger visual indications of progress, and "waypoint finished" calls back to this script
// And this script on startup has to sort the waypoints to make the in-order list

// printl(" Loading Dash mode")

MutationOptions <-
{   
	// since we are going to move the spawn point to "ahead" on the track as we hit waypoints
	PreferredMobDirection = SPAWN_NEAR_POSITION
	PreferredMobPositionRange = 600

	// we should change this to zero
	PreferredMobPosition = Vector( 0,0,0 )

	SpawnSetRule = SPAWN_POSITIONAL
	SpawnSetRadius = 2000
	SpawnSetPosition = Vector( -7150, -3647, -97 )

	WanderingZombieDensityModifier = 0 // get rid of wanderers

	cm_ShouldEscortHumanPlayers = 1
	cm_AggressiveSpecials = 1

	BoomerLimit  = 0
	ChargerLimit = 0
	HunterLimit  = 0
	JockeyLimit  = 0
	SpitterLimit = 0
	SmokerLimit  = 0
	MaxSpecials  = 0
	CommonLimit  = 20
	MegaMobSize	 = 20
	TankLimit    = 0
	WitchLimit	 = 0

	function EndScriptedMode()
	{
		local finished = true;
		foreach ( val in g_RoundState.WaypointList )
		{
			if ( val.GetScriptScope().active == true )
			{
				finished = false
				break;
			}
		}

		if ( finished )
		{
			// Finished the course
			return 0 // SCENARIO_RESTART
		}
		else
		{
			return 1 // SCENARIO_SURVIVORS_DEAD
		}
	}

	/*  Not using this method anymore
	function GetScoreboardFilename( endreason )
	{
		switch ( endreason )
		{
			case 1:
				return "Resource/UI/scriptedmodeplaceholderlost.res"
			case 0:
				return "Resource/UI/scriptedmodeplaceholderwon.res"
			default:
				return "Resource/UI/scriptedmodeplaceholder.res"
		}
	}
	*/

	// challenge mode experiment - make sure old CM type stuff works fine in new mutation code
	cm_HeadshotOnly = 0

	DefaultItems =
	[
		"weapon_rifle_m60",
		"weapon_pistol_magnum",
		"adrenaline",
		"pipe_bomb"
	]

	// Set characters up with default items
	function GetDefaultItem( idx )
	{
		if ( idx < DefaultItems.len() )
		{
			return DefaultItems[idx];
		}
		return 0;
	}	
}

// SessionState for this mode - mostly about the waypoint tracking/which on next/etc/etc
MutationState <-
{
	StartActive = true	
	CurrentWaypoint = 0
	FinalWaypoint = 0
	JustHitWaypoint = false
	YouWon = false
	FirstWaypointHit = false
	TimerWaypoint = 0   // for a soon to be written more flexible setup
	MobWaypoint = 0
	CommonIncrement = 0
}

// callback for waypoint generation - as we spawn each one we add it to the list
function SetWaypointCB( waypointObj, rarity )
{
	local waypointScr = waypointObj.GetScriptScope()
	waypointScr.myID <- g_ModeScript.MutationState.FinalWaypoint++

	
	// if we're using a custom list set the initial touch count on this waypoint
	if( "CustomWaypointList" in g_MapScript )
		waypointScr.initialTouchCount = g_MapScript.CustomWaypointList[ g_RoundState.WaypointList.len() ].startingTouchCount

	g_RoundState.WaypointList.append(waypointObj)
}

// the startbox needs a custom precache function if you are using it
function Precache()
{
	Startbox_Precache()
}

// called on startup - setup a few callbacks if they arent there
// and then go ahead and teleport players to start, spawn the startbox, activate first waypoint
function OnGameplayStart()
{
	CheckOrSetMapCallback( "MapOverrideOptions", @() false )
	CheckOrSetMapCallback( "MapGameplayStart", @() false )

	Scoring_LoadTable( SessionState.MapName, SessionState.ModeName )

	//teleport players to the start point
	if (!TeleportPlayersToStartPoints( "gamemode_playerstart" ) )
		printl(" ** TeleportPlayersToStartPoints: Spawn point count or player count incorrect! Verify that there are 4 of each.")

	// create a start box
	if ( !SpawnStartBox( "startbox_origin" ) )
	{
		printl("Note: SpawnStartBox() called but there is no startbox_origin in map.")
	}

	// If the map script supplies a CustomWaypointList of points spawn the entities in that order
	if( "CustomWaypointList" in g_MapScript )
	{
		SpawnCustomWaypointList()
	}
	else
	{
		// Spawn waypoints in order
		SortAndSpawnWaypointList()
	}

	if (g_RoundState.WaypointList.len() > 0)
	{
		EntFire( g_RoundState.WaypointList[0].GetName(), "StartGlowing" )
		EntFire( g_RoundState.WaypointList[0].GetName(), "fireuser1" )
		g_RoundState.WaypointList[0].GetScriptScope().active = true
		SessionOptions.PreferredMobPosition = g_RoundState.WaypointList[0].GetOrigin()
	}

	SessionOptions.ScriptedStageType = STAGE_SETUP
	SessionOptions.ScriptedStageValue = 1000

	MapGameplayStart()
}

//=========================================================
// Uses the CustomWaypointList defined in the map script to
// spawn waypoints.  The info_item_position entities are
// collected in the order they appear in the list and then
// the waypoints are spawned in that order.
//=========================================================
function SpawnCustomWaypointList()
{
	// collect the info_item_position entities into a list
	local currentEnt  = null
	local tempWaypointList = []

	foreach( idx, val in CustomWaypointList )
	{
		currentEnt = Entities.FindByName( null, val.targetName )
		
		// store the waypoint if we found it, otherwise complain
		if( currentEnt )
		{
			tempWaypointList.append( currentEnt )
		}
		else
		{
			printl( " ** Waypoint Spawn ERROR**  I can't find the waypoint named: " + val.targetName )
		}
	}

	SpawnWaypointList( tempWaypointList )
}

//=========================================================
// Collect all the info_item_position entities that are named 
// "short_waypoint_*" OR "waypoint_*", put them into a list
// and sort it by suffix.  This will allow us to spawn all
// the waypoints in order so they can know their touch order
//=========================================================
function SortAndSpawnWaypointList()
{
	local currentWaypoint = Entities.FindByClassname( null, "info_item_position" )
	local tempWaypointList = []

	while( currentWaypoint )
	{
		local name = currentWaypoint.GetName()
		if( ( name.find( "waypoint_" ) == 0 ) || ( name.find( "short_waypoint_" ) == 0 ) )
		{
			tempWaypointList.append( currentWaypoint )
		}

		currentWaypoint = Entities.FindByClassname( currentWaypoint, "info_item_position" )
	}

	// sort the list by suffix	
	tempWaypointList.sort(@(a,b) a.GetName().slice(-4) <=> b.GetName().slice(-4) )

	SpawnWaypointList( tempWaypointList )
}

//=========================================================
// Spawn the provided list of waypoints on their positions
//=========================================================
function SpawnWaypointList( list )
{
	local waypointGroup = g_MapScript.GetEntityGroup( "DashWaypoint" )
	local shortWaypointGroup = g_MapScript.GetEntityGroup( "DashWaypointShort" )

	// set callbacks
	shortWaypointGroup.SpawnTables[ "waypoint" ].PostPlaceCB <- SetWaypointCB
	waypointGroup.SpawnTables[ "waypoint" ].PostPlaceCB <- SetWaypointCB

	// spawn the waypoints	
	foreach( idx, ent in list )
	{
		// does our waypoint start with "short_"?  If so, it is a short waypoint! Spawn it.
		if( ent.GetName().find( "short_" ) == 0  )
		{
			SpawnSingleAt( shortWaypointGroup, ent.GetOrigin(), ent.GetAngles() )
		}
		else // assuming it must be a normal waypoint since it isn't "short_"
		{
			SpawnSingleAt( waypointGroup, ent.GetOrigin(), ent.GetAngles() )
		}
	}
}

function DashDisplayScores( )
{
	local final_time = HUDReadTimer(1)
	local score_strings = Scoring_AddScoreAndBuildStrings( Scoring_MakeName(), final_time )
	HUDPlace( HUD_TICKER, 0.32, 0.27, 0.36, 0.20 )
	HUDPlace( HUD_FAR_RIGHT, 0.28, 0.50, 0.44, 0.06 )
	HUDPlace( HUD_FAR_LEFT,  0.28, 0.58, 0.44, 0.06 )
	HUDPlace( HUD_LEFT_BOT,  0.28, 0.66, 0.44, 0.06 )
	HUDPlace( HUD_RIGHT_BOT, 0.28, 0.74, 0.44, 0.06 )
	local ticker_str = score_strings.yourtime
	if ("finish" in score_strings)
		ticker_str = ticker_str + "\n" + score_strings.finish
	ticker_str = ticker_str + "\nBest Times So Far"
	Ticker_SetBlink( false )   // or true if you came in first? hmmm....
	Ticker_NewStr( ticker_str, 120 )
	foreach (idx, val in score_strings.topscores)
	{
		local hudfield = "score"+(idx+1)
		DashHUD.Fields[hudfield].dataval = val
		DashHUD.Fields[hudfield].flags = DashHUD.Fields[hudfield].flags & ~HUD_FLAG_NOTVISIBLE
	}
	Scoring_SaveTable( SessionState.MapName, SessionState.ModeName )
}

// this array holds the waypoints in order, up to SessionState.FinalWaypoint (though really, could just use len() for safety?
g_RoundState.WaypointList <- []

// called at basically each waypoint via ForceNextStage - or when a PANIC ends we just go to a delay
function GetNextStage()
{
	smDbgPrint("GetNextStage called")

	if ( SessionState.YouWon )
	{
		smDbgPrint("Really winningz, going to rezultz, yo!")
		SessionOptions.ScriptedStageType = STAGE_RESULTS
		SessionOptions.ScriptedStageValue = 10
		DashDisplayScores()
	}
	else if ( SessionState.JustHitWaypoint && SessionState.FinalWaypoint > 0 )
	{	
		if ( !SessionState.FirstWaypointHit ) // could be start line, or waypoint 0
		{  // hit firstone, kick things off
			smDbgPrint("Hit First Waypoint!")
			SessionState.FirstWaypointHit = true
			HUDManageTimers( 1 , TIMER_COUNTUP, 0)   // generalize this?

			// calculate the number of common infected to add each time a waypoint is completed
			SessionState.CommonIncrement = (100 - SessionOptions.CommonLimit) / SessionState.FinalWaypoint
		}  
		
		if( !MapOverrideOptions() )
		{
			// really, do we need this? or can we just always do panic here else a delay???
			local PercentageComplete = SessionState.CurrentWaypoint * 1.0 / SessionState.FinalWaypoint
		
			SessionState.JustHitWaypoint = false
			SessionOptions.ScriptedStageType = STAGE_PANIC
			SessionOptions.ScriptedStageValue = 1
			foreach (val in SpecialNames)
			{
				local limit = RandomInt(1,3) + PercentageComplete * 3
				SessionOptions[val + "Limit"] = limit
			}
			SessionOptions.MaxSpecials = RandomInt(2,6) + PercentageComplete * 3
			if (SessionOptions.CommonLimit < 100)
				SessionOptions.CommonLimit += SessionState.CommonIncrement

			if (SessionOptions.MegaMobSize < 50 )
				SessionOptions.MegaMobSize += SessionState.CommonIncrement

			if (SessionState.CurrentWaypoint == SessionState.FinalWaypoint - 1 )
			{
				Ticker_NewStr("One gate to go!")
				SessionOptions.TankLimit = 1 // how do i force a tank from script?
				SessionOptions.ScriptedStageType = STAGE_ESCAPE  // i guess i do an escape stage
			}
			else // display some help text on the ticker
			{
				switch( SessionState.CurrentWaypoint )
				{
					case 1:
						Ticker_NewStr( "Each time you activate a new waypoint more infected will attack.", 15 )
						break
					
					case 2:
						Ticker_NewStr( "You cleared the waypoint! Here come more infected!", 15 )
						break
				}
			}
		}
	}
	else
	{
		SessionOptions.ScriptedStageType = STAGE_DELAY
		SessionOptions.ScriptedStageValue = 1000
		SessionOptions.MaxSpecials = 0
	}

	smDbgPrint("Dash setting Mode " + SessionOptions.ScriptedStageType + " val " + SessionOptions.ScriptedStageValue)

	// we also want to positionally spawn at the mob target - though really i'd like to do some distance/scale stuff, too
	SessionOptions.SpawnSetPosition = SessionOptions.PreferredMobPosition
}

// because call DWD is where we active/deactive the waypoints, we can just treat the start line as secret pre-waypoint
function SurvivorLeftStartBox()
{
	SessionState.JustHitWaypoint = true   // and since the main loop never looks up into the array, that is o.k.
	Director.ForceNextStage()
}

// each time a waypoint is hit this is called to get us moving forward
// i.e. we need to activate the next waypoint, see if it is the final one, track counts, and so on
function DashWaypointDone( id )
{
	smDbgPrint("Dash Waypoint finished! " + id)

	// sanity check!
	if ( id != SessionState.CurrentWaypoint )
		printl("Hey! you think you are done with waypoint " + id + " but current is " + SessionState.CurrentWaypoint )

	EntFire( g_RoundState.WaypointList[id].GetName(), "StopGlowing" )
	g_RoundState.WaypointList[id].GetScriptScope().active = false
	// printl( "Deactivated waypoint " + id )
	local next_id = ++SessionState.CurrentWaypoint
	if ( next_id >= SessionState.FinalWaypoint )
	{
		// printl("Youz da Winner, yo!")
		SessionState.CurrentWaypoint--   // for safety
		SessionState.YouWon = true

		// stop the clock
		HUDManageTimers( 1, TIMER_STOP, 0 )
	}
	else
	{
		EntFire( g_RoundState.WaypointList[next_id].GetName(), "StartGlowing" )
		EntFire( g_RoundState.WaypointList[next_id].GetName(), "fireuser1" )
		//printl("Moving on to next waypoint " + next_id )
		g_RoundState.WaypointList[next_id].GetScriptScope().active = true
		SessionOptions.PreferredMobPosition = g_RoundState.WaypointList[next_id].GetOrigin()
	}
	SessionState.JustHitWaypoint = true
	Director.ForceNextStage()
}

// add Criteria for the new Response Rule speech criteria system
// i.e. this is to add data to the RR table the characters use to choose statements
function AddCriteria( criteriaTable )
{
	criteriaTable.PercentComplete <- SessionState.FinalWaypoint > 0 ? (SessionState.CurrentWaypoint*1.0)/SessionState.FinalWaypoint : 0
//	printl("Dash AddCriteria added " + criteriaTable.PercentComplete)
}

//-------------------------------------------------------
// Include all entity group interfaces needed for this mode
//-------------------------------------------------------
IncludeScript( "entitygroups/dash_waypoint_group" )
IncludeScript( "entitygroups/dash_waypoint_short_group" )

DashHUD <- {}

// HUD setup/control - our HUD is pretty simple in dash
function SetupModeHUD( )
{
	DashHUD =
	{
		Fields = 
		{
			waypoint = { slot = HUD_RIGHT_TOP, name = "waypoint", staticstring = "Waypoint: ", datafunc = @() SessionState.CurrentWaypoint },
			timer    = { slot = HUD_LEFT_TOP, name = "timer", staticstring = "Time: ", special = HUD_SPECIAL_TIMER1 },

			// this is kinda a lie! we are really going to move these w/HUDPlace for displaying final scores!
			// we will move the Ticker down to be a "header" - then need 4 slots for top 4
			score1   = { slot = HUD_FAR_RIGHT, name = "score1", dataval = "Score1", flags = HUD_FLAG_NOTVISIBLE | HUD_FLAG_ALIGN_LEFT },
			score2   = { slot = HUD_FAR_LEFT,  name = "score2", dataval = "Score2", flags = HUD_FLAG_NOTVISIBLE | HUD_FLAG_ALIGN_LEFT },
			score3   = { slot = HUD_LEFT_BOT,  name = "score3", dataval = "Score3", flags = HUD_FLAG_NOTVISIBLE | HUD_FLAG_ALIGN_LEFT },
			score4   = { slot = HUD_RIGHT_BOT, name = "score4", dataval = "Score4", flags = HUD_FLAG_NOTVISIBLE | HUD_FLAG_ALIGN_LEFT },
		}
	}

	Ticker_AddToHud( DashHUD, "Hurry past the poles, go for a best time" )
	HUDSetLayout( DashHUD )
}

Holdout

///////////////////////////////////////////////////////////////////////////////
// The Holdout game mode! Brought to you by the new Mutation system!
// A Holdout Is...
//   Mostly a demo of the new mutation system, but a game mode as well
//   A Continuous cycle of attack/cooldown/attack/cooldown
//   in code, implemented as 3 phases
//        PANIC - spawn some enemies around the players
//        CLEAROUT - wait until the enemies are (mostly) all gone
//        DELAY - a cooldown time before the next wave of enemies
// There are several "special behaviors" as this happens
//   The Escape wave can happen at a specified wave #, or as a result of in game action (timer running down, for instance)
//   There can be special events that take over based on the current Stage #

// This is a very Structured mode - it uses stagetables and other "helpers" in the mutation system
// You dont need to use these in your own mutations, though you are of course welcome to

// as a map, you should go implement the following functions, returning a "stageTable"
// or null to ignore
//   DoMapEventCheck()
//   DoMapSetup()
//   GetMapEscapeStage()
//   IsMapSpecificStage()    // dont like this! potential out of sync/parallel variables
//   GetMapSpecificStage()   //    maybe IsMap sets a "stageSpecific" that GetMap can use?
//   GetAttackStage()
//   GetMapClearoutStage()
//   GetMapDelayStage()
///////////////////////////////////////////////////////////////////////////////

//---------------------------------------------------------
// This is the SessionState - any non-director data all your session code wants to be able to look at
// So we hold data about what wave, some UI controls, and info about managing warnings
MutationState <-
{
	HUDWaveInfo = true           // do you want the Wave # in middle of UI
	HUDRescueTimer = false       // or would you rather have the rescue timer (cant have both)
	HUDTickerText = "Holdout as long as you can"
	RescueStarted = false
	RawStageNum = -1
	ForcedEscapeStage = -1
	ScriptedStageWave = 0
	CooldownEndWarningTime = 8.5 // seconds before end of cooldown to play warning
	CooldownEndWarningChance = 15 // percent chance to play warning
	CooldownEndWarningFrequency = 0 // how many waves to wait before playing another warning
	LastWaveCooldownWarningPlayed = -1 // the last wave that the cooldown warning played on
}

//---------------------------------------------------------
// the DirectorOptions defaults for Holdout - though the maps will override many of these per wave and phase
MutationOptions <-
{
	PreferredMobDirection = SPAWN_NO_PREFERENCE
	SpawnSetRule = SPAWN_ANYWHERE
	JournalString = ""
	SpecialInfectedAssault = 0
	AllowWitchesInCheckpoints = 1
	AllowCrescendoEvents = 0
	EnforceFinaleNavSpawnRules = 0
	IgnoreNavThreatAreas = 1
	ZombieDiscardRange = 10000

	WanderingZombieDensityModifier = 0
	BoomerLimit  = 0
 	ChargerLimit = 0
 	HunterLimit  = 0
	JockeyLimit  = 0
	SpitterLimit = 0
	SmokerLimit  = 0
	MaxSpecials  = 0
	CommonLimit  = 20
	TankLimit    = 0
	MegaMobMaxSize = 100
	MegaMobMinSize = 75
	PanicWavePauseMax = 5
	PanicWavePauseMin = 1
	AddToSpawnTimer = 0
	ShouldAllowMobsWithTank = true
	ShouldAllowSpecialsWithTank = true
	ZombieSpawnRange = 800
	ZombieTankHealth = 6000 // SP default is 4000
	BileMobSize = 20
	EscapeSpawnTanks = true
	ZombieDontClear = 1
	MegaMobSize = 50       // i have no idea why this defaults to not between min and max

	cm_ShouldEscortHumanPlayers = 1
	cm_AggressiveSpecials = 1
}

//=========================================================
// Utilities and helpers
//=========================================================

const PHASE_PANIC = 1
const PHASE_CLEAR = <