Back to Index Page generated: Sep 15, 2026, 9:03:40 PM

Expansion Extra Thargoids

Content

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 7.0 7.0
Tags thargoids, ships, system thargoids, ships, system
Required Oolite Version
Maximum Oolite Version
Required Expansions
Optional Expansions
  • oolite.oxp.UK_Eliter.Ferdelance_3G:7.0
  • oolite.oxp.UK_Eliter.InterstellarTweaks:8.0
  • oolite.oxp.UK_Eliter.Ferdelance_3G:7.0
  • oolite.oxp.UK_Eliter.InterstellarTweaks:8.0
  • Conflict Expansions
    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 1789431282

    Relationships Diagram

    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.

    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 Blue Meany
    Thargoid Meany Ball Turret
    Thargoid missile
    Poet Ball Turret
    extraThargoids_satellite
    Thargoid mine sweeper
    extraThargoids_tharglet_uncontrolled
    Thargoid UberBuggly

    Models

    This expansion declares no models.

    Scripts

    Path
    Scripts/extraThargoids_buggly.js
    /*
    ////////////////////////////////////////////////////////////////////////////////
    
    This file: 			nexus_warhead_basicOrPremiumOrUltimate.js 
    
    Associated AI(s): 	extraThargoids_kamikazeAI.plist
    					extraThargoids_bugglyAI.plist
    
    Part of:			extraThargoids OXP
    Author: 			UK_Eliter
    
    License: 			2017 Creative Commons: attribution, non-commercial, sharealike
    
    //////////////////////////////////////////////////////////////////////////////////
    */
    
    
    /*
    ==============
    LINTER OPTIONS
    ==============
    */
    
    /* jshint esversion: 6 */
    /* jshint -W030 */
    
    
    /*
    ======
    HEADER
    ======
    */
    
    
    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";
    
    
    
    /*
    ==================
    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.entityDestroyed = function() {
    		this.$deleteTimers();
    	};
    
    	this.playerWillEnterWitchspace = function() {
    		this.$deleteTimers();
    	};
    
    	this.shipDied = function() {
    		this.$deleteTimers();
    	};
    
    	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.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);
    	};
    
    
    	/*
    	------------
    	CALLED BY AI
    	------------
    	*/
    
    	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);
    	};
    
    
    	/*
    	-----
    	OTHER
    	-----
    	*/
    
    	this.$checkStunTimed = function $checkStunTimed() {
    		if (this.ship.AIState == 'STUNNED') {
    			this.ship.exitAI();
    			this.explode_notCausedByPlayer();
    		}
    	};
    
    	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.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.$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;
    		}
    	};
    
    	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.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);
    	};
    
    
    	/*
    	----------------------
    	JSLINT: end of wrapper
    	----------------------
    	*/
    
    }).call(this);
    
    // EOF
    Scripts/extraThargoids_exploding_generic.js
    /*
    ////////////////////////////////////////////////////////////////////////////////
    
    This file: 			extraThargoids_exploding_generic.js 
    
    Associated AI(s): 	extraThargoids_exploding_causedByPlayerAI
    					extraThargoids_exploding_notCausedByPlayerAI.plist
    
    Part of:			extraThargoids OXP
    
    Author: 			UK_Eliter
    
    License: 			2017 Creative Commons: attribution, non-commercial, sharealike
    
    //////////////////////////////////////////////////////////////////////////////////
    */
    
    
    /*
    ==============
    LINTER OPTIONS
    ==============
    */
    
    /* jshint esversion: 6 */
    /* jshint -W030 */
    
    
    /*
    ======
    HEADER
    ======
    */
    
    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' thargoid ships";
    
    
    /*
    ==================
    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
    /*
    ////////////////////////////////////////////////////////////////////////////////
    
    This file:          extraThargoids_frigatePoet.js 
    
    Associated AI(s):   thargoidAI (which is inbuilt)
    
    Part of:            extraThargoids OXP
    
    Author:             UK_Eliter
    
    License:            2017 Creative Commons: attribution, non-commercial, sharealike
    
    //////////////////////////////////////////////////////////////////////////////////
    */
    
    
    /*
    ==============
    LINTER OPTIONS
    ==============
    */
    
    /* jshint esversion: 6 */
    /* jshint -W030 */
    
    
    /*
    ======
    HEADER
    ======
    */
    
    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.entityDestroyed = function() {
            this.$cleanUp();
        };
    
        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.shipDied = function() {
            this.$cleanUp();
        };
    
        this.shipEnergyIsLow = function() {
            if (Math.random() > 0.13) {
                this.ship.isCloaked = false;
                this.$displace();
            }
        };
    
        this.shipSpawned = function() {
            this.canDisplaceCurrently = true;
            delete this.shipSpawned;
        };
    
        this.shipTargetCloaked = function() {
            if (Math.random() > 0.82) {
                this.$displace();
            }
            if (this.ship.target) {
                this.ship.target = null;
            }
        };
    
    
        /*
        -----
        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;
            }
            const ps = player.ship; // for speed, but changes won't affect the original variable
            const xDistance = ((Math.random() * 2) - 1) * 8600;
            const yDistance = ((Math.random() * 2) - 1) * 8600;
            const 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);
            }
            const 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;
                }
            }
            const 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
    /*
    //////////////////////////////////////////////////////////////////////////////////
    
    This file:          extraThargoids_populator.js
    Author:             UK_Eliter
    Part of:            extraThargoids OXP
    License:            2017 Creative Commons: attribution, non-commercial, sharealike
    
    //////////////////////////////////////////////////////////////////////////////////
    */
    
    /*
    ==============
    LINTER OPTIONS
    ==============
    */
    
    /* jshint esversion: 6 */
    /* jshint -W030 */
    
    
    /*
    ======
    HEADER
    ======
    */
    
    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';
    
    
        /*
        --------------
        EVENT HANDLERS
        --------------
        */
    
        this.entityDestroyed = function () {
            this.invasionExtantInCurrentSystem = false;
        };
    
        this.interstellarSpaceWillPopulate = function () {
            // INTERSTELLAR POPULATOR
            // Use 'callback: this._myFunction.bind(this)' - as Vs 'callback: this._myFunction' - in order to use global variables and global functions.
            // if two populator entries have same 'priority', Oolite will randomly determine their order.
            // ADD THARGOIDS
    
            system.setPopulator('extraThargs-interstellarThargoids', {
                priority: 101,
                callback: this._perhapsAddInterstellarThargoids.bind(this)
            });
        };
    
        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
        };
    
        // INTERSTELLAR _RE_POPULATOR - none
        /*
        this.interstellarSpaceWillRepopulate = function()
        {
        }
        */
    
        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);
            }
        };
    
        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.startUp = function () {
            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.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.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;
    
            this.repopulationsCounter++;
    
            if (Math.random() > 0.05) return;
    
            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);
                s = system.addShips('interceptor', 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;
                // 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
                }
            }
        };
    
        /*
        -----
        OTHER
        -----
        */
    
        this._addInvasionEntities = function (pos) {
            var
                baseThargoidNum,
                basePoliceNum,
                i,
                n,
                n2,
                r,
                s;
    
            // THARGOIDS
            if (player.score < 5000) {
                baseThargoidNum = 18;
            } else {
                baseThargoidNum = 23;
            }
    
            // 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;
                }
                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 {
                s = system.addShips('extraThargoids_uberBuggly', 2, pos, 37000); // has sweeper escorts
                baseThargoidNum -= 2;
            }
            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;
            }
            if (Math.random() < 0.5) {
                s = system.addShips('leviathan', 1, pos, 23000);
                basePoliceNum -= 2;
                if (s[0] && s[0].isValid) basePoliceNum -= 2;
            }
            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_uncontrolled', 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_uncontrolled', n2, Math.random(), 'wp');
                if (Math.random() < 0.3) {
                    s = system.addShipsToRoute('alloy', 1, r, 'wp');
                }
            }
            s = system.addShipsToRoute('extraThargoids_tharglet_uncontrolled', n, Math.random(), 'wp');
        };
    
        this._perhapsAddInterstellarThargoids = function () {
    
            if (!player.ship.isValid) return;
    
            // log('extraThargoids_populator.js', 'Considering adding thargoids to interstellar space by populator script.'); // TEST
            const pos = player.ship.position;
            var
                n,
                s;
            if (player.score > 3750) {
                n = 37;
            } else {
                n = 42;
            }
            switch (Math.ceil(Math.random() * n)) {
            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, 19000);
                break;
            case 10:
                s = system.addShips('extraThargoids_sweeper', 1, pos, 24000);
                break;
            case 11:
                s = system.addShips('extraThargoids_sweeper', 1, pos, 28000);
                break;
            case 12:
                s = system.addShips('extraThargoids_sweeper', 2, pos, 30000);
                break;
            case 13:
                s = system.addShips('extraThargoids_meany', Math.ceil(Math.random() * 8), pos, 20000);
                break;
            default:
            }
        };
    
        this.$is_targettedNonMainStation = function (e) {
            return e.isStation &&
                e.scanClass === 'CLASS_STATION' &&
                (e.position.distanceTo(system.mainPlanet.position) < system.mainPlanet.radius * 13);
        };
    
        this.$is_witchpointBuoy = 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_targettedNonMainStation, system.mainStation);
            for (i = 0; i < s.length; i++) {
                if (!s[i].isValid) {
                    continue;
                }
                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.$is_witchpointBuoy);
            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) continue;
                if ((i % 2 === 0) && (s[i].distanceToPlayer > 30000)) {
                    s[i].explode();
                } else {
                    s[i].remove(true); // no death action
                }
            }
        };
    
        this.$roleExists = function (roleString) {
            const dummyShips = system.addShips(roleString, 1, [100000, 100000, 100000]);
            if (dummyShips == null) return false;
            const exists = (dummyShips.length == 1);
            if (exists) dummyShips[0].remove(true);
            return exists;
        };
    
    
    
        /*
        ----------------------
        JSHINT: end of wrapper
        ----------------------
        */
    
    }).call(this);
    
    // EOF
    Scripts/extraThargoids_sweeper.js
    /*
    ////////////////////////////////////////////////////////////////////////////////
    
    This file:          extraThargoids_sweeper.js
    Associated AI(s):   extraThargoids_sweeperAI.plist
    Part of:            extraThargoids OXP
    Author:             UK_Eliter
    License:            2017 Creative Commons: attribution, non-commercial, sharealike
    
    //////////////////////////////////////////////////////////////////////////////////
    */
    
    
    /*
    ==============
    LINTER OPTIONS
    ==============
    */
    
    /* jshint esversion: 6 */
    /* jshint -W030 */
    
    
    /*
    ======
    HEADER
    ======
    */
    
    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.entityDestroyed = function () {
            this.$deleteTimers();
        };
    
        this.playerWillEnterWitchspace = function () {
            this.$deleteTimers();
        };
    
        this.shipDied = 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.$_validateAttackerAsTarget = function () {
            if (
                this.ship.target &&
                (!(this.ship.target.isThargoid) || this.ship.target.primaryRole === 'thargoidRebel')
            ) {
                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
                return;
            }
            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;
            }
            const ps = player.ship;
            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
    /*
    ////////////////////////////////////////////////////////////////////////////////
    
    This file:          extraThargoids_uber.js 
    Associated AI(s):   extraThargoids_uberAI.plist
    Part of:            extraThargoids OXP
    Author:             UK_Eliter
    License:            2017 Creative Commons: attribution, non-commercial, sharealike
    
    //////////////////////////////////////////////////////////////////////////////////
    */
    
    
    /*
    ==============
    LINTER OPTIONS
    ==============
    */
    
    /* jshint esversion: 6 */
    /* jshint -W030 */
    
    
    /*
    ======
    HEADER
    ======
    */
    
    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' Thargoid 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.entityDestroyed = function() {
            this.$_deleteTimers();
        };
    
        this.playerWillEnterWitchspace = function() {
            this.$_deleteTimers();
        };
        
        this.shipDied = function() {
            this.$_deleteTimers();
        };
    
        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);
        };
    
    
        /*
        ------------
        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