"use strict";
this.name        = "Escort_Contracts_Mother"; 
this.version     = "1.7.1";      // 2015-12-04
this.author      = "capt murphy"; 
this.copyright   = "2011 capt murphy";
this.licence     = "CC BY-NC-SA 3.0"; // see http://creativecommons.org/licenses/by-nc-sa/3.0/ for more info.
this.description = "Escort Contracts mother shipscript."; 

this.$debugMode = 0;  // write debug information to logfile?

// this script contains several event handler driven functions and script defined functions used to control the mother's AI. Many of the script defined functions are called from the various ec_mother*AI.plists and send messages back to these AI.plists - the code should be considered alongside these AI.plists.

// functions used in the starting system. ec_mother1AI.plist is the main AI used in the starting system.
this.shipSpawned = function()
{
	
	// Don't change a name already assigned by Randomshipnames or any other naming OXP.
	// Checking conditions taken from Randomshipnames
	// Usually this is called before the world script "shipSpawned" functions, but better be safe...
    if(ship.displayName.substr(0, 4) !== "RRS " && ship.displayName.indexOf(":") === -1 && ship.displayName.indexOf("~") === -1 && ship.displayName.indexOf(" - ") === -1) 
	{	
		var oldDisplayName = ship.displayName;
		this.ship.displayName = worldScripts["Escort_Contracts"].ec_mothername +"'s " + ship.name;  // "Trader" removed, this made the "ship name" longer than necessary (1.7.1)
		if(this.$debugMode) log ("Escort contracts: name assigned: " + this.ship.displayName + ", oldDisplayName: " + oldDisplayName);
	}	
	else
	{	
		if(this.$debugMode) log ("Escort contracts: name unchanged: " + this.ship.displayName);
	}
	this.ship.setAI ("ec_mother1AI.plist");
	this.ec_playerwarnings = 0;
	this.ec_playerinflicteddamage = 0;
	this.ec_suppresslowenergyalert = 0;
	this.ec_suppressoutofrange = 0;
	this.ec_suppressapproach = 0;
	this.ec_suppressunderattack = 0;    // added with version 1.7.1
	this.ec_suppresstorustooclose = 0;  // added with version 1.7.1
	this.ec_suppresstorusmismatch = 0;  // added with version 1.7.1
	
}

// functions called by ec_mother1AI.plist.

// sets the mothers destination in starting system.
this.ec_getCoordinates = function()
{
	this.ship.savedCoordinates = system.mainStation.position.subtract(system.mainStation.vectorForward.multiply(36000));
	this.ship.reactToAIMessage("GOT_COORD");
}

// checks to see how close player is prior to initialising witchspace jump to target system.
this.ec_monitorplayerlocation = function()
{
	if(!player.ship || player.ship.docked === true)
	{
		this.ship.reactToAIMessage("TARGET_LOST");
		return;
	}
	
	var playerDistance = this.ship.position.distanceTo(player.ship.position);
	if(playerDistance > this.ship.scannerRange * 4) // if player is ignoring comms to approach for witch jump leave anyway
	{
		player.commsMessage(expandDescription("[ec_comms_leaving_without]"));
        this.ship.reactToAIMessage("PLAYER_CLOSE");
	}
	else if (playerDistance > 9000)		
	{
		this.ship.reactToAIMessage("PLAYER_FAR");   // will result in repeated messages to approach
	} 
	else
	{
		this.ship.reactToAIMessage("PLAYER_CLOSE"); // will result in mother attempting to jump to the target system.
	}	
}		

// initiates the mother witchspace jump to the target system and calls a timer in the OXP's worldscript to check if the player has followed.
this.ec_motherjumptotarget = function()
{
	this.ship.fuel = 7;
	var jumpsuccess = this.ship.exitSystem(worldScripts["Escort_Contracts"].ec_targetsystem);
	if (!jumpsuccess)
		{this.ship.reactToAIMessage("JUMP_FAILED");}
	else
		{worldScripts["Escort_Contracts"].ec_motherexitedsystem(this.ship.mass);}
}

// player comms functions used in starting system.
this.ec_prepareforjump = function()
{this.ship.commsMessage("[ec_comms_please_approach]");}

