| Scripts/hiredGuns_escort.js | /*
=================================
HEADER
=================================
*/
this.name					= "hiredGuns_escort";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike.";
this.description			= "AI-actions script for the escorts to the player ship";
this.version				= "1.1";
/*
============================
Start of wrapper for JSHINT
============================
*/
(
	function (){
/*
============================
DIRECTIVE
============================
*/
"use strict";
/*
========
GLOBALS
========
*/
this.debug = false; 					// There is a 'this.debug' also in hiredGuns_system.js.
var consoleDebugMessages;
if (this.debug === true) {
	this.logging = true;
	consoleDebugMessages = true;
}
else {
	consoleDebugMessages = false;		// This does need setting to something, even when debugging is off.
}
/*
=========
FUNCTIONS
=========
*/
/*
--------------
Debugging
--------------
*/
this.$dbg = function(msg)
{
	log(this.name,"DEBUG: " + msg);
};
/*
--------------
Event handlers
--------------
*/
this.shipSpawned = function ()
{
	if (this.debug) {
		this.ship.reportAIMessages = true;
		this.$dbg("Debugging ON");
	}
	// Initialise an important global variable
	this.shipStats = {};
	// Assign 'sensitivity' and heat insulation to the ship.
	var r = Math.random();
	if (this.ship.hasRole("hiredGuns_escortHigh")) {
		if (r < 0.3) {
			this.shipPrudence = 1.25;
		}
		else if (r < 0.6) {
			this.shipPrudence = 1.2;
		}
		else {
			this.shipPrudence = 1.15;
		}
		this.ship.heatInsulation = 2.5; // Speeds of the escorts mean they will get hot.
	}
	else {
		if (r < 0.2) {
			this.shipPrudence = 1.2;
		}
		else if (r < 0.4) {
			this.shipPrudence = 1.05;
		}
		else {
			this.shipPrudence = 1;
		}
		this.ship.heatInsulation = 2;
	}
	if (this.debug) { this.$dbg("heatInsulation = " + this.ship.heatInsulation); }
	delete this.shipSpawned;
};
this.shipTargetDestroyed = function(target)
{
	// Just in case an escort kills the constrictor, let's not break the mission for the player...
	if (target.primaryRole == "constrictor" && missionVariables.conhunt && missionVariables.conhunt == "STAGE_1") {
		missionVariables.conhunt = "CONSTRICTOR_DESTROYED";
	}
	if (target.isRock || target.isBoulder || target.isCargo || target.isDerelict || target.isWeapon) {
		return;
	}
	player.score += 1;
	player.credits += target.bounty;
	player.consoleMessage("Escort kill - " + target.name + " : " + target.bounty + "₢ awarded.", 5);
	log("Escort kill - " + target.name + " : " + target.bounty);
};
this.shipDied = function(whom,why)
{
	if (whom && whom.isValid) {
		player.commsMessage("Escort terminated.", 6);
	}
	missionVariables.hiredGuns_count -= 1;
};
/*
------------------------
Temperature AND altitude
------------------------
*/
this.$checkTemperatureOrAltitude = function ()
{
	if (this.debug) { this.$dbg("checkTemperatureOrAltitude"); }
	// Anything to do?
	if (
		! this.ship || ! this.ship.isValid || (player.ship && player.ship.isValid && player.ship.docked === true)
		) {
		if (this.debug) { this.$dbg("Nothing to do"); }
		this.ship.reactToAIMessage("PROCEED");
		return;
	}
	this.$setShipStats();
	if (Math.random() < 0.5) {
		this.$checkTemperature();
	}
	else {
		this.$checkAltitude();
	}
};
/*
----------------------
Temperature
----------------------
*/
// This function or its children must tell the AI to STALL or FLEE or else PROCEED.
// Needs this.$setShipStats() to have been called already.
this.$checkTemperature = function ()
{
	if (this.debug) { this.$dbg("checkTemperature"); }
	var escortTemperature = this.ship.temperature;
	if (this.debug) { this.$dbg("temperature_timed: escortTemperature = " + escortTemperature); }
	if (escortTemperature < 0.6) {
		if (this.debug) { this.$dbg("temperature - below threshold"); }
		this.ship.reactToAIMessage("PROCEED");
		return;
	}
	if (escortTemperature > 0.85) { 
		this.$temperature_veryHigh();
	}
	else { this.$temperature_high(); }
};
this.$temperature_veryHigh = function ()
{
	var isHeadingInto;
	if (this.shipStats === null) {
		isHeadingInto = false;
	}
	else {
		isHeadingInto = this.shipStats.isHeadingInto;
	}
	var r = Math.random() * this.shipPrudence;
	if (this.debug) { this.$dbg("temperature_veryHigh"); }
	if (isHeadingInto === true) {
		if (this.debug) { this.$dbg("Heading is TOWARDS body; action is .."); }
		if (r < 0.1) {
			if (this.debug) { this.$dbg(".. none"); }
			this.ship.reactToAIMessage("PROCEED");
		} else if (r < 0.35) {
			if (this.debug) { this.$dbg(".. message"); }
			this.$temperature_veryHigh_messaging();
			this.ship.reactToAIMessage("PROCEED");
		}
		else if (r < 0.6) {
			if (this.debug) { this.$dbg("..flee"); }
			this.$flee();
		}
		else {
			if (this.debug) { this.$dbg("..message and flee"); }
			this.$flee();
			this.$temperature_veryHigh_messaging();
		}
	}
	else {
		if (this.debug) { this.$dbg("Heading is AWAY from body; action is .."); }
		if (r < 0.1) {
			if (this.debug) { this.$dbg(".. none"); }
			this.ship.reactToAIMessage("PROCEED");
		} else if (r < 0.35) {
			if (this.debug) { this.$dbg(".. message"); }
			this.$temperature_veryHigh_messaging();
			this.ship.reactToAIMessage("PROCEED");
		}
		else if (r < 0.6) {
			if (this.debug) { this.$dbg("..flee"); }
			this.$flee();
		}
		else {
			if (this.debug) { this.$dbg("..message and flee"); }
			this.$flee();
			this.$temperature_veryHigh_messaging();
		}
	}
};
this.$temperature_high = function ()
{
	var isHeadingInto;
	if (this.shipStats === null) {
		isHeadingInto = false;
	}
	else {
		isHeadingInto = this.shipStats.isHeadingInto;
	}
	var r = Math.random() * this.shipPrudence;
	if (this.debug) { this.$dbg(".. temperature_high"); }
	if (isHeadingInto === true) {
		if (this.debug) { this.$dbg("Heading is TOWARDS body; action is .."); }
		if (r < 0.2) {
			if (this.debug) { this.$dbg(".. none"); }
			this.ship.reactToAIMessage("PROCEED");
		} else if (r < 0.3) {
			// Do message only
			this.$temperature_high_messaging();
			if (this.debug) { this.$dbg(".. message"); }
			this.ship.reactToAIMessage("PROCEED");
		}
		else if (r < 0.45) {
			if (this.debug) { this.$dbg(".. stall"); }
			this.ship.reactToAIMessage("STALL");
		}
		else {
			if (this.debug) { this.$dbg("message and stall"); }
			this.$temperature_high_messaging();
			this.ship.reactToAIMessage("STALL");
		}
	}
	else {
		if (this.debug) { this.$dbg("Heading is AWAY from body; action is .."); }
		if (r < 0.6) {
			if (this.debug) { this.$dbg(".. none"); }
			this.ship.reactToAIMessage("PROCEED");
		} else {
			this.$temperature_high_messaging();
			if (this.debug) { this.$dbg(".. message"); }
			this.ship.reactToAIMessage("PROCEED");
		}
	}
};
/*
----------------------
Altitude
----------------------
*/
// This function or its children must tell the AI to STALL or FLEE or else PROCEED.
// Needs this.$setShipStats() to have been called already (but check that variable here).
this.$checkAltitude = function ()
{
	if (this.debug) { this.$dbg("checkAltitude"); }
	if (this.shipStats === null) {
		if (this.debug) { this.$dbg(".. no celestial bodies"); }
		this.ship.reactToAIMessage("PROCEED");
		return;
	}
	if (this.shipStats.altitude < 8150) { this.$altitude_veryLow(); }
	else if (this.shipStats.altitude < 17500) { this.$altitude_low(); }
	else {
		if (this.debug) { this.$dbg(".. altitude above thresholds i.e. is OK"); }
		this.ship.reactToAIMessage("PROCEED");
	}
};
this.$altitude_veryLow = function ()
{
	var isHeadingInto = this.shipStats.isHeadingInto;
	var r = Math.random() * this.shipPrudence;
	if (this.debug) { this.$dbg(".. altitude_veryLow"); }
	
	if (isHeadingInto === true) {
		if (this.debug) { this.$dbg("Heading is TOWARDS body; action is .."); }
		if (r < 0.06) {
			if (this.debug) { this.$dbg(".. none"); }
			this.ship.reactToAIMessage("PROCEED");
		}
		else if (r < 0.25) {
			this.$altitude_low_messaging();
			if (this.debug) { this.$dbg(".. send message"); }
			this.ship.reactToAIMessage("PROCEED");
		}
		else if (r < 0.5) {
			if (this.debug) { this.$dbg(".. stall"); }
			this.ship.reactToAIMessage("STALL");
		}
		else if (r < 0.85) {
			// Flee with message
			if (this.debug) { this.$dbg(".. message and flee"); }
			this.$flee();
			this.$altitude_low_messaging();
		}
		else {
			// Flee without message
			if (this.debug) { this.$dbg(".. flee"); }
			this.$flee();
		}
	}
	else {
		if (this.debug) { this.$dbg("Heading is NOT towards body"); }
		if (r < 0.15) {
			if (this.debug) { this.$dbg(".. none"); }
			this.ship.reactToAIMessage("PROCEED");
		}
		else if (r < 0.45) {
			this.$altitude_low_messaging();
			if (this.debug) { this.$dbg(".. send message"); }
			this.ship.reactToAIMessage("PROCEED");
		}
		else if (r < 0.9) {
			// Flee with message
			if (this.debug) { this.$dbg(".. message and flee"); }
			this.$flee();
			this.$altitude_low_messaging();
		}
		else {
			// Flee without message
			if (this.debug) { this.$dbg(".. flee"); }
			this.$flee();
		}
	}
};
this.$altitude_low = function ()
{
	var isHeadingInto = this.shipStats.isHeadingInto;
	var r = Math.random() * this.shipPrudence;
	if (this.debug) { this.$dbg(".. altitude_low"); }
	// r = 1; // TEST
	if (isHeadingInto === true) {
		if (this.debug) { this.$dbg("Heading is TOWARDS body; action is .."); }
		if (r < 0.15) {
			if (this.debug) { this.$dbg(".. none"); }
			this.ship.reactToAIMessage("PROCEED");
			return;
		} else if (r < 0.3) {
			// Do message only
			this.$altitude_low_messaging();
			if (this.debug) { this.$dbg(".. message"); }
			this.ship.reactToAIMessage("PROCEED");
		}
		else if (r < 0.45) {
			if (this.debug) { this.$dbg(".. stall"); }
			this.ship.reactToAIMessage("STALL");
		}
		else {
			this.$altitude_low_messaging();
			if (this.debug) { this.$dbg("message and stall"); }
			this.ship.reactToAIMessage("STALL");
		}
	}
	else {
		if (this.debug) { this.$dbg("Heading is NOT towards body; action is .."); }
		if (r < 0.6) {
			if (this.debug) { this.$dbg(".. none"); }
			this.ship.reactToAIMessage("PROCEED");
		} else {
			this.$altitude_low_messaging();
			if (this.debug) { this.$dbg(".. message"); }
			this.ship.reactToAIMessage("PROCEED");
		}
	}
};
/*
----------------------
Altitude and temperature
(lower-level routines)
----------------------
*/
this.$altitudeOverPlanetaryBody = function $altitudeOverPlanetaryBody(body, ship)
{
	var shippos = ship.position;
	if (!shippos) return -1;
	return shippos.distanceTo(body) - body.radius - ship.collisionRadius;
};
// Gets:
// the escort ship's distance from nearest celestial body;
// what type of body that nearest one is;
// whether the *player* is heading into or away from the body.
this.$setShipStats = function ()
{
	var alt;
	var bodyType = null;
	var closest = null;
	var deviation = 1;
	var ent = [];
	var isHeadingInto = false;
	var ship = this.ship;
	var smallest = -1;
	var l;
	if (system.isInterstellarSpace === false && system.sun) ent.push(system.sun);
	ent = ent.concat(system.planets);
	l = ent.length;
	if (l === 0) {
		this.shipStats = null;
		if (this.debug) {
			this.$dbg("Are no celestial bodies");
		}
	}
	else {
		for (var i = 0; i < l; i++) {
			alt = this.$altitudeOverPlanetaryBody(ent[i], ship);
			if (smallest === -1 || (alt != -1 && alt < smallest)) { smallest = alt; closest = ent[i]; }
		}
		if (closest) {
			bodyType = closest.toString().substring(1, 4);
			deviation = player.ship.vectorForward.angleTo(closest.position.subtract(player.ship.position));
			if ((deviation < 0.2) && (player.ship.speed > 0)) {
				isHeadingInto = true;
			}
		}
		// Set globals
		this.shipStats.altitude = smallest;
		this.shipStats.body = closest;
		this.shipStats.bodyType = bodyType;
		this.shipStats.isHeadingInto = isHeadingInto;
		if (this.debug) {
			this.$dbg("this.shipStats.altitude = "+this.shipStats.altitude);
			this.$dbg("this.shipStats.body = "+this.shipStats.body);
			this.$dbg("this.shipStats.bodyType = "+this.shipStats.bodyType);
			this.$dbg("this.shipStats.isHeadingInto = "+this.shipStats.isHeadingInto);
		}
	}
};
/*
----------------------
Altitude & temperature
- messaging
----------------------
*/
this.$altitude_low_messaging = function ()
{
	var		msg_main;
	var		msg_salutation;
	var		msg;
	var		picker;
	msg_salutation = this.$msg_getSalutation();
	// Determine main message
	picker = Math.ceil(Math.random() * 40);
	switch (picker) {
		case 1:		msg_main = "Aren't we a bit low here?"; break;
		case 2:		msg_main = "Are you sure about this?"; break;
		case 3:		msg_main = "We are quite close to the surface!"; break;
		case 4:		msg_main = "Pull up?"; break;
		case 5:		msg_main = "Altitude low!"; break;
		case 6:		msg_main = "Low altitude!"; break;
		case 7:		msg_main = "I can't land on that!"; break;
		case 8:		msg_main = "Too low!"; break;
		case 9:		msg_main = "Too low?"; break;
		case 10:	msg_main = "We're getting a bit low."; break;
		case 11:	msg_main = "We're rather low."; break;
		case 12:	msg_main = "I'm worried."; break;
		case 13:	msg_main = "Aren't we rather low?"; break;
		case 14:	msg_main = "Are you entirely sure about this?"; break;
		case 15:	msg_main = "I am not so sure about this."; break;
		case 16:	msg_main = "I suggest pulling up."; break;
		case 17:	msg_main = "Altitude low."; break;
		case 18:	msg_main = "Altitude low!"; break;
		case 19:	msg_main = "Altitude very low!"; break;
		case 20:	msg_main = "Altitude low!"; break;
		case 21:	msg_main = "Low altitude!"; break;
		case 22:	msg_main = "Low altitude!"; break;
		case 23:	msg_main = "Do we want to make friends with the ground?"; break;
		case 24:	msg_main = "I can't land on that!"; break;
		case 25:	msg_main = "Too low!"; break;
		case 26:	msg_main = "Pull up!"; break;
		case 27:	msg_main = "I don't like this."; break;
		case 28:	msg_main = "Are you trying to land?"; break;
		case 29:	msg_main = "I feel we are a bit close."; break;
		case 30:	msg_main = "This is cutting it a bit fine."; break;
		default:	msg_main = "Altitude is low!"; break;
	}
	this.$msg_assembleAndIssue(msg_salutation,msg_main);
};
this.$temperature_veryHigh_messaging = function ()
{
	var		msg_main;
	var		msg_salutation;
	var		msg;
	var		picker;
	msg_salutation = this.$msg_getSalutation();
	// Determine main message
	picker = Math.ceil(Math.random() * 27);
	switch (picker) {
		case 1:		msg_main = "Aren't we getting very hot?"; break;
		case 2:		msg_main = "Are you entirely sure about this?"; break;
		case 3:		msg_main = "I am not at all sure about this."; break;
		case 4:		msg_main = "You could fry an egg on my ship!"; break;
		case 6:		msg_main = "Temperature high!"; break;
		case 7:		msg_main = "High temperature!"; break;
		case 8:		msg_main = "Too hot!"; break;
		case 9:		msg_main = "I am getting very hot!"; break;
		case 10:	msg_main = "I'm getting grilled!"; break;
		case 11:	msg_main = "Do we want to make cook ourselves?"; break;
		case 12:	msg_main = "I don't have enough temperature shielding!"; break;
		case 13:	msg_main = "Too hot!"; break;
		case 14:	msg_main = "Too hot!"; break;
		case 15:	msg_main = "Let's get out of here!"; break;
		case 16:	msg_main = "I don't like this."; break;
		case 17:	msg_main = "Temperature high!"; break;
		case 18:	msg_main = "I'm going to explode!"; break;
		case 19:	msg_main = "I'm worried."; break;
		case 20:	msg_main = "This is cutting it rather fine."; break;
		default:	msg_main = "Temperature very high!"; break;
	}
	this.$msg_assembleAndIssue(msg_salutation,msg_main);
};
this.$temperature_high_messaging = function ()
{
	var		msg_main;
	var		msg_salutation;
	var		msg;
	var		picker;
	if (this.debug) {
		this.$dbg("temperature_high_messaging");
	}
	msg_salutation = this.$msg_getSalutation();
	// Determine main message
	picker = Math.ceil(Math.random() * 20);
	switch (picker) {
		case 1:		msg_main = "Aren't we getting a bit hot here?"; break;
		case 2:		msg_main = "Are you sure about this?"; break;
		case 3:		msg_main = "This is toasty!"; break;
		case 4:		msg_main = "You might want to go somewhere cooler!"; break;
		case 5:		msg_main = "Temperature high!"; break;
		case 6:		msg_main = "The temperature is high!"; break;
		case 7:		msg_main = "My temperature shielding isn't that great."; break;
		case 8:		msg_main = "Is this in the contract?"; break;
		case 9:		msg_main = "High temperature!"; break;
		case 10:	msg_main = "Are you watching the temperature?"; break;
		case 11:	msg_main = "Too hot?"; break;
		case 12:	msg_main = "Should we get out of here?"; break;
		case 13:	msg_main = "We're getting a bit hot."; break;
		case 14:	msg_main = "Er, it's a bit hot."; break;
		case 15:	msg_main = "It's hot here!"; break;
		default:	msg_main = "High temperature!"; break;
	}
	this.$msg_assembleAndIssue(msg_salutation,msg_main);
};
/*
----------------------
Altitude & temperature
- Fleeing actions
----------------------
*/
this.$flee = function ()
{
	if (! this.ship || ! this.ship.isValid) { return; }
	if (this.shipStats === null) {
		var position = new Vector3D(ps);
		var x = Math.random() * 5000; var y = Math.random() * 5000; var z = Math.random() * 5000;
		position = this.ship.position.add([x, y, z]);
		this.ship.destination = position;
	}
	else {
		this.ship.destination = this.$positionAwayFromEntity(this.ship,this.shipStats.body);
	}
	this.ship.reactToAIMessage("FLEE");
};
// This function is called by the AI.
this.$flee_stage2 = function ()
{
	if (! this.ship || ! this.ship.isValid) { return; }
	// var range = 15000 + Math.ceil(Math.random() * 10000);
	// this.ship.desiredRange = this.shipStats.body.radius + this.shipStats.altitude + range;
	// this.ship.target = null;
	// NB: desiredRange is from the point being fled *to*; see also $positionAwayFromEntity.
	if (this.shipStats.bodyType === "Sun") {
		// SUN
		this.ship.desiredRange = Math.ceil(Math.random() * 10000);
		this.ship.desiredSpeed = this.ship.maxSpeed * 7;
	} else
	{
		// PLANET
		this.ship.desiredRange = 16000;
		// If fleeing planet, need not go as far away as when fleeing sun.
		// Also, if send ship too far away, it might use injectors to get back, and crash.
		if (Math.random() < 0.7) {
			this.ship.desiredSpeed = this.ship.maxSpeed * 0.5;
		} else {
			this.ship.desiredSpeed = this.ship.maxSpeed;
		}
		// Less need with planet for speed and also high speed in atmosphere raises heat.
	}
	this.ship.performFlyToRangeFromDestination();
};
// returns a position that's the same distance as between the ship and the entity, but in the opposite direction
// (and with some randomisation of position). 
this.$positionAwayFromEntity = function $positionAwayFromEntity(ship, entity) {
	return Vector3D.interpolate(ship.position.add(Vector3D.random(10E3)), entity.position, -1);
};
/*
--------------
AI, Misc.
--------------
*/
this.$locatePlayer = function ()
{
	this.playerArray = system.shipsWithPrimaryRole("player");
	if (this.playerArray.length > 0) {
		this.ship.target = this.playerArray[0];
		this.ship.reactToAIMessage("PLAYER_FOUND");
	}
};
this.$checkPlayerDistance = function ()
{
	if (!player.ship){ return; }
	this.playerDistance = this.ship.position.distanceTo(player.ship.position);
	// If the escort is more than 2 scanner ranges from the player, move it just off-scanner.
	if (this.playerDistance > 51200) {
		var playerDirection = player.ship.position.subtract(this.ship.position).direction(); 
		var newPosition = player.ship.position.subtract(playerDirection.multiply(30000));
		this.ship.position = newPosition;
		this.ship.reactToAIMessage("PLAYER_MID");
		return;
	}
	if (this.playerDistance < 25600) {
		this.ship.reactToAIMessage("PLAYER_NEAR");
	}
	else {
		if (this.playerDistance > 38400 || player.ship.speed > 2000) {
			this.ship.reactToAIMessage("PLAYER_FAR");
		}
		else {
			this.ship.reactToAIMessage("PLAYER_MID");
		}
	}
};
this.$combatCheck = function ()
{
	if (this.ship.target && this.ship.target.isDerelict) {
		this.ship.target = null;
		this.ship.reactToAIMessage("TARGET_EJECTED");
	}
	this.playerDistance = this.ship.position.distanceTo(player.ship.position);
	// If the escort is more than 2 scanner ranges from the player, break off combat
	if (this.playerDistance > 51200) {
		this.ship.AIState = "LOCATE_PLAYER";
	}
	else {
		this.ship.reactToAIMessage("ENEMY_FIRE");
	}
};
this.$isHostileToPlayer = function(entity) 
{
	return (entity.isThargoid || (entity.isShip && entity.target && entity.target == player.ship && entity.hasHostileTarget && !entity.isDerelict));
};
this.$findPlayerHostiles = function ()
{
	if (this.ship.target == player.ship && player.ship.target && player.ship.target.bounty > 2) {
		this.ship.target = player.ship.target;
		this.ship.reactToAIMessage("HOSTILE_FOUND");
	}
	else {
		var targets = system.filteredEntities(this, this.$isHostileToPlayer, this.ship, this.ship.scannerRange);
		if (targets.length > 0) {
			this.ship.target = targets[0];
			this.ship.reactToAIMessage("HOSTILE_FOUND");
		}
		else {
			this.ship.reactToAIMessage("NO_HOSTILE_FOUND");
		}
	}
};
this.$fireCheck = function ()
{
	if ((this.ship.target.hasRole("hiredGuns_escort")) || (player.ship.target == this.ship && this.ship.target.isPlayer)) {
		this.ship.reactToAIMessage("FRIENDLY_FIRE");
	}
	else {
		this.ship.reactToAIMessage("ENEMY_FIRE");
	}
};
this.$switchID = function ()
{
	this.ship.switchAI("route1patrolAI.plist");
	this.ship.displayName = "Bounty Hunter";
	this.ship.primaryRole = "hunter";
	log("Hired escort switched to bounty hunter role");
};
/*
---------------
Misc messaging
---------------
*/
this.$greetPlayer_probably = function ()
{
	if (this.debug) { this.$greetPlayer_core(); return ; }
	if (Math.random() < 0.9) { this.$greetPlayer_core(); }
};
this.$greetPlayer_perhaps = function ()
{
	if (this.debug) { this.$greetPlayer_core(); return ; }
	if (Math.random() < 0.6) { this.$greetPlayer_core(); }
};
this.$greetPlayer_core = function ()
{
	var chance_interstellar = 0.7;
	var chance_stellar = 0.7;
	var length;
	var	msg;
	var	msg_main;
	var	msg_salutation;
	var	picker;
	var isInterstellarSpace;
	if (system.isInterstellarSpace === true) {
		isInterstellarSpace = true;
	} else {
		isInterstellarSpace = false;
	}
	if (isInterstellarSpace === true && Math.random() < 0.7) {
		// *Interstellar space*
		msg_salutation = this.$msg_getSalutation();
		// Determine main message
		picker = Math.ceil(Math.random() * 20);
		switch (picker) {
			case 1:		msg_main = "Er, where to?"; break;
			case 2:		msg_main = "Are you sure about this?"; break;
			case 3:		msg_main = "Er, Thargoids?"; break;
			case 4:		msg_main = "I don't like the look of this."; break;
			case 5:		msg_main = "This could get hairy .."; break;
			case 6:		msg_main = "Welcome to interstellar space!"; break;
			case 7:		msg_main = "So this is .. where exactly?"; break;
			case 8:		msg_main = "Is this in the contract?"; break;
			case 9:		msg_main = "This is NOT in the contract."; break;
			case 10:	msg_main = "Should we get out of here?"; break;
			case 11:	msg_main = "Oh dear."; break;
			case 12:	msg_main = "Uh oh."; break;
			case 13:	msg_main = "Where you go, we follow."; break;
			case 14:	msg_main = "We're in interstellar space!"; break;
			case 15:	msg_main = "I want to home."; break;
			case 16:	msg_main = "Shall we get out of here?"; break;
			default:	msg_main = "Ready."; break;
		}
	}
	else {
		// *Normal* space (or just possibly interstellar)
		// Create message - using mostly Thargoids's original materials.
		msg_salutation = this.$msg_getSalutation();
		// Determine main message
		picker = Math.ceil(Math.random() * 11);
		switch (picker) {
			case 1:		msg_main = "Reporting for duty."; break;
			case 2:		msg_main = "Reporting for duty."; break;
			case 3:		msg_main = "Forming up on your six."; break;
			case 4:		msg_main = "Forming up on your six."; break;
			case 5:		msg_main = "Where to?"; break;
			case 6:		msg_main = "Where to?"; break;
			case 7:		msg_main = "Ready."; break;
			case 8:		msg_main = "Hello."; break;
			default:	msg_main = "Greetings."; break;
		}
	}
	this.$msg_assembleAndIssue (msg_salutation,msg_main);
};
/*
--------------------
Low-level messaging
-------------------
*/
this.$str_deCapitalizeFirstLetter = function(string) {
	if (string.charAt(0) === "I") {
		return string;
	}
	else {
		return string.charAt(0).toLowerCase() + string.slice(1);
	}
};
this.$str_prependSalutation = function(msg_salutation,msg_main)
{
	return msg_salutation + ", " + this.$str_deCapitalizeFirstLetter(msg_main);
};
this.$str_appendSalutation = function(msg_salutation,msg_main)
{
	var length = msg_main.length;
	var msg_lastCharOfMain = msg_main.charAt(length - 1);
	return msg_main.replace(/.$/,", ") + msg_salutation + msg_lastCharOfMain;
};
this.$msg_getSalutation = function ()
{
	var	msg_salutation;
	var r = Math.ceil(Math.random() * 10);
	if (r < 3) { msg_salutation = "NONE"; } else {
		switch (r) {
			case 3:		msg_salutation = "Commander"; break;
			case 4:		msg_salutation = "Commander"; break;
			case 5:		msg_salutation = "Captain"; break;
			case 6:		msg_salutation = "Sir"; break;
			case 7:		msg_salutation = "Chief"; break;
			case 8:		msg_salutation = "Chief"; break;
			default:	msg_salutation = "Boss"; break;
		}
	}
	return msg_salutation;
};
this.$msg_assembleAndIssue = function (msg_salutation,msg_main)
{
	var msg;
	switch (msg_salutation) {
		case "NONE":
			msg = msg_main;
			break;
		default:
			if ( Math.random() < 0.5 ) {
				msg = this.$str_prependSalutation(msg_salutation,msg_main);
			} else {
				msg = this.$str_appendSalutation(msg_salutation,msg_main);
			}
			break;
	}
	if (this.debug) {
		this.$dbg("msg_salutation = " + msg_salutation);
		this.$dbg("msg_main = " + msg_main);
		this.$dbg("msg = " + msg);
	}
	// Issue message
	this.ship.commsMessage(msg, player.ship);
};
/*
=========================
End of wrapper for JSHINT
=========================
*/
}).call(this);
// EOF
 | 
                
                    | Scripts/hiredGuns_system.js | /*
=================================
HEADER
=================================
*/
this.name					= "hiredGuns_system";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike.";
this.description			= "Set up the player escorts when bought";
this.version				= "1.1";
/*
===========================
Start of wrapper for JSHINT 
============================
*/
(
	function(){
/*
===========================
DIRECTIVE
===========================
*/
"use strict";
/*
===========================
GLOBALS
===========================
*/
this.debug = false;				// There is a 'this.debug' also in hiredGuns_system.js.
if ( this.debug === true ) {
	this.logging = true;
}
/*
===========================
FUNCTIONS
===========================
*/
/*
--------------
Debugging
--------------
*/
this.$dbg = function(msg)
{
	log(this.name,"DEBUG: " + msg);
};
this.startUp = function()
{
	missionVariables.hiredGuns_chance = Math.random() + (system.government * 0.05);
	// That value above is set also in this.shipExitedWitchspace.
	if ( this.debug ) {
		this.$dbg("Debugging ON");
	}
};
this.shipWillDockWithStation = function(station)
{
	if (!station.isMainStation) {
		return;
	}
	// Have we gone anywhere?
	if (system.ID == missionVariables.hiredGuns_launchSystem) {
		return;
	}
	var escortArray = system.shipsWithRole("hiredGuns_escort");
	if (escortArray.length > 0) {
		for (var i = 0; i < escortArray.length; i++) {
			escortArray[i].remove();
		}
	}
};
this.shipWillEnterWitchspace = function()
{
	var escortArray = system.shipsWithRole("hiredGuns_escort");
	if (escortArray.length > 0) {
		for (var i = 0; i < escortArray.length; i++) {
			escortArray[i].remove();
			missionVariables.hiredGuns_count += 1;
		}
	}
};
this.alertConditionChanged = function()
{
	if (player.alertCondition == 3 && player.alertHostiles && missionVariables.hiredGuns_purchased) {
		var escortArray = system.shipsWithRole("hiredGuns_escort", player.ship, 35000);
		if (escortArray.length > 0)
		{
			for (var i = 0; i < escortArray.length; i++) { 
				escortArray[i].AIState = ("FIND_PLAYER_HOSTILES");
			}
		}
	}
};
this.shipExitedWitchspace = function()
{
	if (missionVariables.hiredGuns_count > 0) {
		this.escorts = system.addShips(missionVariables.hiredGuns_role, missionVariables.hiredGuns_count, player.ship.position, 20000);
		for (var i = 0; i < missionVariables.hiredGuns_count; i++) {
			this.escorts[i].scannerDisplayColor1 = "yellowColor";
			this.escorts[i].scannerDisplayColor2 = "magentaColor";
		}
	}
	missionVariables.hiredGuns_chance = Math.random() + (system.government * 0.05);
	missionVariables.hiredGuns_launchSystem = null; // in case we jump out and back to the same system.
	if ( this.debug && system.isInterstellarSpace ) {
		this.$dbg("In interstellar space.");
	}
};
this.playerEnteredNewGalaxy = function(galaxyNumber)
{
	var escortArray = system.shipsWithRole("hiredGuns_escort");
	if (escortArray.length > 0) {
		for (var i = 0; i < escortArray.length; i++) {
			escortArray[i].remove();
		}
	}
	missionVariables.hiredGuns_count = 0;
	missionVariables.hiredGuns_role = null;
	missionVariables.hiredGuns_purchased = null;
	missionVariables.hiredGuns_launchSystem = null;
};
this.shipWillLaunchFromStation = function(station)
{
	if (station.isMainStation && system.countShipsWithRole("hiredGuns_escort") == 0 && missionVariables.hiredGuns_count > 0 && missionVariables.hiredGuns_role) {
		this.escorts = system.addShips(missionVariables.hiredGuns_role, missionVariables.hiredGuns_count, player.ship.position, 20000);
		for (var i = 0; i < missionVariables.hiredGuns_count; i++) { 
			this.escorts[i].scannerDisplayColor1 = "yellowColor";
			this.escorts[i].scannerDisplayColor2 = "magentaColor";
			this.escorts[i].AIState = "INITIALISE_FROM_STATION";
		}
		this.removeEq();
	}
};
this.shipEnteredStationAegis = function(station)
{
	if (!this.ship || !this.ship.isValid) { return; }
	this.ship.addCollisionException(station);
	 // Only main stations have aegis, but just to be safe:
	if (!station.isMainStation) {
		return;
	}
	// Have we gone anywhere?
	if (system.ID == missionVariables.hiredGuns_launchSystem) {
		return;
	}
	if ( this.debug ) {
		this.$dbg("Mission accomplished - removing escorts.");
	}
	var escortArray = system.shipsWithRole("hiredGuns_escort");
	if (escortArray.length > 0) {
		for (var i = 0; i < escortArray.length; i++) { 
			escortArray[i].AIState = ("MISSION_ACCOMPLISHED");
		}
	}
	missionVariables.hiredGuns_count = 0;
	missionVariables.hiredGuns_role = null;
	missionVariables.hiredGuns_purchased = null;
};
this.playerBoughtEquipment = function(equipment)
{
	switch(equipment) {
		case "EQ_HIREDGUN_LOW":
		{
			missionVariables.hiredGuns_role = "hiredGuns_escortLow";
			this.admin();
			player.ship.removeEquipment("EQ_HIREDGUN_LOW");
			break;
		}
		case "EQ_HIREDGUN_MID":
		{
			missionVariables.hiredGuns_role = "hiredGuns_escortMid";
			this.admin();
			player.ship.removeEquipment("EQ_HIREDGUN_MID");
			break;
		}
		case "EQ_HIREDGUN_HIGH":
		{
			missionVariables.hiredGuns_role = "hiredGuns_escortHigh";
			this.admin();
			player.ship.removeEquipment("EQ_HIREDGUN_HIGH");
			// break;
		}
	}
};
this.guiScreenChanged = function(newScreen, oldScreen)
{
	if (oldScreen == "GUI_SCREEN_EQUIP_SHIP") {
		this.removeEq();
	}
};
this.removeEq = function()
{
	if (player.ship.equipmentStatus("EQ_HIREDGUN_LOW") != "EQUIPMENT_UNAVAILABLE") {
		player.ship.removeEquipment("EQ_HIREDGUN_LOW");
		return;
	}
	if (player.ship.equipmentStatus("EQ_HIREDGUN_MID") != "EQUIPMENT_UNAVAILABLE") {
		player.ship.removeEquipment("EQ_HIREDGUN_MID");
		return;
	}
	if (player.ship.equipmentStatus("EQ_HIREDGUN_HIGH") != "EQUIPMENT_UNAVAILABLE") {
		player.ship.removeEquipment("EQ_HIREDGUN_HIGH");
		return;
	}
};
this.admin = function()
{
	missionVariables.hiredGuns_count = 2;
	missionVariables.hiredGuns_launchSystem = system.ID;
	missionVariables.hiredGuns_purchased = true;
};
/*
=========================
End of wrapper for JSHINT
=========================
*/
}).call(this);
// EOF |