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

Expansion Extra Thargoids

Content

Warnings

  1. Unresolved dependency reference to Extra Thargoids:0
  2. No version in dependency reference to oolite.oxp.UK_Eliter.Ferdelance_3G:null
  3. No version in dependency reference to oolite.oxp.UK_Eliter.InterstellarTweaks:null
  4. No version in dependency reference to Extra Thargoids:null
  5. Optional Expansions mismatch between OXP Manifest and Expansion Manager at character position 0068 (DIGIT ZERO vs LATIN SMALL LETTER N)
  6. Conflict Expansions mismatch between OXP Manifest and Expansion Manager at character position 0049 (DIGIT ZERO vs LATIN SMALL LETTER N)
  7. Unknown key 'upload_date' at https://wiki.alioth.net/img_auth.php/8/8a/Oolite.oxp.UK_Eliter.Extra_Thargoids.oxz!manifest.plist

Manifest

from Expansion Manager's OXP list from Expansion Manifest
Description Adds new types of Thargoid, which are to be encountered especially in interstellar space. Adds new types of Thargoid, which are to be encountered especially in interstellar space.
Identifier oolite.oxp.UK_Eliter.ExtraThargoids oolite.oxp.UK_Eliter.ExtraThargoids
Title Extra Thargoids Extra Thargoids
Category Ships Ships
Author UK_Eliter UK_Eliter
Version 6.73 6.73
Tags thargoids, ships, system thargoids, ships, system
Required Oolite Version
Maximum Oolite Version
Required Expansions
Optional Expansions
  • oolite.oxp.UK_Eliter.Ferdelance_3G:0
  • oolite.oxp.UK_Eliter.InterstellarTweaks:0
  • oolite.oxp.UK_Eliter.Ferdelance_3G:
  • oolite.oxp.UK_Eliter.InterstellarTweaks:
  • Conflict Expansions
  • Extra Thargoids:0
  • Extra Thargoids:
  • Information URL http://aegidian.org/bb/viewtopic.php?t=10154&p=143763 n/a
    Download URL https://wiki.alioth.net/img_auth.php/8/8a/Oolite.oxp.UK_Eliter.Extra_Thargoids.oxz http://wiki.alioth.net/img_auth.php/8/8a/Oolite.oxp.UK_Eliter.Extra_Thargoids.oxz
    License Creative Commons Attribution - Non-Commercial - Share Alike 4.0 license Creative Commons Attribution - Non-Commercial - Share Alike 4.0 license
    File Size n/a
    Upload date 1647012498

    Documentation

    Also read http://wiki.alioth.net/index.php/Extra%20Thargoids

    Readme & License.txt

    ====================================
    'Extra Thargoids' OXP by 'UK_Eliter'
    ====================================
    
    -------------
    GENERAL NOTES
    -------------
    
    � This OXP adds various types of Thargoid. The most commonly (or least infrequently) encountered of these are (/will be) the 'Gruntbuggly' (which itself comes in various versions), the 'anti-poet' Frigate, the minesweeper.
    
    � This OXP works best with the 'Thargorn_Threat' OXP. (I have tested it with Thargorn_Threat version 1.5.)
    
    
    -------
    LICENSE
    -------
    
    This OXP is released under the Creative Commons Attribution - Non-Commercial - Share Alike 3.0 license.
    
    This work is licensed under the Creative Commons Attribution-NonCommercial-ShareAlike License. To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-sa/2.0/ or send a letter to Creative Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA. 
    
    You are free:
    
    � to copy, distribute, display, and perform the work;
    � to make derivative works. 
    
    But only under the following conditions.
    
    � Attribution: you must give the original author credit. 
    � Non-commercial use: you may not use this work for commercial purposes. 
    � Share Alike: if you alter, transform, or build upon this work, you may distribute the resulting work only under a license materially identical to this one. 
    
    Your fair use right and other rights are in no way affected by the above.
    
    ----------------
    ACKNOWLEDGEMENTS
    ----------------
    
    This OXP derives in considerable part from the Thargoid Threat OXP. That OXP was created by 'Arexack_Heretic' and modified by Eric Walch, who himself in so doing drew upon a version of the 'freaky thargoid shader' by Ahruman. The Thargoid Threat OXP contains this statement: 'Created under Creative Commons Licence, as expansion on Oolite, created by Giles Williams (Aegidian). Look the relevant stuff up at http://oolite.aegidian.org. In short however; anyone is welcome to expand to or use parts of this OXP as long as credit is given where due, and as long as not in any commercial enterprise.' I am happy to give that credit!
    
    The 'displacement' code in one of my scripts owes to Thargoid's Armoury OXP.
    
    The displacement sound is an OGG conversion of 'stk-DM300_18.wav', which is a sound provided, under the Creative Commons License and via www.freesound.org/, by user 'stk'.
    
    Other materials are adapted from stuff built-in to Oolite. For the license for Oolite itself, see the OoliteReadMe.pdf file included with that program.
    
    My thanks to the following for help with debugging and for ideas (though probably I omit some people here, by accident): cag; Fleurghber; Milo; Nite Owl; Staer9.
    
    
    ----------------- END ----------------- 

    Equipment

    This expansion declares no equipment. This may be related to warnings.

    Ships

    Name
    extraThargoids_TCAT_droneAndControllerOVERRIDE
    Thargoid Uberbuggly Turret
    Thargoid Gruntbuggly
    extraThargoids_buggly1b
    extraThargoids_buggly2
    extraThargoids_buggly2b
    Thargoid Gruntbuggly
    Thargoid Gruntbuggly Ball Turret
    extraThargoids_exploding_causedByPlayer
    Thargoid Gruntbuggly
    Terroriser Frigate (anti-poet variant)
    Thargoid Warship
    Thargoid missile
    Poet Ball Turret
    extraThargoids_satellite
    Thargoid mine sweeper
    extraThargoids_tharglet
    Thargoid UberBuggly

    Models

    This expansion declares no models. This may be related to warnings.

    Scripts

    Path
    Scripts/extraThargoids_buggly.js
    /*
    ========================================================================
    extraThargoids_buggly.js
    
    This file is part of the ExtraThargoids expansion pack.
    Author: UK_Eliter
    License: 2017 Creative Commons: attribution, non-commercial, sharealike.
    ========================================================================
    
    */
    
    /*
    -------------------
    JSLINT: set options
    -------------------
    */
    
    /*jshint esversion: 6*/
    /*jshint sub:true*/
    
    
    this.name = "extraThargoids_buggly";
    this.author = "UK_Eliter";
    this.copyright = "2014 Creative Commons: attribution, non-commercial, sharealike.";
    this.licence = "CC-NC-SA 3";
    this.description = "Script for the 'Gruntbuggly' Thargon ships";
    
    
    /*
    -----------------------------------------------------------------------
    Corresponding AIs:
    
    	- extraThargoids_bugglyAI.plist (which sends no messages to script)
    
    	- extraThargoids_kamikazeAI
    -----------------------------------------------------------------------
    */
    
    
    
    /*
    ------------------
    DEBUGGING SWITCHES
    ------------------
    */
    
    // this.logging = true;
    var consoleDebugMessages = false; // do NOT comment this out. Rather, to disable, set to false.
    
    
    /*
    ------
    SOUNDS
    ------
    */
    
    var mysound_Warning = new SoundSource();
    mysound_Warning.sound = "warning.ogg";
    //	This sound is built-in to Oolite - and does not need to be declared in the 'customsounds' file.
    
    
    /*
    ------------------------
    JSHINT: start of wrapper
    ------------------------
    */
    
    (function() {
    	"use strict";
    
    
    	/*
    	--------------
    	EVENT HANDLERS
    	--------------
    	*/
    
    	this.shipSpawned = function() {
    		// set timer to check for stun bomb from Armoury OXP - and give some buggly models missiles
    		if (worldScripts["armoury_machineScript.js"]) {
    			this.checkStun = new Timer(this, this.$checkStunTimed, 12, 12);
    		}
    		delete this.shipSpawned;
    	};
    
    	this.shipDied = this.entityDestroyed = this.playerWillEnterWitchspace = function() {
    		this.$deleteTimers();
    	};
    
    	this.shipTakingDamage = function(amount, whom, type) {
    		if ((amount < 50) && (this.ship.energy > 19)) {
    			return;
    		}
    		var chance;
    		if (amount > 49) {
    			if (this.ship.energy < 20) {
    				chance = 0.5;
    			} else {
    				chance = 0.1;
    			}
    		} else {
    			chance = 0.2; // this will mean both (this.ship.energy < 20) AND NOT (amount > 49)
    		}
    		if (this.ship.target && this.ship.target.isValid) {
    			this.distanceToTarget = this.ship.position.distanceTo(this.ship.target);
    			if (this.distanceToTarget < 650) {
    				chance += 0.67;
    			} else {
    				if (this.distanceToTarget < 1000) {
    					chance += 0.55;
    				} else if (this.distanceToTarget < 3000) {
    						chance += 0.3;
    				}
    			}
    		}
    		if (Math.random() < chance) {
    			this.ship.energy += 100 + amount; // stop it blowing up normally 
    			if (whom && whom.isPlayer) {
    				this.$explode_causedByPlayer();
    			} else {
    				this.explode_notCausedByPlayer();
    			}
    		}
    	};
    
    	this.shipTargetCloaked = function() {
    		if (this.decloakTargetTimer && this.decloakTargetTimer.isRunning) {
    			return;
    		}
    		if (this.ship.target.isThargoid) {
    			return;
    		}
    		var r = Math.random();
    		if (r < 0.16) {
    			return;
    		}
    		if (r > 0.75) {
    			this.ship.commsMessage(expandDescription("[thargoid_curses]"));
    		}
    		this.ship.cloakedTarget = this.ship.target;
    		var seconds = 0.5 + ~~(Math.random() * 8);
    		if (this.decloakTargetTimer) {
    			delete this.decloakTargetTimer;
    		}
    		this.decloakTargetTimer = new Timer(this, this.$decloakTargetTimed, seconds);
    	};
    
    
    	/*
    	---------------------------------------------------
    	ROUTINES CALLED BY EVENT HANDLERS
    	or called by the routines called by event handlers.
    	---------------------------------------------------
    	*/
    
    	this.explode_notCausedByPlayer = function() {
    		this.$deleteTimers();
    		if (!this.ship) {
    			return;
    		}
    		this.explodingShip = this.ship.spawnOne("extraThargoids_exploding_notCausedByPlayer");
    		this.explodingShip.newBounty = this.ship.bounty;
    		this.explodingShip.bounty = this.ship.bounty;
    		this.explodingShip.orientation = this.ship.orientation;
    		this.explodingShip.position = this.ship.position;
    		if (Math.random() < 0.4) {
    			this.ship.commsMessage("We boom your bases!");
    		}
    		if (!this.ship) {
    			return;
    		}
    		this.ship.remove(true);
    	};
    
    	this.$explode_causedByPlayer = function() {
    		this.$deleteTimers();
    		if (!this.ship) {
    			return;
    		}
    		if (Math.random() < 0.7) {
    			this.ship.commsMessage("We boom your bases!");
    		}
    		this.explodingShip = this.ship.spawnOne("extraThargoids_exploding_causedByPlayer");
    		this.explodingShip.newBounty = this.ship.bounty;
    		this.explodingShip.bounty = this.ship.bounty;
    		this.explodingShip.orientation = this.ship.orientation;
    		this.explodingShip.position = this.ship.position;
    		if (!this.ship) {
    			return;
    		}
    		this.ship.remove(true);
    	};
    
    	this.$decloakTargetTimed = function $decloakTargetTimed() {
    		this.decloakTargetTimer.stop();
    		if (!this.ship || !this.ship.isValid) {
    			delete this.decloakTargetTimer;
    			return;
    		}
    		if (!this.ship.cloakedTarget || !this.ship.cloakedTarget.isValid) {
    			delete this.decloakTargetTimer;
    			return;
    		}
    		if (!this.ship.cloakedTarget.isCloaked) {
    			delete this.decloakTargetTimer;
    			return;
    		}
    		this.distanceToTarget = this.ship.position.distanceTo(this.ship.cloakedTarget);
    		if (this.distanceToTarget < 10000) {
    			mysound_Warning.play();
    			this.ship.cloakedTarget.lightsActive = true;
    			if (Math.random() > 0.91) {
    				this.ship.cloakedTarget.setEquipmentStatus("EQ_CLOAKING_DEVICE", "EQUIPMENT_DAMAGED");
    				if (this.ship.cloakedTarget.isPlayer && !player.ship) {
    					delete this.decloakTargetTimer;
    					return;
    				}
    				this.ship.commsMessage("CLOAKING DEVICE DAMAGED!", this.ship.cloakedTarget);
    			} else {
    				player.consoleMessage("CLOAKING FAILURE!", 10);
    				player.ship.isCloaked = false;
    				this.ship.target = this.ship.cloakedTarget;
    				this.ship.reactToAIMessage("ATTACK_SHIP");
    			}
    		}
    		if (Math.random() < 0.25) {
    			this.ship.commsMessage(expandDescription("[thargoid_curses]"));
    		}
    		delete this.decloakTargetTimer;
    	};
    
    
    	this.$checkStunTimed = function $checkStunTimed() {
    		if (this.ship.AIState == "STUNNED") {
    			this.ship.exitAI();
    			this.explode_notCausedByPlayer();
    		}
    	};
    
    	this.$deleteTimers = function() {
    		if (this.decloakTargetTimer) {
    			if (this.decloakTargetTimer.isRunning) {
    				this.decloakTargetTimer.stop();
    			}
    			delete this.decloakTargetTimer;
    		}
    		if (this.checkStunTimed) {
    			if (this.checkStunTimed.isRunning) {
    				this.checkStunTimed.stop();
    			}
    			delete this.checkStunTimed;
    		}
    	};
    
    
    	/*
    	--------------------------------------------------------------------------
    	CALLED BY AI
    	These routines are called by this AI: 
    	extraThargoids_kamikazeAI (which itself is invoked, only, by the shipdata)
    	--------------------------------------------------------------------------
    	*/
    
    	this.$_seekPrimaryTarget = function() {
    		if (!this.ship) {
    			return;
    		}
    		var primaryTargetsArray = system.shipsWithRole("behemoth", this.ship, 25600);
    		if (primaryTargetsArray.length > 0) {
    			this.ship.target = primaryTargetsArray[0];
    			this.ship.reactToAIMessage("TARGET_FOUND");
    			return;
    		}
    		primaryTargetsArray = system.shipsWithRole("leviathan", this.ship, 25600);
    		if (primaryTargetsArray.length > 0) {
    			this.ship.target = primaryTargetsArray[0];
    			this.ship.reactToAIMessage("TARGET_FOUND");
    			return;
    		}
    		primaryTargetsArray = system.shipsWithRole("patrol-frigate", this.ship, 25600);
    		if (primaryTargetsArray.length > 0) {
    			this.ship.target = primaryTargetsArray[0];
    			this.ship.reactToAIMessage("TARGET_FOUND");
    			return;
    		}
    		primaryTargetsArray = system.shipsWithRole("navy-frigate", this.ship, 25600);
    		if (primaryTargetsArray.length > 0) {
    			this.ship.target = primaryTargetsArray[0];
    			this.ship.reactToAIMessage("TARGET_FOUND");
    		} else {
    			this.ship.reactToAIMessage("NO_PRIMARY");
    		}
    	};
    
    	this.$_rangeAchieved = function() {
    		if (!this.ship || !this.ship.isValid) {
    			return;
    		}
    		// else
    		this.explode_notCausedByPlayer();
    		var explosiveBits = this.ship.spawn("explosive_shrapnel", 5);
    	};
    
    
    	/*
    	----------------------
    	JSLINT: end of wrapper
    	----------------------
    	*/
    
    }).call(this);
    
    // EOF
    Scripts/extraThargoids_exploding_generic.js
    /*
    ========================================================================
    extraThargoids_exploding_generic.js
    
    This file is part of the ExtraThargoids expansion pack.
    Author: UK_Eliter
    License: 2017 Creative Commons: attribution, non-commercial, sharealike.
    ========================================================================
    */
    
    /*
    -------------------
    JSLINT: set options
    -------------------
    */
    
    /*jshint esversion: 6*/
    /*jshint sub:true*/
    
    
    this.name = "extraThargoids_exploding_generic";
    this.author = "UK_Eliter";
    this.copyright = "2014 Creative Commons: attribution, non-commercial, sharealike.";
    this.licence = "CC-NC-SA 3";
    this.description = "Script for exploding 'Gruntbuggly' Thargon ships";
    
    
    
    /*
    ------------------------------------------------------
    CORRESPONDING AIs:
      
      	- extraThargoids_exploding_causedByPlayerAI.plist
    	- extraThargoids_exploding_notCausedByPlayerAI.plist
    ------------------------------------------------------
    
    
    /*
    ------------------
    DEBUGGING SWITCHES
    ------------------
    */
    
    // this.logging = true; // THIS SHOULD BE COMMENTED OUT WHEN NOT TESTING
    // this.ship.reportAIMessages = true; // if set to true, continuously dumps AI stuff to log - and produces vector lines.
    // var consoleDebugMessages = false;
    
    
    /*
    ------
    SOUNDS
    ------
    */
    
    this.bigBang = new SoundSource();
    this.bigBang.sound = "bigbang.ogg";
    
    this.mediumBang = new SoundSource();
    this.mediumBang.sound = "extraThargoids_mediumBang.ogg";
    
    this.quietBang = new SoundSource();
    this.quietBang.sound = "extraThargoids_quietBang.ogg";
    
    
    /*
    ------------------------
    JSLINT: start of wrapper
    ------------------------
    */
    
    (function() {
    	"use strict";
    
    
    	/*
    	--------------
    	EVENT HANDLERS
    	--------------
    	*/
    
    	this.shipDied = this.entityDestroyed = function() {
    		var frags = this.ship.spawn("explosive_shrapnel", 3);
    	};
    
    
    	/*
    	-------------
    	CALLED BY AIs
    	-------------
    	*/
    
    	this.$_bountyHack = function() {
    		if (this.ship.isValid && this.ship.newBounty && (this.ship.newBounty > 0)) {
    			this.ship.bounty = this.ship.newBounty;
    		}
    	};
    
    	this.$_explode_core = function() {
    		this.ship.dealEnergyDamage(980, 1750);
    		// SYNTAX: function dealEnergyDamage(damage : Number, idealRange : Number [, velocityBias : Number])
    		this.$bangSoundEffect();
    	};
    
    	this.$_explode_notCausedByPlayer = function() {
    		this.$_explode_core();
    	};
    
    	this.$_explode_causedByPlayer = function() {
    		var bounty;
    		this.$_explode_core();
    		player.score += 1;
    		if (!this.ship.isValid || !this.ship || this.ship.bounty == 0) {
    			bounty = 200;
    		} else {
    			bounty = this.ship.bounty;
    		}
    		var string = "Bounty: " + bounty;
    		player.consoleMessage(string, 6);
    		player.credits += bounty;
    		if (player.ship.isValid && Math.random() < 0.15) {
    			string = "The navy has awarded a 500 credit bonus!";
    			player.consoleMessage(string, 8);
    			player.credits += 500;
    		}
    	};
    
    
    	/*
    	-----
    	OTHER
    	-----
    	*/
    
    	this.$bangSoundEffect = function() {
    		if (!this.ship || !this.ship.position || !player.ship.position) {
    			return;
    		}
    		var ps = player.ship; // for speed, but changes won't affect the original variable
    		if (this.ship.position.distanceTo(ps.position) < 2000) {
    			this.bigBang.play(); // this one really is VERY loud, so only use for VERY close
    			return;
    		}
    		if (this.ship.position.distanceTo(ps.position) < 15000) {
    			this.mediumBang.play();
    			return;
    		}
    		if (this.ship.position.distanceTo(ps.position) < 27000) {
    			this.quietBang.play();
    		}
    	};
    
    
    	/*
    	-----------------------
    	JSHINT: end of wrapper
    	----------------------
    	*/
    
    }).call(this);
    
    // EOF
    Scripts/extraThargoids_frigatePoet.js
    /*
    ========================================================================
    extraThargoids_frigatePoet.js
    
    This file is part of the ExtraThargoids expansion pack.
    Author: UK_Eliter
    License: 2017 Creative Commons: attribution, non-commercial, sharealike.
    ========================================================================
    */
    
    /*
    -------------------
    JSLINT: set options
    -------------------
    */
    
    /*jshint esversion: 6*/
    /*jshint sub:true*/
    
    
    this.name = "extraThargoids_frigatePoet";
    this.author = "UK_Eliter";
    this.copyright = "2014 Creative Commons: attribution, non-commercial, sharealike.";
    this.licence = "CC-NC-SA 3";
    this.description = "extraThargoids_frigatePoet";
    
    // NB: There is no special AI - but rather an inbuilt one - corresponding to this script.
    
    
    /*
    -------------
    VARIABLES etc
    -------------
    */
    
    this.displace = new SoundSource();
    this.displace.sound = "extraThargoids_displacement.ogg";
    
    this.quietDisplace = new SoundSource();
    this.quietDisplace.sound = "[extraThargods_displacementQuiet.ogg]";
    
    this.warning = new SoundSource();
    this.warning.sound = "warning.ogg"; // this sound built-in to Oolite - and does not need to be declared in the 'customsounds' file.
    
    
    /*
    ------------------------
    JSHINT: start of wrapper
    ------------------------
    */
    
    (function() {
    	"use strict";
    
    
    	/*
    	--------------
    	EVENT HANDLERS
    	--------------
    	*/
    
    	this.shipSpawned = function() {
    		this.canDisplaceCurrently = true;
    		delete this.shipSpawned;
    	};
    
    	this.shipDied = this.entityDestroyed = this.playerWillEnterWitchspace = function() {
    		this.$cleanUp();
    	};
    
    	this.shipAttackedWithMissile = function() // run by oolite core itself {
    	{
    		if (Math.random() > 0.7) {
    			return;
    		}
    		this.ship.isCloaked = true;
    		// but if can't cloak . .
    		if (this.ship.isCloaked === false) {
    			this.$displace();
    		}
    	};
    
    	this.shipTargetCloaked = function() {
    		if (Math.random() > 0.82) {
    			this.$displace();
    		}
    		if (this.ship.target) {
    			this.ship.target = null;
    		}
    	};
    
    	this.shipEnergyIsLow = function() {
    		if (Math.random() > 0.13) {
    			this.ship.isCloaked = false;
    			this.$displace();
    		}
    	};
    
    
    	/*
    	-----
    	OTHER
    	-----
    	*/
    
    	// Following code includes parts adapted from Thargoid's armoury_displacerMissile.js script in his Armoury OXP.
    
    	this.$displace = function() {
    		if (this.canDisplaceCurrently === false || !this.ship) {
    			return;
    		}
    		if (this.ship.fuel < 4) {
    			this.canDisplaceCurrently = false;
    			return;
    		}
    		this.canDisplaceCurrently = false;
    		if (this.timerToReenableDisplace) {
    			if (this.timerToReenableDisplace.isRunning) {
    				this.timerToReenableDisplace.stop(); // should never happen
    			}
    			delete this.timerToReenableDisplace;
    		}
    		if (!player.ship.position) {
    			return;
    		}
    		var ps = player.ship; // for speed, but changes won't affect the original variable
    		var xDistance = ((Math.random() * 2) - 1) * 8600;
    		var yDistance = ((Math.random() * 2) - 1) * 8600;
    		var zDistance = ((Math.random() * 2) - 1) * 8600;
    		this.ship.position = this.ship.position.add([xDistance, yDistance, zDistance]);
    		if (Math.random() < 0.7) {
    			this.ship.commsMessage(expandDescription("[thargoid_curses]"), ps);
    		}
    		var shipDistanceToDisplacedShip = this.ship.position.distanceTo(ps.position); // would be good to alter the following to occur to ANY ship within the specified range . .
    		var displacementChance = 0;
    		this.ship.position = this.ship.position.add([xDistance, yDistance, zDistance]);
    		this.ship.isCloaked = false;
    		if (shipDistanceToDisplacedShip < 1200) {
    			if (shipDistanceToDisplacedShip < 500) {
    				displacementChance = 0.5;
    			} else {
    				displacementChance = 0.2;
    			}
    		}
    		if (shipDistanceToDisplacedShip < 12000) {
    			this.displace.play();
    		} else if (shipDistanceToDisplacedShip < 18000) {
    			this.quietDisplace.play();
    		}
    		this.ship.fuel -= (Math.ceil(Math.random() * 5)); // drain off some fuel to simulate the jump
    		if (this.ship.target) {
    			if (Math.random() < 0.5) {
    				this.ship.target = null;
    			}
    		}
    		if (Math.random() <= displacementChance) {
    			player.ship.position = player.ship.position.add([xDistance, yDistance, zDistance]);
    			this.warning.play();
    			player.consoleMessage("EMERGENCY - position changed by nearby displacement device", 6);
    			if (Math.random() < 0.1) {
    				player.ship.scriptedMisjump = true;
    				player.ship.scriptedMisjumpRange = r;
    			}
    		}
    		var t = 8 + Math.ceil(Math.random() * 6);
    		this.timerToReenableDisplace = new Timer(this, this.$reenableDisplace, t);
    	};
    
    	this.$reenableDisplace = function $reenableDisplace() {
    		this.canDisplaceCurrently = true;
    	};
    
    	this.$cleanUp = function() {
    		this.canDisplaceCurrently = true;
    		if (this.timerToReenableDisplace) {
    			if (this.timerToReenableDisplace.isRunning) {
    				this.timerToReenableDisplace.stop();
    			}
    			delete this.timerToReenableDisplace;
    		}
    	};
    
    
    	/*
    	----------------------
    	JSLINT: end of wrapper
    	----------------------
    	*/
    
    }).call(this);
    
    // EOF
    Scripts/extraThargoids_populator.js
    /*
    ========================================================================
    extraThargoids_populator.js
    
    This file is part of the ExtraThargoids expansion pack.
    Author: UK_Eliter
    License: 2017 Creative Commons: attribution, non-commercial, sharealike.
    ========================================================================
    */
    
    
    /*
    -------------------
    JSLINT: set options
    -------------------
    */
    
    /*jshint esversion: 6*/
    /*jshint sub:true*/
    
    this.name = "extraThargoids_populator";
    this.author = "UK_Eliter";
    this.copyright = "2014 Creative Commons: attribution, non-commercial, sharealike.";
    this.licence = "CC-NC-SA 3";
    this.description = "This script adds some of the 'Extra Thargoids' to the game";
    
    
    /*
    --------
    SWITCHES
    --------
    */
    
    this.logging = false; // NB: Most log lines are commented out at present.
    
    
    /*
    ----------------
    GLOBAL VARIABLES
    ----------------
    */
    
    this.invasionChance = 0.003;
    // 0.003. Increased somewhat by player score . . - see this.startUp.
    
    
    /*
    ------------------------
    JSHINT: start of wrapper
    ------------------------
    */
    
    (function() {
    	"use strict";
    
    
    	/*
    	---------
    	MAIN CODE
    	---------
    	*/
    
    	this.startUp = function() {
    		this.threeG_Installed = (worldScripts.ferdelance3G_populator !== undefined);
    		var score = player.score;
    		if (score > 7000) {
    			if (score > 19200) {
    				this.invasionChance += 0.0035;
    			} else {
    				this.invasionChance += 0.0025;
    			} // 'Elite' score is 6400.
    		}
    		this.invasionExtantInCurrentSystem = false;
    		// log("Extra Thargoids", "Initialised OK");
    	};
    
    	this.playerWillEnterWitchspace = function() {
    		if (this.invasionExtantInCurrentSystem || this.invasionChance === 0 || player.score < 1500 || Math.random() > this.invasionChance) {
    			this.invasionExtantInCurrentSystem = false;
    		} else {
    			this.repopulationsCounter = 0;
    			this.invasionExtantInCurrentSystem = true; // well, will occur in the NEXT system.
    			this.maxRepopulations = 40 + Math.ceil(Math.random() * 100);
    			this.repopulationsCounter = 0;
    		}
    		// this.invasionExtantInCurrentSystem = true; // TEST
    	};
    
    	this.entityDestroyed = function() {
    		this.invasionExtantInCurrentSystem = false;
    	};
    
    	this.interstellarSpaceWillPopulate = function() {
    		// INTERSTELLAR POPULATOR
    		// Use 'callback: this._myFunction.bind(this)' not callback: this._myFunction' in order to use global variables and functions.
    		// if two populator entries have same 'priority', Oolite will randomly determine their order.
    		// if (Math.random() > 0.86) {return;}
    		// ADD THARGOIDS
    		system.setPopulator("extraThargs-interstellarThargoids", {
    			priority: 101,
    			callback: this._perhapsAddInterstellarThargoids.bind(this)
    		});
    	};
    
    	this._perhapsAddInterstellarThargoids = function(pos) {
    		//
    		// log("extraThargoids_populator.js", "One or more Thargoid(s) added to interstellar space by populator script.");
    		var n;
    		if (player.score > 3750) {
    			n = 36;
    		} else {
    			n = 40;
    		}
    		var c = Math.ceil(Math.random() * n);
    		var s;
    		switch (c) {
    			case 1:
    				s = system.addShips("extraThargoid", 1, pos, 27000);
    				break;
    			case 2:
    				s = system.addShips("extraThargoid", 1, pos, 27000);
    				break;
    			case 3:
    				s = system.addShips("extraThargoid", 2, pos, 27000);
    				break;
    			case 4:
    				s = system.addShips("extraThargoid", 3, pos, 30000);
    				break;
    			case 5:
    				s = system.addShips("extraThargoids_buggly3", 3, pos, 15000);
    				break;
    			case 6:
    				s = system.addShips("extraThargoid", 2, pos, 60000);
    				break;
    			case 7:
    				s = system.addShips("extraThargoid", 2, pos, 27000); /* falls through */
    			case 8:
    				s = system.addShips("extraThargoids_buggly2", 1, pos, 27000);
    				break;
    			case 9:
    				s = system.addShips("extraThargoids_sweeper", 1, pos, 20000);
    				break;
    			case 10:
    				s = system.addShips("extraThargoids_sweeper", 1, pos, 25000);
    				break;
    			case 11:
    				s = system.addShips("extraThargoids_sweeper", 1, pos, 28000);
    				break;
    			case 12:
    				s = system.addShips("extraThargoids_sweeper", 2, pos, 40000);
    				break;
    			default:
    		}
    	};
    
    	// INTERSTELLAR REPOPULATOR - NONE
    	/*
    	this.interstellarSpaceWillRepopulate = function()
    	{
    	}
    	*/
    
    	this.systemWillPopulate = function() {
    		// STELLAR POPULATOR
    
    		// LANE_PS, LANE_WP, LANE_WS, STATION_AEGIS, PLANET_ORBIT, OUTER_SYSTEM, OUTER_SYSTEM_OFFPLANE
    
    		// INVASION stuff
    		// Use 'callback: this._myFunction.bind(this)' not callback: this._myFunction' in order to use global variables and functions
    		if (this.invasionExtantInCurrentSystem === false) {
    			return;
    		} // Else - INVASION
    		// Add thargoids and other ships
    		system.setPopulator("extraThargs-invasionShips", {
    			groupCount: 1,
    			priority: 100,
    			location: "LANE_WP",
    			callback: this._addInvasionEntities.bind(this)
    		});
    		if (Math.random() > 0.8) {
    			return;
    		}
    		var gc = 1;
    		if (Math.random() < 0.3) {
    			gc += 1;
    		}
    		system.setPopulator("extraThargs-invasionShips_More", {
    			groupCount: gc,
    			priority: 101,
    			location: "LANE_WPS",
    			callback: this._addInvasionEntities.bind(this)
    		});
    	};
    
    	this.shipWillExitWitchspace = function() {
    		if (!this.invasionExtantInCurrentSystem) {
    			return;
    		}
    		// Under certain conditions, cancel it. (And can't do this earlier, i.e. not initiate in the first place, because of technical problems. .
    		// Or, rather, because cannot cancel in sense of undo the population function, just try to make sure all ships I've added leave.
    		if (system.isInterstellarSpace) {
    			this.invasionExtantInCurrentSystem = false;
    			return;
    			// This will not actually cancel anything, because interstellar - rather than stellar - populator will have been called.
    		}
    		// Else
    		if (system.sun.isGoingNova || system.sun.hasGoneNova) {
    			system.sendAllShipsAway();
    			this.invasionExtantInCurrentSystem = false;
    			return;
    		}
    		// Else
    		//
    		// Remove some existing ships - try to do this as late as possible
    		this.$removeShipsEtcForInvasion();
    	};
    
    	this.shipExitedWitchspace = function() {
    		if (this.invasionExtantInCurrentSystem) {
    			// this.invasionExtantInCurrentSystem will have been set to false if we are in interstellar space.
    			player.consoleMessage("** POLICE ALERT **: Thargoid invasion!", 300);
    		}
    		// var s = system.addShips("extraThargoids_uberBuggly", 1, [0,0,0], 22000); // TEST
    	};
    
    	this.$is_a_targettedNonMainStation = function(e) {
    		return e.isStation && e.scanClass === "CLASS_STATION" && (e.position.distanceTo(system.mainPlanet.position) < system.mainPlanet.radius * 13);
    	};
    
    	this.$isWitchpointBuoy = function(e) {
    		return e.isShip && e.scanClass === "CLASS_BUOY" && e.hasRole("buoy-witchpoint") && (e.position.distanceTo(system.mainPlanet.position) < system.mainPlanet.radius * 50);
    	};
    
    	this.$removeShipsEtcForInvasion = function() {
    		// Some populators might add stuff *after* this, so err on removing too much
    		var i, l, f, r, s;
    
    		// Stations
    		s = system.filteredEntities(this, this.$is_a_targettedNonMainStation, system.mainStation);
    		for (i = 0; i < s.length; i++) {
    			r = Math.random();
    			if (r < 0.5) {
    				if (r < 0.4) {
    					s[i].explode();
    				} else {
    					s[i].remove(true); // no death action
    				}
    			}
    		}
    		// Buoys
    		s = system.filteredEntities(this, this.$isWitchpointBuoy);
    		l = s.length;
    		if (l > 0) {
    			i = s.length;
    			while (i--) {
    				if (Math.random() < 0.6) {
    					s[i].remove(true);
    				}
    			}
    		}
    		// Neutral ships
    		s = system.entitiesWithScanClass("CLASS_NEUTRAL");
    		l = s.length;
    		if (l > 9) {
    			if (Math.random() < 0.6) {
    				f = 0.9;
    			} else {
    				f = 0.75;
    			}
    			// NB: The following will run before various (old-style) OXP populators
    			l = Math.ceil(s.length * f);
    			i = l;
    			while (l--) {
    				if (
    					(s[i].isValid) &&
    					s[i].isShip &&
    					!s[i].isPlayer &&
    					s[i].primaryRole !== "hunter-medium" &&
    					s[i].primaryRole !== "hunter-heavy" &&
    					!s[i].hasRole("ecl_escape_pod_beacon") &&
    					!s[i].hasRole("escape-capsule") &&
    					!s[i].hasRole("hiredGuns_escort")
    				) {
    					if ((i % 3 === 0) && (s[i].distanceToPlayer > 30000) && (s[i].distanceToPlayer < 3072000)) {
    						if (s[i].abandonShip === true) {
    							s[i].energy = (s[i].energy * f) - 100 - i;
    						} else if (i % 6 === 0) {
    							s[i].explode();
    						} else {
    							s[i].remove(true);
    						}
    					}
    				}
    			}
    		}
    		// Remove some rocks
    		s = system.entitiesWithScanClass("CLASS_ROCK", player.ship, 3072000);
    		if (s.length < 1) {
    			return;
    		}
    		l = Math.ceil(s.length * 0.35);
    		i = l;
    		while (i--) {
    			if (s[i].isValid) {
    				if ((i % 2 === 0) && (s[i].distanceToPlayer > 30000)) {
    					s[i].explode();
    				} else {
    					s[i].remove(true); // no death action
    				}
    			}
    		}
    	};
    
    	this._addInvasionEntities = function(pos) {
    		var baseThargoidNum, basePoliceNum, i, n, n2, r, s;
    
    		// THARGOIDS
    		if (player.score < 5000) {
    			baseThargoidNum = 18;
    		} else {
    			baseThargoidNum = 23;
    		}
    
    		// log("ET TESTING", "OK");
    
    		// Main action - carriers and other ships
    		if (this.$roleExists("thargoidCarrier") !== undefined && system.mainStation && Math.random() < 0.9) {
    			n = 1 + Math.ceil(Math.random() * 2);
    			if (n === 1 && Math.random() < 0.5) {
    				n++;
    			}
    			i = n;
    			while (i--) {
    				s = system.addShips("thargoid_carrier", 1, pos, 26000);
    				s[0].script.targetStation = system.mainStation;
    				s[0].script.destinationCoordinates = system.mainStation.position;
    				if (Math.random() < 0.6) {
    					s = system.addShips("extraThargoids_sweeper", 1, pos, 15000);
    				}
    			}
    			baseThargoidNum -= 5; // if (baseThargoidNum < 10) {baseThargoidNum = 10;}
    			if (Math.random() < 0.7) {
    				s = system.addShips("extraThargoids_uberBuggly", 1, pos, 20000); // has sweeper escorts
    				baseThargoidNum -= 1;
    			}
    		} else if (Math.random() < 0.92) {
    			s = system.addShips("extraThargoids_uberBuggly", 1, pos, 17000); // has sweeper escorts
    			baseThargoidNum -= 2;
    			if (Math.random() < 0.57) {
    				s = system.addShips("extraThargoids_uberBuggly", 1, pos, 20000); // has sweeper escorts
    				baseThargoidNum -= 1;
    			}
    		}
    		s = system.addShips("thargoid", baseThargoidNum, pos, 20000);
    		// Witchspace - planet
    		n = Math.ceil(Math.random() * baseThargoidNum);
    		s = system.addShipsToRoute("thargoid", baseThargoidNum, Math.random(), "wp");
    		// POLICE
    		// Main action - normal ships and carriers
    		basePoliceNum = baseThargoidNum + 2 + Math.ceil(Math.random() * 4);
    		if (Math.random() < 0.7) {
    			n = 1 + Math.ceil(Math.random() * 5);
    			s = system.addShips("interceptor", n, pos, 23000);
    			if (s[0] && s[0].isValid) {
    				basePoliceNum--;
    			}
    		}
    		r = Math.random();
    		if (r < 0.7) {
    			s = system.addShips("behemoth", 1, pos, 23000);
    			if (r < 0.35) {
    				s = system.addShips("behemoth", 1, pos, 32000);
    			}
    			if (s[0] && s[0].isValid) {
    				basePoliceNum -= 2;
    			} // have done no check actually are these ships installed
    		}
    		if (Math.random() < 0.5) {
    			s = system.addShips("leviathan", 1, pos, 23000);
    			basePoliceNum -= 2;
    			if (s[0] && s[0].isValid) {
    				basePoliceNum -= 2;
    			} // have done no check actually are these ships installed
    		}
    		if (basePoliceNum > 0) {
    			s = system.addShips("police", basePoliceNum, pos, 20000);
    		}
    		// Witchpoint - planet
    		if (Math.random() < 0.5) {
    			n = 1 + Math.ceil(Math.random() * basePoliceNum);
    			s = system.addShipsToRoute("police", n, Math.random(), "wp");
    		}
    		// HUNTERS
    		r = Math.random();
    		if (r < 0.25) {
    			n = 1 + Math.ceil(Math.random() * 2);
    			s = system.addShips("hunter-medium", n, pos, 35000);
    		}
    		if (r < 0.2) {
    			n = 1 + Math.ceil(Math.random() * 3);
    			s = system.addShips("hunter-heavy", n, pos, 30000);
    		}
    		// WRECKAGE. Note that removal of entities via removeShipsEtcForInvasion will tend to generate some wreckage.
    		// At the site of the main action
    		n = 1 + Math.ceil(Math.random() * 4);
    		s = system.addShips("alloy", n, pos, 45000);
    		n = 1 + Math.ceil(Math.random() * 9);
    		s = system.addShips("extraThargoids_tharglet", n, pos, 40000);
    		// Witchpoint
    		r = Math.random();
    		if (r < 0.6) {
    			n = 1 + Math.ceil(r * 3);
    			s = system.addShips("alloy", n, [0, 0, 0], 45000);
    			n = 1 + Math.ceil(r * 13);
    			s = system.addShips("extraThargoids_tharglet", n, [0, 0, 0], 40000);
    			if (Math.random() < 0.5) {
    				n2 = 1 + Math.ceil(Math.random() * 4);
    				s = system.addShips("cargopod", n2, [0, 0, 0], 4000);
    			}
    		}
    		// Witchpoint - planet
    		n = 1 + Math.ceil(Math.random() * 7);
    		i = n;
    		while (i--) {
    			n2 = 1 + Math.ceil(Math.random() * 4);
    			s = system.addShipsToRoute("cargopod", n2, Math.random(), "wp");
    		}
    		n = 1 + Math.ceil(Math.random() * 4);
    		i = n;
    		while (i--) {
    			n2 = 1 + Math.ceil(Math.random() * 4);
    			s = system.addShipsToRoute("extraThargoids_tharglet", n2, Math.random(), "wp");
    			if (Math.random() < 0.3) {
    				s = system.addShipsToRoute("alloy", 1, r, "wp");
    			}
    		}
    		s = system.addShipsToRoute("extraThargoids_tharglet", n, Math.random(), "wp");
    	};
    
    	this.systemWillRepopulate = function() {
    		// It is important for smooth gameplay that this function runs very quickly. If calculations are needed, run as many as possible in the populator function to save the result.
    		// 'systemWillRepopulate' does *not* use system.setPopulator 
    
    		if (this.invasionExtantInCurrentSystem === false) {
    			return;
    		}
    		// THE FOLLOWING ONLY RUNS *WHEN IS AN INVASION*
    		if (this.repopulationsCounter > this.maxRepopulations) {
    			return;
    		}
    		// Else
    		this.repopulationsCounter++;
    		if (Math.random() > 0.04) {
    			return;
    		}
    		// Else
    		var i, g, n, s, s2;
    		// Perhaps add police
    		if (Math.random() < 0.45) {
    			n = system.countShipsWithRole("police");
    			if (n > 50) {
    				return;
    			}
    			// Else
    			n = 3 + Math.ceil(Math.random() * 8);
    			if (this.threeG_Installed && r < 0.015) {
    				s = system.addShips("ferdelance3G_interstellar", n);
    				i = s.length;
    				while (i--) {
    					s[i].ai.switchAI("route1patrolAI.plist");
    					// Need to set AI to stop them trying to go interstellar
    				}
    			} else {
    				s = system.addShips("police", n);
    			}
    			return;
    		}
    		// perhaps add thargoids
    		if (Math.random() < 0.35) {
    			n = system.countShipsWithRole("thargoid");
    			if (n > 150) {
    				return;
    			}
    			n = 2 + Math.ceil(Math.random() * 30);
    			s = system.addShips("thargoid", n);
    			return;
    		}
    		// Else, add hunters
    		s = system.addShips("hunter-heavy", 1);
    		if (s[0]) {
    			g = new ShipGroup("hunter group vs invasion", s[0]);
    			s[0].group = g;
    			log("ET TESTING", "add hunter-heavy(" + g.name + ") to ...");
    			// TODO: Set weapons, skill, AI
    			if (s[0].autoAI) {
    				s[0].switchAI("oolite-bountyHunterLeaderAI.js");
    			}
    			n = 2 + Math.ceil(Math.random() * 5);
    			s2 = system.addShips("hunter", n);
    			for (i = 0; i < n; i++) {
    				s2[i].group = g;
    				g.addShip(s2[i]);
    				// s2[i].setAI("oolite-bountyHunterAI.js"); // autoAI
    			}
    			// log("ET TESTING", "added "+n+" hunters("+g.name+") to ...");
    		}
    	};
    
    	this.$roleExists = function(roleString) {
    		var dummyShips = system.addShips(roleString, 1, [100000, 100000, 100000]);
    		if (dummyShips == null) {
    			return false;
    		}
    		var exists = (dummyShips.length == 1);
    		if (exists) dummyShips[0].remove(true);
    		return exists;
    	};
    
    
    	/*
    	----------------------
    	JSHINT: end of wrapper
    	----------------------
    	*/
    
    }).call(this);
    
    // EOF
    Scripts/extraThargoids_sweeper.js
    /*
    ========================================================================
    extraThargoids_sweeper.js
    
    This file is part of the ExtraThargoids expansion pack.
    Author: UK_Eliter
    License: 2017 Creative Commons: attribution, non-commercial, sharealike.
    ========================================================================
    */
    
    
    /*
    -------------------
    JSLINT: set options
    -------------------
    */
    
    /*jshint esversion: 6*/
    /*jshint sub:true*/
    
    
    this.name = "extraThargoids_sweeper";
    this.author = "UK_Eliter";
    this.copyright = "2014 Creative Commons: attribution, non-commercial, sharealike.";
    this.licence = "CC-NC-SA 3";
    this.description = "Script for Thargon sweepers";
    
    
    /*
    ------------------
    DEBUGGING SWITCHES
    ------------------
    */
    
    //this.logging = true; // THIS SHOULD BE COMMENTED OUT WHEN NOT TESTING
    //var consoleDebugMessages = false;
    
    
    /*
    --------------
    VARIABLES etc.
    --------------
    */
    
    this.sweep = new SoundSource();
    this.sweep.sound = "extraThargoids_sweep.ogg";
    
    this.mediumSweep = new SoundSource();
    this.mediumSweep.sound = "extraThargoids_mediumSweep.ogg";
    
    this.quietSweep = new SoundSource();
    this.quietSweep.sound = "extraThargoids_quietSweep.ogg";
    
    
    /*
    ------------------------
    JSHINT: start of wrapper
    ------------------------
    */
    
    (function() {
    	"use strict";
    
    
    	/*
    	--------------
    	EVENT HANDLERS
    	--------------
    	*/
    
    	this.shipSpawned = function() {
    		// If the scanTimer does not exist, create it.
    		// If it does exist, and it is not running, start it.
    		// Then - as usual - remove the ship object created by this function.
    		if (!this.$scanTimer) {
    			this.$scanTimer = new Timer(this, this.$scan, 1, 2.8);
    		} else if (!this.$scanTimer.isRunning) {
    			this.$scanTimer.start();
    		}
    		delete this.shipSpawned;
    	};
    
    	this.shipDied = this.entityDestroyed = this.playerWillEnterWitchspace = function() {
    		this.$deleteTimers();
    	};
    
    
    	/*
    	------------
    	CALLED BY AI
    	------------
    	*/
    
    	this.$_incomingMissile = function() {
    		// This run (only) by extraThargoids_sweeperAI
    		if (this.$incomingMissileTimer) {
    			if (this.$incomingMissileTimer.isRunning) {
    				return;
    			} else {
    				this.$incomingMissileTimer.start();
    			}
    		} else {
    			this.$incomingMissileTimer = new Timer(this, this.$incomingMissileActual, 0.2);
    		}
    	};
    
    	this.$_validateTarget = function() {
    		if (this.ship.target && !this.ship.target.isThargoid) {
    			this.ship.reactToAIMessage("TARGET_VALID");
    		} else {
    			this.ship.reactToAIMessage("TARGET_INVALID");
    		}
    	};
    
    	this.$_sophistatedBeligerentAttackResponse = function() {
    		// = if has this missile type, fire it, else . .
    		if (this.ship.fireMissile("EQ_THARGON") == null) {
    			this.ship.reactToAIMessage("DO_ATTACK");
    		}
    	};
    
    	this.$_checkForPossiblyFarThargoids = function() {
    		var s = system.filteredEntities(this, this.$isBug, this.ship, 55000);
    		if (s.length > 0) {
    			this.ship.reactToAIMessage("TARGET_FOUND"); // AI does actual SETTING of the target
    		} else if (s.length > 0) {
    			this.ship.reactToAIMessage("NOTHING_FOUND");
    		}
    	};
    
    
    	/*
    	-------
    	FILTERS
    	-------
    	*/
    
    	this.$poppable = function(e) {
    		return e.isShip && (e.isMissile || e.isMine);
    	};
    
    	this.$isBug = function(e) {
    		return (e.isShip && e.isThargoid);
    	};
    
    	this.$isTarget = function(e) {
    		return e.isShip && (e.isMissile || e.isMine) && e.primaryRole !== "EQ_BUGGLYMISSILE" && e.name !== "Thargoid Projectile";
    	};
    
    
    	/*
    	-------
    	GENERAL
    	-------
    	*/
    
    	this.$scan = function() {
    		/*
    		MASSES:
    			MM Hardened Missile: 114 (!)M
    			Nexus premium warhead: 67ish
    			mini-missile mass: 114
    			intercept missile mass: 210.3
    			q-bomb: 135
    			normal mass: 1324
    			hardhead mass: 1638
    			TCAT_claw: 472.5
    			TCAT_talon: 135
    			s[c].hasRole("EQ_HARDENED_MISSILE") || s[c].hasRole("armoury_MMHardMissile" || s[c].hasRole("EQ_MISSILE")
    		*/
    
    		if (!this.ship) {
    			return;
    		}
    		if (this.$incomingMissileTimer && this.$incomingMissileTimer.isRunning) {
    			return;
    		}
    		this.ship.lightsActive = false; // turn them off, in case they've been turned on before
    		if (this.ship.energy < 45) {
    			return;
    		}
    		this.ship.energy -= 1; // merely scanning takes a bit of energy
    		var s = system.filteredEntities(this, this.$isTarget, this.ship, 26000);
    		var num = s.length;
    		if (num === 0) {
    			return;
    		}
    
    		// What follows  starts going through all ordinance at large, and stops when the sweeper detects one of those pieces (or when reaches end of list)
    		var detected = false;
    		var chance;
    		var c = num;
    		while (c--) {
    			if (s[c].mass < 125) {
    				chance = 0.05;
    			} else {
    				chance = 0.86;
    			}
    			if (this.ship.position.distanceTo(s[c]) < 15000) {
    				chance += 0.12;
    			}
    			if (Math.random() <= chance) {
    				detected = true;
    				break;
    			}
    		}
    		// if has been a detection, sweeper fires its special ECM . .
    		if ((detected) && (this.ship.energy > 14)) {
    			this.$actualSweep();
    		}
    	};
    
    	this.$actualSweep = function() {
    		this.ship.lightsActive = true;
    		this.ship.energy -= 14; // takes more energy to destroy (or try to destroy, anyway) something
    		var s = system.filteredEntities(this, this.$poppable, this.ship, 26000);
    		var num = s.length;
    		if (num > 0) {
    			var c = num;
    			var chance;
    			while (c--) {
    				// log("EXTRA", s[c].primaryRole);
    				switch (s[c].primaryRole) {
    					case "TCAT_talon":
    						chance = 0.05;
    						break;
    					case "TCAT_claw":
    						chance = 0.035;
    						break;
    					case "EQ_BUGGLYMISSILE":
    						chance = 0.01;
    						break;
    					case "EQ_F3G_ANTISWEEP_MISSILE":
    						chance = 0.02;
    						break;
    					default:
    						chance = 0.97;
    				}
    				if (Math.random() <= chance) {
    					var damage = 16 + (Math.ceil(Math.random() * 40));
    					if (s[c].energy <= damage) {
    						s[c].explode();
    					} else {
    						s[c].energy -= damage;
    					}
    				}
    			}
    		}
    		if (!this.ship || !player.ship) {
    			return;
    		}
    		var ps = player.ship; // for speed, but changes won't affect the original variable
    		var distanceToPlayer = this.ship.position.distanceTo(ps);
    		if (distanceToPlayer < 28000) {
    			if (distanceToPlayer < 13000) {
    				this.sweep.play();
    				if (Math.random() > 0.83) {
    					this.ship.commsMessage(expandDescription("[thargoid_curses]", ps));
    				}
    			} else if (distanceToPlayer < 17500) {
    				this.mediumSweep.play();
    				if (Math.random() > 0.9) {
    					this.ship.commsMessage(expandDescription("[thargoid_curses]", ps));
    				}
    			} else {
    				this.quietSweep.play();
    				if (Math.random() > 0.95) {
    					this.ship.commsMessage(expandDescription("[thargoid_curses]", ps));
    				}
    			}
    		}
    	};
    
    	this.$incomingMissileActual = function $incomingMissileActual() {
    		this.$incomingMissileTimer.stop();
    		if (this.ship.energy > 14) {
    			this.$actualSweep();
    		} else {
    			this.ship.reactToAIMessage("GO_FLEE");
    		}
    	};
    
    
    	/*
    	-------
    	CLEANUP
    	-------
    	*/
    
    	this.$deleteTimers = function() {
    		this.$deleteIncomingMissileTimer();
    		this.$deleteScanTimer();
    	};
    
    	this.$deleteIncomingMissileTimer = function() {
    		if (!this.$incomingMissileTimer) {
    			return;
    		}
    		if (this.$incomingMissileTimer.isRunning) {
    			this.$incomingMissileTimer.stop();
    		}
    		delete this.$incomingMissileTimer;
    	};
    
    	this.$deleteScanTimer = function() {
    		if (!this.$scanTimer) {
    			return;
    		}
    		if (this.$scanTimer.isRunning) {
    			this.$scanTimer.stop();
    		}
    		delete this.$scanTimer;
    	};
    
    
    	/*
    	----------------------
    	JSHINT: end of wrapper
    	----------------------
    	*/
    
    }).call(this);
    
    // EOF
    Scripts/extraThargoids_uber.js
    /*
    ========================================================================
    extraThargoids_uber.js
    
    This file is part of the ExtraThargoids expansion pack.
    Author: UK_Eliter
    License: 2017 Creative Commons: attribution, non-commercial, sharealike.
    ========================================================================
    */
    
    /*
    -------------------
    JSLINT: set options
    -------------------
    */
    
    /*jshint esversion: 6*/
    /*jshint sub:true*/
    
    
    
    this.name = "thargoid_uberbuggly";
    this.author = "UK_Eliter";
    this.copyright = "2014 Creative Commons: attribution, non-commercial, sharealike.";
    this.licence = "CC-NC-SA 3";
    this.description = "Script for the 'uberbuggly' Thargon ships";
    
    
    /*
    ------------------
    DEBUGGING SWITCHES
    ------------------
    */
    
    // this.logging = true;
    var consoleDebugMessages = false; // do NOT comment this out. Rather, to disable, set to false.
    
    
    /*
    ------
    SOUNDS
    ------
    */
    
    var mysound_Warning = new SoundSource();
    mysound_Warning.sound = "warning.ogg"; // this sound built-in to Oolite - and does not need to be declared in the 'customsounds' file.
    
    
    /*
    ------------------------
    JSHINT: start of wrapper
    ------------------------
    */
    
    (function() {
    	"use strict";
    
    
    	/*
    	--------------
    	EVENT HANDLERS
    	--------------
    	*/
    
    	this.shipTargetCloaked = function() {
    		var seconds;
    
    		if (this.decloakTargetTimer && this.decloakTargetTimer.isRunning) {
    			return;
    		}
    		if (this.ship.target.isThargoid) {
    			return;
    		}
    		if (Math.random() < 0.13) {
    			return;
    		}
    		if (Math.random() > 0.4) {
    			this.ship.commsMessage(expandDescription("[thargoid_curses]"));
    		}
    		this.ship.cloakedTarget = this.ship.target;
    		seconds = (1 + Math.floor(Math.random() * 6));
    		if (this.decloakTargetTimer) {
    			delete this.decloakTargetTimer;
    		}
    		this.decloakTargetTimer = new Timer(this, this.$decloakTargetTimed, seconds);
    	};
    
    	this.shipDied = this.entityDestroyed = this.playerWillEnterWitchspace = function() {
    		this.$_deleteTimers();
    	};
    
    
    	/*
    	------------
    	CALLED BY AI
    	------------
    	*/
    
    	this.$_deleteTimers = function() {
    		if (!this.decloakTargetTimer) {
    			return;
    		}
    		if (this.decloakTargetTimer.isRunning) {
    			this.decloakTargetTimer.stop();
    		}
    		delete this.decloakTargetTimer;
    	};
    
    	this.$_deleteTimersAndSetMisJump = function() {
    		this.ship.scriptedMisjump = true;
    		this.$_deleteTimers();
    	};
    
    	this.$_curse = function() {
    		this.ship.commsMessage(expandDescription("[thargoid_curses]"));
    	};
    
    	this.$_possiblyCurse = function() {
    		if (Math.random() < 0.35) {
    			this.ship.commsMessage(expandDescription("[thargoid_curses]"));
    		}
    	};
    
    	this.$_fireQuirium = function() {
    		this.ship.awardEquipment("EQ_QC_MINE");
    		this.ship.fireMissile("EQ_QC_MINE");
    		this.$_possiblyCurse();
    		this.$_deleteTimers();
    	};
    
    
    	/*
    	-----
    	OTHER
    	-----
    	*/
    
    	this.$decloakTargetTimed = function() {
    		this.decloakTargetTimer.stop();
    		if (!this.ship || !this.ship.isValid) {
    			delete this.decloakTargetTimer;
    			return;
    		}
    		if (!this.ship.cloakedTarget || !this.ship.cloakedTarget.isValid) {
    			delete this.decloakTargetTimer;
    			return;
    		}
    		if (!this.ship.cloakedTarget.isCloaked) {
    			delete this.decloakTargetTimer;
    			return;
    		}
    		this.distanceToTarget = this.ship.position.distanceTo(this.ship.cloakedTarget);
    		if (this.distanceToTarget < 10000) {
    			mysound_Warning.play();
    			this.ship.cloakedTarget.lightsActive = true;
    			if (Math.random() > 0.9) {
    				this.ship.cloakedTarget.setEquipmentStatus("EQ_CLOAKING_DEVICE", "EQUIPMENT_DAMAGED");
    				if (this.ship.cloakedTarget.isPlayer && !player.ship) {
    					delete this.decloakTargetTimer;
    					return;
    				}
    				this.ship.commsMessage("CLOAKING DEVICE DAMAGED!", this.ship.cloakedTarget);
    				this.ship.target = this.ship.cloakedTarget;
    				this.ship.reactToAIMessage("TARGET_REGAINED");
    			} else {
    				player.consoleMessage("CLOAKING FAILURE!", 10);
    				player.ship.isCloaked = false;
    				this.ship.target = this.ship.cloakedTarget;
    				this.ship.reactToAIMessage("TARGET_REGAINED");
    			}
    		}
    		if (Math.random() < 0.26) {
    			this.ship.commsMessage(expandDescription("[thargoid_curses]"));
    		}
    		delete this.decloakTargetTimer;
    	};
    
    
    	/*
    	----------------------
    	JSHINT: end of wrapper
    	----------------------
    	*/
    
    }).call(this);
    
    // EOF