this.ec_abouttojump = function()
{this.ship.commsMessage("[ec_comms_init_witchspace]");}

this.ec_motherjumpfailed = function()
{this.ship.commsMessage("[ec_comms_witchspace_failed]");}	

// called post witchspace jump to check if mother is in target system - if so the AI switches to ec_mother2AI.plist which is the main AI used in the target system.
this.ec_checksystem = function()
{
	this.ship.fuel = 7;
	this.ec_motherfrustration = 0;
	this.ec_playerwarnings = 0;
	this.ec_playerinflicteddamage = 0;
	this.ec_suppresslowenergyalert = 0;
	if (system.info.systemID === worldScripts["Escort_Contracts"].ec_targetsystem)
		{this.ship.reactToAIMessage("TARGET_SYSTEM_TRUE");}
	else
		{this.ship.reactToAIMessage("TARGET_SYSTEM_FALSE");}
}

// functions to ensure the mother matches the player's max speed when the player.ship is slower than the mother. Called from the AI.
this.ec_setDesiredSpeed = function()
{if (this.ship.maxSpeed >= player.ship.maxSpeed*0.95){this.ship.desiredSpeed = (player.ship.maxSpeed*0.95);} else {this.ship.desiredSpeed = this.ship.maxSpeed;}}

this.ec_setDesiredSpeedInject = function()
{if (this.ship.maxSpeed >= player.ship.maxSpeed*0.95){this.ship.desiredSpeed = (player.ship.maxSpeed*6.65);} else {this.ship.desiredSpeed = (this.ship.maxSpeed*7);}}

// the following functions are used once the mother is in the target system and also in combat situations.

// event handler driven function - dual role - defines communication and mother actions if repeatedly 
// attacked by player and also communication when mother is taking damage from other hostiles.
this.shipTakingDamage = function(amount, whom, type)
{  
	if (whom === player.ship)
	{
		if (this.ec_playerinflicteddamage === 0)
			{this.ec_playerwarnings = 0;}
		this.ec_playerinflicteddamage += amount;
		if (this.ec_playerwarnings === 1 && this.ec_playerinflicteddamage > (this.ship.maxEnergy * 0.50))
			{this.ec_playerwarnings = 2;}
		if (this.ec_playerwarnings === 3 && this.ec_playerinflicteddamage > (this.ship.maxEnergy * 0.75))
			{this.ec_playerwarnings = 4;}
	
		switch (this.ec_playerwarnings)
		{
			case 0:
			    // message depending on damage type (fg, 2015-11-05)
				if (type === "scrape damage")    
					this.ship.commsMessage("[ec_comms_scrape_damage_1]");      
				else 
					this.ship.commsMessage("[ec_comms_friendly_fire_1]");
					
				this.ec_playerwarnings = 1;
				break;
			case 1:
				break;
			case 2:
			    // message depending on damage type (fg, 2015-11-05)
				if (type === "scrape damage")    
					this.ship.commsMessage("[ec_comms_scrape_damage_2]");      
				else 
					this.ship.commsMessage("[ec_comms_friendly_fire_2]");

				this.ec_playerwarnings = 3;
				break;
			case 3:
				break;
			case 4:
				this.ship.commsMessage("[ec_comms_enough]");
				this.ec_playerwarnings = 5;
				this.ship.target = player.ship;
				this.ship.setAI("ec_killplayerAI.plist"); 							// very simple AI - mother will attempt to kill player - no fleeing.
				delete worldScripts["Escort_Contracts"].ec_currentcontract;
				worldScripts["Escort_Contracts"].ec_escortrep -= 2;
				if (worldScripts["Escort_Contracts"].ec_escortrep < -10)
					{worldScripts["Escort_Contracts"].ec_escortrep = -10;}
				mission.setInstructionsKey (null, "Escort_Contracts");
			case 5:
				break;
		}
	}
	else if (this.ship.energy < (this.ship.maxEnergy * 0.67) && this.ec_suppresslowenergyalert === 0)
	{
		this.ship.commsMessage("[ec_comms_taking_damage_1]");
		this.ec_suppresslowenergyalert = 1;
	}
	else if (this.ship.energy < (this.ship.maxEnergy * 0.33) && this.ec_suppresslowenergyalert === 1)
	{
		this.ship.commsMessage("[ec_comms_taking_damage_2]");
		this.ec_suppresslowenergyalert = 2;
	}
}

