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

Expansion Armoury

Content

Manifest

from Expansion Manager's OXP list from Expansion Manifest
Description A meta-OXP featuring Anti-Missile System, Captured Thargons, Drones, Missile Rack and Probe OXPs plus some new mines, missiles, bombs and the Missile Machine. A meta-OXP featuring Anti-Missile System, Captured Thargons, Drones, Missile Rack and Probe OXPs plus some new mines, missiles, bombs and the Missile Machine.
Identifier oolite.oxp.Thargoid.Armoury oolite.oxp.Thargoid.Armoury
Title Armoury Armoury
Category Weapons Weapons
Author Thargoid Thargoid
Version 1.22 1.22
Tags equipment, weapons equipment, weapons
Required Oolite Version
Maximum Oolite Version
Required Expansions
Optional Expansions
Conflict Expansions
Information URL http://wiki.alioth.net/index.php/Armoury_OXP n/a
Download URL https://wiki.alioth.net/img_auth.php/5/58/Armoury_1.22.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 1703041568

Documentation

Also read http://wiki.alioth.net/index.php/Armoury

Readme & License.txt

Armoury OXP by Thargoid.

This is a meta-OXP combining a few of my previous weapons and defense equipment OXPS. It supersedes and replaces the following:

AMS.oxp
Captured Thargons.oxp
Drones.oxp
Missile Rack.oxp
Probe.oxp

Any existing installations of these should be removed, as they are completely incorporated in this OXP and may clash with updates.

This OXP requires Oolite v1.82 or higher, and will not run correctly on previous versions.

-------------------------------------------------------------------------------------------------------------------------------------
Anti-Missile System (AMS):

The Weapons Department of the Aquarian Shipbuilding Corporation would like to present an extension to the recently introduced Missile Analyser. This adds a rack of six anti-missile darts, which when the analyser detects a non-standard missile targeted at the ship, automatically launches to intercept and attempt to destroy them. This is designed to supplement standard ECM systems, which are not always effective on such exotic weaponry.

Once the rack of darts is exhausted, the system will notify the ships pilot before automatically shutting down. The darts can be purchased from any suitable tech level planet, and are sold bundled to replenish the launcher to full capacity.For safety the system automatically goes offline when the ship docks, and comes back online at launch, when it notifies the pilot of its remaining dart count. It can also be deactivated (and reactivated) using the equipment priming keys (n and shift-N by default) whilst in-flight. Launching from a station automatically reactivates the equipment.

Note that the Missile Analyser (by Eric Walch) must be installed on your system and the analyser fitted to the ship for the AMS to operate. Also as the dart must physically intercept the missile, the system is not 100% effective (especially at point-blank range), and even occasionally when interception does occur the missile can survive intact enough to still pose a threat. In this latter case the system will warn the pilot.

-------------------------------------------------------------------------------------------------------------------------------------
Captured Thargons:

Following some recent advances in drone technology and analysis of alien artifacts, the Aquarian Shipbuilding Corporation are proud to be able to offer their latest equipment in the fight against the Thargoid threat. Having taken some captured Thargon robot fighters and reprogrammed them, these can now be slaved to any ship and used in combat in much the same way that the aliens do.

Control is achieved via a special controller, which mounts onto an available missile pylon on the ship. When this is triggered (as with a normal missile or mine), the Thargon fighters are released. They search for alien or hostile craft in the vicinity, and ruthlessly attack any that they find. After combat, the fighters will attempt to return to the vicinity of the ship, where they can be scooped using the crafts fuel scoops for repair and reuse. 

Note that this modification should only be fitted once per craft, and requires fuel scoops to be present. It also only works with the specially reprogrammed Thargon fighters. Any other hostile alien ones cannot be manipulated by this technology, and if scooped will class as "alien items" in the normal way.

The system is available from any tech level 12+ planet, for 5500 credits. Lost or destroyed Thargons can also be replaced new at a cost of 1000 credits each or if you have Thargons scooped during your travels, these can also be reprogrammed for 500 credits each at tech 12+ stations.

-------------------------------------------------------------------------------------------------------------------------------------
Drones:

The Weapons Department of the Aquarian Shipbuilding Corporation would like to present to you their latest range of products. After a recent encounter near to Aqualine with a Thargoid battle-fleet, a small number of Tharglet robot fighters were captured intact and sent for analysis. This has resulted in the production of three types of fighting drone which are now available for to the market to aid commanders in their daily life. 

All versions are "fire and forget" in that they are self-targeting from the ships scanning computers, although if they are not destroyed in action they can be reclaimed via fuel scooping and sometimes re-used, so don't forget them too much!

* Combat drone - A specialised defensive drone. It carries a beam laser as standard, and links in with the targeting scanner to attack ships which are registered as hostile to the launcher.
* Kamikaze drone - A slightly more aggressive variant on the combat drone, these feature a light yield warhead which the onboard battle computer can elect to use against its target. This of course does destroy the drone in the process.
* Anti-Thargoid drone - Especially commissioned by Galcop, these drones have special military technology built into them, giving them a harder punch (by a military-grade laser) and faster and more maneuverable flight through upgraded engines. Their  are battle computers are specifically tuned and set-up to target Thargoid vessels only. 

-------------------------------------------------------------------------------------------------------------------------------------
Missile Racks:

The Ships Weapons Department of the Aquarian Shipbuilding Corporation would like to present their latest item, the missile rack. With the increase in frequency of combat with the Thargoids and the rise of pirate activity, many commanders are finding their old trusty ships less capable due to the limited amount of missiles that can be carried.

To help with this shortcoming, each rack mounts on a weapons pylon in place of a standard missile, and contains three miniature missiles which can be targeted and fires like normal ones. Due to their size constraints however these miniaturised missiles are slightly slower and of lower yield than conventional missiles, and have a reduced range due to carrying less fuel. They are still however capable of dealing a sizable kick to hostile craft. 

The racks come in four varieties, with standard missiles, more advanced ECM hardened ones and anti-missile interceptor missiles. The racks themselves cannot be refilled and are automatically ejected after they are empty, and for safety they self-destruct at a safe range from the ship.

* Missile Rack (Std) - Pylon mounted rack containing three standard miniaturised missiles.
* Missile Rack (Hard) - Pylon mounted rack containing three ECM hardened miniaturised missiles.
* Interceptor Missile Rack - Pylon mounted rack containing three FdL interceptor anti-missile missiles.
* Stun Missile Rack - Pylon mounted rack containing three stun anti-ship missiles.

Racks are indicated by a special icon on the ship's HUD, which changes as the rack empties to signify the number of missiles left on it.

-------------------------------------------------------------------------------------------------------------------------------------
Probe Missile:

The Weapons Department of the Aquarian Shipbuilding Corporation would like to present to you their latest product, the Probe Missile. This is a modification on a standard ships missile, with the warhead removed and replaced by a long-range fuel tank and communications system. This allows it to fly to a range of 100km, performing a scan at half-way and on arrival, before returning to the ship for fuel-scoop pick-up. 

The missile has no offensive capabilities, but is also unaffected by ECM. It is however completely defenseless, as it is now essentially just a utility transportation vehicle for the scanner.

-------------------------------------------------------------------------------------------------------------------------------------
Missiles

*Tracker Missile:

A special interceptor missile which does no damage, but instead deploys a cloud of nanoscale tracking beacons. These can be detected via an advanced space compass (which must be fitted for the missile to be available for purchase) anywhere in the system.

Available from tech level 6 at a cost of 500cr each.

*Displacer Missile:

A new experimental piece of equipment which uses synchronised quirium resonance to induce a short-range witchspace jump in the target vessel, moving it a random distance away from its current location (but leaving it otherwise undamaged unless it hits something on arrival). Is only effective against ships which are powered and have witchspace fuel.

Available from tech level 4 at a cost of 400cr each.

*Kicker Missile:

A non-lethal missile which is designed to give a high-velocity kick to any ship which it detonates against. Depending on the mass and thrust of the target, this can be enough to send it out of scanner range.

Available from tech level 8 at a cost of 800cr each.

*Cutpurse Missile:

An illegal hack based on a standard missile which carries no explosive warhead. Instead it has a small limpet mine transmitter, which attaches onto the target ship and attempts to interface with the ships system (which can take a few seconds). If successful it triggers the ships emergency cargo dumping routine.

