| Path | 
                
                    | Scripts/TCAT_base.js | this.name					= "TCAT_base";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Thargoid base script.";
this.version				= "1.0";
this.shipSpawned = function()
	{
	this.ship.target = player.ship; 
    this.ship.alertCondition = 2;
	
	if(this.scanTimer)
		{
		this.scanTimer.start();
		}
	else
		{
		this.scanTimer = new Timer(this, this.mineScan, 0, 4.75);
		}
	this.launchAllFlag = "NO";	
	}
	
this.playerWillEnterWitchspace = function()
	{
	if(this.scanTimer)
		{
		this.scanTimer.stop();
		}
	}
this.mineScan = function()
	{
	function TCAT_localMines(entity) { 
	var isWeapon = entity && entity.isValid && entity.isWeapon, 
		isActive = entity.AI !== "TCAT_deactivatedAI.plist", 
		mines = ["EQ_QC_MINE", "EQ_RMB_CASCADE_MISSILE", "EQ_RMB_LAW_MISSILE", "EQ_RMB_OVERRIDE_MISSILE", 
			"energy-bomb", "EQ_NUKE2_MISSILE", "EQ_HARPOON_NUKE2_MISSILE", "EQ_PHOTON_MISSILE", "RANDOM_HITS_MINE"], 
		isLocalMine = mines.some(function (mine, i, array) {return entity.hasRole(mine);}); 
	return isWeapon && isActive && isLocalMine; 
	} 
	
	this.mineArray = system.filteredEntities(this, TCAT_localMines, this.ship, 25600);
	
	if(this.mineArray.length > 0)
		{
		player.consoleMessage("Cascade suppression field detected", 6);
		this.mineArray.forEach(function (mine) 
			{
			mine.setAI("TCAT_deactivatedAI.plist"); 
			mine.displayName = "Disarmed " + mine.displayName;
			mine.scannerDisplayColor1 = "whiteColor";
			mine.scannerDisplayColor2 = "whiteColor";
			}
			)
		}
	}
	this.findNonThargoid = function() 
	{ 
	function targetShips(entity) {return entity.isShip && !entity.docked && entity.scanClass != "CLASS_ROCK" && entity.scanClass != "CLASS_CARGO" && entity.scanClass != "CLASS_BUOY" && entity.scanClass != "CLASS_THARGOID" && !entity.isCloaked && !entity.hasRole("thargoid") && !entity.hasRole("thargon") && !entity.hasRole("TCAT_jumpGate")}; 
	this.nearArray = system.filteredEntities(this, targetShips, this.ship, 25600); 
 
	if(this.nearArray.length > 0)
		{
		this.targetNumber = Math.floor(Math.random() * this.nearArray.length);
		if(this.targetNumber > this.nearArray.length)
			{
			log(this.name + " script error, target number too high - please report");
			this.targetNumber = 0;
			}
		this.ship.target = this.nearArray[this.targetNumber];
        this.ship.reactToAIMessage("TARGET_FOUND");
		}
	else
		{
		this.ship.reactToAIMessage("NOTHING_FOUND")
		}
	}
this.launchAll = function()
	{
	this.launchAllFlag = "LAUNCH";
	this.launchGuards();
	}
	
this.launchGuards = function()
	{
	this.guardLength = this.ship.subEntities.length;
	if(this.guardLength > 0)
		{
		for(var guardCounter = this.guardLength-1;guardCounter >= 0;guardCounter--)
			{
			if(Math.random() > 0.25 || this.launchAllFlag == "LAUNCH")
				{
				this.guardPosition = this.ship.subEntities[guardCounter].position;
				this.guardOrientation = this.ship.subEntities[guardCounter].orientation;
				this.ship.subEntities[guardCounter].remove();
				var subGuard = this.ship.spawnOne("TCAT_guardRot");
				subGuard.position = this.localToGlobal(this.guardPosition);
				subGuard.orientation = this.guardOrientation.multiply(this.ship.orientation);
				}
			}
		}
	}
this.localToGlobal = function(position)
	{ // sub-ent position is relative to mother, but for swapping we need the absolute global position
	let orientation = this.ship.orientation;
	orientation.w = -orientation.w; // this is needed to get things in the right place for some reason?
	return this.ship.position.add(position.rotateBy(orientation));
	}
	
this.otherShipDocked = function(whom)		
	{	
	if(whom.isPlayer)
		{ // if we're in the bug station
		mission.runScreen({title: "Right species, wrong place", messageKey:"TCAT_bugBase"}, this.relaunch);
		}
	}	
	
this.relaunch = function()
	{
	player.ship.launch();
	}
	
this.shipDied = function(who, why)
	{
	if(this.scanTimer && this.scanTimer.isRunning)
		{
		this.scanTimer.stop();
		}
		
	this.fuelCount = 15 + Math.ceil(Math.random() * 10);
	this.largeCount = 4 + Math.ceil(Math.random() * 3);
	this.smallCount = 5 + Math.ceil(Math.random() * 5);
	system.legacy_addShipsWithinRadius("TCAT_burningFuel", this.fuelCount, "abs", this.ship.position, 10);
	system.legacy_addShipsWithinRadius("TCAT_smallWreckage", this.smallCount, "abs", this.ship.position, 5);
	if(this.largeCount > 0)
		{
		system.legacy_addShipsWithinRadius("TCAT_largeWreckage", this.largeCount, "abs", this.ship.position, 5);
		}
	} | 
                
                    | Scripts/TCAT_bugStation.js | this.name					= "TCAT_bugStation";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Thargoid station ship script.";
this.version				= "1.0";
function TCAT_localMines(entity) { 
	var isWeapon = entity.isWeapon, 
		isActive = entity.AI !== "TCAT_deactivatedAI.plist", 
		mines = ["EQ_QC_MINE", "EQ_RMB_CASCADE_MISSILE", "EQ_RMB_LAW_MISSILE", "EQ_RMB_OVERRIDE_MISSILE", 
			"energy-bomb", "EQ_NUKE2_MISSILE", "EQ_HARPOON_NUKE2_MISSILE", "EQ_PHOTON_MISSILE", "RANDOM_HITS_MINE"], 
		isLocalMine = mines.some(function (mine, i, array) {return entity.hasRole(mine);}); 
	return isWeapon && isActive && isLocalMine; 
	} 
	
function targetShips(entity) {return entity.isShip && !entity.docked && entity.scanClass != "CLASS_ROCK" && entity.scanClass != "CLASS_CARGO" && !entity.isThargoid && !entity.isCloaked}; 		
this.shipSpawned = function()
	{
	this.ship.target = player.ship; 
    this.ship.alertCondition = 2;
	if(this.scanTimer)
		{
		this.scanTimer.start();
		}
	else
		{
		this.scanTimer = new Timer(this, this.mineScan, 0, 4.75);
		}
		
	if(this.cavalryTimer)
		{
		this.cavalryTimer.start();
		}
	else
		{
		this.cavalryTimer = new Timer(this, this.addRaptor, 60, 60);
		}	
	this.cavalryChance = 0;
	this.cavalryCount = 0;
	
	missionVariables.TCAT_door = "DROPPED";
	this.spawnDoor();
	}
this.findNonThargoid = function() 
	{ 
	if(this.ship.target)
		{
		this.ship.reactToAIMessage("TARGET_FOUND");
		return;
		}
	
	this.targetArray = system.filteredEntities(this, targetShips, this.ship, 25600); 
 
	if(this.targetArray.length > 0)
		{
		this.targetNumber = Math.floor(Math.random() * this.targetArray.length);
		if(this.targetNumber > this.targetArray.length)
			{
			log(this.name + " script error, target number too high - please report");
			this.targetNumber = 0;
			}
		this.ship.target = this.targetArray[this.targetNumber];
        this.ship.reactToAIMessage("TARGET_FOUND");
		}
	else
		{
		this.ship.reactToAIMessage("NOTHING_FOUND")
		}
	}; 	
	
this.playerWillEnterWitchspace = this.shipDied = function()
	{
	if(this.doorTimer && this.doorTimer.isRunning)
		{
		this.doorTimer.stop();
		}
	if(this.scanTimer && this.scanTimer.isRunning)
		{
		this.scanTimer.stop();
		}	
	if(this.cavalryTimer && this.cavalryTimer.isRunning)
		{
		this.cavalryTimer.stop();
		}	
		
	}
