::CONST <- getconsttable()
::ROOT <- getroottable()
::MAX_CLIENTS <- MaxClients().tointeger()

if (!("ConstantNamingConvention" in ROOT))
{
	foreach (a, b in Constants)
	{
		foreach (k,v in b)
		{
			CONST[k] <- v != null ? v : 0;
			ROOT[k] <- v != null ? v : 0;
		}
	}
}

foreach(k, v in ::Entities.getclass())
{
	if (k != "IsValid" && !(k in ROOT))
	{
		ROOT[k] <- ::Entities[k].bindenv(::Entities);
	}
}

foreach(k, v in ::NetProps.getclass())
{
	if (k != "IsValid" && !(k in ROOT))
	{
		ROOT[k] <- ::NetProps[k].bindenv(::NetProps);
	}
}

if (!("tankextensions_main" in getroottable()))
{
	IncludeScript("tankextensions_main", getroottable())
}

if (!("alternatewaves" in getroottable()))
{
	IncludeScript("alternatewaves", getroottable())
}

if (!("tankextensions/blimp" in getroottable()))
{
	IncludeScript("tankextensions/blimp", getroottable())
}

if (!("tankextensions/combattank" in getroottable()))
{
	IncludeScript("tankextensions/combattank", getroottable())
}

if (!("tankextensions/combattank_weapons/railgun" in getroottable()))
{
	IncludeScript("tankextensions/combattank_weapons/railgun", getroottable())
}

if (!("tankextensions/combattank_weapons/rocketpod" in getroottable()))
{
	IncludeScript("tankextensions/combattank_weapons/rocketpod", getroottable())
}

TankExt.CreatePaths({
	haha_funny_blimp = [
		Vector(-301.020264, 9463.896484, 1963.09765)
		Vector(-621.298767, 5136.060059, 1280.645020)
		Vector(-2953.340820, 4762.269531, 988.009399)
	]
})

if(!("WatersMissionName" in ROOT))
{
	::WatersMissionName <- ""
	local ent = FindByClassname(null, "tf_objective_resource");
	WatersMissionName = GetPropString(ent, "m_iszMvMPopfileName");
}