This missile is illegal to sell or purchase in any Galcop station and its use is seen as an act of piracy by police vessels. 

Available from tech level 8 at a cost of 800cr each.

*Field Missile:

An ECM-hardened missile which scatters several mini-mines upon detonation. These proximity weapons will detonate on close contact, causing additional damage to any craft in the vicinity as well as any survivors of the original missile strike. 

Available from tech level 6 at a cost of 1000cr each.


-------------------------------------------------------------------------------------------------------------------------------------
Missile Machine:

A special system mounted in the ships cargo hold, with an auto-mount feed system to one of the ships weapon pylons. When fed 1 ton of radioactives and 1 ton of alloys from your cargo hold plus a little ships fuel, in a short time the machine can produce up to three standard-grade missiles. The optional addition of 1 ton of computers (if they are available the machine will use them) allows better and more complex control circuitry making the missiles hardened against ECM.

Production can go on as long as there is suitable cargo in the ships hold and pylons available to accept the missiles (if there are less than three available less missiles will be produced). The only time production is not possible is when docked for maintenance crew safety.

-------------------------------------------------------------------------------------------------------------------------------------
Bombs and Mines:

After the discussions concerning the trunk energy bomb (and it's impending removal), I thought it may be worth playing around with a few new bombs which could be used as replacements. All are pylon mounted due to the current limitations on keyboard triggering of OXP equipment, and all can also be carried by NPCs (currently with low probability and Uni bomb set not to be an instant-killer for the player ship).

*Buzz bomb - the base version of the weaponry. Consists of a special energy emitter mounted on the pylon and linked to the ships energy banks. On triggering it sets off an EMP (electromagnetic pulse) through the ships shield emitters. The energy drained from the ships energy banks is amplified and transmitted, causing damage to any powered ship in scanner range (cargo pods and asteroids are unaffected). Damage inflicted depends on proximity to the bomb and the energy supplied to the emitter (which is destroyed in the process of firing). Fail-safes are included to prevent the bomb causing damage to the carrier ship itself, and also to ensure that at least one full energy bank is left after detonation (the bomb has a small built-in energy reserve of its own which is added to the energy drawn from the ship if available). Due to technological differences these bombs have been reported to be less effective against alien technology.

*Bug bomb - a variant on the buzz bomb, tuned especially to be damaging to alien technology, at the expense of some potency against normal targets.

*Stun bomb - another variant, tuned to affect ships systems, especially flight control and pilot interfacing. In practice tends to leave the target ship helplessly spinning in space for a short period of time. When used against a player craft it causes the ship to tumble into a new random orientation.

*Uni bomb - the final variant, tuned to resonance with the active Quirium in the targets engines and energy systems. In practice this tends to overload them and cause catastrophic and explosive failure. To prevent a negative impact to the gaming experience, when used against the player this bomb just causes severe energy loss.

*Interdictor Mine - classified Galcop munition for pirate and large-scale fleet pacification. It is an offense for non-Galcop ships to utilise such weaponry.

*Screamer Mine - special anti-Thargoid weapon which induces disorientation and nausea in them at a range of up to 25km. This infuriates and distracts them so much that they usually target the mine, useful in drawing alien fire and causing a diversion.

*Void Bomb - a small singularity kept in stasis until the bomb detonates, at which point it is released and grows in size. This is a high-density entity which attracts all things within scanner range, and damages them on contact. It lasts for a short period of time until it has consumed all local matter, at which point it collapses.

*Decoy Mine - a modified cargo pod containing several warship-class Thargoid fake mylar balloons produced by Ganelon Inc. These expand on launch and simulate alien vessels, causing a distraction and allowing the launcher to slip away or press the attack.

-------------------------------------------------------------------------------------------------------------------------------------
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.
* 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.
* Even though it is not compulsory, if you are re-using any sizable or recognisable piece of this OXP, please let me know :)

-------------------------------------------------------------------------------------------------------------------------------------
Instructions:

Use the in-game expansions manager to install Armoury, or if you downloaded it manually, place the OXZ in the Oolite AddOns folder.

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

Version history:

10/02/2010 - WIP version for feedback
31/05/2010 - Added missile machine and icon gluing for missile rack (thanks Kaks!)
18/06/2010 - Version 1.01, minor bugfix to include player as stun bomb victim.
22/06/2010 - Version 1.02, corrected problem with probe missile, added reload noise to missile machine and added displacer missile
07/07/2010 - Version 1.03, made unibomb not a suicide weapon, plus amended CT and Drones for Constrictor mission compatibility.
				Also added some new weaponry...
13/02/2011 - Version 1.04, removal of upper limit, to allow running with 1.75
19/02/2011 - Version 1.05, update of scripting to fix Manifest glitch
23/03/2011 - Version 1.06, removal of a problem in the AMS script (depreciation of this.reset) plus change to its cost.With thanks to Lohwengk for the heads-up on that one.
03/04/2011 - Version 1.07, further tweaking of AMS script to remove reload glitches.
17/07/2011 - Version 1.08, some new weapons again
16/09/2011 - Version 1.09, three small script tweaks for compatibility with 1.76 trunk (shipDied double-call).
16/03/2012 - Version 1.10, added the tracker missile.
02/01/2013 - Version 1.11, added a few scan classes into the shipdata to deal with 1.77 trunk warnings.
22/03/2013 - Version 1.12, add primable deactivation and activation to the AMS, and also adjusted prices.
02/04/2013 - Version 1.13, fix for small bug in interdictor mine script which awards a target autolock for free.

13/12/2018 - Version 1.14, --update by rustem for oolite 1.80--
 - Required oolite version up to 1.80.
 - Added "use strict" in the scripts.
 - Updated a subentities format.
 - Fixed for small bug in drone script.
 - Adds unpiloted status for bombs, drones and etc.
 - Adds more time interval for activate a void bomb.
 - Adds missile role(0.01) for kamikaze drone.

14/12/2018 - Version 1.15
 - Fixed for small bug in drone subentity.
 - Formatted the JS code: AIs, plist and scripts.

16/06/2020 - Version 1.21, --update by Milo for Oolite 1.82--
 - Required Oolite version up to 1.82.
 - Fixed Captured Thargons script for 1.82 (alienItems no longer works, must use alien_items).
 - Revised description of Anti-Missile System to clarify that it only targets non-standard missiles.
 - Removed Anti-Missile System from primeable equipment list: it just provided an on/off switch, and why turn it off?

10/08/2021 - Version 1.22 - update by phkb
- Added condition script for all equipment items that had conditions or used TL99.

With thanks to Ganelon for the thoughts on the decoy mine idea (WTF!), Okti for the kicker missile, Commander CommonSenseOTB for the cutpurse missile and Ramirez for the "virtual entity" trick used in the tracker missile.

Also thanks to UK_Eliter for reporting the 1.76 shipDied double-call issue, and Eric Walch for the suggestion of the elegant fix. Also to Spara for the interdictor mine script bug report.

Equipment

Name Visible Cost [deci-credits] Tech-Level
Anti-Missile System yes 80000 10+
Single AMS Dart yes 5000 10+
Two AMS Darts yes 10000 10+
Three AMS Darts yes 15000 10+
Four AMS Darts yes 20000 10+
Five AMS Darts yes 25000 10+
Six AMS Darts yes 30000 10+
Remove AMS System yes 5000 7+
Bug Bomb yes 10000 8+
Buzz Bomb yes 10000 8+
Cutpurse Missile yes 6000 9+
Decoy Mine yes 8000 6+
Displacer Missile yes 4000 8+
Field Missile yes 10000 6+
ECM Hardened Missile yes 1000 99+
Black Market Interdictor Mine yes 25000 6+
Kicker Missile yes 8000 8+
Missile Machine yes 50000 10+
Screamer Mine yes 10000 9+
Missile yes 200 99+
Stun Bomb yes 10000 8+
Tracker Missile yes 5000 6+
Uni Bomb yes 15000 10+
Void Bomb yes 30000 10+
Anti-Thargoid Drone yes 4500 9+
Combat Drone yes 4500 9+
Reprogram Thargon Fighter yes 5000 12+
Replacement Thargon Fighter yes 10000 12+
Replacement Thargon Fighters x2 yes 20000 12+
Replacement Thargon Fighters x3 yes 30000 12+
Replacement Thargon Fighters x4 yes 40000 12+
Captured Thargon Ship Defense System yes 55000 12+
Missile Rack (Hard) yes 10000 1+
Missile Rack (Hard) yes 10000 1+
Missile Rack (Hard) yes 10000 5+
Interception Missile Rack yes 1200 1+
Interception Missile Rack yes 1200 1+
Interception Missile Rack yes 1200 5+
Kamikaze Drone yes 5000 10+
Missile Rack (Std) yes 1000 1+
Missile Rack (Std) yes 1000 1+
Missile Rack (Std) yes 1000 5+
Probe Missile yes 4500 7+
Missile Rack (Stun) yes 10000 1+
Missile Rack (Stun) yes 10000 1+
Missile Rack (Stun) yes 10000 5+

