//========================================================================================================
//
//	Operation Resurrection (Final) - resurrection.gm
//
//	Who			When			What
//--------------------------------------------------------------------------------------------------------
//	Mateos		~2009			Updated ailmanki waypoints for OB 0.71
//	Crapshoot	~2010			Conversion of my unfinished 0.71 waypoints to the Beta 0.8
//	Mateos		21/05/2011		Conversion of the final 0.71 waypoints to 0.81
//	Blindman	09/11/2012		Added and modified goals, arty at end game, some position mods.
//
//========================================================================================================

/****************************************************
Original waypoints by ailmanki (12th July 2007)
Path mapping updated and script by Mateos
Correspondant WAY file hash: -
Last update: 18th October 2024
****************************************************/

/*
 * TODO:
 * - Axis roles enforce classes; Have DEFENDER Covi stay on scaffolding goals, and DEFENDER1 Engi close to Main Gate Power? With specific raised priorities
 *		* Perhaps if Axis get to ground level by sides and not front, it'd safer for them? (3)
 * - main_gate_power_Planted: Axis Smokebomb mid-way walls/power box?
 * - MOBILEMORTAR_Walls: DEFEND_MainGate is within radius... Tempted to let it be
 * - Start - Axis: jumping into water does not seem the brightest idea... or get out faster/safer (3)
 * - Main Entrance Power cover spot: bot runs towards the dyna, change it to 30 sec
 *
 * - Do Axis defend the room flag? A single DEFEND_AxisFlag... and DEFEND_MansionFront (6 spots) covering Mansion front _only_
 *		* DEFEND_Mansion inside but not focused on room flag
 *		* ATTACK_Charlie1/2/3 are the Allies equivalent (4 covers the back of the Mansion...) - Change them to CAMP_RoomFlag or something
 * - Cover/Detonate spots
 * - Move PLANT_Basement_Bridge position at the other side
 * - K43 results too much in selfkill... only when the focus is inside the walls
 * - Axis tripmines?
 * - DEFEND_WaterwayDoorDestroyed management is a mess
 * - Try to see if it's possible to exclude tomb stones from being targeted
 *		* Once in 2nd phase, change the TargetBreakableDist to 0 (have a Map var for late joiners)
 * - PLANT_Crypt_Gate > PLANT_Axis_Command_Post for Covis
 * - PLANTMINE_Obj should be off when Crypt Gate gets built and Catacomb Door isn't destroyed
 * - I tried to make Axis jump in the Crypt area near a tree south, seems to fail hard...
 * - Make use of the Axis Soldier during this phase while Allies own the flag
 * - Due to role, DEFENDER rarely has Engi to do PLANTMINE_Garden
 *
 * - If Crypt Gate gets built after obj taken and Catacomb Door is intact, have the bot suicide if stuck inside
 *		* Or cover the Catacomb Door from that side...
 * - When stealing the Book, goals are roughly managed; disable Engi goals at least... excepted PLANT_Crypt_Gate! (2)
 *		* Also Soldier (MG at Main Gate?) + Arty on Raft
 * - Obj taken: split Allies spawn?
 * - Obj taken: too much focus on room flag and not Raft area...
 *
 * - Book_of_the_Resurrection_Secured: Axis suicide spawn if not on Walls
 * - Mortar on Raft when it's not escorted back by Axis?
 */

// DEFEND_start3/4/6 => DEFEND_Walls1/2/3
// DEFEND_start1/2/5/7 => DEFEND_WallsGround1/2/3/4
// DEFEND_Alpha => DEFEND_MansionFront
// DEFEND_Bravo => DEFEND_Mansion & DEFEND_Backyard
// DEFEND_Delta => DEFEND_Book
// DEFEND_End

// DEFEND_AxisFlag
// DEFEND_Catacomb => DEFEND_CatacombDoor
// DEFEND_High => DEFEND_MainGate - TODO: Replace by Mobile MG?
// DEFEND_Obj => DEFEND_BookDropoff
// DEFEND_DoorDestroyed => DEFEND_WaterwayDoorDestroyed

global Spawn =
{
	AXIS_HELLSPAWN	= 1,
	BOATHOUSE		= 2,
	FORWARD_SPAWN	= 3,
	CAVE			= 4,

	Init = function()
	{
		Spawn.axisSpawn		= Spawn.FORWARD_SPAWN;
		Spawn.alliedSpawn	= Spawn.BOATHOUSE;
	},

	MoveAxisSpawn = function( newSpawn )
	{
		Spawn.axisSpawn		= newSpawn;
		foreach ( bot in BotTable )
		{
			Spawn.SetAxisBotSpawn( bot );
		}
	},

	SetAxisBotSpawn = function( bot )
	{
		if ( !Spawn.init ) { Spawn.Init(); Spawn.init = true; }
		if ( bot.GetTeam() == TEAM.ALLIES) { return; }
		Util.MapDebugPrint( "Moving " + bot.Name + Util.DebugColorString + " to " + Spawn.GetName( Spawn.axisSpawn ) + "...", true );
		bot.ChangeSpawnPoint( Spawn.axisSpawn );
	},

	GetName = function( spawnId )
	{
		switch ( spawnId )
		{
			case Spawn.AXIS_HELLSPAWN:	{ return "Axis Hellspawn"; }
			case Spawn.BOATHOUSE:		{ return "Boathouse"; }
			case Spawn.FORWARD_SPAWN:	{ return "Forward Spawn"; }
			case Spawn.CAVE:			{ return "Cave"; }
		}

		return "{Unknown spawn ID: " + spawnId + "}";
	}
};