local defTurnRate = Convars.GetFloat("tf_base_boss_max_turn_rate")
AlternateWaves.bTrackIcons = false
::WatersRegime <-
{
	WaveInProgress = false
	AvailableEMPSpawns = []
	SelectedEMPSpawn = -1
	WinEntity = null
	RedWon = false
	MajorShocksBot = null
	CurrentMusicTrack = ""

	function DoExplanation(message)
	{
		ClientPrint(null, 3, message);
	}

	function EmitSoundToAllPlayers(sound, volume = 1.0)
	{
		for (local i = 1, player; i <= MAX_CLIENTS; i++)
		{
			if ((player = PlayerInstanceFromIndex(i)) != null && player.IsValid() && !IsPlayerABot(player))
			{
				EmitSoundEx({sound_name = sound
				entity = player
				filter_type = 4
				volume = volume})
			}
		}
	}

	function ScreenFadeAllPlayers(red, green, blue, alpha, fade_time, fade_hold, flags)
	{
		for (local i = 1, player; i <= MAX_CLIENTS; i++)
		{
			if ((player = PlayerInstanceFromIndex(i)) != null && player.IsValid() && !IsPlayerABot(player))
			{
				ScreenFade(player, red, green, blue, alpha, fade_time, fade_hold, flags);
			}
		}
	}

	function KillAllBots()
	{
		for (local i = 1, player; i <= MAX_CLIENTS; i++)
		{
			if ((player = PlayerInstanceFromIndex(i)) == null || !player.IsValid() || !player.IsBotOfType(TF_BOT_TYPE) || player.GetTeam() != 3 || !player.IsAlive())
			{
				continue;
			}

			player.SetHealth(0);
			player.TakeDamageCustom(player, player, null, Vector(0, 0, 0), player.GetCenter(), player.GetHealth().tofloat() + 10.0, 0, 6);
		}
	}

	function PlayMusicTrack(track)
	{
		ResetMusicTrack();
		for (local i = 1, player; i <= MaxClients().tointeger(); i++)
		{
			if ((player = PlayerInstanceFromIndex(i)) != null && player.IsValid() && !IsPlayerABot(player))
			{
				EmitSoundEx({
						sound_name = track
						entity = player
						filter_type = 4
						flags = 1
						volume = 1.0
					});
			}
		}
		CurrentMusicTrack = track;
	}

	function ResetMusicTrack()
	{
		if (CurrentMusicTrack.len() <= 0)
		{
			return;
		}
		for (local i = 1, player; i <= MaxClients().tointeger(); i++)
		{
			if ((player = PlayerInstanceFromIndex(i)) != null && player.IsValid() && !IsPlayerABot(player))
			{
				EmitSoundEx({
						sound_name = CurrentMusicTrack
						entity = player
						filter_type = 4
						flags = 4
						volume = 1.0
					});
			}
		}
		CurrentMusicTrack = "";
	}

	function SetMaxWaveNumber(wave)
	{
		local resource = Entities.FindByClassname(null, "tf_objective_resource");
		NetProps.SetPropInt(resource, "m_nMannVsMachineMaxWaveCount", wave);
	}

	function SetMaxTurnRate(value)
	{
		Convars.SetValue("tf_base_boss_max_turn_rate", value);
	}

	function ResetMaxTurnRate()
	{
		Convars.SetValue("tf_base_boss_max_turn_rate", defTurnRate);
	}

	function GetPlayerReadyCount()
	{
		local gamerules = FindByClassname(null, "tf_gamerules");
		local roundtime = GetPropFloat(gamerules, "m_flRestartRoundTime");
		if (WatersRegime.WaveInProgress)
		{
			return 0;
		}
		local ready = 0;

		for (local i = 0; i < GetPropArraySize(gamerules, "m_bPlayerReady"); i++)
		{
			if (!GetPropBoolArray(gamerules, "m_bPlayerReady", i))
			{
				continue;
			}
			ready++;
		}

		return ready;
	}

	function OnGameEvent_mvm_wave_complete(params)
	{
		WaveInProgress = false;
	}

	function OnGameEvent_mvm_wave_failed(params)
	{
		WaveInProgress = false;
	}

	function OnGameEvent_mvm_begin_wave(params)
	{
		local interface = Entities.FindByClassname(null, "point_populator_interface");
		if (interface == null)
		{
			SpawnEntityFromTable("point_populator_interface", { targetname = "pop_interface" });
		}
		WaveInProgress = true;
	}

	function OnGameEvent_mvm_reset_stats(params)
	{
		WaveInProgress = true;
	}

	function Initialize()
	{
		WaveInProgress = false;
		if (WinEntity != null && WinEntity.IsValid())
		{
			WinEntity.Kill();
			WinEntity = null;
		}
		local gamerules = FindByClassname(null, "tf_gamerules");
		gamerules.TerminateScriptScope();
		NetProps.SetPropString(gamerules, "m_iszScriptThinkFunction", "");
		AddThinkToEnt(gamerules, null);
		MajorShocksBot = null;
		ResetMusicTrack();

		local ent = Entities.FindByClassname(null, "tf_objective_resource");
		if (ent)
		{
			if (WatersMissionName.len() > 0 && WatersMissionName != NetProps.GetPropString(ent, "m_iszMvMPopfileName")) // BAIL
			{
				CleanUp();
				return;
			}
		}

		gamerules.ValidateScriptScope();
		gamerules.GetScriptScope().WaveTimeThink <- function()
		{
			if (WatersRegime.WaveInProgress)
			{
				return;
			}

			local roundTime = GetPropFloat(gamerules, "m_flRestartRoundTime");
			local playerCount = 0;
			for (local i = 1, player; i <= MaxClients().tointeger(); i++)
			{
				if ((player = PlayerInstanceFromIndex(i)) != null && player.IsValid() && !IsPlayerABot(player))
				{
					playerCount++;
				}
			}
			if (roundTime > 0.0 && roundTime > Time() + 3 && (WatersRegime.GetPlayerReadyCount() >= playerCount || roundTime <= Time() + 12.0))
			{
				SetPropFloat(gamerules, "m_flRestartRoundTime", Time() + 3.0);
			}
		}

		AddThinkToEnt(gamerules, "WaveTimeThink");

		if (!("waters_of_a_robot_regime/point_templates" in getroottable()))
		{
			IncludeScript("waters_of_a_robot_regime/point_templates");
		}

		if (!("waters_of_a_robot_regime/major_shocks" in getroottable()))
		{
			IncludeScript("waters_of_a_robot_regime/major_shocks", getroottable())
		}

		if (!("waters_of_a_robot_regime/overclocked_dave" in getroottable()))
		{
			IncludeScript("waters_of_a_robot_regime/overclocked_dave", getroottable())
		}

		if (!("waters_of_a_robot_regime/shockwave_shields" in getroottable()))
		{
			IncludeScript("waters_of_a_robot_regime/shockwave_shields", getroottable())
		}
	}

	function OnGameEvent_recalculate_holidays(params)
	{
		Initialize();
	}

	function OnGameEvent_teamplay_broadcast_audio(params)
	{
		if (params.sound == "Announcer.MVM_Wave_End" || params.sound == "Announcer.MVM_Get_To_Upgrade")
		{
			for (local i = 1, player; i <= MAX_CLIENTS; i++)
			{
				if ((player = PlayerInstanceFromIndex(i)) != null && player.IsValid() && !IsPlayerABot(player))
				{
					StopSoundOn(params.sound, player);
				}
			}
		}
	}

	function OnGameEvent_player_spawn(params)
	{
		local player = GetPlayerFromUserID(params.userid);
		if (player == null || !player.IsValid() || !player.IsBotOfType(TF_BOT_TYPE))
		{
			return;
		}
		player.TerminateScriptScope();
		NetProps.SetPropString(player, "m_iszScriptThinkFunction", "");
		AddThinkToEnt(player, null);
		EntFireByHandle(player, "RunScriptCode", "WatersRegime.TagCheck(self)", -1, null, null);
	}

	function TagCheck(player)
	{
		if (player.HasBotTag("dave_boss"))
		{
			OverclockedDave.OnCreate(player);
		}

		if (player.HasBotTag("major_shocks"))
		{
			MajorShocks.OnCreate(player);
		}

		if (player.HasBotTag("fallen_chief_pyro"))
		{
			local model = Entities.FindByName(null, "the_funny_man_4");
			if (model != null && model.IsValid())
			{
				model.Destroy();
			}
		}

		if (player.HasBotTag("fallen_chief_heavy"))
		{
			local model = Entities.FindByName(null, "the_funny_man");
			if (model != null && model.IsValid())
			{
				model.Destroy();
			}
		}

		if (player.HasBotTag("fallen_chief_demo"))
		{
			local model = Entities.FindByName(null, "the_funny_man_2");
			if (model != null && model.IsValid())
			{
				model.Destroy();
			}
		}

		if (player.HasBotTag("emp_generator"))
		{
			player.ValidateScriptScope();
			local scope = player.GetScriptScope();
			scope.Think <- function()
			{
				self.SnapEyeAngles(QAngle(0.0, 0.0, 0.0));
				return 0;
			}
			AddThinkToEnt(player, "Think");
			for (local weapon; weapon = Entities.FindByClassname(weapon, "tf_weapon*");)
			{
				if (weapon == null || weapon.GetOwner() != player)
				{
					continue;
				}

				NetProps.SetPropInt(weapon, "m_nRenderMode", 1);
				NetProps.SetPropInt(weapon, "m_clrRender", 0);
			}
		}

		if (player.HasBotTag("emp_spawn"))
		{
			local spawn = Vector(0, 0, 0);
			if (SelectedEMPSpawn < 0)
			{
				SelectedEMPSpawn = RandomInt(0, AvailableEMPSpawns.len() - 1);
				spawn = AvailableEMPSpawns[SelectedEMPSpawn];

				SendGlobalGameEvent("show_annotation", {
					text = "Uber Generator!"
					lifetime = 4.0
					worldPosX = spawn.x
					worldPosY = spawn.y
					worldPosZ = spawn.z
					id = -1
					play_sound = "misc/null.wav"
					show_distance = false
					show_effect = true
					visibilityBitfield = 0
				});
				WatersRegime.EmitSoundToAllPlayers("mvm/mvm_tele_deliver.wav", 0.9);
			}
			else
			{
				spawn = AvailableEMPSpawns[SelectedEMPSpawn];
				AvailableEMPSpawns.remove(SelectedEMPSpawn);
				SelectedEMPSpawn = -1;
			}
			player.Teleport(true, spawn, false, QAngle(0, 0, 0), false, Vector(0, 0, 0));
			player.RemoveCondEx(TF_COND_INVULNERABLE, true);
			player.RemoveCondEx(TF_COND_INVULNERABLE_HIDE_UNLESS_DAMAGED, true);
			player.RemoveCondEx(TF_COND_INVULNERABLE_CARD_EFFECT, true);
			player.RemoveCondEx(TF_COND_INVULNERABLE_USER_BUFF, true);
			player.RemoveCondEx(TF_COND_PHASE, true);
		}

		if (player.HasBotTag("bot_timer"))
		{
			player.AddCond(TF_COND_STEALTHED_USER_BUFF_FADING);
			player.ValidateScriptScope();
			local scope = player.GetScriptScope();
			scope.NextHealthDegen <- Time() + 1.0;
			scope.Think <- function()
			{
				if (self.GetTeam() != 3 || !self.IsAlive())
				{
					self.TerminateScriptScope();
					NetProps.SetPropString(self, "m_iszScriptThinkFunction", "");
					AddThinkToEnt(self, null);
					return 0;
				}

				if (scope.NextHealthDegen <= Time())
				{
					self.SetHealth(self.GetHealth() - 1);
					if (self.GetHealth() <= 0)
					{
						self.TakeDamageCustom(self, self, null, Vector(0, 0, 0), self.GetCenter(), 900.0, 0, 6);
					}
					scope.NextHealthDegen = Time() + 1.0;
				}
				return 0;
			}

			AddThinkToEnt(player, "Think");
		}

		if (player.HasBotTag("doomsday_sound_high"))
		{
			WatersRegime.EmitSoundToAllPlayers("misc/doomsday_lift_warning.wav", 0.9);
		}

		if (player.HasBotTag("doomsday_sound_low"))
		{
			WatersRegime.EmitSoundToAllPlayers("misc/doomsday_lift_warning.wav", 0.5);
		}

		if (player.HasBotTag("always_glow"))
		{
			SetPropBool(player, "m_bGlowEnabled", true);
		}

		local tags = {}
		player.GetAllBotTags(tags)
		foreach (tag in tags)
		{
			if (tag.find("extra_spawn_point") != null)
			{
				local params = split(tag, "|")
				if (params.len() == 1)
				{
					ClientPrint(null, 3, format("\x04[MentTags] \x07FF0000ERROR:\x01 \"extra_spawn_point\" tag must have at least 1 parameter!", params.len()))
					continue;
				}
				if (params.len() == 2)
				{
					params.append("0 0 0")
				}
				local tempVector = split(params[1], " ")
				local tempAngles = split(params[2], " ")
				local vector = Vector(tempVector[0].tofloat(), tempVector[1].tofloat(), tempVector[2].tofloat())
				local angle = QAngle(tempAngles[0].tofloat(), tempAngles[1].tofloat(), tempAngles[2].tofloat())
				player.Teleport(true, vector, true, angle, true, player.GetAbsVelocity())
				player.RemoveCondEx(TF_COND_INVULNERABLE, true)
				player.RemoveCondEx(TF_COND_INVULNERABLE_HIDE_UNLESS_DAMAGED, true)
				player.RemoveCondEx(TF_COND_INVULNERABLE_CARD_EFFECT, true)
				player.RemoveCondEx(TF_COND_INVULNERABLE_USER_BUFF, true)
				player.RemoveCondEx(TF_COND_PHASE, true)
			}

			if (tag.find("custom_model") != null)
			{
				local params = split(tag, "|")
				if (params.len() == 1)
				{
					ClientPrint(null, 3, format("\x04[MentTags] \x07FF0000ERROR:\x01 \"custom_model\" tag must have at least 1 parameter!", params.len()))
					continue;
				}

				local model = params[1];
				PrecacheModel(model);
				player.SetCustomModelWithClassAnimations(model);
			}
		}
	}

	function OnGameEvent_player_death(params)
	{
		local player = GetPlayerFromUserID(params.userid);
		if (player == null || !player.IsValid() || !player.IsBotOfType(TF_BOT_TYPE))
		{
			return;
		}

		if (player.HasBotTag("dave_boss"))
		{
			ResetMusicTrack();
			PointTemplates.BossDeadDave(player.GetOrigin(), player.GetAngles());
		}

		if (player.HasBotTag("major_shocks"))
		{
			ResetMusicTrack();
			RedWon = true;
			PointTemplates.GrandFinale(player.GetOrigin(), player.GetAngles());
		}

		if (player.HasBotTag("fallen_chief_pyro"))
		{
			PointTemplates.BossDeadPyro(player.GetOrigin(), player.GetAngles());
		}

		if (player.HasBotTag("fallen_chief_heavy"))
		{
			PointTemplates.BossDeadHeavy(player.GetOrigin(), player.GetAngles());
		}

		if (player.HasBotTag("fallen_chief_demo"))
		{
			PointTemplates.BossDeadDemo(player.GetOrigin(), player.GetAngles());
		}

		if (player.HasBotTag("generic_kill_effect") || player.HasBotTag("emp_generator"))
		{
			PointTemplates.EMPDown(player.GetOrigin(), player.GetAngles());
		}

		if (player.HasBotTag("bot_timer") && !RedWon)
		{
			if (WinEntity == null || !WinEntity.IsValid())
			{
				WinEntity = SpawnEntityFromTable("game_round_win",
				{
					targetname = "all_blue_win",
					TeamNum = 3,
					force_map_reset = 1
				});
			}
			WinEntity.AcceptInput("RoundWin", "", null, null);
		}
	}

	function KillBotWithTag(name)
	{
		for (local i = 1, player; i <= MAX_CLIENTS; i++)
		{
			if ((player = PlayerInstanceFromIndex(i)) == null || !player.IsValid() || !player.IsBotOfType(TF_BOT_TYPE) || player.GetTeam() != 3 || !player.IsAlive())
			{
				continue;
			}

			if (player.HasBotTag(name))
			{
				player.SetHealth(0);
				player.TakeDamageCustom(player, player, null, Vector(0, 0, 0), player.GetCenter(), player.GetHealth().tofloat() + 10.0, 0, 6);
			}
		}
	}

	function CleanUp()
	{
		delete ::WatersMissionName;
		delete ::WatersRegime;
		PointTemplates.CleanUp();
		delete ::PointTemplates;
		delete ::OverclockedDave;
		delete ::MajorShocks;
		delete ::ShockwaveShields;
	}
}