Ships

Name
Thargon launch system
Reprogrammed Fighter
Anti-missile dart
Bomb
armoury_bugBomb
armoury_buzzBomb
Cutpurse Missile
Decoy Mine
Displacer Missile
Field Mine
Field Missile
Interdictor Mine
Interdictor Mine
Interdictor Mine
Interdictor Mine
Interdictor Mine
Kicker Missile
MM Hardened Missile
MM Standard Missile
Missile Machine
Screamer Mine
Screamer Mine
Screamer Mine
Screamer Mine
Screamer Mine
armoury_stunBomb
Tracker Beacon
Tracker Missile
armoury_uniBomb
Void Bomb
Void Bomb
armoury_wtfDecoy
Anti-Thargoid Drone launcher
Combat Drone launcher
Kamikaze Drone launcher
Anti-Thargoid Drone
Combat Drone
Kamikaze Drone
Kamikaze Drone Laser Sub-entity
Mini-missile
missileRack_hMissile1
missileRack_hMissile2
missileRack_hMissile3
missileRack_iMissile1
missileRack_iMissile2
missileRack_iMissile3
missileRack_nMissile1
missileRack_nMissile2
missileRack_nMissile3
missileRack_sMissile1
missileRack_sMissile2
missileRack_sMissile3
Empty missile rack
Probe Missile launcher
Probe Missile

Models

This expansion declares no models.

Scripts

Path
Scripts/CT_launcher.js
"use strict";
this.name = "CT_Launcher";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "Spawn Captured Thargons";
this.version = "1.00";

this.shipDied = function () {
	if (missionVariables.CT_thargonCount > 0) {
		player.consoleMessage("Robot fighters activated", 5);
		this.ship.spawn("CT_thargon", missionVariables.CT_thargonCount);
	}
	delete this.shipDied;
}
Scripts/CT_masterScript.js
"use strict";
this.name = "CT_Script";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "Captured Thargons - for 1.74+";
this.version = "2.01";

this.startUp = function () {
	if (missionVariables.CT_thargonCount == null) {
		missionVariables.CT_thargonCount = 0;
	}

	if (missionVariables.CT_thargonCargo == null) {
		missionVariables.CT_thargonCargo = manifest.alien_items;
	}
}

this.guiScreenChanged = function (to, from) {
	if (to == "GUI_SCREEN_EQUIP_SHIP") {
		missionVariables.CT_thargonCargo = manifest.alien_items;
	}
}

this.playerBoughtEquipment = function (equipment) {
	switch (equipment) {
		case "EQ_CT_MINE":
			{
				missionVariables.CT_thargonCount = 5;
				mission.runScreen({
					title: "Captured Thargon Purchase",
					messageKey: "CT_instructions",
					model: "CT_thargon"
				});
				break;
			}

		case "EQ_MISSILE_REMOVAL":
			{
				if (missionVariables.CT_thargonCount > 0) { // Player gets 5000 back for the removal, but if <5 drones left deduct 1000 per drone
					player.credits -= (1000 * (5 - missionVariables.CT_thargonCount));
					missionVariables.CT_thargonCount = 0;
				}
				break;
			}

		case "EQ_CT_DRONE_1":
			{
				missionVariables.CT_thargonCount += 1;
				player.ship.removeEquipment("EQ_CT_DRONE_1");
				break;
			}

		case "EQ_CT_DRONE_2":
			{
				missionVariables.CT_thargonCount += 2;
				player.ship.removeEquipment("EQ_CT_DRONE_2");
				break;
			}

		case "EQ_CT_DRONE_3":
			{
				missionVariables.CT_thargonCount += 3;
				player.ship.removeEquipment("EQ_CT_DRONE_3");
				break;
			}

		case "EQ_CT_DRONE_4":
			{
				missionVariables.CT_thargonCount += 4;
				player.ship.removeEquipment("EQ_CT_DRONE_4");
				break;
			}
		case "EQ_CT_CONVERT":
			{
				if (manifest.alien_items > 0) {
					manifest.alien_items--;
					missionVariables.CT_thargonCount++;
					missionVariables.CT_thargonCargo = manifest.alien_items;
					player.ship.removeEquipment("EQ_CT_CONVERT");
				}
				break;
			}
	}

	if (missionVariables.CT_thargonCount > 5) {
		missionVariables.CT_thargonCount = 5;
	}
}
Scripts/CT_thargon.js
"use strict";
this.name = "CT_thargon";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "AI-actions script for the captured thargon";
this.version = "2.0";

this.shipSpawned = function () {
	missionVariables.CT_thargonCount -= 1;
}

this.shipWasScooped = function (scooper) {
	if (scooper.isPlayer) {
		missionVariables.CT_thargonCount += 1;
		if (missionVariables.CT_thargonCount == 1) {
			player.ship.awardEquipment("EQ_CT_MINE");
		}
	}
}

this.locatePlayer = function () {
	this.playerArray = system.shipsWithPrimaryRole("player");
	if (this.playerArray.length == 0) // where'd they go?
	{
		this.ship.remove(); // If no player, then we can remove the thargon.
	} else {
		this.ship.target = this.playerArray[0];
		this.ship.reactToAIMessage("PLAYER_FOUND");
	}
}

this.findPlayerHostiles = function () {
	function isHostileToPlayer(entity) {
		return (entity.isThargoid || (entity.isShip && entity.target && entity.target == player.ship && entity.hasHostileTarget));
	}

	if (this.ship.target == player.ship && player.ship.target && player.ship.target.bounty > 10) {
		this.ship.target = player.ship.target;
		this.ship.reactToAIMessage("HOSTILE_FOUND");
	} else {
		var targets = system.filteredEntities(this, isHostileToPlayer, this.ship, this.ship.scannerRange);
		if (targets.length > 0) {
			this.ship.target = targets[0];
			this.ship.reactToAIMessage("HOSTILE_FOUND");
		} else {
			this.ship.reactToAIMessage("NO_HOSTILE_FOUND");
		}
	}
}

this.fireCheck = function () {
	if ((this.ship.target.hasRole("CT_thargon")) || (player.ship.target == this.ship && this.ship.target.isPlayer)) {
		this.ship.reactToAIMessage("FRIENDLY_FIRE");
	} else {
		this.ship.reactToAIMessage("ENEMY_FIRE");
	}
}

this.shipTargetDestroyed = function (target) {
	player.score += 1;
	player.credits += target.bounty;
	player.consoleMessage("Thargon kill - " + target.name + " : " + target.bounty + "₢ awarded.", 5);
	log("Thargon kill - " + target.name + " : " + target.bounty);

	if (target.primaryRole == "constrictor" && missionVariables.conhunt && missionVariables.conhunt == "STAGE_1") { // just in case a thargon kills the constrictor, let's not break the mission for the player...
		missionVariables.conhunt = "CONSTRICTOR_DESTROYED";
	}
}

this.shipDied = function (whom, why) {
	if (whom && whom.isValid) {
		player.consoleMessage("Contact lost with Thargon fighter", 5);
	}
}
Scripts/ams_activate.js
"use strict";
this.name = "ams_activate.js";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "Script for manual activation of the anti-missile system";
this.version = "1.00";