this.mineScan = function()
	{
	this.mineArray = system.filteredEntities(this, TCAT_localMines, this.ship, 25600);
	
	if(this.mineArray.length > 0)
		{
		player.consoleMessage("Cascade suppression field detected", 6);
		this.mineArray.forEach(function (mine) 
			{
			mine.setAI("TCAT_deactivatedAI.plist"); 
			mine.displayName = "Disarmed " + mine.displayName;
			mine.scannerDisplayColor1 = "whiteColor";
			mine.scannerDisplayColor2 = "whiteColor";
			}
			)
		}
	
	if(player.ship.isCloaked && this.ship && this.ship.position.distanceTo(player.ship.position) < 512000 && Math.random() > 0.33) // so that the cloak can't really be used to defeat the mission
		{
		player.ship.removeEquipment("EQ_CLOAKING_DEVICE"); // done this way rather than by setEquipmentStatus to 
		player.ship.awardEquipment("EQ_CLOAKING_DEVICE"); // avoid triggering repair bots, repair system or DCN.
		player.consoleMessage("Cloaking device feedback malfunction!", 6);
		}
	}
this.addRaptor = function()
	{
	if(Math.random() < this.cavalryChance)
		{
		system.legacy_addShipsAtPrecisely("TCAT_raptor", 1, "abs", this.ship.position.add([2000, 5000, -22000]));
		this.cavalryChance = 0;
		this.cavalryCount += 1;
		}
	else
		{
		this.cavalryChance += 0.1;
		}
	if(this.cavalryCount == 3 || !this.ship || !this.ship.isValid)
		{
		this.cavalryTimer.stop();
		}
	}
	
this.otherShipDocked = function(whom)		
	{	
	if(whom.isPlayer)
		{ // if we're in the bug station
		mission.runScreen({title: "The Fat Lady Sings", messageKey:"TCAT_bugStation"}, this.bugBang);
		}
	}
this.stationLaunchedShip = function(whom)
	{
	if(!whom.isPlayer && missionVariables.TCAT_door == "DROPPED")
		{
		this.doorTimer = new Timer(this, this.spawnDoor, 2);
		}
	if(whom.isPlayer)
		{
		player.consoleMessage("Warning - Quirium escape detected in station. Detonation imminent - evacuate the area!",6);
		this.doorArray = system.shipsWithRole("TCAT_blastDoor");
		if(this.doorArray.length > 0)
			{
			this.doorArray[0].remove();
			missionVariables.TCAT_door = "DESTROYED";
			}
		}
	}
	
this.bugBang = function()
	{
	missionVariables.TCAT_mission = "SUCCESS";
	mission.setInstructionsKey("TCAT_shortSuccess", "TCAT_missionScript");
	mission.unmarkSystem(168);
	mission.markSystem(138);
	this.ship.switchAI("TCAT_stationExplodeAI.plist");
	player.ship.launch();
	}
	
this.spawnDoor = function()
	{
	var blastDoor = this.ship.spawnOne("TCAT_blastDoor");
	blastDoor.orientation = this.ship.orientation;
	blastDoor.position = this.ship.position.add(this.ship.orientation.vectorForward().multiply(500));
	missionVariables.TCAT_door = "PRESENT";
	}
this.dropDoor = function()
	{
	this.doorArray = system.shipsWithRole("TCAT_blastDoor");
	if(this.doorArray.length > 0)
		{
		this.doorArray[0].remove();
		missionVariables.TCAT_door = "DROPPED";
		}		
	this.ship.reactToAIMessage("LAUNCH_DEFENDER");	
	} | 
                
                    | Scripts/TCAT_claw.js | this.name           = "TCAT_claw";
this.author         = "Thargoid";
this.copyright		= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description    = "Ship script for the claw mine";
this.version        = "1.0";
this.spawnTalons = function() 
	{ 
	if(player.ship && this.ship.position.distanceTo(player.ship.position) <25600)
		{
		player.consoleMessage("Mine launch detected - Raptor's Claw mine");
		}
	
	this.talonCount = 4 + Math.ceil(Math.random() * 12);
	function allAlienShips(entity) {return entity.isShip && entity.scanClass == "CLASS_THARGOID"}; 
	function notThargons(entity) {return !entity.hasRole("thargon") && !entity.hasRole("tharglet")};
	var anyAlienShips = system.filteredEntities(this, allAlienShips, this.ship, 25600); 
	var motherShips = anyAlienShips.filter(notThargons);
	
	var loopCounter = 0 ; // reset the counter
	for(loopCounter = 0; loopCounter < this.talonCount; loopCounter++)
		{
		var Talon = this.ship.spawnOne("TCAT_talon");
		this.xDistance = ((Math.random() * 10) - 5);
		this.yDistance = ((Math.random() * 10) - 5);
		this.zDistance = ((Math.random() * 10) - 5);
		Talon.position = this.ship.position.add([this.xDistance, this.yDistance, this.zDistance]);
		let launchVector = Talon.position.subtract(this.ship.position).direction(); // unit vector pointing away from the mine
		let angle = Talon.heading.angleTo(launchVector); // angle between current heading and target heading
		let cross = Talon.heading.cross(launchVector).direction(); // set the plane where we should rotate in
		Talon.orientation = Talon.orientation.rotate(cross, -angle);
		
		if(motherShips.length > 0) // if we have Thargoid motherships around, target them in preference
			{
			Talon.target = motherShips[Math.floor(Math.random() * motherShips.length)];
			}
		else
			{
			if(anyAlienShips.length > 0) // if we have something to target
				{
				Talon.target = anyAlienShips[Math.floor(Math.random() * anyAlienShips.length)];
				}
			else
				{
				Talon.explode(); // self destruct if nothing to target
				}
		}	}
	} 
 | 
                
                    | Scripts/TCAT_door.js | this.name		= "TCAT_door";
this.author       = "Thargoid";
this.copyright	= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description  = "Script for blast door";
this.version	= "1.0";
this.shipDied = function(whom, why)
	{
	if(why != "removed")
		{
		missionVariables.TCAT_door = "DESTROYED";
		}		
	}
this.shipBeingAttackedByCloaked = function()
	{
	if(player.ship.isCloaked)
		{
		player.ship.removeEquipment("EQ_CLOAKING_DEVICE"); // done this way rather than by setEquipmentStatus to 
		player.ship.awardEquipment("EQ_CLOAKING_DEVICE"); // avoid triggering repair bots, repair system or DCN.
		player.consoleMessage("Cloaking device feedback malfunction!", 6);
		}
	} | 
                
                    | Scripts/TCAT_ecmJammer.js | this.name					= "ECM_jammerScript";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Script for ECM Jammer";
this.version				= "1.00";
function ECMJammer_ecmShips(entity) {return entity.isShip && (entity.equipmentStatus("EQ_TCAT_DISABLED_ECM") === "EQUIPMENT_OK" || entity.equipmentStatus("EQ_ECM") === "EQUIPMENT_OK")};
this.activated = function()
	{	
	if(!this.ecmTimer)
		{
		player.consoleMessage("ECM Jammer activated",6);
		this.ecmTimer = new Timer(this, this.disableECM,0,0.25)		
		}
	else
		{
		player.consoleMessage("ECM Jammer deactivated",6);
		this.stopTimer();
		}
	}
	
this.stopTimer = this.shipWillEnterWitchspace = this.shipWillDockWithStation = this.shipDied = function()
	{
	if(this.ecmTimer && this.ecmTimer.isRunning)
		{
		this.enableECM();		
		this.ecmTimer.stop();
		delete this.ecmTimer;
		}
	}
this.disableECM = function()
	{
	player.ship.energy = player.ship.energy - 4;
	if(player.ship.energy < 64)
		{
		player.consoleMessage("Energy low - deactivating ECM Jammer", 6);
		this.stopTimer();
		return;
		}
		
	var fullList = system.filteredEntities(this, ECMJammer_ecmShips, player.ship); // all ships in the system with ECM or disabled ECM
	var closeList = system.filteredEntities(this, ECMJammer_ecmShips, player.ship, 25600); // all ships in scanner range with ECM or disabled ECM
	
	if(fullList.length > 0)
		{
		var ecmCounter = 0 ; // reset the counter
		for(ecmCounter = 0;ecmCounter<fullList.length;ecmCounter++)
			{
			fullList[ecmCounter].removeEquipment("EQ_TCAT_DISABLED_ECM");
			fullList[ecmCounter].awardEquipment("EQ_ECM");
			}
		}
	if(closeList.length > 0)
		{
		var ecmCounter = 0 ; // reset the counter
		for(ecmCounter = 0;ecmCounter<closeList.length;ecmCounter++)
			{
			closeList[ecmCounter].removeEquipment("EQ_ECM");
			closeList[ecmCounter].awardEquipment("EQ_TCAT_DISABLED_ECM");
			}
		}	
		
	}
	
