::PEA_GLOBAL <-
{
	strip_romevision_items = false
	
    gamerules_entity = Entities.FindByName(null, "gamerules")
	objective_resource_entity = Entities.FindByClassname(null, "tf_objective_resource")
	
	mission = NetProps.GetPropString(Entities.FindByClassname(null, "tf_objective_resource"), "m_iszMvMPopfileName")

	GLOBAL_CALLBACKS =
	{
		OnGameEvent_recalculate_holidays = function(params) // do cleanup after mission switch
		{
			if ("MapCleanup" in PEA) ::MapCleanup()
			
			if (GetRoundState() == 3 && NetProps.GetPropString(objective_resource_entity, "m_iszMvMPopfileName") != mission)
			{
				foreach (thing, var in PEA) if (thing in getroottable()) delete getroottable()[thing]
				
				for (local i = 1; i <= Constants.Server.MAX_PLAYERS; i++)
				{
					local player = PlayerInstanceFromIndex(i)
					if (player == null) continue
					if (player.GetScriptScope() != null)
					{
						foreach (thing in player.GetScriptScope())
						{
							try { thing.GetClassname() }
							catch (e) { continue }
							
							if (!thing.IsPlayer()) thing.Kill()
						}
					}

					player.TerminateScriptScope()
				}
				
				delete ::PEA
			}
		}
		
		OnGameEvent_player_spawn = function(params)
		{
			local bot = GetPlayerFromUserID(params.userid);
			
			if (bot.IsFakeClient())
			{
				EntFireByHandle(bot, "CallScriptFunction", "GlobalBotTagCheck", -1.0, null, null)
				
				if (strip_romevision_items) for (local child = bot.FirstMoveChild(); child != null; child = child.NextMovePeer()) if (child.GetClassname() == "tf_wearable" && child.GetModelName().find("tw_") != null) EntFireByHandle(child, "Kill", null, -1.0, null, null)
			}
		}
		
		OnGameEvent_player_death = function(params)
		{
			local dead_player = GetPlayerFromUserID(params.userid)
			
			if (dead_player.IsFakeClient())
			{	
				for (local cash; cash = Entities.FindByClassname(cash, "item_currencypack_custom"); ) AddThinkToEnt(cash, "ImprovedCashCollection_Think")
				
				local projectiles_leftbehind = false
				
				for (local ent; ent = Entities.FindByClassname(ent, "tf_projectile_*"); )
				{
					if (ent.GetOwner() == dead_player || NetProps.GetPropEntity(ent, "m_hThrower") == dead_player) { projectiles_leftbehind = true; break }
				}
				
				if (!projectiles_leftbehind) EntFireByHandle(dead_player, "RunScriptCode", "self.ForceChangeTeam(1, true)", -1.0, null, null)
			}
		}
	}

	ImprovedCashCollection_Think = function()
	{
		try // tends to throw harmless errors about collecting player becoming null
		{
			for (local player_to_collect; player_to_collect = Entities.FindByClassnameWithin(player_to_collect, "player", self.GetOrigin(), 288); )
			{
				if (player_to_collect != null && player_to_collect.GetTeam() == 2 && !player_to_collect.IsFakeClient() && NetProps.GetPropInt(player_to_collect, "m_lifeState") == 0 && NetProps.GetPropInt(player_to_collect, "m_PlayerClass") == Constants.ETFClass.TF_CLASS_SCOUT)
				{
					self.Teleport(true, player_to_collect.GetOrigin(), false, QAngle(0, 0, 0), false, Vector(0, 0, 0))
					self.DisableDraw()
					return 1 // stop reiterating after the teleport
				}
			}
		}
		
		catch (e) { return }

		return -1
	}
	
	DisableRomevision = function(change_carrier = true, change_bots = false)
	{
		if (change_carrier)
		{
			for (local ent; ent = Entities.FindByModel(ent, "models/bots/boss_bot/carrier.mdl"); )
			{
				NetProps.SetPropIntArray(ent, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/carrier.mdl"), 0)
				NetProps.SetPropIntArray(ent, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/carrier.mdl"), 1)
				NetProps.SetPropIntArray(ent, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/carrier.mdl"), 2)
				NetProps.SetPropIntArray(ent, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/carrier.mdl"), 3)
			}
			
			for (local ent; ent = Entities.FindByModel(ent, "models/bots/boss_bot/carrier_parts.mdl"); )
			{
				NetProps.SetPropIntArray(ent, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/carrier_parts.mdl"), 0)
				NetProps.SetPropIntArray(ent, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/carrier_parts.mdl"), 1)
				NetProps.SetPropIntArray(ent, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/carrier_parts.mdl"), 2)
				NetProps.SetPropIntArray(ent, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/carrier_parts.mdl"), 3)
			}
		}
		
		if (change_bots) strip_romevision_items = true
	}
	
	GlobalBotTagCheck = function()
	{
		if (self.HasBotTag("disband_squad")) self.DisbandCurrentSquad()
	}
	
	BlimpFinder_Think = function()
	{
		for (local blimp; blimp = Entities.FindByName(blimp, "blimpboss"); )
		{
			if (blimp.GetScriptScope() != null) continue
			else SetUpBlimp(blimp)
		}
	}
	
	InstantReady_Think = function()
	{
		if (!countdown_skipped && NetProps.GetPropBoolArray(gamerules_entity, "m_bPlayerReady", 1))
		{
			NetProps.SetPropFloat(gamerules_entity, "m_flRestartRoundTime", Time())
			countdown_skipped = true
		}
	}
	
	ThinksTable = {}
	
	GlobalThinker = function()
	{
		local scope = self.GetScriptScope()
		
		if (!("tick" in scope)) scope.tick <- 1
		
		foreach (func in ThinksTable) func()
		
		scope.tick <- scope.tick + 1
		
		return -1
	}
	
	AssignThinkToThinksTable = function(think)
	{		
		getroottable()["ThinksTable"][think] <- getroottable()[think]
		delete getroottable()[think]
	}
	
	SetUpBlimp = function(blimp)
	{
		NetProps.SetPropIntArray(blimp, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/boss_blimp.mdl"), 0)
		NetProps.SetPropIntArray(blimp, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/boss_blimp.mdl"), 1)
		NetProps.SetPropIntArray(blimp, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/boss_blimp.mdl"), 2)
		NetProps.SetPropIntArray(blimp, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/boss_blimp.mdl"), 3)
		
		local child_array = []
		
		for (local child = blimp.FirstMoveChild(); child != null; child = child.NextMovePeer()) child_array.append(child)
		
		foreach (child in child_array) child.Kill()
		
		blimp.SetSkin(1)
		
		local train = SpawnEntityFromTable("func_tracktrain",
		{
			targetname              = "blimp_train"
			target                  = "blimp_path1"
			startspeed              = blimp_speed
			speed                   = blimp_speed
			origin                  = Entities.FindByName(null, "blimp_path1").GetOrigin()
			orientationtype         = 1
			spawnflags              = 136
			volume					= 10
			model					= "*2"
			rendermode				= 1
			renderamt				= 0
			solid					= 0
		})
		
		train.ValidateScriptScope()
		train.GetScriptScope().parented_blimp <- blimp
		
		blimp.ValidateScriptScope()
		blimp.GetScriptScope().pathtrain <- train
		
		blimp.SetModelScale(("blimp_scale" in PEA) ? blimp_scale : 1, -1)
		
		AddThinkToEnt(blimp, "Blimp_Think")
	}
	
	CleanUpBlimp = function()
	{
		RemoveBlimpIcon()
		for (local train; train = Entities.FindByName(train, "blimp_train"); ) if (!train.GetScriptScope().parented_blimp.IsValid()) train.Kill()
		for (local cash; cash = Entities.FindByClassname(cash, "item_currencypack_custom"); ) AddThinkToEnt(cash, "ImprovedCashCollection_Think")
	}

	Blimp_Think = function()
	{
		local scope = self.GetScriptScope()
		
		if (!("tick" in scope))
		{
			PrecacheSound("npc/combine_gunship/ping_search.wav")
			
			scope.tick <- 1
			scope.model <- self.GetModelName()
			
			scope.Deploy_Bomb_1 <- function()
			{	
				self.ResetSequence(1)
				self.SetCycle(0)
				
				EntFire("!self", "CallScriptFunction", "Deploy_Bomb_2", 8.0)
				
				EmitSoundEx(
				{
					sound_name = "MVM.TankDeploy",
					filter = 5,
					entity = self,
					volume = 1,
					soundlevel = 150,
					flags = 1,
					channel = 6
				})
			}
			
			scope.Deploy_Bomb_2 <- function()
			{
				if (self == null) return
				
				switch (GetMapName())
				{
					case "mvm_hanami_rc1":
					{
						EntFire("win_bots", "RoundWin")
						EntFire("hatch_destroy_relay", "Trigger")
						break
					}
					case "mvm_null_b9a":
					{
						soldier_statue.Kill()

						EntFire("bots_win", "RoundWin")
						EntFire("end_pit_destroy_particle", "Start")
						EntFire("pit_explosion_wav", "PlaySound")
						EntFire("hatch_explo_kill_players", "Enable")
						EntFire("hatch_explo_kill_players", "Disable", null, 0.5)
						EntFire("hatch_magnet_pit", "Enable")
						EntFire("trigger_hurt_hatch_fire", "Enable")
						break
					}
				}
			}
		}
		
		self.SetOrigin(scope.pathtrain.GetOrigin())
		self.SetAbsAngles(QAngle(0, scope.pathtrain.GetAbsAngles().y, 0))
		self.GetLocomotionInterface().Reset()
		
		if (scope.model != self.GetModelName())
		{
			if (self.GetModelName() == "models/bots/boss_bot/boss_tank_damage1.mdl")
			{
				NetProps.SetPropIntArray(self, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/boss_blimp_damage1.mdl"), 0)
				NetProps.SetPropIntArray(self, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/boss_blimp_damage1.mdl"), 1)
				NetProps.SetPropIntArray(self, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/boss_blimp_damage1.mdl"), 2)
				NetProps.SetPropIntArray(self, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/boss_blimp_damage1.mdl"), 3)
			}
			if (self.GetModelName() == "models/bots/boss_bot/boss_tank_damage2.mdl")
			{
				NetProps.SetPropIntArray(self, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/boss_blimp_damage2.mdl"), 0)
				NetProps.SetPropIntArray(self, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/boss_blimp_damage2.mdl"), 1)
				NetProps.SetPropIntArray(self, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/boss_blimp_damage2.mdl"), 2)
				NetProps.SetPropIntArray(self, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/boss_blimp_damage2.mdl"), 3)
			}
			if (self.GetModelName() == "models/bots/boss_bot/boss_tank_damage3.mdl")
			{
				NetProps.SetPropIntArray(self, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/boss_blimp_damage3.mdl"), 0)
				NetProps.SetPropIntArray(self, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/boss_blimp_damage3.mdl"), 1)
				NetProps.SetPropIntArray(self, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/boss_blimp_damage3.mdl"), 2)
				NetProps.SetPropIntArray(self, "m_nModelIndexOverrides", PrecacheModel("models/bots/boss_bot/boss_blimp_damage3.mdl"), 3)
			}
		}
		
		EmitSoundEx(
		{
			sound_name = "MVM.TankPing",
			filter = 5,
			entity = self,
			volume = 1,
			soundlevel = 150,
			flags = 4,
			channel = 6
		})
		
		EmitSoundEx(
		{
			sound_name = "MVM.TankEngineLoop",
			filter = 5,
			entity = self,
			volume = 1,
			soundlevel = 150,
			flags = 4,
			channel = 6
		})
		
		if (scope.tick % 333 == 0)
		{
			EmitSoundEx(
			{
				sound_name = "npc/combine_gunship/ping_search.wav",
				filter = 5,
				entity = self,
				volume = 1,
				soundlevel = 150,
				flags = 1,
				channel = 6
			})
		}
		
		scope.tick = scope.tick + 1
		
		return -1
	}
}

if (!("PEA" in getroottable()))	::PEA <- {}

foreach (thing, var in PEA_GLOBAL) getroottable()["PEA"][thing] <- getroottable()["PEA_GLOBAL"][thing]
foreach (thing, var in PEA) getroottable()[thing] <- getroottable()["PEA"][thing]

delete ::PEA_GLOBAL

if ("SetUpBlimpPath" in PEA) ::SetUpBlimpPath()

for (local ent; ent = Entities.FindByName(ent, "blimp_path*"); ) if (NetProps.GetPropEntity(ent, "m_pnext") == null) EntityOutputs.AddOutput(ent, "OnPass", "!activator", "RunScriptCode", "self.GetScriptScope().parented_blimp.GetScriptScope().Deploy_Bomb_1()", -1.0, -1.0)

switch (GetMapName())
{
	case "mvm_hanami_rc1": AssignThinkToThinksTable("BlimpFinder_Think"); break
	case "mvm_null_b9a": AssignThinkToThinksTable("BlimpFinder_Think"); break
	case "mvm_quetzal_rc5": AssignThinkToThinksTable("BlimpFinder_Think"); break
	case "mvm_hoovydam_b10": DisableRomevision(); EntFire("initMain_roadside", "Trigger"); break
	case "mvm_derelict_rc2": DisableRomevision(); break
	case "mvm_sequoia_rc4": DisableRomevision(); break
	case "mvm_decay_rc1": DisableRomevision(true, true); break
}

if (debug)
{
	PEA["countdown_skipped"] <- false
	getroottable()["countdown_skipped"] <- PEA["countdown_skipped"]
	
	AssignThinkToThinksTable("InstantReady_Think")
}

if ("CALLBACKS" in PEA) __CollectGameEventCallbacks(PEA.CALLBACKS)

__CollectGameEventCallbacks(PEA.GLOBAL_CALLBACKS)

AddThinkToEnt(gamerules_entity, "GlobalThinker")