this.activated = function () {
	if (player.ship.equipmentStatus("EQ_AMS") !== "EQUIPMENT_OK") {
		missionVariables.amActive = "INACTIVE";
		return;
	}

	if (!worldScripts["ams_system"]) {
		player.consoleMessage("AMS script error.");
		log(this.name, "AMS script activation failed.");
		return;
	}

	if (missionVariables.amActive && missionVariables.amActive === "ACTIVE") {
		missionVariables.amActive = "INACTIVE";
		player.consoleMessage("Anti-Missile System deactivated.");
	} else {
		missionVariables.amActive = "ACTIVE";
		player.consoleMessage("Anti-Missile System activated.");
		worldScripts["ams_system"].$scanForMissiles();
	}
}
Scripts/ams_dart.js
"use strict";
this.name = "ams_dart";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "Needle-dart interception script.";
this.version = "1.01";

this.shipIntercept = function () {
	let chance = Math.random();
	if (chance > 0.9) {
		player.consoleMessage("AMS - Interception failed.", 6);
	} else {
		if (this.ship && this.ship.target) {
			this.ship.target.explode();
		}
	}
}
Scripts/ams_system.js
"use strict";
this.name = "ams_system";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "Needle-dart anti-missile system extension of the Missile Analyser, good for 6 uses before replacement.";
this.version = "2.12";

this.startUp = function () {
	if (missionVariables.amCount === undefined || missionVariables.amCount === null) {
		this.amCount = 6;
	} else {
		this.amCount = missionVariables.amCount;
	}
	if (missionVariables.amActive === undefined || missionVariables.amActive === null) {
		missionVariables.amActive = "INACTIVE";
	}
}

this.shipLaunchedFromStation = function () {
	if (player.ship.equipmentStatus("EQ_AMS") === "EQUIPMENT_OK" && player.ship.equipmentStatus("EQ_MISSILE_ANALYSER") === "EQUIPMENT_OK") {
		missionVariables.amActive = "ACTIVE";
		switch (this.amCount) {
			case 0:
				{
					player.consoleMessage("AMS - System offline, no darts loaded.", 5);
					break;
				}
			case 1:
				{
					player.consoleMessage("AMS - System online, 1 dart loaded.", 5);
					break;
				}
			default:
				{
					player.consoleMessage("AMS - System online, " + this.amCount + " darts loaded.", 5);
					break;
				}
		}
	}

}

this.$scanForMissiles = function () {
	function missileArray(entity) {
		return entity.isShip && entity.isMissile && entity.target && entity.target === player.ship;
	};
	let incomingMissiles = system.filteredEntities(this, missileArray, player.ship, 25600);
	if (incomingMissiles.length > 0) {
		this.shipAttackedWithMissile(incomingMissiles[0], null);
	}
}

this.shipAttackedWithMissile = function (missile, whom) {
	if (player.ship.equipmentStatus("EQ_AMS") === "EQUIPMENT_OK" && player.ship.equipmentStatus("EQ_MISSILE_ANALYSER") === "EQUIPMENT_OK" && missionVariables.amActive === "ACTIVE" && missile.name !== "Missile" && this.amCount > 0) {
		player.consoleMessage("AMS - Dart " + (7 - this.amCount) + " launched.", 6);
		let antiMissile = player.ship.spawnOne("ams_dart");
		antiMissile.target = missile;
		let amOrientation = player.ship.orientation;
		let offsetVector = amOrientation.vectorUp(); // vector at right angles to the player, downwards
		let spawnOffset = offsetVector.multiply(20); // set the offset to 20m downwards (for larger ships, increase 20).
		antiMissile.orientation = amOrientation; // point the dart in the same direction as the player
		antiMissile.position = player.ship.position.subtract(spawnOffset); // move the dart to it's correct start point.

		if (--this.amCount < 1) {
			player.consoleMessage("AMS - system exhausted, shutting down", 8);
		}
		missionVariables.amCount = this.amCount;
	}
}

this.playerBoughtEquipment = function (equipment) {
	switch (equipment) {
		case "EQ_AMS":
			{
				missionVariables.amActive = "ACTIVE";
				this.amCount = 6;
				break;
			}
		case "EQ_AMS_1":
			{
				this.amCount += 1;
				player.ship.removeEquipment("EQ_AMS_1");
				break;
			}
		case "EQ_AMS_2":
			{
				this.amCount += 2;
				player.ship.removeEquipment("EQ_AMS_2");
				break;
			}
		case "EQ_AMS_3":
			{
				this.amCount += 3;
				player.ship.removeEquipment("EQ_AMS_3");
				break;
			}
		case "EQ_AMS_4":
			{
				this.amCount += 4;
				player.ship.removeEquipment("EQ_AMS_4");
				break;
			}
		case "EQ_AMS_5":
			{
				this.amCount += 5;
				player.ship.removeEquipment("EQ_AMS_5");
				break;
			}
		case "EQ_AMS_6":
			{
				this.amCount += 6;
				player.ship.removeEquipment("EQ_AMS_6");
				break;
			}
		case "EQ_AMS_REMOVAL":
			{
				player.ship.removeEquipment("EQ_AMS");
				player.ship.removeEquipment("EQ_AMS_REMOVAL");
				player.credits += (5000 + (this.amCount * 400));
				this.amCount = 0;
				delete missionVariables.amCount;
				delete missionVariables.amActive;
				break;
			}
	}
	missionVariables.amCount = this.amCount;
}
Scripts/armoury_conditions.js
"use strict";
this.name        = "Armoury_Conditions";
this.author      = "phkb";
this.copyright   = "2023 phkb";
this.description = "Condition script for equipment.";
this.licence     = "CC BY-NC-SA 3.0";

this.allowAwardEquipment = function(equipment, ship, context) {
	if (context === "scripted") return true;
    switch (equipment) {
        case "EQ_ARMOURY_CUTPURSE_MISSILE":
            if (system.government < 2 && Math.random() < 0.5) return true;
            break;
        case "EQ_ARMOURY_INTERDICTOR_MINE":
            if (system.government < 2 && Math.random() < 0.25) return true;
            break;
        case "EQ_CT_MINE":
            if (missionVariables.CT_thargonCount == 0) return true;
            break;
        case "EQ_CT_DRONE_1":
            if (missionVariables.CT_thargonCount == 4) return true;
            break;
        case "EQ_CT_DRONE_2":
            if (missionVariables.CT_thargonCount == 3) return true;
            break;
        case "EQ_CT_DRONE_3":
            if (missionVariables.CT_thargonCount == 2) return true;
            break;
        case "EQ_CT_DRONE_4":
            if (missionVariables.CT_thargonCount == 1) return true;
            break;
        case "EQ_CT_CONVERT":
            if (missionVariables.CT_thargonCount < 5 && missionVariables.CT_thargonCargo > 0) return true;
            break;
        case "EQ_AMS_1":
            if (missionVariables.amCount == 5) return true;
            break;
        case "EQ_AMS_2":
            if (missionVariables.amCount == 4) return true;
            break;
        case "EQ_AMS_3":
            if (missionVariables.amCount == 3) return true;
            break;
        case "EQ_AMS_4":
            if (missionVariables.amCount == 2) return true;
            break;
        case "EQ_AMS_5":
            if (missionVariables.amCount == 1) return true;
            break;
        case "EQ_AMS_6":
            if (missionVariables.amCount == 0) return true;
            break;
    }
    return false;
}
Scripts/armoury_cutpurseMissile.js
"use strict";
this.name = "armoury_cutpurseMissile.js";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "Ship script for the cutpurse missile";
this.version = "1.00";

this.performSteal = function () {
	if (!this.ship.target || !this.ship.target.isValid || this.ship.target.isCargo || this.ship.target.isWeapon || this.ship.target.isRock || this.ship.target.isThargoid) {
		return;
	}
	this.ship.target.script.$armoury_cpTimer = this.$armoury_cpTimer;
	this.ship.target.script.$armoury_cpAction = this.$armoury_cpAction;
	if (this.ship.target.isPlayer) {
		this.ship.target.script.$armoury_cpPlayerDump = this.$armoury_cpPlayerDump;
	}
	this.ship.target.script.$armoury_cpTimer();
}

this.$armoury_cpTimer = function () {
	if (this.$armoury_dumpTimer) {
		return;
	}
	this.$timerDelay = Math.random() * 10;
	this.$armoury_dumpTimer = new Timer(this, this.$armoury_cpAction, this.$timerDelay);
}