// event handler driven function - turns back on communication alerts if previously suppressed by above function.
this.shipEnergyBecameFull = function()
{this.ec_suppresslowenergyalert = 0;}

// called from intercept AI to check for friendly fire from player or an player's escort from hired guns OXP.
this.ec_friendlyfirecheck = function()
{
	if(this.ship.target && ((this.ship.target.isPlayer && this.ec_playerwarnings !== 5) || (this.ship.target.hasRole("hiredGuns_escort") && this.ec_playerwarnings !== 5)))
	{
		this.ship.target = null;
		this.ship.reactToAIMessage("FRIENDLY_FIRE");
	}
	else
	{
		this.ship.reactToAIMessage("ENEMY_FIRE");
	}
}

// called from ec_mothergroupjump.plist  - controls synchronised jump drive behaviour.
this.ec_checkplayermasslock = function()
{

	if (this.ship.position.distanceTo(player.ship.position) > this.ship.scannerRange)
	{
		if (player.alertCondition !=3)
		{
			if (this.ec_suppressoutofrange === 0) // do not issue comms every iteration. Frequency depends on government type.
				{player.commsMessage(expandDescription("[ec_comms_outside_scanner_range]"));}
			this.ec_suppressoutofrange++;
			if (this.ec_suppressoutofrange >= (8+system.government)*2)    // *2 added 1.7.1, frequency was still too high for eSpeak
				{this.ec_suppressoutofrange = 0;}
		}
		this.ship.reactToAIMessage ("NO_JUMP");
		// if player is out of range there is a chance that the mother will be ambushed by pirates. Higher in more dangerous government types.
		if ((Math.random()*200)<(14-(system.government*2)) && system.countShipsWithPrimaryRole("pirate",this.ship,this.ship.scannerRange) === 0)
			{system.addGroup("pirate",1+Math.round(Math.random()*2),this.ship.position, 1000);}
		return;
	}
	if (player.alertMassLocked || this.ship.speed > this.ship.maxSpeed)
    {
		this.ship.reactToAIMessage ("NO_JUMP");
		if (this.ec_suppresssynchedjumpdrive) 
			{delete this.ec_suppresssynchedjumpdrive;} // this variable suppresses the Jump Drive Synchronised message - reset on a masslock event
		this.ec_suppressoutofrange = 0;
		return;
	}
	var targets = system.filteredEntities(this, ec_checkformasslock, this.ship, this.ship.scannerRange);
	if (targets.length > 0)
	{
		this.ship.reactToAIMessage ("NO_JUMP");
		if (this.ec_suppresssynchedjumpdrive) {delete this.ec_suppresssynchedjumpdrive;}
			this.ec_suppressoutofrange = 0;
		return;
	}
	if(this.ship.position.distanceTo(player.ship.position) > 2500)
	{
		if (this.ec_suppressapproach === 0)
			{this.ship.commsMessage("[ec_comms_torus_synchronise]");}
		this.ec_suppressapproach++
		if (this.ec_suppressapproach >= 10)
			{this.ec_suppressapproach = 0;}
		this.ship.reactToAIMessage ("NO_JUMP");
		if (this.ec_suppresssynchedjumpdrive) 
			{delete this.ec_suppresssynchedjumpdrive;}
		this.ec_suppressoutofrange = 0;
		this.ec_suppresstorustooclose = 0;
		return;
	}
	if(this.ship.position.distanceTo(player.ship.position) > 500 && this.ship.heading.dot(player.ship.heading) > 0.98)
	{
		if (this.ec_motherfrustration > 0)
			{this.ec_motherfrustration--;}
		if (!this.ec_suppresssynchedjumpdrive)
		{
			this.ship.commsMessage("[ec_comms_torus_start]"); 
			this.ec_suppresssynchedjumpdrive = true; // only give message when drive is first synchronised
		}	
		this.ec_jumpCallback = addFrameCallback(this.ec_matchVelocity.bind(this));
		this.ship.velocity = this.ship.heading.multiply(this.ship.maxSpeed * 32); // velocity boost to mother to simulate torus speeds.
		// reset message repeat counters - if they are torus driving, everything must be ok.
		this.ec_suppresstorustooclose = 0;
		this.ec_suppresstorusmismatch = 0;
		this.ec_suppressunderattack = 0;
		return;
	}
	if (this.ship.position.distanceTo(player.ship.position) < 500)
	{
		if (this.ec_suppresstorustooclose === 0)
			this.ship.commsMessage("[ec_comms_torus_too_close]");
		this.ec_suppresstorustooclose++;
		if (this.ec_suppresstorustooclose>=10) 
			this.ec_suppresstorustooclose = 0;
		
		this.ship.reactToAIMessage ("NO_JUMP");
		if (this.ec_suppresssynchedjumpdrive) 
			{delete this.ec_suppresssynchedjumpdrive;}
		return;
	}
	if (this.ship.heading.dot(player.ship.heading) < 0.98)       // I made this a little more difficult (old value was 0.9) but it still isn't really difficult (fg, 2015-11-06)
	{
		if (ec_suppresstorusmismatch === 0)
			this.ship.commsMessage("[ec_comms_torus_heading]");
		ec_suppresstorusmismatch++;
		if (ec_suppresstorusmismatch >= 10)
			ec_suppresstorusmismatch = 0;
		
		this.ship.reactToAIMessage ("NO_JUMP");
		if (this.ec_suppresssynchedjumpdrive) 
			{delete this.ec_suppresssynchedjumpdrive;}
		return;
	}
	else
	{
		this.ship.reactToAIMessage ("NO_JUMP");
	}
}