global Map =
{
	Debug = 0,		// Please set to zero before distributing your script
	Talk = true,	// Set Talk to false if you don't want the bots to talk
	ShowMovers = false,
	Movers = { "MOVER_train2" },

	// Make Axis bots not fall back when some stuff need to be built/defused/destroyed
	Book_of_the_Resurrection_Status = 0,	// 0 Initial/Not stolen - 1 Stolen - 2 Secured TODO Own table for values

	Main_Gate_Status = 0,		// Main Gate closed
	Catacomb_Door_Status = 0,	// Catacomb Door intact

	isCryptGateBuilt = false,
	isWaterwayDoorDestroyed = false,

	RoomFlagOwner = TEAM.AXIS,

	// -------------------------------------------------------------------------

	escort =
	{
		Name = "b1",
		TriggerOnEntity = GetGoal( "MOVER_train2" ).GetEntity(),
		OnEnter = function( ent )
		{
			SetAvailableMapGoals( TEAM.AXIS, true, {
				"ARTILLERY_S_end9",
				"CALLARTILLERY_end9",
				"MOBILEMG42_end9"
			});

			Util.MapDebugPrint( "escort", true );
		}
	},

	// -------------------------------------------------------------------------

	Navigation =
	{
		caretaker_house_ladder =
		{
			navigate = function( _this )
			{
				_this.Bot.HoldButton( BTN.FORWARD, 3 );
				sleep( 3 );
			}
		},

		front_gate_ladder_1 = // waypoint 1109, uid 1148
		{
			navigate = function( _this )
			{
				// look up, move backwards for 3 seconds
				// /bot mypos will show your current facing in console
				_this.AddAimRequest( Priority.High, "facing", Vector3( -0.672, 0.035, 0.740 ) );
				_this.Bot.HoldButton( BTN.BACKWARD, 3 );

				sleep( 3 );

				// spin around and press forward for 1 second
				_this.AddAimRequest( Priority.High, "facing", Vector3( 0.994, -0.091, -0.053 ) );
				_this.Bot.HoldButton( BTN.FORWARD, 1 ); // may consider 0.5 for this
				sleep( 1 );
				_this.ReleaseAimRequest();
			}
		}
	},

	// -------------------------------------------------------------------------

	Roles =
	{
		AXIS =
		{
			AllBots = true, // Each bot is considered for a role

			DEFENDER = // Upper scaffolding spots, then Mansion
			{
				numbots = 4,					// Maximum of 4 bots
				crucialClass = CLASS.COVERTOPS,	// First bot, to potentially destroy the Dock
				voice = "defend"				// Signals voice chat to randomly announce
			},

			DEFENDER1 = // Ground outside walls, then Basement/Catacombs/Crypt
			{
				numbots = 4,					// Maximum of 4 bots
				crucialClass = CLASS.ENGINEER,	// First bot, to potentially defuse
				voice = "defend"				// Signals voice chat to randomly announce
			},

			DEFENDER2 = // DEFEND_MainGate, then Back Terrace/Backyard/Cemetery/Crypt
			{
				numbots = 1,					// A single bot needed
				voice = "defend"				// Signals voice chat to randomly announce
			},

			DEFENDER3 = // MOBILEMORTAR_Walls TODO: Perhaps merge with DEFENDER2?
			{
				numbots = 1,					// A single bot needed
				crucialClass = CLASS.SOLDIER,	// First bot
				voice = "defend"				// Signals voice chat to randomly announce
			}
		},

		ALLIES =
		{
			AllBots = true,

			DEFENDER = // Mansion (Room Flag, being common to both teams)
			{
				numbots = 8,
				voice = "attack"
			}
		}
	},

	// -------------------------------------------------------------------------

	// *************************
	// *** TRIGGER CALLBACKS ***
	// *************************

	Allied_Command_Post_Built = function( trigger )
	{
		Util.MapDebugPrint( "Allied_Command_Post_Built", true );
	},

	Axis_Command_Post_Built = function( trigger )
	{
		Util.MapDebugPrint( "Axis_Command_Post_Built", true );
	},

	Basement_Bridge_Built = function( trigger )
	{
		if ( Map.Catacomb_Door_Status == 0 )
		{
			SetAvailableMapGoals( TEAM.ALLIES, true, "PLANT_catacomb_door" );
		}

		Util.MapDebugPrint( "Basement_Bridge_Built", true );
	},

	Crypt_Gate_Built = function( trigger )
	{
		Map.isCryptGateBuilt = true;

		SetAvailableMapGoals( TEAM.ALLIES, false, "ROUTE_CryptGate1" );

		// If both Catabomb and Crypt ways are closed, do not take objective
		// If Catacomb Door is destroyed, at least one way to escape is available
		SetAvailableMapGoals( TEAM.ALLIES, Map.Catacomb_Door_Status == 1, "FLAG_Book_Of_The_Resurrection" );

		Util.MapDebugPrint( "Crypt_Gate_Built" );
	},

	Dock_Built = function( trigger )
	{
		Wp.SetWaypointFlag( "WaterwayDoorLadderBottom", "closed", false );

		// If main_gate_power_destroyed, do not enable the side Defend spot
		if ( Map.Main_Gate_Status == 0 )
		{
			SetAvailableMapGoals( TEAM.AXIS, true, "DEFEND_Walls2" );
		}

		if ( !Map.isWaterwayDoorDestroyed ) // TODO More conditions (map start, or Axis-owned flag, not Book taken/secured)
		{
			SetAvailableMapGoals( TEAM.AXIS, true, "DEFEND_WaterwayDoor" );
			SetAvailableMapGoals( TEAM.ALLIES, true, "PLANT_waterway_door" );
		}

		Util.MapDebugPrint( "Dock_Built", true );
	},

	Allied_Command_Post_Planted = function( trigger )
	{
		Util.MapDebugPrint( "Allied_Command_Post_Planted", true );
	},

	Axis_Command_Post_Planted = function( trigger )
	{
		Util.MapDebugPrint( "Axis_Command_Post_Planted", true );
	},

	Basement_Bridge_Planted = function( trigger )
	{
		Util.MapDebugPrint( "Basement_Bridge_Planted", true );
	},

	Crypt_Gate_Planted = function( trigger )
	{
		Util.MapDebugPrint( "Crypt_Gate_Planted", true );
	},

	Dock_Planted = function( trigger )
	{
		Util.MapDebugPrint( "Dock_Planted", true );
	},

	catacomb_door_Planted = function( trigger )
	{
		Util.MapDebugPrint( "catacomb_door_Planted", true );
	},

	main_gate_power_Planted = function( trigger )
	{
		thread( Map.ManageAxisMortarShootingOnMainGatePowerPlanted );

		Util.MapDebugPrint( "main_gate_power_Planted", true );
	},

	waterway_door_Planted = function( trigger )
	{
		// Allow Axis Engi to potentially reach and defuse it
		SetAvailableMapGoals( TEAM.AXIS, false, "PLANT_Dock" );

		Util.MapDebugPrint( "waterway_door_Planted", true );
	},

	Allied_Command_Post_Defused = function( trigger )
	{
		Util.MapDebugPrint( "Allied_Command_Post_Defused", true );
	},

	Axis_Command_Post_Defused = function( trigger )
	{
		Util.MapDebugPrint( "Axis_Command_Post_Defused", true );
	},

	Basement_Bridge_Defused = function( trigger )
	{
		Util.MapDebugPrint( "Basement_Bridge_Defused", true );
	},

	Crypt_Gate_Defused = function( trigger )
	{
		Util.MapDebugPrint( "Crypt_Gate_Defused", true );
	},

	Dock_Defused = function( trigger )
	{
		Util.MapDebugPrint( "Dock_Defused", true );
	},

	catacomb_door_Defused = function( trigger )
	{
		Util.MapDebugPrint( "catacomb_door_Defused", true );
	},

	main_gate_power_Defused = function( trigger )
	{
		Util.MapDebugPrint( "main_gate_power_Defused", true );
	},

	waterway_door_Defused = function( trigger )
	{
		// TODO: Handle through ManageAxisDefendGoals
		if ( Map.Book_of_the_Resurrection_Status == 0
			&& Map.RoomFlagOwner == TEAM.AXIS
			&& !Map.Exists_Goal( "DEFUSE_waterway_door.*" ) )
		{
			SetAvailableMapGoals( TEAM.AXIS, true, "PLANT_Dock" );
		}

		Util.MapDebugPrint( "waterway_door_Defused", true );
	},

	Allied_Command_Post_Destroyed = function( trigger )
	{
		Util.MapDebugPrint( "Allied_Command_Post_Destroyed", true );
	},

	Axis_Command_Post_Destroyed = function( trigger )
	{
		Util.MapDebugPrint( "Axis_Command_Post_Destroyed", true );
	},

	Basement_Bridge_Destroyed = function( trigger )
	{
		Util.MapDebugPrint( "Basement_Bridge_Destroyed", true );
	},

	Crypt_Gate_Destroyed = function( trigger )
	{
		Map.isCryptGateBuilt = false;

		if ( Map.Book_of_the_Resurrection_Status == 0 )
		{
			SetAvailableMapGoals( TEAM.AXIS, true, {
				"PLANTMINE_Obj.*",
				"PLANTMINE_BackyardPond.*"
			});
		}

		SetAvailableMapGoals( TEAM.ALLIES, true, {
			"CAPPOINT_Raft",
			"ROUTE_CryptGate1",
			"FLAG_Book_Of_The_Resurrection"
		});

		Util.MapDebugPrint( "Crypt_Gate_Destroyed" );
	},

	Dock_Destroyed = function( trigger )
	{
		Wp.SetWaypointFlag( "WaterwayDoorLadderBottom", "closed", true );

		SetAvailableMapGoals( TEAM.AXIS, false, {
			"DEFEND_Walls2",
			"DEFEND_WaterwayDoor"
		});

		Util.MapDebugPrint( "Dock_Destroyed", true );
	},

	catacomb_door_Destroyed = function( trigger )
	{
		Map.Catacomb_Door_Status = 1;	// Catacomb Door Destroyed

		// If obj not stolen/secured only, disabled on Taken, re-enabled if Returned
		if ( Map.Book_of_the_Resurrection_Status == 0 )
		{
			SetAvailableMapGoals( TEAM.AXIS, true, {
				"DEFEND_Book[1-9]",
				"PLANTMINE_Obj.*",
				"PLANTMINE_BackyardPond.*"
			});
			SetAvailableMapGoals( TEAM.AXIS, false, {
				"DEFEND_Backyard.*",
				"DEFEND_Mansion[1-9]"
			});
		}

		SetAvailableMapGoals( TEAM.AXIS, false, "DEFEND_Mansion.*" );

		SetAvailableMapGoals( TEAM.ALLIES, true, {
			"CAPPOINT_Raft",
			"FLAG_Book_Of_The_Resurrection",
			"ROUTE_Ladder1"
		});

		Util.MapDebugPrint( "catacomb_door_Destroyed" );
	},

	// TODO: Handle depending on who owns the flag too (1) + if Catacomb Door is already destroyed...
	main_gate_power_Destroyed = function( trigger )
	{
		Map.Main_Gate_Status = 1;	// Main Gate opened

		// If obj stolen/secured, exit right now
		if ( Map.Book_of_the_Resurrection_Status != 0 )
		{
			Util.MapDebugPrint( "main_gate_power_Destroyed", true );
			return;
		}

		// ---

		// TODO: The equivalent role/obj handling is not done in Book_of_the_Resurrection_Returned...

		// Moving roles to the next step
		ClearGoalRole( ".*", ROLE );

		// Mansion
		Util.SetRoleForTable( {
			"ARTILLERY_S_Dock",
			"ATTACK_Charlie[123]",
			"BUILD_Axis_Command_Post",
			"CALLARTILLERY_Dock",
			"CHECKPOINT_room_flag",
			"DEFEND_AxisFlag",
			"DEFEND_Mansion.*",
			"MOBILEMG42_EntranceMobileMG42",
			"MOUNTMG42_MansionMG42",
			"PLANT_Allied_Command_Post",
			"PLANT_Basement_Bridge",
			"PLANT_Dock",
			"PLANTMINE_Garden.*",
			"REPAIRMG42_MansionMG42"
		}, ROLE.DEFENDER );
		SetGoalRole( "DEFUSE_Axis_Command_Post.*", ROLE.DEFENDER, true );

		// Basement/Catacomb/Crypt TODO: Add missing goals
		SetGoalRole( "DEFEND_CatacombDoor.*", ROLE.DEFENDER1 ); // Reminder: crucialClass is Engineer

		// Back Terrace/Backyard/Cemetery/Crypt entrance
		Map.Roles.AXIS.DEFENDER2.numbots = 4;
		Util.ChangeCrucialClassForRole( TEAM.AXIS, ROLE.DEFENDER2, CLASS.ENGINEER ); // Build Crypt Gate, plant landmines
		Util.SetRoleForTable( {
			"BUILD_Crypt_Gate",
			"DEFEND_Backyard.*",
			"PLANTMINE_CryptGate.*",
			"PLANTMINE_BackyardCatacombAccess.*",
			"PLANTMINE_BackyardPond.*"
		}, ROLE.DEFENDER2 );
		SetGoalRole( "DEFUSE_Crypt_Gate.*", ROLE.DEFENDER2, true );

		// ---

		Util.EnableGoal( "CHECKPOINT_room_flag" );

		// *** AXIS GOALS ***
		SetAvailableMapGoals( TEAM.AXIS, true, {
			"ATTACK_Charlie[123]",
			"BUILD_Axis_Command_Post",
			"BUILD_Crypt_Gate",
			"DEFEND_MansionFront.*",
			"DEFEND_AxisFlag",
			"DEFEND_CatacombDoor.*",
			"MOBILEMG42_EntranceMobileMG42",
			//"MOUNTMG42_MansionMG42",
			"PLANT_Allied_Command_Post",
			"PLANT_Basement_Bridge",
			"PLANTMINE_BackyardCatacombAccess.*",
			"PLANTMINE_BackyardPond.*",
			"PLANTMINE_CryptGate.*",
			"PLANTMINE_Garden.*",
			//"REPAIRMG42_MansionMG42"
		});

		SetAvailableMapGoals( TEAM.AXIS, false, {
			"DEFEND_MainGate",
			"DEFEND_Walls.*",
			"MOBILEMORTAR_Walls", // TODO: Perhaps keep it up? (1)
			"MOUNTMG42_StartMG42",
			"REPAIRMG42_StartMG42"
		});

		// *** ALLIED GOALS ***
		SetAvailableMapGoals( TEAM.ALLIES, true, {
			"AMMOCAB_main_ammocabinet",
			"ATTACK_Charlie.*",
			"BUILD_Allied_Command_Post",
			"BUILD_Basement_Bridge",
			"HEALTHCAB_main_healthcabinet",
			"PLANT_Crypt_Gate",
			"PLANT_catacomb_door",
			"PLANT_Axis_Command_Post"
		});

		SetAvailableMapGoals( TEAM.ALLIES, false, "ATTACK_Echo.*" );

		// ---

		if ( Map.Talk )
		{
			foreach ( id and bot in BotTable )
			{
				if ( bot.GetTeam() == TEAM.AXIS )
				{
					r = RandRange( 0, 10 );
					if ( r < 2 )
					{
						bot.SayVoice( VOICE.REINFORCE_DEF );
					}
					else if ( r > 8 )
					{
						bot.SayVoice( VOICE.DEFEND_OBJECTIVE );
					}
				}

				if ( bot.GetTeam() == TEAM.ALLIES )
				{
					r = RandRange( 0, 10 );
					if ( r < 2 )
					{
						bot.SayVoice( VOICE.LETS_GO );
					}
					else if ( r > 8 )
					{
						sleep( 1.2 );
						bot.SayVoice( VOICE.G_CHEER );
					}
				}
			}
		}

		Util.MapDebugPrint( "main_gate_power_Destroyed" );

		Util.RollCall(); // TODO: Don't forget to remove
	},

	waterway_door_Destroyed = function( trigger )
	{
		Map.isWaterwayDoorDestroyed = true;

		Util.EnableGoal( "AMMOCAB_basement_ammocabinet" );
		Util.EnableGoal( "HEALTHCAB_basement_healthcabinet" );

		SetAvailableMapGoals( TEAM.AXIS, false, "DEFEND_WaterwayDoor" );

		if ( Map.Book_of_the_Resurrection_Status < 2 )
		{
			SetAvailableMapGoals( TEAM.AXIS, true, "DEFEND_WaterwayDoorDestroyed" );
			SetAvailableMapGoals( TEAM.ALLIES, true, "BUILD_Basement_Bridge" );
		}

		if ( Map.Book_of_the_Resurrection_Status == 0 && Map.RoomFlagOwner == TEAM.AXIS )
		{
			// Restore from waterway_door_Planted
			SetAvailableMapGoals( TEAM.AXIS, true, "PLANT_Dock" );
		}

		Util.MapDebugPrint( "waterway_door_Destroyed" );
	},

	Book_of_the_Resurrection_Taken = function( trigger )
	{
		Map.Book_of_the_Resurrection_Status = 1;

		// Above most of other goals, to ensure they're used over BUILD/PLANT/etc.
		SetGoalPriority( "ATTACK_Echo.*", 0.95 );
		if ( Map.Catacomb_Door_Status == 0 )
		{
			SetGoalPriority( "PLANT_catacomb_door", 0.91 ); // Make sure it is a priority
			SetGoalPriority( "ATTACK_Echo.*", 0.50, TEAM.ALLIES, CLASS.ENGINEER );
		}

		if ( Map.isCryptGateBuilt )
		{
			SetGoalPriority( "ATTACK_Echo.*", 0.50, TEAM.ALLIES, CLASS.COVERTOPS );
		}

		SetGoalPriority( "DEFEND_Walls.*", 0.95 );
		SetGoalPriority( "DEFEND_BookDropoff.*", 0.96 ); // Important camping spots, but limited to 2 bots
		SetGoalPriority( "CHECKPOINT_room_flag", 0.97 );
		SetGoalPriority( "FLAGRETURN_.*", 1.01, 0, 0, true ); // TODO: Manage things to be < 1

		SetAvailableMapGoals( TEAM.AXIS, false, {
			"ATTACK_Charlie[123]",
			"DEFEND_.*",
			"MOUNTMG42_MansionMG42",
			"PLANTMINE_*",
			"REPAIRMG42_MansionMG42"
		});

		SetAvailableMapGoals( TEAM.AXIS, true, {
			"DEFEND_BookDropoff.*",
			"DEFEND_Walls[13]",
			"DEFEND_WallsGround[14]"
		});

		SetAvailableMapGoals( TEAM.ALLIES, true, "ATTACK_Echo.*" );

		if ( Map.Talk )
		{
			foreach ( id and bot in BotTable )
			{
				if ( bot.GetTeam() == TEAM.AXIS )
				{
					r = RandRange( 0, 10 );
					if( r < 2 )
					{
						bot.SayVoice( VOICE.G_NEGATIVE );
					}
					else if ( r > 8 )
					{
						bot.SayTeam( "All to the Raft!!!" );
					}
				}
				else if ( bot.GetTeam() == TEAM.ALLIES )
				{
					r = RandRange( 0, 10 );
					if ( r < 2 )
					{
						bot.SayVoice( VOICE.MOVE );
					}
				}
			}
		}

		Util.MapDebugPrint( "Book_of_the_Resurrection_Taken" );
	},

	Book_of_the_Resurrection_Returned = function( trigger )
	{
		Map.Book_of_the_Resurrection_Status = 0;

		// Restore
		SetGoalPriority( "ATTACK_Echo.*", 0.50 );
		SetGoalPriority( "DEFEND_Walls.*", 0.50 );
		SetGoalPriority( "DEFEND_BookDropoff.*", 0.50 ); // Important camping spots, but limited to 2 bots
		SetGoalPriority( "CHECKPOINT_room_flag", 0.70 );

		Util.EnableGoal( "CHECKPOINT_room_flag", true );

		if ( Map.Catacomb_Door_Status == 1 )
		{
			DEFEND_Axis = "DEFEND_Book[1-9]";
		}
		else if ( Map.RoomFlagOwner == TEAM.AXIS )
		{
			DEFEND_Axis = "DEFEND_MansionFront.*";
			SetAvailableMapGoals( TEAM.AXIS, true, {
				"ATTACK_Charlie[123]",
				"DEFEND_AxisFlag"
			});
		}
		else
		{
			DEFEND_Axis = "DEFEND_Mansion[1-9]";
			SetAvailableMapGoals( TEAM.AXIS, true, {
				"ATTACK_Charlie[123]",
				"DEFEND_AxisFlag"
			});
		}

		SetAvailableMapGoals( TEAM.AXIS, true, {
			DEFEND_Axis,
			"DEFEND_Backyard.*",
			"PLANTMINE_BackyardCatacombAccess.*",
			"PLANTMINE_BackyardPond.*",
			"PLANTMINE_CryptGate.*",
			"PLANTMINE_Garden.*",
			"PLANTMINE_Obj.*",
			"PLANTMINE_BackyardPond.*"
		});

		if ( Map.Catacomb_Door_Status == 0 )
		{
			SetAvailableMapGoals( TEAM.AXIS, true, "DEFEND_CatacombDoor.*" );
		}

		if ( Map.RoomFlagOwner == TEAM.AXIS )
		{
			SetAvailableMapGoals( TEAM.AXIS, true, "PLANT_Dock" );
		}

		SetAvailableMapGoals( TEAM.AXIS, false, {
			"DEFEND_BookDropoff.*",
			"DEFEND_Walls.*"
		});

		SetAvailableMapGoals( TEAM.ALLIES, false, "ATTACK_Echo.*" );

		if ( Map.Talk )
		{
			foreach ( id and bot in BotTable )
			{
				if ( bot.GetTeam() == TEAM.AXIS )
				{
					r = RandRange( 0, 10 );
					if( r < 2 )
					{
						bot.SayVoice( VOICE.G_GOODGAME );
					}
					else if( r > 8 )
					{
						bot.SayVoice( VOICE.DEFEND_OBJECTIVE );
					}
				}
			}
		}

		Util.MapDebugPrint( "Book_of_the_Resurrection_Returned" );
	},

	Book_of_the_Resurrection_Secured = function( trigger )
	{
		Map.Book_of_the_Resurrection_Status = 2;
		Map.RoomFlagOwner = TEAM.ALLIES;

		Spawn.MoveAxisSpawn( Spawn.CAVE );

		// Restore
		SetGoalPriority( "DEFEND_Walls.*", 0.50 );

		// Making bots only focus on end goals
		Util.DisableGoal( ".*", true );	// All but Routes
		SetGoalPriority( "DEFUSE_.*", 0.0, TEAM.AXIS, CLASS.ENGINEER, true );
		SetGoalPriority( "DEFUSE_.*", 0.0, TEAM.ALLIES, CLASS.ENGINEER, true );

		Util.EnableGoal( "MOVER_train2", true );
		Util.EnableGoal( "RIDE_Raft", true );

		SetAvailableMapGoals( TEAM.AXIS, true, {
			"ARTILLERY_S_end",
			"ARTILLERY_D_end",
			"CALLARTILLERY_end.*",
			"DEFEND_End.*"
		});

		// TODO: ATTACK spots + Roles for Walls/WallsGround
		SetAvailableMapGoals( TEAM.ALLIES, true, {
			"ARTILLERY_S_ally.*",
			"ARTILLERY_D_ally.*",
			"CALLARTILLERY_ally.*",
			"DEFEND_Walls[13]",
			"DEFEND_WallsGround4",
			"MOUNTMG42_StartMG42",
			"REPAIRMG42_StartMG42"
		});

		if ( Map.Talk )
		{
			foreach ( id and bot in BotTable )
			{
				if ( bot.GetTeam() == TEAM.AXIS )
				{
					r = RandRange( 0, 10 );
					if ( r < 2 )
					{
						bot.SayVoice( VOICE.G_NEGATIVE );
					}
					else if ( r > 8 )
					{
						bot.SayTeam( "All to the Raft!!!" );
					}
				}
				else if ( bot.GetTeam() == TEAM.ALLIES )
				{
					r = RandRange( 0, 10 );
					if ( r < 2 )
					{
						bot.SayVoice( VOICE.G_GOODGAME );
					}
					else if ( r > 8 )
					{
						sleep( 1.2 );
						bot.SayVoice( VOICE.ESCORT_VEHICLE );
					}
				}
			}
		}

		Util.MapDebugPrint( "Book_of_the_Resurrection_Secured" );
	},

	room_flag_Axis_Captured = function( trigger )
	{
		Map.RoomFlagOwner = TEAM.AXIS;

		if ( Map.Book_of_the_Resurrection_Status == 0 )
		{
			SetAvailableMapGoals( TEAM.AXIS, false, "DEFEND_Mansion[1-9]" );
			SetAvailableMapGoals( TEAM.AXIS, true, {
				"ARTILLERY_S_Dock",
				"ATTACK_Charlie[123]",
				"CALLARTILLERY_Dock",
				"DEFEND_AxisFlag",
				"DEFEND_MansionFront.*",
				"MOBILEMG42_EntranceMobileMG42",
				//"MOUNTMG42_MansionMG42",
				"PLANT_Dock",
				//"REPAIRMG42_MansionMG42"
			});

			SetAvailableMapGoals( TEAM.ALLIES, true, "BUILD_Dock" );

			if ( Map.isWaterwayDoorDestroyed )
			{
				SetAvailableMapGoals( TEAM.AXIS, true, "DEFEND_WaterwayDoorDestroyed" );
			}
			else
			{
				SetAvailableMapGoals( TEAM.ALLIES, true, "PLANT_waterway_door" );
			}
		}

		Util.MapDebugPrint( "room_flag_Axis_Captured", true );
	},

	room_flag_Allies_Captured = function( trigger )
	{
		Map.RoomFlagOwner = TEAM.ALLIES;

		SetAvailableMapGoals( TEAM.AXIS, false, "PLANT_Dock" );

		// If they sneaked by some mean
		SetAvailableMapGoals( TEAM.ALLIES, true, {
			"AMMOCAB_main_ammocabinet",
			"HEALTHCAB_main_healthcabinet"
		});

		// If obj not stolen/secured only, disabled on Taken, re-enabled if Returned
		if ( Map.Book_of_the_Resurrection_Status == 0 )
		{
			SetAvailableMapGoals( TEAM.AXIS, true, "DEFEND_Mansion[1-9]" );
			SetAvailableMapGoals( TEAM.AXIS, false, {
				"ARTILLERY_S_Dock",
				"ATTACK_Charlie[123]",
				"CALLARTILLERY_Dock",
				"DEFEND_AxisFlag",
				"DEFEND_MansionFront.*",
				"DEFEND_WaterwayDoorDestroyed",
				"MOBILEMG42_EntranceMobileMG42",
				"MOUNTMG42_MansionMG42",
				"REPAIRMG42_MansionMG42"
			});

			SetAvailableMapGoals( TEAM.ALLIES, false, "BUILD_Dock" );

			if ( !Map.isWaterwayDoorDestroyed )
			{
				SetAvailableMapGoals( TEAM.ALLIES, false, "PLANT_waterway_door" );
			}
		}

		Util.MapDebugPrint( "room_flag_Allies_Captured", true );
	},

	Allies_Win = function( trigger )
	{
		if ( Map.Talk )
		{
			foreach ( id and bot in BotTable )
			{
				if ( bot.GetTeam() == TEAM.AXIS )
				{
					r = RandRange( 0, 10 );
					if ( r < 2 )
					{
						bot.SayVoice( VOICE.G_NEGATIVE );
					}
					else if ( r > 8 )
					{
						bot.Say( "Verdammt!" );
					}
				}
				else if ( bot.GetTeam() == TEAM.ALLIES )
				{
					r = RandRange( 0, 10 );
					if ( r < 2 )
					{
						bot.SayVoice( VOICE.G_GOODGAME );
					}
					else if ( r > 8 )
					{
						sleep( 1.2 );
						bot.SayVoice( VOICE.G_CHEER );
					}
				}
			}
		}

		Util.MapDebugPrint( "Allies_Win", true );
	},

	AdjusMainAmmoCabRangeForWallsMortar = function()
	{
		amg = GetGoal( "AMMOCAB_main_ammocabinet" );
		mmg = GetGoal( "MOBILEMORTAR_Walls" );
		dst = ToInt( Ceil( DistanceBetween( amg, mmg ) ) ) + 20;
		amg.SetRange( dst );

		print( Util.DebugColorString + mmg.GetName() + "'s range set to " + dst );

		DrawDebugLine( amg.GetPosition(), mmg.GetPosition(), COLOR.CYAN, 20 );
	},

	Exists_Goal = function( goalExpr )
	{
		t = Util.GoalTable( goalExpr );
		return tableCount( t ) > 0;
	},

	ManageAxisDefendGoals = function()
	{
		// TODO Write and call it
	},

	mutex = false,
	ManageAxisMortarShootingOnMainGatePowerPlanted = function()
	{
		// Allow only one execution at once
		if ( Map.mutex )
		{
			Util.MapDebugPrint( "ManageAxisMortarShootingOnMainGatePowerPlanted already running, exiting", true );
			return;
		}

		Map.mutex = true;

		// SetAvailableMapGoals( Map.DEFENCE_TEAM, true, "SMOKEBOMB_Defuse_Vehicle_Barrier" );

		axisMortarMapGoal = GetGoal( "MOBILEMORTAR_Walls" );

		Util.MapDebugPrint( "Pausing " + axisMortarMapGoal.GetName() + " shooting...", true );

		axisMortarMapGoal.DontShootMortar = true;
		while ( Map.Exists_Goal( "DEFUSE_main_gate_power.*" ) )
		{
			sleep ( 1 );
		}

		axisMortarMapGoal.DontShootMortar = false;

		Util.MapDebugPrint( "Resumed " + axisMortarMapGoal.GetName() + " shooting", true );

		// SetAvailableMapGoals( Map.DEFENCE_TEAM, false, "SMOKEBOMB_Defuse_Vehicle_Barrier" );

		Map.mutex = false;
	},

	MovePlantMainGatePower = function()
	{
		t = {};
		if ( Wp.GetWaypointByName( "Main Gate Power", t ) )
		{
			GetGoal( "PLANT_main_gate_power" ).SetPosition( t.position );
		}
	}
};