this.$armoury_cpAction = function () {
	if (this.ship.isPlayer) {
		player.consoleMessage("Emergency cargo dump triggered", 4);
		this.$armoury_playerTimer = new Timer(this, this.$armoury_cpPlayerDump, 0, 0.51);
	} else {
		this.ship.setAI("armoury_ejectCargoAI.plist");
	}
}

this.$armoury_cpPlayerDump = function () {
	if (player.ship.cargoSpaceUsed === 0 || Math.random() < 0.1) {
		this.$armoury_playerTimer.stop();
		delete this.$armoury_dumpTimer;
		delete this.$armoury_playerTimer;
		delete this.$armoury_cpTimer;
		delete this.$armoury_cpAction;
		return;
	} else {
		player.ship.dumpCargo();
	}
}
Scripts/armoury_decoyMine.js
"use strict";
this.name = "armoury_decoyMine.js";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "Ship script for the decoy mine";
this.version = "1.00";

this.deployDecoys = function () {
	this.decoyCount = Math.ceil((0.00001 + Math.random()) * 4);
	this.decoyList = system.addShips("armoury_wftDecoy", this.decoyCount, this.ship.position, 500);
}
Scripts/armoury_displacerMissile.js
"use strict";
this.name = "armoury_displacerMissile.js";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "Ship script for the displacer missile";
this.version = "1.00";

this.removeSelf = function () {
	this.ship.remove();
}

this.performDisplace = function () {
	if (!this.ship.target || !this.ship.target.isShip || !this.ship.target.isValid || this.ship.target.isCargo || this.ship.target.fuel == 0) { // displacement only for ships with fuel
		this.ship.remove();
		return;
	}

	this.ship.target.fuel *= Math.random(); // drain off some fuel to simulate the jump	

	if (this.ship.target.target) { // remove the displaced ship's target, if it has one
		this.ship.target.target = null;
	}

	this.xDistance = ((Math.random() * 2) - 1) * 1024000;
	this.yDistance = ((Math.random() * 2) - 1) * 1024000;
	this.zDistance = ((Math.random() * 2) - 1) * 1024000;

	this.ship.target.position = this.ship.target.position.add([this.xDistance, this.yDistance, this.zDistance]);

	if (this.ship.target.isPlayer) {
		player.consoleMessage("EMERGENCY - position changed by displacement weapon - compensating", 6);
	}

	this.ship.remove();
}
Scripts/armoury_fieldMine.js
"use strict";
this.name = "armoury_fieldMine.js";
this.author = "Thargoid";
this.copyright = "Creative Commons Attribution - Non-Commercial - Share Alike 3.0 license with clauses - see readme.txt.";
this.description = "Ship script for field missile's mine.";
this.version = "1.0";

this.shipSpawned = function () {
	// create an array with all possible target ships ("isShip" includes barrels and rock etc.) 
	this.fieldTimer = new Timer(this, this.findPrey, 0, 1);
}

this.findPrey = function () {
	function targetShips(entity) {
		return (entity.isShip && !entity.hasRole("armoury_fieldMissile") && !entity.hasRole("armoury_fieldMine"))
	};
	let prey = system.filteredEntities(this, targetShips, this.ship, 500);
	if (prey.length > 0) {
		this.stopTimer();
		this.ship.AIState = "BANG";
	}
}

this.stopTimer = function () {
	if (this.fieldTimer && this.fieldTimer.isRunning) {
		this.fieldTimer.stop();
		delete this.fieldTimer;
	}
}
Scripts/armoury_fieldMissile.js
"use strict";
this.name = "armoury_fieldMissile.js";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "Ship script for the field missile";
this.version = "1.00";

this.dropMines = function () {
	if (!this.ship.target || !this.ship.target.isValid) // if the missile doesn't have a proper target to spawn mines around
	{
		this.scatterMines();
		return;
	}

	this.mineCount = Math.ceil(Math.random() * 11) + 19; // 20-30 mines to be thrown around target position (250m radius).
	system.legacy_addShipsWithinRadius("armoury_fieldMine", this.mineCount, "abs", this.ship.target.position, 250);
}

this.scatterMines = function () {
	this.mineCount = Math.ceil(Math.random() * 11) + 19; // 20-30 mines to be thrown around missile position (250m radius).
	system.legacy_addShipsWithinRadius("armoury_fieldMine", this.mineCount, "abs", this.ship.position, 250);
}
Scripts/armoury_generalScript.js
"use strict";
this.name = "armoury_generalScript.js";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "Ship script for Armoury OXP";
this.version = "1.11";

// functions for various bombs supplied in the OXP

this.detonate = function () {
	if (!this.ship.owner.isValid) {
		this.ship.explode();
		return;
	}
	this.bomber = this.ship.owner;
	this.totalBounty = 0;
	this.energyAvailable = 64; // bombs own single energy bank.
	this.bomberEnergy = this.bomber.energy - 64; // max energy possible to put into EMP broadcast (leave bomber at least 1 energy bank)
	if (this.bomberEnergy > 0) {
		this.energyTransfer = 0.25 + (Math.random() * 0.75); // between 25% and 100% transfer of available energy
		this.energyDeduction = (this.bomberEnergy * this.energyTransfer); // how much energy we're going to transfer to the blast.
		this.bomber.energy -= this.energyDeduction; // drain the bomber's energy banks accordingly
		this.energyAvailable += this.energyDeduction; // add the bomber energy to the bomb's own energy bank
	}

	switch (this.ship.scriptInfo.bombType) // check which type of bomb has just gone off.
	{
		case "Bug bomb":
		case "Buzz bomb":
			{
				this.generalBomb();
				break;
			}
		case "Uni bomb":
		case "Stun bomb":
			{
				this.effectBomb();
				break;
			}
	}
}

this.tidyUp = function () {
	if (this.totalBounty > 0 && this.bomber.isPlayer) {
		player.consoleMessage("Total bomb bounty : " + this.totalBounty + " ₢.", 6);
		player.credits += this.totalBounty;
	}

	if (!this.bomber.isPlayer && player.ship && this.bomber && this.bomber.position.distanceTo(player.ship.position) < 25600) {
		player.consoleMessage(this.ship.scriptInfo.bombType + " detonation detected", 6);
	}
	this.ship.explode(); // get rid of the bomb entity
}

this.effectBomb = function () {
	function isPowered(entity) {
		return (entity.isShip && (this.ship.owner && entity != this.ship.owner) && (entity.isPlayer || (!entity.isMainStation && entity != this.bomber && entity.scanClass != "CLASS_CARGO" && entity.scanClass != "CLASS_ROCK"))); // ships who aren't cargo or rock.
	}

	var targetArray = system.filteredEntities(this, isPowered, this.ship, 25600);
	if (targetArray.length > 0) {
		var targetCounter = 0; // reset the counter
		for (targetCounter = 0; targetCounter < targetArray.length; targetCounter++) {
			this.distanceChance = 1 - ((this.ship.position.distanceTo(targetArray[targetCounter].position)) / 25600); // distance dependence
			this.energyChance = this.energyAvailable / targetArray[targetCounter].energy; // energy dependence
			if (Math.random() < (this.distanceChance * this.energyChance)) {
				switch (this.ship.scriptInfo.bombType) // check which type of bomb has just gone off.
				{
					case "Uni bomb":
						{
							if (this.bomber.isPlayer) {
								player.score += 1;
								this.totalBounty += targetArray[targetCounter].bounty;
								targetArray[targetCounter].explode();
							} else {
								if (targetArray[targetCounter].isPlayer) {
									this.newEnergy = 32 + (Math.random() * 32);
									if (player.ship.energy > this.newEnergy) {
										player.ship.energy = this.newEnergy; // rather than nuking the player, set their energy very low
									}
								} else {
									targetArray[targetCounter].explode();
								}
							}
							break;
						}

					case "Stun bomb":
						{
							if (targetArray[targetCounter].isPlayer) {
								player.ship.target = null;
								this.newOrient = new Quaternion;
								this.newOrient.w = (Math.random() * 2) - 1;
								this.newOrient.x = (Math.random() * 2) - 1;
								this.newOrient.y = (Math.random() * 2) - 1;
								this.newOrient.z = (Math.random() * 2) - 1;
								player.ship.orientation = this.newOrient; // as we can't stun the player, let's spin him about.
								player.consoleMessage("Flight control restored.", 6);
							} else {
								targetArray[targetCounter].setAI("armoury_stunnedAI.plist");
							}
							break;
						}
				}

			}
		}
	}
	this.tidyUp();
}