this.ec_removeFrameCallback = function() // called from ec_mothergroupjumpAI.plist to reset jump synchronisation in between each check for masslock.
{
	if (this.ec_jumpCallback)
	{
		removeFrameCallback(this.ec_jumpCallback);
		delete this.ec_jumpCallback;
	}
	this.ship.velocity = this.ship.thrustVector; // reset velocity if jump has just occurred.
	player.ship.velocity = player.ship.thrustVector;
}

this.ec_matchVelocity = function() // called by framecall back whilst jump drives synchronised to match player and mother velocity.
{player.ship.velocity = this.ship.velocity;}

// called from ec_mother2AI.plist - dual function - checks for ships in scanner range that are actively hostile for player and if so sends mother into combat mode - or if none present controls decision to inject from mass lock behaviour.
this.ec_checkplayerhostilealert = function()
{
	if (player.alertHostiles)
	{
		var targets = system.filteredEntities(this, ec_checkplayerhostiles, this.ship, this.ship.scannerRange);
		if(targets.length > 0)
		{
			this.ship.target = targets[0];
			this.ship.setAI("ec_motherIntercept2AI.plist");
		}
		else
		{
			this.ec_motherfrustration++;
			this.ship.reactToAIMessage ("NO_PLAYER_HOSTILE");
		}
	}
	else if (player.alertMassLocked && this.ship.position.distanceTo(player.ship.position) < this.ship.scannerRange)
	{
		if (((Math.random()*4)+1) < this.ec_motherfrustration && player.ship.equipmentStatus("EQ_FUEL_INJECTION") === "EQUIPMENT_OK" && player.ship.fuel > 1)
		{
			this.ship.fuel = 7; 
			this.ec_maxinjectiterations = Math.round((this.ship.scannerRange*2.5)/(this.ship.maxSpeed * 7));
			this.ship.commsMessage("[ec_comms_injector]");  
			this.ec_injectcounter = 0;
			this.ship.reactToAIMessage ("INJECT");
			this.ec_motherfrustration = 0;
		}
		else
		{
			this.ec_motherfrustration++;
			this.ship.reactToAIMessage ("NO_PLAYER_HOSTILE");
		}
	}
}

// used to check for entities other than the player masslocking the mother
this.ec_checkformasslock = function(entity) 
{return ((entity.scanClass === "CLASS_NEUTRAL" || entity.scanClass === "CLASS_MILITARY" || entity.scanClass === "CLASS_POLICE" || entity.scanClass === "CLASS_THARGOID" || entity.scanClass === "CLASS_STATION") && !entity.isPlayer);}

// used to check for entities hostile to the player
this.ec_checkplayerhostiles = function(entity) 
{return (entity.isThargoid || (entity.isShip && entity.target && entity.target === player.ship && entity.hasHostileTarget && !entity.isDerelict));}