this.enableECM = function()
	{
	var fullList = system.filteredEntities(this, ECMJammer_ecmShips, player.ship); // all ships in the system with ECM or disabled ECM
	if(fullList.length > 0)
		{
	var ecmCounter = 0 ; // reset the counter
	for(ecmCounter = 0;ecmCounter<fullList.length;ecmCounter++)
			{
			fullList[ecmCounter].removeEquipment("EQ_TCAT_DISABLED_ECM");
			fullList[ecmCounter].awardEquipment("EQ_ECM");
			}
		}
	}
	
this.equipmentDamaged = this.equipmentDestroyed = function(equipment)
	{	
	if(equipment == "EQ_TCAT_ECM_JAMMER") // if the jammer's broken in-flight
		{
		this.stopTimer();
		}
		
	var fullList = system.filteredEntities(this, ECMJammer_ecmShips, player.ship); // all ships in the system with ECM or disabled ECM
	if(fullList.length > 0)
		{
		var ecmCounter = 0 ; // reset the counter
		for(ecmCounter = 0;ecmCounter<fullList.length;ecmCounter++)
			{
			fullList[ecmCounter].removeEquipment("EQ_TCAT_DISABLED_ECM");
			fullList[ecmCounter].awardEquipment("EQ_ECM");
			}
		}	
		
	} | 
                
                    | Scripts/TCAT_guard.js | this.name					= "TCAT_guard";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Thargoid Guardian ship script.";
this.version				= "1.0";
this.launchOrbiters = function()
	{
	this.subLength = this.ship.subEntities.length;
	if(this.subLength > 0)
		{
		for(var subCounter = this.subLength-1;subCounter >= 0;subCounter--)
			{
			var subOrientation = this.ship.subEntities[subCounter].orientation;
			var offsetVector = this.ship.subEntities[subCounter].orientation.vectorForward();
			var subOffset = offsetVector.multiply(100);
			this.ship.subEntities[subCounter].remove();
			var orbiter = this.ship.spawnOne('TCAT_freeOrbiter');
			orbiter.orientation = subOrientation;
			orbiter.position = this.ship.position.add(subOffset);
			if(this.ship.target) {orbiter.target = this.ship.target;}
			}
		}
	}
	
	this.findNonThargoid = function() 
	{ 
	function targetShips(entity) {return entity.isShip && !entity.docked && entity.scanClass != "CLASS_ROCK" && entity.scanClass != "CLASS_CARGO" && entity.scanClass != "CLASS_THARGOID" && !entity.isCloaked && !entity.hasRole("thargoid") && !entity.hasRole("thargon") && !entity.hasRole("TCAT_jumpGate")}; 
	this.nearArray = system.filteredEntities(this, targetShips, this.ship, 25600); 
	this.farArray = system.filteredEntities(this, targetShips, this.ship, 60000); 
 
	if(this.nearArray.length > 0)
		{
		this.targetNumber = Math.floor(Math.random() * this.nearArray.length);
		if(this.targetNumber > this.nearArray.length)
			{
			log(this.name + " script error, target number too high - please report");
			this.targetNumber = 0;
			}
		this.ship.target = this.nearArray[this.targetNumber];
        this.ship.reactToAIMessage("TARGET_FOUND");
		}
	else
		{
		if(this.farArray.length > 0)
			{
			this.ship.target = this.farArray[0]; // set to the closest target off-scanner but in range
			this.ship.reactToAIMessage("INTERCEPT_TARGET");
			}
		else
			{
			this.ship.reactToAIMessage("NOTHING_FOUND")
			}
		}
	}
this.checkTarget = function()
	{
	if(this.ship.target && this.ship.target.scanClass != "CLASS_THARGOID" && !this.ship.target.hasRole("thargoid") && !this.ship.target.hasRole("thargon"))
		{
		this.ship.reactToAIMessage("TARGET_VALID");
		}
	else
		{
		this.ship.AIState = "LOOK_FOR_TARGETS";
		}
	}
 | 
                
                    | Scripts/TCAT_guardRot.js | this.name					= "TCAT_guardRot";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Thargoid Guardian ship swap.";
this.version				= "1.0";
this.shipSwap = function()
	{	
	var newShip = this.ship.spawnOne('TCAT_guard');
	newShip.orientation = this.ship.orientation.rotate(this.ship.orientation.vectorRight(), (0.5 * Math.PI));
	newShip.position = this.ship.position;
	newShip.energy = this.ship.energy;
	if(this.ship.group)
		{
		newShip.group = this.ship.group;
		}
	if(this.ship.target) 
		{
		newShip.target = this.ship.target;
		}
	this.ship.remove();
	}
 | 
                
                    | Scripts/TCAT_jammer.js | this.name           = "TCAT_jammer";
this.author         = "Thargoid";
this.copyright		= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description    = "Ship script for Thargon jammer mine";
this.version        = "1.00";
this.performJamming = function() 
	{ 
	if(player.ship && this.ship.position.distanceTo(player.ship.position) <25600)
		{
		player.consoleMessage("Mine launch - Thargon Jammer mine");
		}
	function isThargon(entity) 
		{ 
		return (entity.isShip && (entity.primaryRole == "thargon" || entity.primaryRole == "tharglet" || entity.primaryRole == "CT_thargon"));
    		}
	var thargonArray = system.filteredEntities(this, isThargon, this.ship, 25600)
	if(thargonArray.length > 0)
		{
		var thargCounter = 0 ; // reset the counter
		for(thargCounter = 0; thargCounter < thargonArray.length; thargCounter++)
			{
			this.jamChance = (this.ship.position.distanceTo(thargonArray[thargCounter].position)) / 25600;
			this.diceRole = Math.random();
			if(this.diceRole > this.jamChance)
				{
				thargonArray[thargCounter].switchAI("TCAT_uncontrolledThargonAI.plist");
				}
			}
		}
	}
 | 
                
                    | Scripts/TCAT_jumpGate.js | this.name					= "TCAT_jumpGate";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Script for Thargoid jumpgates";
this.version				= "1.0";
function TCAT_allGates(entity) {return entity.isShip && entity != this.ship && entity.hasRole("TCAT_jumpGate")}; // all gates except this one
function TCAT_clients(entity) {return entity.isShip && !entity.isStation && entity.primaryRole != "TCAT_gateGuard" && !entity.isWeapon && !entity.isRock && !entity.isCargo && (!entity.hasOwnProperty("TCAT_receivingGate")|| entity.TCAT_receivingGate == null)}; // ships that can use jumpgates (exclude spawned defenders, missiles, mines, asteroids and cargo pods
function TCAT_oldClients(entity) {return entity.isShip && entity.hasOwnProperty("TCAT_receivingGate") && entity.TCAT_receivingGate != null && entity.position.distanceTo(this.ship.position) > 500 && entity.position.distanceTo(this.ship.position) < 5000}; // ships which have come through this gate and are now between 500m and 5km from it
this.shipSpawned = function()
	{
	this.ship.orientation = [1, 0, 0, 0]; // point them all in the same direction, along universal Z.
	this.defenderCount = 10; // max number of defenders that can be launched overall by the gate if attacked
	
	if(this.scanTimer)
		{
		this.scanTimer.start();
		}
	else
		{
		this.scanTimer = new Timer(this, this.shipApproach, 0, 1.0); // scan for jumping ships every 1 second - too long/short?
		}
	}
this.playerWillEnterWitchspace = this.shipDied = function()
	{
	if(this.scanTimer)
		{
		this.scanTimer.stop();
		}
	}
this.shipApproach = function()
	{		
	var pastClients = system.filteredEntities(this, TCAT_oldClients, this.ship, 5000) // if any old clients are around, strip their client status
	if(pastClients.length > 0)
		{
		for(var i = 0; i < pastClients.length; i++)
			{
			pastClients[i].TCAT_receivingGate = null;
			if(pastClients[i].isPlayer) 	
				{
				player.consoleMessage("Jump gate cleared.", 6);
				}
			}
		}
	
	var queue = system.filteredEntities(this, TCAT_clients, this.ship, 250); // if there is no-one wanting to jump, end the test
	if(queue.length == 0)
		{
		return;
		}
	
	var gates = system.filteredEntities(this, TCAT_allGates, this.ship); // check whether there is a second gate to receive the jumper
	if(gates.length == 0) 
		{
		if(queue[0].isPlayer) 
			{
			player.consoleMessage("Jump gate off-line.", 6);
			}
		return;
		}
	
	if(queue[0].hasOwnProperty("TCAT_receivingGate") && queue[0].TCAT_receivingGate == this.ship) // if the jumper has just arrived, end the test
		{
		return;
		}
	
	if(queue[0].isPlayer)
		{
		player.consoleMessage("Jump gate activated.", 6);
		player.ship.target = null;
		}
	queue[0].TCAT_receivingGate = gates[0]; // note the gate that is about to receive the jumper
	queue[0].position = gates[0].position; // move the jumper to the receiving gate
	queue[0].orientation = gates[0].orientation; // re-orient the jumper to point out of the gate
	if(queue[0].AI == "TCAT_useGateAI.plist")
		{
		queue[0].reactToAIMessage("JUMPED");
		}
	}