global OnMapLoad = function()
{
	// *** TRIGGERS ***
	OnTrigger( "Allied Command Post constructed. Charge speed increased!", Map.Allied_Command_Post_Built );
	OnTrigger( "Axis Command Post constructed. Charge speed increased!", Map.Axis_Command_Post_Built );
	OnTrigger( "Basement Bridge has been constructed.", Map.Basement_Bridge_Built );
	OnTrigger( "Gate to Crypt has been constructed.", Map.Crypt_Gate_Built );
	OnTrigger( "Dock to waterway door has been constructed.", Map.Dock_Built );

	OnTrigger( "Planted at the Allied Command Post.", Map.Allied_Command_Post_Planted );
	OnTrigger( "Planted at the Axis Command Post.", Map.Axis_Command_Post_Planted );
	OnTrigger( "Planted at Basement Bridge.", Map.Basement_Bridge_Planted );
	OnTrigger( "Planted at Crypt Gate.", Map.Crypt_Gate_Planted );
	OnTrigger( "Planted at Dock.", Map.Dock_Planted );
	OnTrigger( "Planted at the catacomb door.", Map.catacomb_door_Planted );
	OnTrigger( "Planted at the main gate power.", Map.main_gate_power_Planted );
	OnTrigger( "Planted at the waterway door.", Map.waterway_door_Planted );

	OnTrigger( "Defused at the Allied Command Post.", Map.Allied_Command_Post_Defused );
	OnTrigger( "Defused at the Axis Command Post.", Map.Axis_Command_Post_Defused );
	OnTrigger( "Defused at Basement Bridge.", Map.Basement_Bridge_Defused );
	OnTrigger( "Defused at Crypt Gate.", Map.Crypt_Gate_Defused );
	OnTrigger( "Defused at Dock.", Map.Dock_Defused );
	OnTrigger( "Defused at the catacomb door.", Map.catacomb_door_Defused );
	OnTrigger( "Defused at the Main Gate Power.", Map.main_gate_power_Defused );
	OnTrigger( "Defused at the waterway door.", Map.waterway_door_Defused );

	OnTrigger( "Axis team has destroyed the Allied Command Post!", Map.Allied_Command_Post_Destroyed );
	OnTrigger( "Allied team has destroyed the Axis Command Post!", Map.Axis_Command_Post_Destroyed );
	OnTrigger( "Basement Bridge has been destroyed.", Map.Basement_Bridge_Destroyed );
	OnTrigger( "Gate to Crypt has been destroyed.", Map.Crypt_Gate_Destroyed );
	OnTrigger( "Dock to waterway door has been destroyed.", Map.Dock_Destroyed );
	OnTrigger( "Allies Breached the catacomb tunnel entrance", Map.catacomb_door_Destroyed );
	OnTrigger( "The Main Gate has been breached!", Map.main_gate_power_Destroyed );
	OnTrigger( "Allies Breached the waterway entrance", Map.waterway_door_Destroyed );

	OnTrigger( "Allies have stolen Book of the Resurrection!", Map.Book_of_the_Resurrection_Taken );
	OnTrigger( "Flag returned Book of the Resurrection!", Map.Book_of_the_Resurrection_Returned );
	OnTrigger( "Allied team is escaping with the Book!", Map.Book_of_the_Resurrection_Secured );

	OnTrigger( "Axis reclaims the Forward room!", Map.room_flag_Axis_Captured );
	OnTrigger( "Allies capture the Forward room!", Map.room_flag_Allies_Captured );

	OnTrigger( "Allied team escaped with the Book!", Map.Allies_Win );

	// *** NO PUSH ZONES ***
	nopush1 = OnTriggerRegion(AABB(-2021.686,-822.044,900.956,-1786.145,-448.430,1117.125),RegionTrigger.DisableBotPush);
	nopush2 = OnTriggerRegion(AABB(-1841.861,-254.305,441.926,-1519.329,-28.528,1157.042),RegionTrigger.DisableBotPush);

	b1 = OnTriggerRegion( AABB( -4716, -1337 ,160.5 ,-4601.5 ,-596,170.5 ), Map.escort );

	// -------------------------------------------------------------------------

	// *** CLEAR ALL GOALS FOR BOTH TEAMS ***
	Util.DisableGoal( ".*", true );	// All but Routes
	ClearGoalRole( ".*", ROLE );

	// *** AXIS GOALS ***
	SetAvailableMapGoals( TEAM.AXIS, true, {
		"AMMOCAB_main_ammocabinet",
		"ARTILLERY_S_Dock",
		"CALLARTILLERY_Dock",
		"DEFEND_MainGate",
		"DEFEND_Walls[1-9]",
		"DEFEND_WallsGround.*",
		"HEALTHCAB_main_healthcabinet",
		"MOBILEMORTAR_Walls",
		"MOUNTMG42_StartMG42",
		"REPAIRMG42_StartMG42",
		"PLANT_Dock"
	});

	// *** ALLIED GOALS ***
	SetAvailableMapGoals( TEAM.ALLIES, true, {
		"ATTACK_Echo.*",
		"BUILD_Dock",
		"PLANT_main_gate_power"
	});
	SetAvailableMapGoals( TEAM.ALLIES, false, "ROUTE_Ladder1" );

	// -------------------------------------------------------------------------

	// *** GOALS PROPERTIES ***
	// DEFEND is 0.50 and CHECKPOINT 0.60
	// TODO See to replace with roles?
	SetGoalPriority( "ATTACK_Charlie.*", 0.55 );
	//SetGoalPriority( "DEFEND_AxisFlag", 0.55 );
	//SetGoalPriority( "DEFEND_CatacombDoor.*", 0.55 );
	SetGoalPriority( "DEFEND_WaterwayDoor.*", 0.55 );

	Util.AddInvVehicle( "MOVER_train2" );

	Util.AddUsePoint( "PLANT_catacomb_door", Vec3( 3660, 1290, -370 ) ); // Outside
	Util.AddUsePoint( "PLANT_catacomb_door", Vec3( 3785, 1290, -370 ) ); // Inside
	Util.AddUsePoint( "PLANT_Crypt_Gate", Vec3( 4525, 15, 110 ) ); // Outside
	Util.AddUsePoint( "PLANT_Crypt_Gate", Vec3( 4655, 15, 100 ) ); // Inside

	// Doomed unreachable otherwise... TODO Review waypoints to avoid these lines
	Util.SetGoalPosition(   985.875,   131.861,   71.866, "BUILD_Basement_Bridge" );
	Util.SetGoalPosition( -2126.000, -1557.000,  226.000, "BUILD_Dock" );
	Util.SetGoalPosition(  1126.700,   392.300,   78.125, "PLANT_Basement_Bridge" );

	Util.SetMaxUsers( 1, "BUILD_.*" );
	Util.SetMaxUsers( 1, "PLANT_.*" );
	Util.SetMaxUsers( 1, "MOUNTMG42_.*" );
	Util.SetMaxUsers( 1, "REPAIRMG42_.*" );
	Util.SetMaxUsers( 2, "CHECKPOINT_.*" );
	Util.SetMaxUsers( 3, "FLAG.*" );

	Util.SetMaxUsersInProgress( 2, "RIDE_Raft" );
	Util.SetMaxUsersInUse( 1, "RIDE_Raft" );

	Util.SetRoleForTable( {
		"ARTILLERY_S_Dock",
		"CALLARTILLERY_Dock",
		"DEFEND_Walls[1-9]",
		"MOUNTMG42_StartMG42",
		"REPAIRMG42_StartMG42",
		"PLANT_Dock"
	}, ROLE.DEFENDER );

	Util.SetRoleForTable( { "DEFEND_WallsGround.*", "DEFEND_WaterwayDoor" }, ROLE.DEFENDER1 );
	SetGoalRole( "DEFUSE_main_gate_power.*", ROLE.DEFENDER1, true ); // TODO: Is this a mistake?
	SetGoalRole( "DEFUSE_waterway_door.*", ROLE.DEFENDER1, true );

	SetGoalRole( "DEFEND_MainGate", ROLE.DEFENDER2 );

	SetGoalRole( "MOBILEMORTAR_Walls", ROLE.DEFENDER3 );

	// Dock: throw satchel from the top of the scaffolding
	Util.SetGoalPosition( -1980.019, -1366.759, 1066.125, "PLANT_Dock" );
	GetGoal( "PLANT_Dock" ).SatchelFunction = function( _this )
	{
		_this.AddAimRequest( Priority.VeryHigh, "facing", Vec3( -0.522, -0.850, 0.068 ) );
		sleep( 0.5 );
		_this.AddWeaponRequest( Priority.High, WEAPON.SATCHEL );
		sleep( 0.8 );
		_this.Bot.PressButton( BTN.ATTACK1 );
		sleep( 0.2 );
		_this.AddWeaponRequest( Priority.High, WEAPON.SATCHEL_DET );
		sleep( 1.0 );
		_this.Bot.PressButton( BTN.ATTACK1 );
	};

	Wp.SetWaypointFlag( "WaterwayDoorLadderBottom", "closed", true );

	Map.AdjusMainAmmoCabRangeForWallsMortar();
	Map.MovePlantMainGatePower();

	// -------------------------------------------------------------------------

	Util.MapDebugPrint( "Omni-bot map script for Operation Resurrection (Final) by Mateos" );
};