__CollectGameEventCallbacks(WatersRegime);
PrecacheModel("models/props_mvm/robot_spawnpoint_warning.mdl");
PrecacheModel("models/bots/demo/bot_sentry_buster_gibby.mdl");
PrecacheModel("models/bots/demo/bot_demo_gibby.mdl");
PrecacheModel("models/bots/soldier/bot_soldier_gibby.mdl");
PrecacheModel("models/bots/pyro_boss/bot_pyro_boss_gibby.mdl");
PrecacheModel("models/bots/demo_boss/bot_demo_boss_gibby.mdl");
PrecacheModel("models/bots/heavy_boss/bot_heavy_boss_gibby.mdl");
PrecacheModel("models/bots/soldier_boss/bot_soldier_boss_gibby.mdl");
PrecacheModel("models/buildables/amplifier_test/amplifier.mdl");
PrecacheModel("models/props_halloween/fist_projectile_purple.mdl");
PrecacheModel("models/bots/boss_blimp/boss_blimp.mdl");
PrecacheModel("models/bots/boss_blimp/boss_blimp_damage1.mdl");
PrecacheModel("models/bots/boss_blimp/boss_blimp_damage2.mdl");
PrecacheModel("models/bots/boss_blimp/boss_blimp_damage3.mdl");
PrecacheModel("models/bots/soldier/major_shocks/bot_major_shocks.mdl");
PrecacheModel("models/empty.mdl");
PrecacheModel("models/props_mvm/mvm_player_shield.mdl");
PrecacheModel("models/props_combine/headcrabcannister01a.mdl");
PrecacheModel("models/props_mvm/robot_spawnpoint.mdl");
PrecacheModel("models/weapons/w_models/w_repair_claw.mdl");
PrecacheModel("models/bots/heavy_boss/bot_heavy_boss_extra.mdl");
PrecacheModel("models/weapons/c_models/c_fists_of_steel/c_fists_of_steel.mdl");