this.findGates = function()
	{
	var gates = system.filteredEntities(this, TCAT_allGates, this.ship);	
	if(gates.length == 0)
		{
		this.ship.reactToAIMessage("NO_GATE");
		}
	else
		{
		this.ship.reactToAIMessage("GATE_FOUND");
		}
	};
	
this.underAttack = function()
	{
	if(this.defenderCount > 0 && worldScripts["TCAT_masterScript"] && worldScripts["TCAT_masterScript"].defenderAllowed())
		{
		var defender = this.ship.spawnOne("TCAT_gateGuard");
		defender.position = this.ship.position;
		defender.orientation = this.ship.orientation;
		this.defenderCount -= 1;
		}
	}
 | 
                
                    | Scripts/TCAT_largeWreck.js | this.name 			= "TCAT_largeWreck"; 
this.author 		= "Thargoid"; 
this.copyright		= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description	= "Break-up of larger wreckage into small ones"; 
this.version 		= "1.0"; 
this.shipDied = function()
	{
	this.subSmallCount = (Math.ceil(Math.random() * 5) - 2); // between 0 (-1) and 3 pieces of small wreckage spawned from large one
	if(this.subSmallCount > 0) // if we have a positive amount of small wreckage
		{
		this.ship.spawn("TCAT_smallWreckage", this.subSmallCount); // spawn the small wreckage
		}
	this.fuelCount = (Math.ceil(Math.random() * 10)); // between 1-10 burning fuel entities
	this.ship.spawn("TCAT_burningFuel", this.fuelCount); // spawn the burning fuel
	delete this.shipDied;
	}
 | 
                
                    | Scripts/TCAT_mantis.js | this.name					= "TCAT_mantis";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Thargoid base script.";
this.version				= "1.0";
function TCAT_localMines(entity) { 
	var isWeapon = entity.isWeapon, 
		isActive = entity.AI !== "TCAT_deactivatedAI.plist", 
		mines = ["EQ_QC_MINE", "EQ_RMB_CASCADE_MISSILE", "EQ_RMB_LAW_MISSILE", "EQ_RMB_OVERRIDE_MISSILE", 
			"energy-bomb", "EQ_NUKE2_MISSILE", "EQ_HARPOON_NUKE2_MISSILE", "EQ_PHOTON_MISSILE", "RANDOM_HITS_MINE"], 
		isLocalMine = mines.some(function (mine, i, array) {return entity.hasRole(mine);}); 
	return isWeapon && isActive && isLocalMine; 
	} 
function targetShips(entity) {return entity.isShip && !entity.docked && entity.scanClass != "CLASS_ROCK" && entity.scanClass != "CLASS_CARGO" && !entity.isThargoid && !entity.isCloaked}; 	
	
this.shipSpawned = function()
	{
	this.armadaGroup = system.addGroup("TCAT_armada", 6, this.ship.position, 15000);
	this.armadaGroup.leader = this.ship;
	
	if(this.scanTimer)
		{
		this.scanTimer.start();
		}
	else
		{
		this.scanTimer = new Timer(this, this.mineScan, 0, 4.75);
		}
	this.scrambleFlag = null;
	}
	
this.playerWillEnterWitchspace = function()
	{
	if(this.scanTimer)
		{
		this.scanTimer.stop();
		}
	}
this.checkTarget = function()
	{
	if(this.ship.target && !this.ship.target.scanClass == "CLASS_THARGOID" && !this.ship.target.hasRole("thargoid") && !this.ship.target.hasRole("thargon"))
		{
		this.ship.reactToAIMessage("TARGET_VALID");
		}
	else
		{
		this.ship.reactToAIMessage("NO_TARGET");
		}
	}
this.mineScan = function()
	{
	this.mineArray = system.filteredEntities(this, TCAT_localMines, this.ship, 25600);
	
	if(this.mineArray.length > 0)
		{
		player.consoleMessage("Cascade suppression field detected", 6);
		this.mineArray.forEach(function (mine) 
			{
			mine.setAI("TCAT_deactivatedAI.plist"); 
			mine.displayName = "Disarmed " + mine.displayName;
			mine.scannerDisplayColor1 = "whiteColor";
			mine.scannerDisplayColor2 = "whiteColor";
			}
			)
		}
	}
this.findStation = function()
	{
	this.stationArray = system.shipsWithPrimaryRole("TCAT_navyStation");
	if(this.stationArray.length == 0)
		{
		this.ship.reactToAIMessage("NOTHING_FOUND");	
		}
	else
		{
		this.ship.target = this.stationArray[0];
		this.ship.reactToAIMessage("STATION_FOUND");
		}
	}
	
this.scramble = function()
		{
		this.scrambleFlag = "LAUNCH";
		this.launchGuards();
		}
	
this.launchGuards = function()
	{
	this.subLength = this.ship.subEntities.length;
	if(this.subLength == 0)
		{
		return;
		}
	this.guardArray = [];
	for(var guardCounter = 0;guardCounter < this.subLength;guardCounter++)
		{
		if(this.ship.subEntities[guardCounter].hasRole("TCAT_guardRot"))
			{
			this.guardArray.push(guardCounter);
			}
		}
	if(this.guardArray.length == 0)
		{
		return;
		}
	for(var guardCounter = this.guardArray.length - 1;guardCounter >= 0;guardCounter--)
		{
		if(Math.random() < 0.1 || this.scrambleFlag == "LAUNCH")
			{
			this.guardPosition = this.ship.subEntities[this.guardArray[guardCounter]].position;
			this.guardOrientation = this.ship.subEntities[this.guardArray[guardCounter]].orientation;
			this.ship.subEntities[this.guardArray[guardCounter]].remove();
			var subGuard = this.ship.spawnOne("TCAT_guardRot");
			subGuard.position = this.localToGlobal(this.guardPosition);
			subGuard.orientation = this.guardOrientation.multiply(this.ship.orientation);
			this.armadaGroup.addShip(subGuard);
			}
		}
	}
this.localToGlobal = function(position)
	{ // sub-ent position is relative to mother, but for swapping we need the absolute global position
	let orientation = this.ship.orientation;
	orientation.w = -orientation.w; // this is needed to get things in the right place for some reason?
	return this.ship.position.add(position.rotateBy(orientation));
	}
this.shipEnteredWitchspace = function()
	{
	this.scanTimer.stop();
	if(this.ship.escorts.length > 0) // could we use escortGroup here?
		{
		for(var escortCounter = this.ship.escorts.length - 1;escortCounter >= 0;escortCounter--)
			{
			this.ship.escorts[escortCounter].remove();
			}
		}
	this.ship.remove();
	}
	
this.shipFiredMissile = function(missile, target)
	{
	if(missile.hasRole("TCAT_guard") || missile.hasRole("TCAT_armada")) // if the mantis launched a ship "missile" add it to the ship group
		{
		this.armadaGroup.addShip(missile);
		}
	}	
	
this.shipDied = function(who, why)
	{
	this.scanTimer.stop();
	
	if(missionVariables.TCAT_mission && (missionVariables.TCAT_mission == "INCOMING" || missionVariables.TCAT_mission == "ARMADA"))
		{
		missionVariables.TCAT_mission = "REPELLED";
		}
		
	this.fuelCount = 15 + Math.ceil(Math.random() * 10);
	this.largeCount = 4 + Math.ceil(Math.random() * 3);
	this.smallCount = 5 + Math.ceil(Math.random() * 5);
	system.legacy_addShipsWithinRadius("TCAT_burningFuel", this.fuelCount, "abs", this.ship.position, 10);
	system.legacy_addShipsWithinRadius("TCAT_smallWreckage", this.smallCount, "abs", this.ship.position, 5);
	if(this.largeCount > 0)
		{
		system.legacy_addShipsWithinRadius("TCAT_largeWreckage", this.largeCount, "abs", this.ship.position, 5);
		}
	} | 
                
                    | Scripts/TCAT_masterScript.js | this.name					= "TCAT_masterScript";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "WorldScript for TCAT OXP.";
this.version				= "1.0";
this.shipExitedWitchspace = function()
	{
	if(missionVariables.TCAT_overRide == true || missionVariables.TCAT_mission == "HUNT")
		{
		return;
		}
	this.policeCount = system.entitiesWithScanClass("CLASS_POLICE").length + system.entitiesWithScanClass("CLASS_MILITARY").length; 
	this.thargoidCount = system.entitiesWithScanClass("CLASS_THARGOID").length - system.countShipsWithRole("thargon"); 
	if(this.thargoidCount > 6 || Math.random() < ((this.thargoidCount-this.policeCount) / 10))
		{
		system.legacy_addShips("TCAT_squadRaptor", 1);
		return;
		}
	if(this.thargoidCount > 3 || Math.random() < ((this.thargoidCount-this.policeCount) / 5))
		{
		system.legacy_addShips("TCAT_raptor", 1);
		}
	}
	