global OnBotJoin = function( bot )
{
	Spawn.SetAxisBotSpawn( bot );

	bot.MaxViewDistance = 2000;
	bot.TargetBreakableDist = 300.0;

	weaps =
	{
		WEAPON.THOMPSON, WEAPON.MP40, WEAPON.PPSH,
		WEAPON.SHOTGUN, WEAPON.M97, WEAPON.M7,
		WEAPON.CARBINE, WEAPON.GARAND, WEAPON.K43,
		WEAPON.STEN, WEAPON.MP34, WEAPON.FG42,
		WEAPON.BAR, WEAPON.STG44
	};

	foreach( id and weap in weaps )
	{
		w = bot.GetWeapon( weap );
		w.PrimaryFire.SetTargetBias( CLASS.BREAKABLE, 1.2 );
	}

	w = bot.GetWeapon( WEAPON.KNIFE );
	w.PrimaryFire.SetDesirabilityRange( 0, 48, 0.6 );
};

global InitializeRoutes = function()
{
	MapRoutes =
	{
		CAPPOINT_Raft =
		{
			ROUTE_BookTaken =
			{
				ROUTE_CryptGate1 =
				{
					ROUTE_CryptGate2 =
					{
						ROUTE_CryptGate3 =
						{
							ROUTE_Escape = {}
						}
					}
				},
				ROUTE_Ladder1 =
				{
					ROUTE_Ladder2 =
					{
						ROUTE_CryptGate2 =
						{
							ROUTE_CryptGate3 =
							{
								ROUTE_Escape = {}
							}
						}
					}
				}
			}
		}
	};

	Util.Routes( MapRoutes );
};