this.generalBomb = function () {
	function isPowered(entity) {
		return (entity.isShip && entity != this.bomber && entity.scanClass != "CLASS_CARGO" && entity.scanClass != "CLASS_ROCK");
	}

	this.damageWeight = 0.5; // damage weighting reduction adjustable for gameplay

	var targetArray = system.filteredEntities(this, isPowered, this.ship, 25600);

	if (targetArray.length > 0) {
		var targetCounter = 0; // reset the counter
		for (targetCounter = 0; targetCounter < targetArray.length; targetCounter++) {
			this.damageMultiplier = 1 - ((this.ship.position.distanceTo(targetArray[targetCounter].position)) / 25600); // distance dependence
			this.damage = (this.energyAvailable * this.damageMultiplier);
			if ((this.ship.scriptInfo.bombType) == "Bug bomb" && !targetArray[targetCounter].isThargoid) {
				this.damage *= this.damageWeight;
			}
			if ((this.ship.scriptInfo.bombType) == "Buzz bomb" && targetArray[targetCounter].isThargoid) {
				this.damage *= this.damageWeight;
			}

			if (this.damage > targetArray[targetCounter].energy) {
				if (this.bomber.isPlayer) {
					player.score += 1;
					this.totalBounty += targetArray[targetCounter].bounty;
				}
				targetArray[targetCounter].explode();
			} else {
				targetArray[targetCounter].energy -= this.damage;
			}
		}
	}
	this.tidyUp();
}
Scripts/armoury_interdictorMine.js
"use strict";
this.name = "armoury_interdictorMine.js";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "Ship script for the interdictor mine";
this.version = "1.13";

this.shipSpawned = function () {
	this.ship.desiredSpeed = 0;
	this.destructChance = -1;
	this.messageFlag = "OFF";
	this.autolockFlag = "NULL";

	if (worldScripts["targetAutolock"] && missionVariables.targetAutolock === "TRUE") {
		this.autolockFlag = "STOPPED";
		missionVariables.targetAutolock = null;
	}

	this.policeArray = system.entitiesWithScanClass("CLASS_POLICE", this.ship, 25600)
	if (this.policeArray.length > 0 && this.ship.owner && !this.ship.owner.isPolice) {
		this.ship.owner.bounty += 20;
		this.policeArray[0].reactToAIMessage("OFFENCE_COMMITTED");
	}

	this.interdictTimer = new Timer(this, this.performInterdict, 0, 0.5);
}

this.playerWillEnterWitchspace = this.shipDied = function () {
	if (this.interdictTimer && this.interdictTimer.isRunning) {
		this.interdictTimer.stop();
	}

	if (this.autolockFlag == "STOPPED") {
		this.autolockFlag = "NULL";
		missionVariables.targetAutolock = "TRUE";
	}
}

this.performInterdict = function () {
	if (!this.ship.isValid || !player.ship.isValid) {
		this.shipDied();
		return;
	}

	if (Math.random() < this.destructChance) // increasing chance that the mine's energy has run out and it self-destructs
	{
		this.ship.explode();
		this.shipDied();
		return;
	} else {
		this.destructChance += 0.01;
	}

	if (this.ship.position.distanceTo(player.ship.position) < 25600) {
		if (this.messageFlag == "OFF") {
			player.consoleMessage("Targetting system is being jammed by interdictor mine");
			this.messageFlag = "ON";
		}
	} else {
		this.messageFlag = "OFF";
		if (this.autolockFlag == "STOPPED") {
			this.autolockFlag = "NULL";
			missionVariables.targetAutolock = "TRUE";
		}
	}

	function isInterdictorTarget(entity) {
		return (entity.isShip && entity.target && !entity.isThargoid && !entity.isPolice);
	}

	var targetArray = system.filteredEntities(this, isInterdictorTarget, this.ship, 25600)
	if (targetArray.length > 0) {
		var targetCounter = 0;
		for (targetCounter = 0; targetCounter < targetArray.length; targetCounter++) {
			if (!targetArray[targetCounter].isPlayer) {
				targetArray[targetCounter].target = null;
			} else {
				player.ship.target = null;
				this.messageFlag = "ON";
			}
		}
	}
}
Scripts/armoury_kickerMissile.js
"use strict";
this.name = "armoury_kickerMissile.js";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "Ship script for the kicker missile";
this.version = "1.00";

this.performKick = function () {
	if (!this.ship.target || !this.ship.target.isValid) {
		return;
	}
	this.ship.target.velocity = this.ship.target.velocity.add(this.ship.velocity.multiply(25 * (this.ship.mass / this.ship.target.mass)));
}
Scripts/armoury_machineScript.js
"use strict";
this.name = "armoury_machineScript.js";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "World script for Armoury OXP missile machine";
this.version = "1.00";

this.missileMachine = function () {
	this.mountSound1 = new SoundSource;
	this.mountSound1.sound = "armoury_missileMount1.ogg";
	this.mountSound1.loop = false;
	this.mountSound2 = new SoundSource;
	this.mountSound2.sound = "armoury_missileMount2.ogg";
	this.mountSound2.loop = false;
	this.mountSound3 = new SoundSource;
	this.mountSound3.sound = "armoury_missileMount3.ogg";
	this.mountSound3.loop = false;

	if (this.mmTimer && this.mmTimer.isRunning) {
		player.consoleMessage("Missile machine already running - please wait", 6);
		return;
	}

	if (player.ship.missiles.length == player.ship.missileCapacity) {
		player.consoleMessage("No free missile pylons available - aborting", 6);
		return;
	}

	if (player.ship.manifest["radioactives"] == 0 || player.ship.manifest["alloys"] == 0 || player.ship.fuel < 0.2) {
		player.consoleMessage("Insufficient raw materials for manufacture - aborting", 6);
		return;
	}

	player.ship.manifest["radioactives"] -= 1;
	player.ship.manifest["alloys"] -= 1;
	player.ship.fuel -= 0.1;

	if (player.ship.manifest["computers"] > 0) {
		missionVariables.armoury_MM = "EQ_ARMOURY_HARD_MISSILE";
		player.ship.manifest["computers"] -= 1;
	} else {
		missionVariables.armoury_MM = "EQ_ARMOURY_STANDARD_MISSILE";
	}

	player.consoleMessage("Missile machine working - please wait", 6);
	this.mmTimer = new Timer(this, this.awardWeapons, 60);
}

this.awardWeapons = function () {
	this.mountCount = 0;
	player.consoleMessage("Missile manufacture completed - mounting to pylons", 6);
	this.canMount = player.ship.awardEquipment(missionVariables.armoury_MM);
	if (this.canMount) {
		this.mountCount++;
	}
	this.canMount = player.ship.awardEquipment(missionVariables.armoury_MM);
	if (this.canMount) {
		this.mountCount++;
	}
	this.canMount = player.ship.awardEquipment(missionVariables.armoury_MM);
	if (this.canMount) {
		this.mountCount++;
	}
	switch (this.mountCount) {
		case 1:
			{
				this.mountSound1.play();
				break;
			}
		case 2:
			{
				this.mountSound2.play();
				break;
			}
		case 3:
			{
				this.mountSound3.play();
				break;
			}
	}
}

this.shipWillDockWithStation = function () {
	if (this.mmTimer && this.mmTimer.isRunning) {
		this.mmTimer.stop();
		this.awardWeapons();
	}
}

this.shipDied = function () {
	if (this.mmTimer && this.mmTimer.isRunning) {
		this.mmTimer.stop();
	}
}
Scripts/armoury_missileMachine.js
"use strict";
this.name = "armoury_missileMachine.js";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "Ship script for missile machine";
this.version = "1.00";

this.startMachine = function () {
	player.ship.awardEquipmentToCurrentPylon("EQ_ARMOURY_MISSILEMACHINE_MINE"); // put the HUD icon back
	worldScripts["armoury_machineScript.js"].missileMachine();
	this.ship.remove(); // get rid of the spawned evidence
}
Scripts/armoury_screamerMine.js
"use strict";
this.name = "armoury_screamerMine.js";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "Ship script for the screamer mine";
this.version = "1.00";