this.playerBoughtEquipment = function(equipment) // swap freebies and dummy equipment for the real thing at naval intel station
	{
	switch(equipment)
		{
		case "EQ_TCAT_DUMMY_MINE":
		case "EQ_TCAT_FREE_MINE":
			{
			player.ship.removeEquipment("EQ_TCAT_DUMMY_MINE");
			player.ship.removeEquipment("EQ_TCAT_FREE_MINE");
			player.ship.awardEquipment("EQ_QC_MINE");
			break;
			}
		case "EQ_TCAT_DUMMY_MISSILE":	
		case "EQ_TCAT_FREE_MISSILE":
			{
			player.ship.removeEquipment("EQ_TCAT_DUMMY_MISSILE");
			player.ship.removeEquipment("EQ_TCAT_FREE_MISSILE");
			player.ship.awardEquipment("EQ_HARDENED_MISSILE");
			break;
			}		
				
		case "EQ_TCAT_DUMMY_NEU":
			{ // as the mission can't run without Thargoid Plans being completed, the player must have access to this anyway.
			player.ship.removeEquipment("EQ_TCAT_DUMMY_NEU");
			if (player.ship.equipmentStatus("EQ_ENERGY_UNIT") != "EQUIPMENT_UNAVAILABLE")
				{
				player.ship.setEquipmentStatus("EQ_ENERGY_UNIT", "EQUIPMENT_OK");
				player.ship.removeEquipment("EQ_ENERGY_UNIT");
				}
			player.ship.awardEquipment("EQ_NAVAL_ENERGY_UNIT");
			break;
			}	
		
		case "EQ_TCAT_DUMMY_SHIELDS":
			{ 
			player.ship.removeEquipment("EQ_TCAT_DUMMY_SHIELDS");
			player.ship.awardEquipment("EQ_NAVAL_SHIELD_BOOSTER");
			break;
			}			
		
		case "EQ_TCAT_CLEARPYLONS":
			{
			player.ship.removeEquipment("EQ_TCAT_CLEARPYLONS");
			this.credits = player.credits;
			player.ship.awardEquipment("EQ_MISSILE_REMOVAL");
			player.credits = this.credits;
			break;
			}
		
		case "EQ_TCAT_EU_UPGRADE":
		case "EQ_TCAT_EU_FIXGRADE":
			{
			player.ship.removeEquipment("EQ_TCAT_EU_UPGRADE");
			player.ship.removeEquipment("EQ_ENERGY_UNIT");
			player.ship.awardEquipment("EQ_NAVAL_ENERGY_UNIT");
			}
			case "EQ_TCAT_NEU_REPAIR":
			{
			player.ship.removeEquipment("EQ_TCAT_NEU_REPAIR");
			player.ship.awardEquipment("EQ_NAVAL_ENERGY_UNIT");
			}
		
		}
	}	
	
// Code below is used by ship scripts 
this.jammerTime = clock.absoluteSeconds // for use of spacing Raptor Jammer Mine launches by ship scripts to min of 60s. 
this.clawTime = clock.absoluteSeconds // for use of spacing Raptor Claw Mine launches by ship scripts to min of 120s. 
this.defenderTime = clock.absoluteSeconds // for use of spacing jumpgate defender launches by ship scripts to min of 20s. 
this.jammerAllowed = function() 
	{ 
	if((clock.absoluteSeconds - this.jammerTime) > 60) 
		{ 
		this.jammerTime = clock.absoluteSeconds; 
		return true; 
		} 
	else 
		{
		return false; 
		}
	}
this.clawAllowed = function() 
	{ 
	if((clock.absoluteSeconds - this.clawTime) > 120) 
		{ 
		this.clawTime = clock.absoluteSeconds; 
		return true; 
		} 
	else 
		{
		return false; 
		}
	}
this.defenderAllowed = function() 
	{ 
	if((clock.absoluteSeconds - this.defenderTime) > 20) 
		{ 
		this.defenderTime = clock.absoluteSeconds; 
		return true; 
		} 
	else 
		{
		return false; 
		}
	} | 
                
                    | Scripts/TCAT_missionScript.js | this.name					= "TCAT_missionScript";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Mission script for TCAT OXP.";
this.version				= "1.0";
this.startUp = function()
	{
	// mission has already been rejected or completed, clean up and remove active script parts
	if(missionVariables.TCAT_mission == "CASE_CLOSED") 
		{
		this.cleanUp();
		return;
		}
		
	this.warningSound = new SoundSource; 
	this.warningSound.sound = "TCAT_alert.ogg";
	this.warningSound.loop = false;
	 
	//initialise mission variables
	if(!missionVariables.TCAT_sneakCount)
		{
		missionVariables.TCAT_sneakCount = 0;
		}
		
	if(!missionVariables.TCAT_missionBlock)
		{
		missionVariables.TCAT_missionBlock = false;
		}
	if(!missionVariables.TCAT_navalStatus || (missionVariables.TCAT_navalStatus != "ALIVE" && missionVariables.TCAT_navalStatus != "DEAD"))
		{
		missionVariables.TCAT_navalStatus = "ALIVE";
		}
	}
	
this.cleanUp = function() // get rid of active script elements if no longer needed.
		{
		delete this.shipLaunchedFromStation;
		delete this.playerEnteredNewGalaxy;
		delete this.playerExitedWitchspace;
		delete this.missionScreenOpportunity;
		delete this.playerBoughtNewShip;
		delete this.playerWillEnterWitchspace;
		mission.setInstructionsKey(null, "TCAT_missionScript");
		return;
		}
	
this.playerBoughtNewShip = function(ship)
		{ // if player buys a new ship after the device is fitted, stop the mission
		if(missionVariables.TCAT_mission == "SNEAK" 
			|| missionVariables.TCAT_mission == "CAUGHT" 
			|| missionVariables.TCAT_mission == "INCOMING" 
			|| missionVariables.TCAT_mission == "ARMADA" 
			|| missionVariables.TCAT_mission == "REPELLED" 
			|| missionVariables.TCAT_mission == "HUNT")
			{
			player.consoleMessage("Naval intelligence device self-destructed!", 6);
			missionVariables.TCAT_mission = "CASE_CLOSED";
			this.cleanUp();
			}
		}
	