PrecacheSound("misc/null.wav");
PrecacheSound("mvm/mvm_tele_deliver.wav");
PrecacheSound("#waters_regime/boss_preludeioo.wav");
PrecacheSound("#waters_regime/first_phase_supercharged_v2.wav");
PrecacheSound("#waters_regime/secondphase_supercharged_v2.wav");
PrecacheSound("ambient_mp3/thunder4.mp3");
PrecacheSound("ambient/halloween/thunder_08.wav");
PrecacheSound("misc/ks_tier_04_death.wav");
PrecacheSound("items/cart_explode.wav");
PrecacheSound("ambient/explosions/explode_1.wav");
PrecacheSound("mvm/sentrybuster/mvm_sentrybuster_explode.wav");
PrecacheSound("mvm/mvm_tank_end.wav");
PrecacheSound("mvm/mvm_tank_explode.wav");
PrecacheSound("npc/turret_floor/die.wav");
PrecacheSound("vo/mvm/mght/soldier_mvm_m_paincrticialdeath01.mp3");
PrecacheSound("ambient/explosions/explode_2.wav");
PrecacheSound("misc/doomsday_missile_explosion.wav");
PrecacheSound("weapons/rescue_ranger_teleport_send_01.wav");
PrecacheSound("items/powerup_pickup_crits.wav");
PrecacheSound("ambient/halloween/windgust_10.wav");
PrecacheSound("ambient/halloween/thunder_01.wav");
PrecacheSound("npc/attack_helicopter/aheli_megabomb_siren1.wav");
PrecacheSound("npc/env_headcrabcanister/launch.wav");
PrecacheSound("vo/mvm/mght/pyro_mvm_m_laughevil04.mp3");
PrecacheSound("vo/mvm/mght/heavy_mvm_m_yell12.mp3");
PrecacheSound("vo/mvm/mght/demoman_mvm_m_yes02.mp3");
PrecacheSound("vo/mvm/mght/soldier_mvm_m_cheers01.mp3");
PrecacheSound("vo/mvm/mght/soldier_mvm_m_directhittaunt02.mp3");
PrecacheSound("vo/mvm/mght/soldier_mvm_m_hatoverhearttaunt01.mp3");
PrecacheSound("vo/mvm/mght/soldier_mvm_m_painsharp04.mp3");
PrecacheSound("vo/mvm/mght/soldier_mvm_m_painsharp05.mp3");
PrecacheSound("vo/mvm/mght/soldier_mvm_m_painsharp06.mp3");
PrecacheSound("ambient/alarms/razortrain_horn1.wav");
PrecacheSound("items/powerup_pickup_knockout.wav");
PrecacheSound("ambient/fireball.wav");
PrecacheSound("#music/hl2_song14.mp3");
PrecacheSound("#music/hl2_song31.mp3");
PrecacheSound("misc/doomsday_lift_warning.wav");
PrecacheSound("waters_regime/major_shocks_slam.mp3");
PrecacheSound("waters_regime/major_shocks_spin.mp3");
for (local i = 1; i < 21; i++)
{
	PrecacheSound(format("vo/mvm/mght/taunts/soldier_mvm_m_taunts%s%i.mp3", i < 10 ? "0" : "", i));
}

PrecacheScriptSound("Announcer.MVM_Wave_End");
PrecacheScriptSound("Announcer.MVM_Get_To_Upgrade");
PrecacheScriptSound("Announcer.MVM_Final_Wave_End");
PrecacheScriptSound("music.mvm_end_last_wave");
WatersRegime.Initialize();