this.shipSpawned = function () {
	this.ship.desiredSpeed = 0;
	this.destructChance = -0.5;
	this.screamerTimer = new Timer(this, this.scream, 0, 2);
}

this.playerWillEnterWitchspace = this.shipDied = function () {
	if (this.screamerTimer && this.screamerTimer.isRunning) {
		this.screamerTimer.stop();
	}
}

this.scream = function () {
	if (!this.ship) {
		this.shipDied();
		return;
	}

	if (Math.random() < this.destructChance) // increasing chance that the mine's energy has run out and it self-destructs
	{
		this.ship.explode();
		this.shipDied();
		return;
	} else {
		this.destructChance += 0.01;
	}

	function isAlien(entity) {
		return (entity.isShip && entity.target && entity.target != this.ship && (entity.isThargoid || entity.hasRole("thargoid") || entity.hasRole("thargon") || entity.hasRole("tharglet")));
	}

	var targetArray = system.filteredEntities(this, isAlien, this.ship, 25600)
	if (targetArray.length > 0) {
		var targetCounter = 0;
		for (targetCounter = 0; targetCounter < targetArray.length; targetCounter++) {
			if (Math.random() < 0.25) {
				targetArray[targetCounter].reactToAIMessage("TARGET_LOST");
				targetArray[targetCounter].target = this.ship;
				targetArray[targetCounter].AIState = "ATTACK_SHIP";
			}
		}
	}
}
Scripts/armoury_trackerBeacon.js
"use strict";
this.name = "armoury_trackerBeacon.js";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "Beacon script for tracker missile.";
this.version = "1.00";

this.shipSpawned = function () {
	if (!this.ship || !this.ship.target) {
		return;
	}
	this.callbackID = addFrameCallback(this.positionTag.bind(this));
}

this.positionTag = function () {
	if (!this.ship || !this.ship.isValid || !this.ship.target || !this.ship.target.isValid) {
		if (this.callbackID) {
			removeFrameCallback(this.callbackID);
		}
		delete this.callbackID;
		this.ship.remove();
		return;
	}

	this.ship.position = this.ship.target.position
};

this.shipDied = function (whom, why) { // in cases where the tag does manage to collide with the tracked entity, respawn the tag.
	if (why && why === "scrape damage" && whom && this.ship && this.ship.target && whom === this.ship.target) {
		var Tag = this.ship.spawnOne("armoury_trackerBeacon");
		Tag.target = this.ship.target;
	}
}
Scripts/armoury_trackerMissile.js
"use strict";
this.name = "armoury_trackerMissile.js";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "Ship script for the tag missile";
this.version = "1.00";

this.tagShip = function () {
	var Tag = this.ship.spawnOne("armoury_trackerBeacon");
	if (this.ship && this.ship.target && this.ship.target.isValid) {
		Tag.target = this.ship.target;
	}
	player.consoleMessage("Tracker deployed");
	this.ship.remove();
}
Scripts/armoury_trackerWorldScript.js
"use strict";
this.name = "armoury_trackerWorldScript.js";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "World script for the tracker beacon";
this.version = "1.00";

this.compassTargetChanged = function (whom, mode) {
	if (mode && mode === "COMPASS_MODE_BEACONS" && whom && whom.name === "Tracker Beacon") {
		if (whom.target) {
			player.consoleMessage("Tagged subject - " + whom.target.name);
		}
	}
}
Scripts/armoury_voidBomb.js
"use strict";
this.name = "armoury_voidBomb.js";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "Ship script for the void bomb";
this.version = "1.00";

this.detonate = function () {
	if (this.ship.subEntities.length > 0) {
		this.ship.subEntities[0].explode();
	}
	this.ship.displayName = "Void Bomb Singularity";
	this.voidTimer = new Timer(this, this.beginAttract, 0, 0.25);
}

this.playerWillEnterWitchspace = this.shipDied = this.stopTimer = function () {
	if (this.voidTimer && this.voidTimer.isRunning) {
		this.voidTimer.stop();
	}
}

this.beginAttract = function () {
	if (!this.ship) {
		this.stopTimer();
		return;
	}

	this.ship.velocity = [0, 0, 0];

	function isTarget(entity) {
		return (entity.isShip && entity.isValid);
	}

	var targetArray = system.filteredEntities(this, isTarget, this.ship, 25600)
	if (targetArray.length > 0) {
		var targetCounter = 0;
		for (targetCounter = 0; targetCounter < targetArray.length; targetCounter++) {
			this.targetRange = targetArray[targetCounter].position.distanceTo(this.ship.position);
			this.targetVector = this.ship.position.subtract(targetArray[targetCounter].position).direction();
			this.rangeFactor = (this.targetRange / 25600) * 0.8;
			targetArray[targetCounter].velocity = targetArray[targetCounter].velocity.multiply(this.rangeFactor + 0.1).add(this.targetVector.multiply(25 * (1 - this.rangeFactor)));
		}
	}
}

this.shipTakingDamage = function (amount, fromEntity, damageType) {
	this.ship.energy = this.ship.maxEnergy;
}

this.shipCollided = function (otherShip) {
	if (this.ship.AIState === "VOID") {
		otherShip.energy -= 64;
		this.ship.energy = this.ship.maxEnergy;
	}
}

/* Need to add dependence on mass perhaps? Also perhaps adjust the 10* multiplier for the applied velocity */
Scripts/armoury_wtfDecoy.js
"use strict";
this.name = "armoury_wtfDecoy.js";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "Ship script for the decoy";
this.version = "1.00";

this.shipSpawned = function () {
	function allShips(entity) {
		return entity.isShip && !entity.isRock && !entity.isCargo
	};
	let shipList = system.filteredEntities(this, allShips, this.ship, 25600);

	if (shipList.length === 0) {
		return;
	}

	var shipCounter = 0; // reset the counter
	for (shipCounter = 0; shipCounter < shipList.length; shipCounter++) {

		if (shipList[shipCounter].isPlayer) {
			continue;
		};

		if (shipList[shipCounter].isPolice) {
			shipList[shipCounter].target = this.ship;
			shipList[shipCounter].reactToAIMessage("ATTACKED");
			continue;
		}

		if (shipList[shipCounter].isTrader) {
			shipList[shipCounter].target = this.ship;
			shipList[shipCounter].reactToAIMessage("FLEE");
			continue;
		}

		if (shipList[shipCounter].isPirate && Math.random() > 0.5) {
			shipList[shipCounter].target = this.ship;
			shipList[shipCounter].reactToAIMessage("ATTACKED");
			continue;
		}
	}
}
Scripts/drones_Launcher.js
"use strict";
this.name = "drones_Launcher";
this.author = "Thargoid & Eric Walch";
this.copyright = "Creative Commons Attribution - Non-Commercial - Share Alike 3.0 license with clauses - see readme.txt.";
this.description = "Spawn ship with target mother";
this.version = "2.0";

this.shipDied = function () {
    let drone = this.ship.spawnOne(this.ship.scriptInfo.launch_name)
    drone.master = this.ship.owner
    if (this.ship.target) {
        drone.target = this.ship.target
        drone.position = this.ship.position
    }
    delete this.shipDied;
}
Scripts/drones_Pickup.js
"use strict";
this.name = "drones_Pickup";
this.author = "Thargoid & Eric Walsh";
this.copyright = "Creative Commons Attribution - Non-Commercial - Share Alike 3.0 license with clauses - see readme.txt.";
this.description = "Pickup of a drone after combat, and check if it's reusable (T). New JS Scanning routine (EW). Updated for 1.74";
this.version = "2.01";

this.shipWasScooped = function (scooper) {
	if (scooper.isPlayer) {
		let choice = (Math.ceil(Math.random() * 3));
		if (choice == 3) {
			player.consoleMessage(this.ship.name + " circuits blown - only good for scrap machinery.", 6);
			manifest["machinery"] += 1;
		} else {
			player.consoleMessage(this.ship.name + " recovered, recharged and ready for use.", 6);
			player.ship.awardEquipment(this.ship.scriptInfo.DroneEquipment)
		}
	}
}