this.missionScreenOpportunity = function()
	{	
	// next check if we have any conditions preventing the offering or continuing of the mission
	if(!player.ship.docked // we're in space
			|| player.score < 1500 // need 1500 kills to begin (for a little space between this and Thargoid Plans)
			|| galaxyNumber != 2 // we're not in G3
			|| !missionVariables.thargplans // Thargoid Plans mission never started
			|| missionVariables.thargplans != "MISSION_COMPLETE" // Thargoid Plans mission not completed
			|| missionVariables.TCAT_mission == "NOT_NOW" // player rejected mission and it's not time to re-offer yet
			|| (Math.random() > 0.25 && missionVariables.TCAT_mission == "OFFER") // only 1 in 4 chance of initial offer anyway
			) 
		{
		return;
		}
	
	if(player.ship.dockedStation.isMainStation && system.ID != 138) // initial offering in system main station, as long as it's not Anenveza.
		{	
		switch(missionVariables.TCAT_mission)
			{
			case "OFFER": // make the initial offer
				{
				mission.runScreen({title: "Hide and Sneak", messageKey:"TCAT_initialOffer", choicesKey:"TCAT_choices"}, this.choice);
				break;
				}
			case "REJECTED": // player said no
				{
				missionVariables.TCAT_mission = "NOT_NOW";
				mission.setInstructionsKey(null, "TCAT_missionScript");
				missionVariables.TCAT_reOffer = -0.1;
				mission.runScreen({title: "How unfortunate...", messageKey:"TCAT_rejected"});
				break;
				}
			case "ACCEPTED": // player said yes
				{
				missionVariables.TCAT_mission = "BRIEFING_1";
				mission.setInstructionsKey("TCAT_shortGoToStation", "TCAT_missionScript");
				mission.markSystem(138);
				mission.runScreen({title: "Excellent...", messageKey:"TCAT_accepted"});
				break;
				}
			}
		return;	
		}
	
	if(system.ID == 138 && (missionVariables.TCAT_mission == "INCOMING" || missionVariables.TCAT_mission == "ARMADA") && system.countShipsWithRole("TCAT_mantis") == 0)
		{
		system.addShipsToRoute("TCAT_mantis", 1, 0.9, "sw");
		system.addShipsToRoute("TCAT_squadRaptor", 1, 0.75, "sw");
		}
	
	if(player.ship.dockedStation.hasRole("TCAT_navyStation"))
		{
		switch(missionVariables.TCAT_mission) // offerings once player is at the navy station
			{
			case "BRIEFING_1": // player accepted and went to military station - start sneak briefing
				{
				missionVariables.TCAT_mission = "BRIEFING_2";
				mission.unmarkSystem(138);
				mission.runScreen({title: "Briefing", messageKey:"TCAT_briefing_1"});
				break;
				}
			case "BRIEFING_2": // briefing second screen
				{
				missionVariables.TCAT_mission = "BRIEFING_3";
				mission.runScreen({title: "Know your prey", messageKey:"TCAT_briefing_2", model:"TCAT_sneak"});
				break;
				}
			case "BRIEFING_3": // briefing third screen
				{
				if(!missionVariables.TCAT_missionBlock)
					{
					mission.setInstructionsKey("TCAT_shortHideAndSeek", "TCAT_missionScript");
					missionVariables.TCAT_missionBlock = true;
					missionVariables.TCAT_qDept = true; // make the equipment at the station available
					mission.runScreen({title: "Good luck!", messageKey:"TCAT_briefing_3"});
					}
				break;
				}
			case "SNEAK": // less than 15 sneaks caught
				{
				if(!missionVariables.TCAT_missionBlock)
					{
					missionVariables.TCAT_missionBlock = true;
					mission.runScreen({title: "More Intelligence Needed", messageKey:"TCAT_sneak"});
					}	
				break;
				}
			case "CAUGHT": // 15 sneaks found, next briefing - interrupted by invasion fleet
				{	
				missionVariables.TCAT_mission = "INCOMING";			
				mission.runScreen({title: "A Plan Revealed", messageKey:"TCAT_caught"});
				this.warningSound.play()
				if(system.countShipsWithRole("TCAT_mantis") == 0)
					{
					system.addShipsToRoute("TCAT_mantis", 1, 0.9, "sw");
					system.addShipsToRoute("TCAT_squadRaptor", 1, 0.75, "sw");
					}
				break;
				}	
			case "INCOMING": // 15 sneaks found, next briefing - interrupted by invasion fleet
				{
				if(!missionVariables.TCAT_missionBlock)
					{
					missionVariables.TCAT_missionBlock = true;
					mission.setInstructionsKey("TCAT_shortInvasion", "TCAT_missionScript");	
					missionVariables.TCAT_mission = "ARMADA";			
					player.ship.fuel += 7;
					mission.runScreen({title: "Scramble!", messageKey:"TCAT_invasion"});
					}
				break;
				}	
			case "ARMADA": // if we're back before the invasion is over
				{
				if(!missionVariables.TCAT_missionBlock)
					{	
					player.ship.fuel += 7;
					mission.runScreen({title: "They're still coming!", messageKey:"TCAT_armada"});
					missionVariables.TCAT_missionBlock = true;
					}
				break;
				}
			case "REPELLED": // Invasion fleet repelled, time for the finale
				{
				if(!missionVariables.TCAT_missionBlock)
					{				
					mission.setInstructionsKey("TCAT_shortEndGame", "TCAT_missionScript");
					mission.markSystem(168);
					mission.runScreen({title: "Endgame", messageKey:"TCAT_repelled"});
					missionVariables.TCAT_mission = "HUNT";
					missionVariables.TCAT_missionBlock = true;
					}
				break;
				}
			case "HUNT": // Left for the finale, but back before the kaboom?
				{
				if(!missionVariables.TCAT_missionBlock)
					{	
					mission.runScreen({title: "Back already?", messageKey:"TCAT_hunt"});
					missionVariables.TCAT_missionBlock = true;
					}
				break;
				}
			case "SUCCESS": // Sneak station destroyed - job done
				{
				missionVariables.TCAT_mission = "CASE_CLOSED";
				mission.setInstructionsKey(null, "TCAT_missionScript");
				mission.unmarkSystem(138);
				player.credits += 50000;
				player.bounty = 0;
				mission.runScreen({title: "Mission Accomplished!", messageKey:"TCAT_success"},this.cleanUp);
				break;
				}	
			}	
		}
	}
	
this.choice = function(choice)
	{
	switch(choice)
		{
		case "TCAT_1_ACCEPTED":
			{
			missionVariables.TCAT_mission = "ACCEPTED";
			break;
			}
		case "TCAT_2_REJECTED":
			{
			missionVariables.TCAT_mission = "REJECTED";
			break;
			}
		}
	}
	
this.shipLaunchedFromStation = function()
	{
	switch(missionVariables.TCAT_mission)
		{
		case "BRIEFING_2":
		case "BRIEFING_3":
		case "DISMISSED":
			{ // in case of forced launch during mission screen sequence
			missionVariables.TCAT_mission = "SNEAK";
			break;
			}
		}	
	missionVariables.TCAT_missionBlock = null;	
	}
this.playerWillEnterWitchspace = function()
	{
	if((missionVariables.TCAT_mission == "ARMADA") && galaxyNumber == 3 && system.ID == 138)
		{ // if the player runs away during the armada attack
		missionVariables.TCAT_mission = "CASE_CLOSED";
		missionVariables.TCAT_navalStatus = "DEAD";
		this.cleanUp();
		}
	}
	
this.shipWillLaunchFromStation = function(station) // first launch only
	{
	if(station.isMainStation)
		{
		if(!missionVariables.TCAT_mission) // set the mission up if it's not defined, but so it won't trigger on game start-up
			{missionVariables.TCAT_mission = "OFFER";}	
		this.shipExitedWitchspace();
		
		if(galaxyNumber == 2 && system.ID == 138 && missionVariables.TCAT_navalStatus == "ALIVE" && system.countShipsWithRole("TCAT_navyStation") == 0)
			{
			system.addShipsToRoute("TCAT_navyStation", 1, 0.5, "sw");
			}
		
		if(galaxyNumber == 2 && system.ID == 168 && missionVariables.TCAT_mission == "HUNT" && system.countShipsWithRole("TCAT_bugStation") == 0)
			{
			this.setUpBugStation(225000, 100000, -4000000);
			return;
			}	
		
		delete this.shipWillLaunchFromStation;
		}
	}
	
this.playerEnteredNewGalaxy	= function(galaxyNumber)
	{ // if we jump galaxies whilst mission is running or after rejection
	if(galaxyNumber == 3 && missionVariables.TCAT_mission != "OFFER")
		{
		missionVariables.TCAT_mission = "CASE_CLOSED";
		missionVariables.TCAT_navalStatus = "DEAD";
		this.cleanUp();
		}
	}
this.shipWillExitWitchspace = function()
	{
	if(system.isInterstellarSpace || galaxyNumber != 2)
		{
		return;
		}
	if(system.ID == 138 && missionVariables.TCAT_navalStatus == "ALIVE" && system.countShipsWithRole("TCAT_navyStation") == 0)
		{
		system.addShipsToRoute("TCAT_navyStation", 1, 0.5, "sw");
		return;
		}
		
	if(system.ID == 168 && missionVariables.TCAT_mission == "HUNT" && system.countShipsWithRole("TCAT_bugStation") == 0)
		{
		this.setUpBugStation(22500, 10000, -400000);
		return;
		}	
	
	this.ambush = (System.infoForSystem(galaxyNumber, system.ID).routeToSystem(System.infoForSystem(galaxyNumber, 168)).route.length - 1) / 6;
	if(missionVariables.TCAT_mission == "HUNT" && (Math.random() > this.ambush))
		{
		this.ambushDelay = Math.ceil(Math.random() * 6) + 4;
		this.ambushTimer = new Timer(this, function() {this.ambushPlayer(this.ambush);}, this.ambushDelay);
		}
	}
	
this.ambushPlayer = function(ambush)
	{
	system.legacy_addShipsWithinRadius("TCAT_warship", (4-Math.ceil(ambush * 3)), "abs", player.ship.position, 15000);
	player.consoleMessage("ALERT - Alien ambush in progress!", 6);
	}
	
this.shipDockedWithStation = function(station)
	{
	if(station.hasRole("TCAT_navyStation"))
		{
		missionVariables.TCAT_navalEquipment = true;
		}
	else
		{
		missionVariables.TCAT_navalEquipment = false;
		}
	}
	
this.guiScreenChanged = function()
	{
	if(!player.ship.docked || !player.ship.dockedStation.hasRole("TCAT_navyStation")) // if we're not at the navy station
		{ return; }
	// replace marketplace screen with exam offering mission screen
	if(guiScreen == "GUI_SCREEN_MARKET")
		{
		mission.runScreen({title: "Do we look like Tescoos?", messageKey: "TCAT_noTrading"});
		}
	}	
	