// called from ec_mother2AI.plist whilst injecting to check for entities hostile to the player.
this.ec_checkplayerhostilealertinject = function()
{
	if (player.alertHostiles)
	{
		var targets = system.filteredEntities(this, ec_checkplayerhostiles, this.ship, this.ship.scannerRange);
		if(targets.length > 0)
		{
			this.ship.target = targets[0];
			this.ship.setAI("ec_motherIntercept2AI.plist");
		}
	}
}

// called whilst mother is injecting to escape masslock to check if scanner range is now clear of masslocking entities and if so stop injectors.
this.ec_checkinjectconditions = function()
{
	var targets = system.filteredEntities(this, ec_checkformasslock, this.ship, this.ship.scannerRange);
	if((targets.length === 0 && !player.alertMassLocked) || this.ec_injectcounter > this.ec_maxinjectiterations)
		{this.ship.reactToAIMessage ("STOP_INJECT");}
	this.ec_injectcounter++;
}

// event handler driven function to define contract success.
this.shipEnteredStationAegis = function()
{
	this.ship.reactToAIMessage ("AEGIS_IN_DOCKING_RANGE");
	if (worldScripts["Escort_Contracts"].ec_currentcontract === true && system.info.systemID === worldScripts["Escort_Contracts"].ec_targetsystem && !this.ship.hasHostileTarget && !player.alertHostiles)
	{
		worldScripts["Escort_Contracts"].ec_currentcontract = "success";
		this.ship.commsMessage("[ec_comms_arrived_in_aegis]"); 
	}
	else if (worldScripts["Escort_Contracts"].ec_currentcontract === true && system.info.systemID === worldScripts["Escort_Contracts"].ec_targetsystem)
	{
		this.ship.commsMessage("[ec_comms_pirates_in_aegis]");
	}
}

// event handler driven function defining contract success.
this.shipWillDockWithStation = function(station)
{
	if(worldScripts["Escort_Contracts"].ec_currentcontract === true)
	{
		worldScripts["Escort_Contracts"].ec_currentcontract = "success";
		this.ship.commsMessage("[ec_comms_safely_docked]");
	}
}

// event handler driven function defining contract failure.
this.shipDied = function()
{   
	if (worldScripts["Escort_Contracts"].ec_currentcontract === true)
	{
		delete worldScripts["Escort_Contracts"].ec_currentcontract;
		worldScripts["Escort_Contracts"].ec_escortrep -= 2;
		if (worldScripts["Escort_Contracts"].ec_escortrep < -10)
			{worldScripts["Escort_Contracts"].ec_escortrep = -10;}
		player.commsMessage(expandDescription("[ec_comms_mother_destroyed]")); 
		player.consoleMessage("Escort Contract: Mother has been destroyed. Contract expired.", 15);
		mission.setInstructionsKey (null, "Escort_Contracts");
	}
	else 
	{
		delete worldScripts["Escort_Contracts"].ec_currentcontract;
	}
}

// only used by ec_killplayerAI.plist - if mother loses player as target she is removed.
this.ec_removemother = function()
{this.ship.remove(true);}

// player communication when player first joins mother in target system.
this.ec_commsplayerwelcome = function()
{this.ship.commsMessage("[ec_comms_arrived]");}

// player communication when mother is attacked by hostile craft.
this.ec_motherunderattack = function()
{
	if (this.ec_playerwarnings !== 5)  // if === 5, mother is attacking the player for being agressive or incompetent!
	{
	    if (this.ec_suppressunderattack === 0)
		{	
			if (this.ship.position.distanceTo(player.ship.position) < this.ship.scannerRange)
			{
				// Mother in range, warn only if the player isn't fighting already 
				if (player.alertCondition !=3)  
				{	
					this.ship.commsMessage("[ec_comms_pirate_detected]");
				}
			}   
			else
			{
				// Mother out of range, call the player in any case!
				player.commsMessage(expandDescription("[ec_comms_under_attack]"));  
			}  
		}	
		this.ec_suppressunderattack++;
		if (this.ec_suppressunderattack >= 10)     
			this.ec_suppressunderattack = 0;  
	}
}