this.findMastersHostiles = function () {
	function isHostileToMaster(entity) {
		return (entity.isShip && (entity.isThargoid || (entity.target && entity.target == this.ship.master && entity.hasHostileTarget)));
	}
	let targets = system.filteredEntities(this, isHostileToMaster, this.ship, 25600)
	if (targets.length > 0) {
		this.ship.target = targets[0];
		this.ship.reactToAIMessage("NEW_DRONE_TARGET");
		if (this.ship.master && this.ship.master.isPlayer) player.commsMessage(this.ship.name + ": Attacking " + targets[0].displayName)
	} else this.ship.reactToAIMessage("NOTHING_FOUND");
}

this.shipTargetDestroyed = function (target) {
	if (this.ship.master && this.ship.master.isPlayer) {
		player.score += 1;
		player.credits += target.bounty;
		player.consoleMessage("Drone kill - " + target.name + " : " + target.bounty + "? awarded.", 5);
	}
	log("Drone kill - " + target.name + " : " + target.bounty);

	if (target.primaryRole == "constrictor" && missionVariables.conhunt && missionVariables.conhunt == "STAGE_1") { // just in case the drone kills the constrictor, let's not break the mission for the player...
		missionVariables.conhunt = "CONSTRICTOR_DESTROYED";
	}
}
Scripts/missileRack_launcher.js
"use strict";
this.name = "missileRack";
this.author = "Thargoid, with optimisation by Eric Walsh";
this.copyright = "Creative Commons Attribution - Non-Commercial - Share Alike 3.0 license with clauses - see readme.txt.";
this.description = "Script for missile rack";
this.version = "1.0";

this.rackRefresh = function () {
	this.launchRack = this.ship.scriptInfo.launchingRack;
	switch (this.launchRack) {
		case "EQ_IRACK_0_MISSILE":
		case "EQ_NRACK_0_MISSILE":
		case "EQ_HRACK_0_MISSILE":
		case "EQ_SRACK_0_MISSILE":
			{
				player.consoleMessage("Missile rack empty - ejecting.", 6);
				player.ship.spawnOne("missileRack_spentRack");
				break;
			}
		default:
			{
				player.ship.awardEquipmentToCurrentPylon(this.launchRack);
				break;
			}
	}
}

// functionality for stun missiles	

this.performStun = function () {
	if (!this.ship.target ||
		!this.ship.target.isValid ||
		!this.ship.target.isShip ||
		this.ship.target.isPlayer ||
		this.ship.target.isStation ||
		this.ship.target.isRock ||
		this.ship.target.isCargo) {
		return;
	} else {
		this.ship.target.setAI("armoury_stunnedAI.plist");
	}
}
Scripts/probe_launcher.js
"use strict";
this.name = "probeLaunch";
this.author = "Thargoid";
this.copyright = "Creative Commons Attribution - Non-Commercial - Share Alike 3.0 license with clauses - see readme.txt.";
this.description = "Spawn probe with correct heading and AI";
this.version = "2.00";

this.shipDied = function () {
	let probe = this.ship.spawnOne('probe_probeMissile');
	let probeOrientation = this.ship.owner.orientation;
	let offsetVector = probeOrientation.vectorUp(); // vector at right angles to the ship/probe, downwards
	let spawnOffset = offsetVector.multiply(50); // set the offset to 50m downwards (for larger ships, increase 50).
	probe.orientation = probeOrientation; // point the probe in the same direction as the player
	probe.position = this.ship.position.subtract(spawnOffset); // move the probe to it's correct start point.
	probe.switchAI('probe_probeMissileAI.plist'); // confirm it's using the correct AI after spawn (no autoAI).
	delete this.shipDied;
}
Scripts/probe_operation.js
"use strict";
this.name = "probeMissile";
this.author = "Thargoid & Eric Walch";
this.copyright = "Creative Commons Attribution - Non-Commercial - Share Alike 3.0 license with clauses - see readme.txt.";
this.description = "Operation and pick-up of probe missile.";
this.version = "2.0";

this.shipWasScooped = function (scooper) {
	if (scooper.isPlayer) {
		player.consoleMessage("Probe missile recovered, refuelled and ready for re-use.", 6);
		player.ship.awardEquipment("EQ_PROBE_LAUNCHER_MINE")
	}
}

this.doScan = function () {
	// create an array with all ships ("isShip" includes barrels and rock etc.) 
	function allShips(entity) {
		return entity.isShip
	};
	let anyShips = system.filteredEntities(this, allShips, this.ship, 25600);

	// do neutral ships 
	function neutralShips(entity) {
		return entity.scanClass == "CLASS_NEUTRAL"
	};
	this.neutralCount = anyShips.filter(neutralShips).length;

	// do police/military ships 
	function policeShips(entity) {
		return (entity.scanClass == "CLASS_POLICE" || entity.scanClass == "CLASS_MILITARY")
	};
	this.policeCount = anyShips.filter(policeShips).length;

	// do thargoid ships 
	function thargoidShips(entity) {
		return entity.scanClass == "CLASS_THARGOID"
	};
	this.thargoidCount = anyShips.filter(thargoidShips).length;

	// do rocks 
	function rockShips(entity) {
		return entity.scanClass == "CLASS_ROCK" && !entity.isStation
	};
	this.rockCount = anyShips.filter(rockShips).length;

	// do stations 
	function stationShips(entity) {
		return (entity.scanClass == "CLASS_STATION" || entity.isStation)
	};
	this.stationCount = anyShips.filter(stationShips).length;

	// do cargo 
	function cargoShips(entity) {
		return entity.scanClass == "CLASS_CARGO"
	};
	this.cargoCount = anyShips.filter(cargoShips).length;

	// do trader ships 
	function isPirateVictim(entity) {
		return entity.isPirateVictim
	};
	this.traderCount = anyShips.filter(isPirateVictim).length;

	// do trader escorts 
	function isTraderEscort(entity) {
		return entity.primaryRole == "escort" && entity.owner && entity.owner.isPirateVictim
	};
	this.traderEscortCount = anyShips.filter(isTraderEscort).length;

	// do pirate ships 
	function isPirate(entity) {
		return entity.isPirate
	};
	this.pirateCount = anyShips.filter(isPirate).length;

	// do pirate escorts 
	function isPirateEscort(entity) {
		return entity.primaryRole == "escort" && entity.owner && entity.owner.isPirate
	};
	this.pirateEscortCount = anyShips.filter(isPirateEscort).length;

	this.totalCount = this.neutralCount + this.policeCount + this.thargoidCount + this.rockCount + this.stationCount + this.cargoCount;
	if (this.totalCount == 0) {
		player.commsMessage("Probe Missile - Nothing within scanner range.", 6);
	} else {
		player.commsMessage("Scan readout :", 6);
		if (this.traderCount + this.traderEscortCount > 0) player.commsMessage("Traders - " + this.traderCount + " (with " + this.traderEscortCount + " escorts)", 6);

		if (this.policeCount > 0) player.commsMessage("System vessels - " + this.policeCount, 6);
		if (this.pirateCount + this.pirateEscortCount > 0) player.commsMessage("Pirates - " + this.pirateCount + " (with " + this.pirateEscortCount + " escorts)", 6);
		if (this.thargoidCount > 0) player.commsMessage("Aliens - " + this.thargoidCount, 6);
		if (this.rockCount > 0) player.commsMessage("Asteroids - " + this.rockCount, 6);
		if (this.cargoCount > 0) player.commsMessage("Loose Cargo - " + this.cargoCount, 6);
		if (this.stationCount > 0) player.commsMessage("Stations - " + this.stationCount, 6);
		let otherShipsCount = this.neutralCount - this.pirateCount - this.pirateEscortCount - this.traderCount - this.traderEscortCount;
		if (otherShipsCount > 0) player.commsMessage("Other ships - " + otherShipsCount, 6);
	}
}

this.shipDied = function () {
	player.consoleMessage("Contact lost with probe missile.", 6);
}

this.probeLaunched = function () {
	player.consoleMessage("Probe missile launch successful.", 6);
}

this.setCoords = function () {
	let dest = this.ship.orientation.vectorForward().multiply(50000);
	this.ship.savedCoordinates = this.ship.position.add(dest);
}

this.probeReturning = function () {
	player.commsMessage("Probe Missile - Returning, please slow for rendezvous.", 6);
}

this.probeLost = function () {
	player.commsMessage("Probe Missile - Unable to reach ship for rendezvous, shutting down.", 6);
}

this.getPlayer = function () {
	this.ship.target = player.ship;
}