this.shipExitedWitchspace = function()
	{	
	if(missionVariables.TCAT_mission == "NOT_NOW")
		{
		missionVariables.TCAT_reOffer += 0.01; // incremental 1% added chance per jump of re-offering the mission
		if(Math.random() < missionVariables.TCAT_reOffer)
			{
			missionVariables.TCAT_mission = "OFFER";
			}
		}
		
	if(system.isInterstellarSpace || system.sun.isGoingNova || system.sun.hasGoneNova || missionVariables.TCAT_mission != "SNEAK")
		{ // stop the addition if interstellar space, system is going Nova, or if this part of the mission is not running.
		return;
		}
		
	this.sneakChance = 0.5 - ((system.techLevel + system.government) / 100);
	if(Math.random() < this.sneakChance)
		{
		system.addShipsToRoute("TCAT_sneak", 1);
		this.delayedMessage("Sneak detected - transferring co-ordinates to ASC", 6, 5);
		}
	}
this.delayedMessage = function(messageString, duration, delay)
	{
	this.messageTimer = new Timer(this, function() {this.displayMessage(messageString, duration);}, delay);
	}
	
this.displayMessage = function(messageString, duration)
	{
	if(messageString)
		{
		if(!duration || duration < 1)
			{
			duration = 6;
			}
		player.consoleMessage(messageString, duration);
		}
	}
	
this.setUpBugStation = function(xPos,yPos,zPos)
	{
	if(!xPos) {xPos = 0;};
	if(!yPos) {yPos = 0;};
	if(!zPos) {zPos = 0;};
	
	system.legacy_addShipsAtPrecisely("TCAT_bugStation", 1, "abs", [xPos, yPos, zPos]);
	system.legacy_addShipsAtPrecisely("TCAT_bugBase", 1, "abs", [xPos-2000, yPos, zPos+1000]);
	system.legacy_addShipsAtPrecisely("TCAT_bugBase", 1, "abs", [xPos+2000, yPos, zPos+1000]);
	system.legacy_addShipsAtPrecisely("TCAT_bugBase", 1, "abs", [xPos+2000, yPos, zPos-24000]);
	system.legacy_addShipsAtPrecisely("TCAT_bugBase", 1, "abs", [xPos-2000, yPos, zPos-24000]);
	system.legacy_addShipsWithinRadius("TCAT_warship", 2, "abs", [xPos, yPos, zPos-24000], 10000);
	
	system.legacy_addShipsAtPrecisely("TCAT_jumpGate", 1, "abs", [xPos+2000, yPos+5000, zPos-48000]); // far jump gate
	system.legacy_addShipsAtPrecisely("TCAT_jumpGate", 1, "abs", [2000, 2000, -100000]); // near jump gate
	
	system.legacy_addShipsAtPrecisely("TCAT_navyBeacon", 1, "abs", [2500, 2500, -80000]); // jump gate marker buoy
	system.legacy_addShipsAtPrecisely("TCAT_bugBase", 1, "abs", [4000, 4000, -98000]); // guard base near jump gate
	system.legacy_addShipsAtPrecisely("TCAT_raptor", 1, "abs", [300, 300, -15000]); // raptor flying about
	
	this.gateRockCount = Math.ceil(Math.random() * 26) + 14; // Between 15 and 40 normal asteroids near the gate
	this.rockCount = Math.ceil(Math.random() * 26) + 94; // Between 95 and 120 normal asteroids
	this.gunCount = Math.floor(this.rockCount / 10); // Between 9 and 12 asteroids with hidden gun emplacements
	this.warshipCount = Math.ceil(Math.random() * 6) + 1; // Between 1 and 7 warships
	system.legacy_addShipsWithinRadius("asteroid", this.gateRockCount, "abs", [xPos, yPos, zPos-24000], 20000);
	
	for(var asteroidCounter = 0;asteroidCounter < this.rockCount;asteroidCounter++)
		{
		this.spawnOffset = new Vector3D.randomDirection().multiply((Math.random() * 44000) + 6000);
		this.newPosition = this.spawnOffset.add([xPos, yPos, zPos]);
		system.legacy_addShipsAtPrecisely("asteroid", 1, "abs", this.newPosition);
		}
	for(var towerCounter = 0;towerCounter < this.gunCount;towerCounter++)
		{
		this.spawnOffset = new Vector3D.randomDirection().multiply((Math.random() * 38000) + 12000);
		this.newPosition = this.spawnOffset.add([xPos, yPos, zPos]);
		system.legacy_addShipsAtPrecisely("TCAT_gunTower", 1, "abs", this.newPosition);
		}
		
	for(var shipCounter = 0;shipCounter < this.warshipCount;shipCounter++)
		{
		this.spawnOffset = new Vector3D.randomDirection().multiply((Math.random() * 38000) + 12000);
		this.newPosition = this.spawnOffset.add([xPos, yPos, zPos]);
		system.legacy_addShipsAtPrecisely("TCAT_warship", 1, "abs", this.newPosition);
		}	
	
	this.spawnOffset = new Vector3D.randomDirection().multiply((Math.random() * 34000) + 6000);
	this.newPosition = this.spawnOffset.add([xPos, yPos, zPos]);
	system.legacy_addShipsAtPrecisely("TCAT_controller5", 1, "abs", this.newPosition);
	};
 | 
                
                    | Scripts/TCAT_nailBomb.js | this.name           = "TCAT_nailBomb";
this.author         = "Thargoid";
this.copyright		= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description    = "Ship script for the Thargoid 'Nailbomb' missile";
this.version        = "1.0";
this.spawnNails = function() 
	{ 
	var loopCounter = 0 ; // reset the counter
	for(loopCounter = 0; loopCounter < 9; loopCounter++)
		{
		var Nail = this.ship.spawnOne("TCAT_nail");
		this.xDistance = ((Math.random() * 4) - 2);
		this.yDistance = ((Math.random() * 4) - 2);
		this.zDistance = ((Math.random() * 4) - 2);
		Nail.position = this.ship.position.add([this.xDistance, this.yDistance, this.zDistance]);
		}
	}; 
 | 
                
                    | Scripts/TCAT_navalStation.js | this.name					= "TCAT_navalStation";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Naval station ship script.";
this.version				= "1.0";
this.shipSpawned = function()
	{
	if(this.rechargeTimer)
		{
		this.rechargeTimer.start();
		}
	else
		{
		this.rechargeTimer = new Timer(this, this.rechargeStation, 0, 1);
		}
	this.squadsLaunched = 0;
	}
this.rechargeStation = function()
	{
	if(missionVariables.TCAT_mission != "ARMADA" && this.ship.energy < this.ship.maxEnergy)
		{
		this.ship.energy += 98;
		}
		
	if(missionVariables.TCAT_mission == "ARMADA" && this.squadsLaunched < 2 && Math.random() > ((this.ship.energy / this.ship.maxEnergy) + 0.4) )
		{
		this.ship.AIState = "LAUNCH_SQUAD";
		this.squadsLaunched++;
		}
		
	if(this.ship.missiles.length < this.ship.missileCapacity && Math.random() < ((this.ship.missileCapacity - this.ship.missiles.length)/(10*this.ship.missileCapacity)))
		{
		this.ship.awardEquipment("EQ_TCAT_NPC_TRIDENT_MISSILE");
		}
	}
this.missileCheck = function()
	{
	if(this.ship.missiles.length > 0 && this.ship.target && Math.random() < ((this.ship.missileCapacity - this.ship.missiles.length)/(this.ship.missileCapacity)))
		{
		this.ship.fireMissile();
		}
	}
	
this.stationLaunchedShip = function(ship)
	{
	if(ship.isPlayer && missionVariables.TCAT_mission == "ARMADA")
		{
		this.ship.AIState = "LAUNCH_SQUAD";
		delete this.stationLaunchedShip;
		}
	}
	
this.shipDied = function()
	{	
	missionVariables.TCAT_mission = "CASE_CLOSED";
	missionVariables.TCAT_navalStation = "DEAD";
	
	this.fuelCount = 35 + Math.ceil(Math.random() * 10);
	this.largeCount = 17 + Math.ceil(Math.random() * 3);
	this.smallCount = 35 + Math.ceil(Math.random() * 5);
	system.legacy_addShipsWithinRadius("TCAT_burningFuel", this.fuelCount, "abs", this.ship.position, 10);
	system.legacy_addShipsWithinRadius("TCAT_smallWreckage", this.smallCount, "abs", this.ship.position, 5);
	system.legacy_addShipsWithinRadius("TCAT_largeWreckage", this.largeCount, "abs", this.ship.position, 5);
	this.playerWillEnterWitchspace();	
	}
