Back to Index Page generated: May 8, 2024, 6:16:03 AM

Expansion To Catch a Thargoid

Content

Manifest

from Expansion Manager's OXP list from Expansion Manifest
Description Mission set in Galaxy 3 for Dangerous or above rank Commanders. Follow-up to Thargoid Plans. Mission set in Galaxy 3 for Dangerous or above rank Commanders. Follow-up to Thargoid Plans.
Identifier oolite.oxp.Thargoid.TCAT oolite.oxp.Thargoid.TCAT
Title To Catch a Thargoid To Catch a Thargoid
Category Missions Missions
Author Thargoid Thargoid
Version 1.11 1.11
Tags equipment, ships, mission equipment, ships, mission
Required Oolite Version
Maximum Oolite Version
Required Expansions
Optional Expansions
Conflict Expansions
Information URL http://wiki.alioth.net/index.php/TCAT_OXP n/a
Download URL https://wiki.alioth.net/img_auth.php/3/3a/TCAT_1.11.oxz n/a
License Creative Commons Attribution - Non-Commercial - Share Alike 3.0 license with clauses - see readme file Creative Commons Attribution - Non-Commercial - Share Alike 3.0 license with clauses - see readme file
File Size n/a
Upload date 1610873275

Documentation

Also read http://wiki.alioth.net/index.php/To%20Catch%20a%20Thargoid

TCAT v1.11 Readme & License.txt

"To Catch a Thargoid" OXP by Thargoid.

A new mission based in Galaxy 3, as a follow-on and sequel to Thargoid Plans. The bugs are back and looking for revenge.

In addition to the mission, this OXP adds several new alien ships plus the response from the Galactic Navy Intelligence to their increased threat - the fearsome Raptor assault strike frigates. 

The Raptors plus a selection of the new Thargoid craft are added generally to the game mix, regardless of galaxy or mission status (if the mission is completed the relevant script parts are automatically removed and do not slow the game). Some may make things a little tougher, but others may come to your rescue!


Requirements -

This OXP is scripted for Oolite v1.75 or later, and will not run on any earlier versions of the game. 

The mission is set in Galaxy 3, and requires the player to have at least 1500 kills to their name and to have completed the "Thargoid Plans" trunk mission. If these conditions are met then the mission may randomly be offered on docking, after which time detailed instructions are provided in mission screens with pointers via the manifest (F5-F5) screen.

--------------------------------------------------------------

License:

This OXP is released under the Creative Commons Attribution - Non-Commercial - Share Alike 3.0 license with the following clauses:

* Whilst you are free (and encouraged) to re-use any of the scripting, models or texturing in this OXP, the usage must be distinct from that within this OXP. Unique identifiers such as (but not limited to) unique shipdata.plist entity keys, mission variables, script names (this.name), equipment identity strings (EQ_), description list arrays and entity roles must not be re-used without prior agreement. Basically if it's unique or would identify or overwrite anything in the original OXP, then you may not re-use it (for obvious compatibility reasons).
* rebundling of this OXP within another distribution is permitted as long as it is unchanged. The following derivates however are permitted and except from the above:
	* the conversion of files between XML and openStep.
	* the merging of files with other files of the same type from other OXPs.
* The license information (either as this file or merged into a larger one) must be included in the OXP.
* Even though it is not compulsory, if you are re-using any sizable or recognisable piece of this OXP, please let me know :)

--------------------------------------------------------------

Instructions:

Unzip the file, and then move the folder "TCAT 1.11.oxp" to the AddOns directory of your Oolite installation. Then start the game up and the ships should be added. 

Version history:

18/05/2010 - Version 1.00, Initial (semi-beta) release, prior to 1.74 test release.
15/06/2010 - Version 1.01, small bugfix (don't try and use the same mission variable for two different purposes!)
16/06/2010 - Version 1.02, two more small fixes (self-correction of 1.00 bug and adding Navy station if save-game in same system).
19/06/2010 - Version 1.03, a couple more small tweaks, plus made the final stage of the mission tougher (no more cloaking device cheating!).
14/07/2010 - Version 1.04, a minor tweak to shipdata.plist to make Raptor turrets actually do some damage!
23/12/2010 - Version 1.05, minor code tweak to ensure Mantis appears even if player goes and saves/restarts during invasion.
13/02/2011 - Version 1.06, removal of upper limit, to allow running with 1.75
19/02/2011 - Version 1.10, 1.75 release. ECM Jammer now equipment-key controlled, and AI glitch fixed in general Thargoid AI.
15/04/2012 - Version 1.11, update of thargoidAI due to recursion frustration issue.

--------------------------------------------------------------

Acknowledgements:

With thanks to another_commander, Ahruman, Eric Walch, PAGroove and a whole host of others for various inputs and suggestions during the long period that this OXP has been brewing.

And special thanks to UK_Eliter for last-minute playtesting and suggestions for 1.03 improvements

Equipment

Name Visible Cost [deci-credits] Tech-Level
Dump all pylon mounted weapons yes 0 1+
Disabled ECM System yes 1 100+
Quirium Cascade Mine yes 25000 1+
ECM Hardened Missile yes 3500 1+
Naval Energy Unit yes 400000 1+
Military Shield Enhancement yes 400000 1+
E.C.M. Jammer yes 50000 1+
Energy Unit Upgrade yes 200000 1+
Energy Unit Upgrade yes 200000 1+
Thargon Jammer Mine yes 0 1+
Quirium Cascade Mine yes 0 1+
ECM Hardened Missile yes 0 1+
Thargon Jammer Mine yes 10000 1+
Fleet of ships for the Mantis Dreadnaught yes 0 101+
Naval Energy Unit Repair yes 50000 1+
NPC Thargon Drone yes 0 101+
NPC Nailbomb Missile yes 0 101+
NPC Trident Missile yes 0 101+

Ships

Name
Thargoid Interceptor
Thargoid Pathfinder
Station Blast Shield
TCAT_blueCircuitRaptor
Thargoid Guard Outpost
Thargoid Spy Station
Burning witchspace fuel
TCAT_circuitRaptor
Raptor's Claw Mine
Thargoid Controller
TCAT_controller10
TCAT_controller5
Thargoid Defence Drone
TCAT_darkRaptor
Raptor Defense Interceptor
Thargoid Defence Drone
TCAT_dummy_mine
TCAT_dummy_missile
Kamikaze Orbiter
TCAT_goldRaptor
TCAT_greenCircuitRaptor
TCAT_grooveRaptor
Thargoid Guardian
Thargoid Guardian
Asteroid
Thargint Robot Interceptor
Thargoid Interceptor
Thargoid Interceptor
Thargon Jamming Mine
Thargoid Jumpgate
Wreckage
TCAT_lightRaptor
Thargoid Mantis Dreadnaught
Mantis Sub-Ent Front Lower Left
Mantis Sub-Ent Front Lower Right
Mantis Sub-Ent Front Upper Left
Mantis Sub-Ent Front Upper Right
Mantis Sub-Ent Rear Lower Left
Mantis Sub-Ent Rear Lower Right
Mantis Sub-Ent Rear Upper Left
Mantis Sub-Ent Rear Upper Right
Explosive Fragment
Thargoid Projectile
Naval Marker Beacon
Navy Intelligence Station
Thargoid Orbiter
Raptor
TCAT_redCircuitRaptor
Mantis Laser Mount
Turret Mount
Wreckage
Thargoid Sneak Courier
TCAT_squadBlueCircuitRaptor
TCAT_squadCircuitRaptor
TCAT_squadDarkRaptor
TCAT_squadDrone
TCAT_squadGoldRaptor
TCAT_squadGreenCircuitRaptor
TCAT_squadGrooveRaptor
TCAT_squadLightRaptor
TCAT_squadRaptor
TCAT_squadRedCircuitRaptor
Laser Mount
Turret Mount
Wreckage
Raptor's Talon
Trident Missile
Trident Tine
Thargoid Pathfinder

Models

This expansion declares no models.

Scripts

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;
			}
		}
	}