this.playerWillEnterWitchspace = function()
	{
	if(this.rechargeTimer && this.rechargeTimer.isRunning)
		{
		this.rechargeTimer.stop();
		}
	} | 
                
                    | Scripts/TCAT_raptor.js | this.name					= "TCAT_raptor";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Raptor class ship script.";
this.version				= "1.0";
this.shipSpawned = function()
	{
	if(!worldScripts["TCAT_masterScript"])
		{
		log("TCAT Master Script Missing!!!!!!!!");
		}
	if(this.ship.primaryRole == "TCAT_squadRaptor" || this.ship.primaryRole == "thargoid")
		{
		this.ship.primaryRole = "TCAT_raptor";
		}
	this.shipName = expandDescription('[TCAT_raptorName]');
	this.ship.displayName = "Raptor - ASF " + this.shipName;
	if(this.ship.hasRole("TCAT_squadRaptor"))
		{
		this.ship.displayName = "Command  " + this.ship.displayName;
		}
	this.jammerCount = 1 + Math.ceil(Math.random() * 3);
	this.clawCount = Math.floor(Math.random() * 3);
	}
this.dropJammer = function()
	{
	if(this.jammerCount > 0 && worldScripts["TCAT_masterScript"] && worldScripts["TCAT_masterScript"].jammerAllowed())
		{
		this.ship.ejectItem("TCAT_jammer");
		this.jammerCount -= 1;
		}
	}
this.dropClaw = function()
	{
	if(this.clawCount > 0 && worldScripts["TCAT_masterScript"] && worldScripts["TCAT_masterScript"].clawAllowed())
		{
		this.ship.ejectItem("TCAT_claw");
		this.clawCount -= 1;
		}
	}
this.locateThargoids = function()
	{
	this.alienArray = system.entitiesWithScanClass("CLASS_THARGOID");
	if(this.alienArray.length == 0)
		{
		this.ship.reactToAIMessage("NOTHING_FOUND");	
		}
	else
		{
		this.ship.target = this.alienArray[0];
		this.ship.reactToAIMessage("TARGET_FOUND");
		}
	}
	
this.scanForAliens = function()
	{
	function allShips(entity) {return entity.isShip}; 
	function thargoidMotherships(entity) {return entity.hasRole("thargoid-mothership") && !entity.hasRole("TCAT_jumpGate")};
	function jammerMines(entity) {return entity.hasRole("TCAT_jammer")}; 
	function clawMines(entity) {return entity.hasRole("TCAT_claw")}; 
	function activeThargons(entity) {return entity.scanClass == "CLASS_THARGOID" && (entity.hasRole("thargon") || entity.hasRole("tharglet"))}; 
	var anyShips = system.filteredEntities(this, allShips, this.ship, 25600);
	this.thargoidCount = anyShips.filter(thargoidMotherships).length; 
	this.thargonCount = anyShips.filter(activeThargons).length;
	this.jammerActive = anyShips.filter(jammerMines).length;
	this.clawActive = anyShips.filter(clawMines).length;
	if(this.jammerCount > 0 && this.jammerActive == 0 && this.thargonCount > 0 && Math.random() > (0.1 + (this.thargonCount * 0.15))) 
		{ 
		this.dropJammer(); 
		} 
		
	if(this.clawCount > 0 && this.clawActive == 0 && this.thargoidCount > 0 && Math.random() > (0.1 + (this.thargoidCount * 0.05))) 
		{ 
		this.dropClaw(); 
		} 	
	if(this.thargoidCount > 0 && (!this.ship.target || (this.ship.target && !this.ship.target.scanClass == "CLASS_THARGOID")))
		{
		this.ship.target = anyShips.filter(thargoidMotherships)[0];
		this.ship.AIState = "ATTACK_SHIP";
		}
} | 
                
                    | Scripts/TCAT_sneak.js | this.name           = "TCAT_sneak";
this.author         = "Thargoid";
this.copyright		= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description    = "Ship script for Thargoid sneak courier.";
this.version        = "1.0";
this.shipWasScooped = function(scooper)
	{
	if (scooper.isPlayer)
		{
		missionVariables.TCAT_sneakCount += 1;
		player.consoleMessage("Thargoid courier captured.", 6);
		this.enoughData = (missionVariables.TCAT_sneakCount / 10) - 0.5; // chance of having enough data, from -0.5 to 1
		if(Math.random() < this.enoughData) // if we've caught enough sneaks now
			{
			player.consoleMessage("We have sufficient data - please return to the Naval station.", 10);
			missionVariables.TCAT_mission = "CAUGHT";
			mission.setInstructionsKey("TCAT_shortBackToBase", "TCAT_missionScript"); //  mission note to go back to base
			}
		else
			{
			mission.setInstructionsKey("TCAT_shortHideAndSeek", "TCAT_missionScript"); //  update the F5-F5 screen count
			}
		}
	}
	
this.shipExitedWormhole = function()
	{
	if(this.ship.escorts.length > 0)
		{
		for(var escortCounter = this.ship.escorts.length-1;escortCounter >= 0;escortCounter--)
			{
			this.ship.escorts[escortCounter].remove();
			}
		}
	this.ship.remove();
	}
	
this.shipBeingAttacked = function()
	{
	this.shutDownChance = this.ship.energy / this.ship.maxEnergy;
	if(Math.random() > this.shutDownChance)
		{
		this.ship.AIState = "SHUTDOWN";
		}
	} | 
                
                    | Scripts/TCAT_thargoid.js | this.name           = "TCAT_thargoid";
this.author         = "Thargoid";
this.copyright		= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description    = "Ship script for general Thargoid vessels";
this.version        = "1.0";
this.spawnedAsEscort = function(mother)
	{
	if(mother.isThargoid)
		{
		this.ship.switchAI("TCAT_escortAI.plist");
		}
	}
this.findNonThargoid = function() 
	{ 
	if(this.ship.target)
		{
		this.ship.AIState = "ATTACK_SHIP";
		return;
		}
	
	function targetShips(entity) {return entity.isShip && !entity.docked && entity.scanClass != "CLASS_ROCK" && entity.scanClass != "CLASS_CARGO" && !entity.isThargoid && !entity.isCloaked && entity.primaryRole != "TCAT_jumpGate"}; 
	this.nearArray = system.filteredEntities(this, targetShips, this.ship, 25600); 
	this.farArray = system.filteredEntities(this, targetShips, this.ship, 60000); 
 
	if(this.nearArray.length > 0)
		{
		this.targetNumber = Math.floor(Math.random() * this.nearArray.length);
		if(this.targetNumber > this.nearArray.length)
			{
			log(this.name + " script error, target number too high - please report");
			this.targetNumber = 0;
			}
		this.ship.target = this.nearArray[this.targetNumber];
        this.ship.reactToAIMessage("TARGET_FOUND");
		}
	else
		{
		if(this.farArray.length > 0)
			{
			this.ship.target = this.farArray[0]; // set to the closest target off-scanner but in range
			this.ship.reactToAIMessage("INTERCEPT_TARGET");
			}
		else
			{
			this.ship.reactToAIMessage("FRUSTRATED")
			}
		}
	}; 
this.validateTarget = function()
	{
	if(this.ship.target && this.ship.target.scanClass != "CLASS_THARGOID" && !this.ship.target.hasRole("thargoid") && !this.ship.target.hasRole("thargon"))
		{
		this.ship.reactToAIMessage("TARGET_VALID");
		}
	else
		{
		this.ship.reactToAIMessage("TARGET_INVALID");
		}
	}
	
this.findStation = function()
	{
	this.stationArray = system.shipsWithPrimaryRole("TCAT_navyStation");
	if(this.stationArray.length == 0)
		{
		this.ship.reactToAIMessage("NOTHING_FOUND");	
		}
	else
		{
		this.ship.target = this.stationArray[0];
		this.ship.reactToAIMessage("STATION_FOUND");
		}
	}
	
this.shipDied = function ()
	{
	this.ship.commsMessage(expandDescription("[thargoid_curses]"));
	};
 | 
                
                    | Scripts/TCAT_trident.js | this.name           = "TCAT_trident";
this.author         = "Thargoid";
this.copyright		= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description    = "Ship script for the trident missile";
this.version        = "1.0";
this.spawnTines = function() 
	{ 
	var loopCounter = 0 ; // reset the counter
	for(loopCounter = 0; loopCounter < 9; loopCounter++)
		{
		var Tine = this.ship.spawnOne("TCAT_tridentTine");
		this.xDistance = ((Math.random() * 40) - 20);
		this.yDistance = ((Math.random() * 40) - 20);
		Tine.position = this.ship.position.add([this.xDistance, this.yDistance, 0]);
		Tine.orientation = this.ship.orientation; 
		if(this.ship.target)
			{
			Tine.target = this.ship.target;
			}
		}
	} 
 |