Back to Index Page generated: Dec 20, 2024, 7:22:09 AM

Expansion Aquatics

Content

Warnings

  1. Unknown key 'requires_oxp' at https://wiki.alioth.net/img_auth.php/f/f1/Aquatics_2.30.oxz!manifest.plist

Manifest

from Expansion Manager's OXP list from Expansion Manifest
Description Various new ships, plus a system station with a mission Various new ships, plus a system station with a mission
Identifier oolite.oxp.Thargoid.Aquatics oolite.oxp.Thargoid.Aquatics
Title Aquatics Aquatics
Category Ships Ships
Author Thargoid Thargoid
Version 2.32 2.32
Tags station, equipment, ships, mission station, equipment, ships, mission
Required Oolite Version
Maximum Oolite Version
Required Expansions
Optional Expansions
Conflict Expansions
Information URL http://wiki.alioth.net/index.php/Aquatics_OXP n/a
Download URL https://wiki.alioth.net/img_auth.php/f/f1/Aquatics_2.30.oxz n/a
License Creative Commons Attribution - Non-Commercial - Share Alike 3.0 license with clauses - see readme file Creative Commons Attribution - Non-Commercial - Share Alike 3.0 license with clauses - see readme file
File Size n/a
Upload date 1610873511

Documentation

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

Aquatics v2.32 Readme & License.txt

Aquatics OXP by Thargoid.

New ships from the Aquarian Shipbuilding Corporation of Aqualina in Galaxy 3. 

Ships:

Manta Ray - A fast interceptor fighter. Popular with the military and Galcop as well as privateer traders. A player version of the ship is also available, a good alternative to the Cobra Mk III.
Shark - An escort fighter, cheaper but less formidable than it's big sister the Manta Ray. Often seen in an escort role for larger ships. Also available for use by the player, if a good but cheaper ship is needed.
Box - A smaller light escort ship, usually seen in convoy with the Man O' War, which serves as a support tender and mobile base.
Man O' War - Originally a versatile exploration ship used by the Aqualinians for first contact with neighbouring races. Now often seen plying the space lanes, although too large to dock with standard Coriolis and Dodec space stations. Usually accompanied by Box escorts.
Conger - A general purpose cargo hauler. Owing to its being often targetted by the less savoury elements in the galaxy, usually escorted in convoy by Shark fighters.
Hawksbill - General purpose tanker, most often seen hauling witchspace fuel. Often escorted due to the high value that a full load commands on the open market.



Update - Version 2.00 brings several new ships, a mission plus an easter egg or two ;)

Police & Military Manta Ray - These ships now sport some additional weaponry compared to their civilian counterparts. Not available on the open market.
Barracuda - Small sleek fighter craft. Designed for minimal visibility and high speed, they are favoured by bounty hunters, escort pilots and law enforcement departments. Unfortunately also popular on the black market with less savoury types.
HammerHead Haulers - Magnificent hauler craft, carrying several cargo sleds at a time. The backbone of the inter-system transport and logistics network arm of the Corporation.
Cargo sled - As carried by the HammerHead, these sleds function as simple shifters for mass cargo containers. Unarmed and quite large, only designed for short-range journeys between their carrier and the system station.
Leviathan platform - With the rise of the Thargoid threat, system and witchpoint defense entities to guard against alien and hostile marauders.
Orca class Destroyer - Massive military ships. Well-armed and deadly, if a little on the slow side. Designed for heavy combat and military war campaigns.
Headquarters - A status symbol of the Corporation, their orbital HQ around Aqualina is a massive station which also serves as the main station for the system. Mainly for administration and R&D, it also houses the Corporation's security and financial logistic arms plus full ship repair facilities and retail opportunities.

Mission - 

New mission for v2.00, chase down and destroy the hijacked AHC Kraken IV before it can leave the system. The good will of the Corporation and all that may entail plus a sizable credit bonus await the successful commander. Only available to commanders of Dangerous rank or above. Go and visit the HQ station in Aqualina for more details.

A save-game is included for those who just want to see the new station. Commander Visitor is docked and ready for the last leg of his journey to the Aqualina system. He is an old friend of the corporation, having already helped them out with the Kraken IV in the past.

Requirements -

This OXP also requires BigShips.oxp, which is included in the package. This will need to be unzipped separately and added to the AddOns folder. It is a populator for large traders (such as the Man O' War and the Hawksbill), to prevent them overloading the spacelanes and from launching from and docking with the main stations. See the readme file within the zip file for details.

It needs Oolite 1.77 to run, and will not work with 1.76.1 or any lower version. 

--------------------------------------------------------------
External controls

The appearance of the system platforms can now be suppressed by external OXPs to prevent problems with missions. To do this simply set

worldScripts["aquatics_populator"].allowInterstellar = false;

or 

worldScripts["aquatics_populator"].allowPlatforms = false;

These will suppress the platforms appearance at either the next entry into interstellar space or into the next system respectively. Note that upon arrival the variables will self-reset.
--------------------------------------------------------------
License:

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

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

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

Unzip the file, and then move the folder "Aquatics 2.31.oxp" to the AddOns directory of your Oolite installation, and also the enclosed zip file of bigShips oxp on which this OXP relies. Then start the game up and the ships should be added. 

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


Version history:

16/08/2008 - First release, version 1.00
18/08/2008 - Version 1.01, big-ships AI improvements suggested by Eric Walsh
31/08/2008 - Version 1.02, shipdata.plist refinement from Commander McLane's running of verify-OXP on it.
04/11/2008 - Version 1.03, update of scripting for v1.72 compatability.
24/01/2009 - Version 1.04, bugfix update to shipdata.plist and scripts.
18/05/2009 - Version 2.00, major OXP update.
19/05/2009 - Version 2.01, minor bugfix for Guardian system, plus change role of destroyer from police to hunter.
28/05/2009 - Version 2.02, a couple of minor bugfixes, plus some code tidying.
28/08/2009 - Version 2.10, update for Oolite 1.73 compatibility.
05/09/2009 - Version 2.11, tweak to adjust position of lasers on player ships.
18/03/2010 - Version 2.20, update for Oolite 1.74 (not compatible with earlier versions)
13/02/2011 - Version 2.21, removal of upper limit, to allow running with 1.75
24/02/2011 - Version 2.22, minor script adjustment for the hammerhead due to the removal of the -w orientation bug in 1.75
23/03/2011 - Version 2.23, script modification to remove problem with scripted commsMessages. Also increased range of platform turrets to 20km.
27/06/2011 - Version 2.24, script modification to allow external suppression of system platforms (for mission compatibility).
17/12/2011 - Version 2.25, shader update for Hammerhead to be compatible with newer ATI/AMD drivers.
08/01/2012 - Version 2.26, minor correction on turret ranges in shipdata.plist
29/01/2012 - Version 2.27, script change to remove score cheat, plus shipdata update of sub-ents.
31/03/2012 - Version 2.28, minor script adjustment for a missing variable set to properly allow system platforms.
08/01/2013 - Version 2.30, major update for Oolite 1.77 (not compatible with earlier versions).
10/10/2014 - Version 2.31, changes to the Aqualina system - separate HQ station and planetary ring station.

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

Acknowledgements:

V1.00 - With thanks to Ahruman, LittleBear and Eric Walsh for their assistance via the Oolite Bulletin forum when I got stuck, and to Lestradae for ship pricing advice and test-piloting.

V2.00 - Some texturing by PAGroove, and shaders by Griff. Company logo design by Deepspace. Everything else herein is my fault :)

V2.22 - A nod to Eric Walch for reminding me about the removal of the orientation -w bug in Oolite 1.75.

V2.24 - By request of Svengali.

V2.28 - With thanks to Cim.

V2.30 - More thanks to both Cim and Svengali for visual effect and break patterns in the HQ.

Equipment

Name Visible Cost [deci-credits] Tech-Level
Guardian Defense System yes 50000 13+
Thargon Jammer Mine yes 10000 13+
Trident Missile yes 5000 13+
Cloaking Device yes 500000 15+
Guardian Defense System yes 50000 15+
Thargon Jammer Mine yes 10000 15+
Trident Missile yes 5000 15+

Ships

Name
Aquarian Shipbuilding Corp. Shipyards
Aquarian Shipbuilding Corp. HQ
HQ Dock
Barracuda
Barracuda
Police Barracuda
Renegade Barracuda
Box Escort
Box Escort Ring
Burning witchspace fuel
Cargo Sled
Conger Cargo Hauler
Conger Pod 1
Conger Pod 2
Conger Pod 3
Conger Pod 4
Orca Class Destroyer
Fugu Tanker
Fugu Tank
Guardian Drone
aquatics_guardianPlayer
HammerHead Hauler Carrier
Hawksbill Fuel Tanker
Thargon Jamming Mine
AHC Kraken IV (Hijacked)
Security Patrol
Security Patrol
aquatics_krakenLeftLaser
aquatics_krakenLeftTurret
Security Patrol
aquatics_krakenRightLaser
aquatics_krakenRightTurret
Wreckage
Man O' War
ManoWar Inner
ManoWar Outer
Manta Ray
Military Manta Ray
Manta Ray
GalCop Manta Ray
Military Manta Ray
Orca Class Destroyer (Hijacked)
Leviathan System Platform (Hijacked)
Leviathan System Platform
Aquatics Ring Dock (Hidden)
Gateway Station
Ring Segment
Shark
Shark Escort
Shark
Wreckage
Weapons Platform Laser Mount
Ship Turret Mount
Weapons Platform Turret Mount
Wreckage
Trident Missile
Trident Tine

Models

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

Scripts

Path
Scripts/aquatics_FX.js
this.name			= "aquatics_FX.js";
this.author			= "Thargoid";
this.copyright		= "Creative Commons Attribution - Non-Commercial - Share Alike 3.0 license with clauses - see readme.txt.";
this.description	= "Effect control script";
this.version		= "2.31";

this.shipWillDockWithStation = function(station)
	{
	if(station.hasRole("aquatics_HQ"))
		{		
		this.fx = system.addVisualEffect("aquatics_dockingFX",player.ship.position.add(player.ship.vectorForward.multiply(500)));
		this.lDoor = system.addVisualEffect("aquatics_lDoor",player.ship.position.add(player.ship.vectorForward.multiply(500)));
		this.rDoor = system.addVisualEffect("aquatics_rDoor",player.ship.position.add(player.ship.vectorForward.multiply(500)));
		} 
	}	
	
this.shipWillLaunchFromStation = function(station)
	{
	if(station.hasRole("aquatics_HQ"))
		{ this.fx = system.addVisualEffect("aquatics_launchFX",player.ship.position.add(player.ship.vectorForward.multiply(250))); } 
	}	
	
this.shipLaunchedFromStation = function(station)
	{
	if(station.hasRole("aquatics_HQ"))
		{
		this.lDoor = system.addVisualEffect("aquatics_lDoor",player.ship.position.add(player.ship.vectorForward.multiply(500)));
		this.rDoor = system.addVisualEffect("aquatics_rDoor",player.ship.position.add(player.ship.vectorForward.multiply(500)));
		this.lDoor.orientation = player.ship.orientation;
		this.rDoor.orientation = player.ship.orientation;
		}
	}
Scripts/aquatics_HQ.js
this.name			= "aquatics_HQ.js";
this.author			= "Thargoid";
this.copyright		= "Creative Commons Attribution - Non-Commercial - Share Alike 3.0 license with clauses - see readme.txt.";
this.description	= "Ship script for HQ station";
this.version		= "2.31";

this.shipSpawned = function()
	{ this.ship.breakPattern = false; }	
Scripts/aquatics_congerPods.js
this.name				= "conger_script";
this.author				= "Original script by Kaks (Kestrel&Falcon OXP), mangled here by Thargoid with invaluable advice from Eric Walsh";
this.copyright			= "Creative Commons: attribution, non-commercial, sharealike.";
this.description		= "Conger ship cargo pod actions and reactions";
this.version			= "2.31";
"use strict";

this.subpleas={
	'aquatics_congerPod1':'[aquatics_pod-1-destroyed]','aquatics_congerPod2':'[aquatics_pod-2-destroyed]',
	'aquatics_congerPod3':'[aquatics_pod-3-destroyed]','aquatics_congerPod4':'[aquatics_pod-4-destroyed]'};

	
this.shipSpawned = function()
	{
	let e = this.ship.subEntities;
	if(!e) { return; }
	e.items = function(){for (let i = 0; i < this.length; i++) yield this[i];}
	for (let i in e.items()) {
		i.script.plea = this.subpleas[i.primaryRole];
		i.script.owner = this.ship;
		i.script.shipDied = function(whom)
			{
			this.owner.spawn('cargopod', Math.ceil(Math.random()*10));
			if(whom.isPlayer)
				{ this.owner.commsMessage(expandDescription(this.plea)); }
			};
	}
}

this.shipDied = function()
	{ this.ship.spawn('alloy', Math.ceil(Math.random()*6)); }


Scripts/aquatics_destroyer.js
this.name					= "aquatics_destroyer";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Ship script for Orca Destroyer";
this.version				= "2.31";
"use strict";

this.findTarget = function()
	{
	function isBadGuy(entity) 
		{ 
		return (entity && entity.isValid && entity.isShip && (entity.isThargoid || entity.isPirate));
		}

	var targets = system.filteredEntities(this, isBadGuy, this.ship, this.ship.scannerRange);
	if(targets.length > 0)
		{
		this.ship.target = targets[0];
		this.ship.reactToAIMessage("HOSTILE_FOUND");
		}
	}

this.collided = function()
	{
	if(this.ship.energy < this.ship.maxEnergy * 0.95)
		{
		this.ship.energy = this.ship.maxEnergy * 0.95;
		}
	}

this.collisionOn = function()
	{
	delete this.collided;
	}

this.switchAI = function()
	{
	this.ship.switchAI("aquatics_destroyerAI.plist");
	}

this.shipDied = function(who,why)
	{
	this.largeCount = (Math.ceil(Math.random() * 4) - 1); // between 0-3 large pieces of wreckage
	this.smallCount = (Math.ceil(Math.random() * 9) - 1); // between 0-8 small pieces of wreckage
	this.fuelCount = (Math.ceil(Math.random() * 30) + 30); // between 31-40 burning fuel entities

	this.ship.spawn("aquatics_burningFuel", this.fuelCount); // spawn the burning fuel

	if(Math.random() > 0.33) //  2 in 3 chance of a piece of non-exploding wreckage
		{
		this.ship.spawn("aquatics_survivingWreckage", 1); // spawn the surviving piece of wreckage
		}

	if(this.largeCount > 0) // if there is large wreckage
		{
		this.ship.spawn("aquatics_largeWreckage", this.largeCount); // spawn the large pieces of exploding wreckage
		}

	if(this.smallCount > 0) // if there is small wreckage
		{
		this.ship.spawn("aquatics_smallWreckage", this.smallCount); // spawn the small pieces of exploding wreckage
		}
	}
Scripts/aquatics_dockingFX.js
this.name = "planetFall_dockingFX";
this.author = "Thargoid";
this.copyright = "CC-by";
this.description = "FX script";
this.version = "2.31";
"use strict";

this.effectSpawned = function()
	{
	this.distance = 750;
	this.visualEffect.position = player.ship.position.add(player.ship.vectorForward.multiply(this.distance));
	this.visualEffect.orientation = player.ship.orientation;
	this.lifetime = 0;
	this.firstFrame = true;
	if(this.visualEffect.subEntities && this.visualEffect.subEntities.length > 0)
		{ // randomise the docked ships
		var listCounter = this.visualEffect.subEntities.length ; // reset the counter
		for(listCounter = this.visualEffect.subEntities.length - 1;listCounter>=0;listCounter--)
			{ if(Math.random() < 0.3) { this.visualEffect.subEntities[listCounter].remove(); } }
		}	
	this.callback = addFrameCallback(this.doorPosition.bind(this));
	}

this.effectRemoved = function()
	{ removeFrameCallback(this.callback); }
	
this.doorPosition = function(delta)
	{
	if(this.firstFrame)
		{
		this.firstFrame = false;
		return;
		}
	
	if(!player.ship.isValid || this.lifetime > 2.5) 
		{ 
		this.visualEffect.remove();
		return;
		}
	if(!delta) { return; }
	if(this.lifetime < 1)
		{ this.visualEffect.position = player.ship.position.add(player.ship.vectorForward.multiply(this.distance));}
	else
		{ this.visualEffect.position = player.ship.position.add(player.ship.vectorForward.multiply(this.distance - (((this.lifetime - 1) / 1.5) * 750))); }
	this.visualEffect.orientation = player.ship.orientation;
	this.lifetime += delta;
	return;
	}
Scripts/aquatics_equipment.js
this.name					= "aquatics_equipment";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Controls equipment availablity in Aquatics.";
this.version				= "2.31";
"use strict";

this.startUp = function()
	{
	if(!missionVariables.aquatics_timer && missionVariables.aquatics_reputation && missionVariables.aquatics_reputation > 0)
		{
		missionVariables.aquatics_timer = clock.days;
		}
	}

this.shipExitedWitchspace = function()
	{
	if(!missionVariables.aquatics_timer)
		{
		return;
		}

	if((clock.days - missionVariables.aquatics_timer > 50) && (missionVariables.aquatics_reputation == 1) )
		{
		missionVariables.aquatics_reputation = 2;
		}

	if((clock.days - missionVariables.aquatics_timer > 150) && (missionVariables.aquatics_reputation == 0) )
		{
		missionVariables.aquatics_reputation = 2;
		}

	if(missionVariables.aquatics_reputation == 2)
		{
		delete this.shipExitedWitchspace;
		}
	}

this.shipWillDockWithStation = this.shipWillEnterWitchspace = function()
	{
	missionVariables.aquatics_guardianActive = null;
	var guardian = system.shipsWithRole("aquatics_guardianPlayer") 
	for (var i = 0; i < guardian.length; i++) 
		{ 
		guardian[i].remove() 
		missionVariables.aquatics_guardianCount += 1; 
		}

	}

this.alertConditionChanged = function()
	{
	if(player.ship.equipmentStatus("EQ_AQUATICS_GUARDIAN") == "EQUIPMENT_UNAVAILABLE" && player.ship.equipmentStatus("EQ_AQUATICS_1_GUARDIAN") == "EQUIPMENT_UNAVAILABLE") // if system is not installed
		{
		return;
		}

	if(missionVariables.aquatics_guardianActive && player.alertCondition < 3) // if alert = yellow/green and Guardians are deployed
		{
		player.consoleMessage("Guardian System Deactivated", 2);
		this.shipWillDockWithStation(); // perform same code as when docking, to remove the guardians
		return;
		}

	if(player.alertCondition == 3 && player.alertHostiles && !missionVariables.aquatics_guardianActive) // if red alert by hostiles and the Guardians aren't deployed
		{
		if(missionVariables.aquatics_guardianCount > 0)
			{
			for(var i=0; i < missionVariables.aquatics_guardianCount; i++)
				{
				player.ship.ejectItem("aquatics_guardianPlayer");
				}
			player.consoleMessage("Guardian System Activated", 2);
			missionVariables.aquatics_guardianCount = 0;
			missionVariables.aquatics_guardianActive = true;
			return;
			}
		else
			{
			if(player.ship.equipmentStatus("EQ_AQUATICS_GUARDIAN") == "EQUIPMENT_OK")
				{
				player.ship.setEquipmentStatus("EQ_AQUATICS_GUARDIAN", "EQUIPMENT_DAMAGED");
				return;
				}
			else
				{
				player.ship.setEquipmentStatus("EQ_AQUATICS_1_GUARDIAN", "EQUIPMENT_DAMAGED");
				return;
				}

			}
		}
	
	}

this.playerBoughtEquipment = function(equipment)
	{
	switch(equipment)
		{
		case "EQ_AQUATICS_GUARDIAN":
		case "EQ_AQUATICS_1_GUARDIAN":
			{
			missionVariables.aquatics_guardianCount = 2;
			missionVariables.aquatics_guardianActive = null;
			break;
			}

		case "EQ_AQUATICS_CLOAK":
			{
			player.ship.removeEquipment("EQ_AQUATICS_CLOAK");
			player.ship.awardEquipment("EQ_CLOAKING_DEVICE");
			if(EquipmentInfo.infoForKey("EQ_CLOAKING_DEVICE").effectiveTechLevel === 99)
				{EquipmentInfo.infoForKey("EQ_CLOAKING_DEVICE").effectiveTechLevel = 14;} // make cloak repairable at tech 14 (in-game 15).
			break;
			}
		}
	}

this.equipmentDamaged = this.equipmentDestroyed = function(equipment)
	{
	if(equipment == "EQ_AQUATICS_GUARDIAN" || equipment == "EQ_AQUATICS_1_GUARDIAN")
		{
		player.consoleMessage("Guardian System Offline", 3);
		this.shipWillDockWithStation(); 
		missionVariables.aquatics_guardianCount = 0;
		}
	}
Scripts/aquatics_fireworks.js
this.name				= "aquatics_fireworks";
this.author				= "Thargoid";
this.copyright			= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description		= "Adds explosive fireworks to the ships death";
this.version			= "2.31";
"use strict";

this.shipDied = function(who, why)
	{
	this.fuelCount = 5 + Math.ceil(Math.random() * 10);
	this.largeCount = Math.ceil(Math.random() * 3);
	this.smallCount = 1 + Math.ceil(Math.random() * 5);
	if(this.ship.hasRole("aquatics_hawksbill"))
		{
		this.fuelCount *= 4; // well the Hawksbill is a fuel tanker ;)
		}

	system.addShips("aquatics_burningFuel", this.fuelCount, this.ship.position, 10);
	system.addShips("aquatics_smallWreckage", this.smallCount, this.ship.position, 5);

	if(this.largeCount > 0)
		{
		system.addShips("aquatics_largeWreckage", this.largeCount, this.ship.position, 5);
		}
	}
Scripts/aquatics_guardianPlayer.js
this.name					= "aquatics_guardianPlayer";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "AI-actions script for the guardian drones for the player ship";
this.version				= "2.31";
"use strict";

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

this.checkPlayerSpeed = function()
	{
	if(player.ship.speed < 1000)
		{ this.ship.reactToAIMessage("PLAYER_SUBSONIC"); }
	else
		{ this.ship.reactToAIMessage("PLAYER_SUPERSONIC"); }
	}

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

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

this.shipTargetDestroyed = function(target)
	{
	if(target.primaryRole == "constrictor" && missionVariables.conhunt && missionVariables.conhunt == "STAGE_1")
		{ missionVariables.conhunt = "CONSTRICTOR_DESTROYED"; } // just in case an escort kills the constrictor, let's not break the mission for the player...
		
	if(target.isRock || target.isBoulder || target.isCargo || target.isDerelict || target.isWeapon)
		{ return; }
	
	player.score += 1;
	player.credits += target.bounty;
	player.consoleMessage("Guardian kill - " + target.name + " : " + target.bounty + "₢ awarded.", 5);
	log("Guardian kill - " + target.name + " : " + target.bounty);
	}	

this.shipDied = function(whom,why)
	{
	if(missionVariables.aquatics_guardianActive == "true") // if the drone was killed whilst in-flight
		{ player.consoleMessage("Guardian drone lost.", 3); }
	}
Scripts/aquatics_hammerHead.js
this.name					= "aquatics_hammerHead";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Aquatics Hammerhead script.";
this.version				= "2.31";
"use strict";

this.shipSpawned = function()
	{
	this.nameShip();
	this.randomiseCargo();
	if(Math.random() > 0.75)
		{ this.ship.switchAI("aquatics_haulerWitchAI.plist"); }
	}

this.nameShip = function()
	{
	this.shipTitle = expandDescription('[aquatics_haulerName]');
	if(Math.random() > 0.25)
		{ this.shipTitle = this.shipTitle + expandDescription('[aquatics_haulerNumber]'); }

	if(this.shipTitle === "Kraken IV") // that name is reserved for the special mission ship
		{ this.nameShip(); }

	this.ship.displayName = "AHC " + this.shipTitle;
	}

this.randomiseCargo = function()
	{
	if(Math.random() < (Math.random() / 3) && this.ship.subEntities && this.ship.subEntities.length > 2)
		{
		this.ship.subEntities[0].remove();
		this.randomiseCargo();
		}
	}

this.beginUnload = function()
	{ this.ship.commsMessage("WARNING - This is the HammerHead class hauler AHC " + this.shipTitle + ". Please exercise caution around this region as we are beginning our unloading process."); }

this.endUnload = function()
	{ this.ship.commsMessage("This is the AHC " + this.shipTitle + ". Our unloading for " + system.name + " is now completed."); }

this.chooseDestination = function() // if we've got some cargo sleds left, go to witchpoint and jump somewhere else, if not sunskim to get rid of hauler.
	{
	if(this.ship.subEntities && this.ship.subEntities.length > 0)
		{ this.ship.switchAI("aquatics_haulerWitchAI.plist"); }
	else
		{ this.ship.switchAI("route2sunskimAI.plist"); }
	}

this.launchSled = function()
	{
	if(this.ship.subEntities && this.ship.subEntities.length > 0)
		{
		if(Math.random() > 0.5)
			{
			this.subPosition = this.ship.subEntities[0].position;
			this.subOrientation = this.ship.subEntities[0].orientation;
			this.ship.subEntities[0].remove();
			let subSled = this.ship.spawnOne("aquatics_cargoSled"); 
			subSled.position = this.localToGlobal(this.subPosition); 
			subSled.orientation = this.subOrientation.multiply(this.ship.orientation); 
			subSled.switchAI("aquatics_launchAI.plist"); 
			if(Math.random() < 0.1) // 1 in 20 chance per sled launch that hauler stops unloading and take some sleds away with it.
				{
				this.endUnload();
				this.ship.AIState = "IDLE";
				}	
			}
		}
	else
		{
		this.endUnload();
		this.ship.AIState = "IDLE"; // if we unload everything, then no need to come back here and we can prepare to leave.
		}
	}

this.localToGlobal = function(position)
	{ // sub-ent position is relative to mother, but for swapping we need the absolute global position
	let orientation = this.ship.orientation;
	return this.ship.position.add(position.rotateBy(orientation));
	}

this.shipDied = function(who,why)
	{
	this.largeCount = (Math.ceil(Math.random() * 4) - 1); // between 0-3 large pieces of wreckage
	this.smallCount = (Math.ceil(Math.random() * 9) - 1); // between 0-8 small pieces of wreckage
	this.fuelCount = (Math.ceil(Math.random() * 30) + 30); // between 31-40 burning fuel entities

	this.ship.spawn("aquatics_burningFuel", this.fuelCount); // spawn the burning fuel

	if(Math.random() > 0.33) //  2 in 3 chance of a piece of non-exploding wreckage
		{ this.ship.spawn("aquatics_survivingWreckage", 1); } // spawn the surviving piece of wreckage

	if(this.largeCount > 0) // if there is large wreckage
		{ this.ship.spawn("aquatics_largeWreckage", this.largeCount); } // spawn the large pieces of exploding wreckage

	if(this.smallCount > 0) // if there is small wreckage
		{ this.ship.spawn("aquatics_smallWreckage", this.smallCount); } // spawn the small pieces of exploding wreckage
	}
Scripts/aquatics_hammerSled.js
this.name					= "aquatics_hammerSled";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Aquatics cargo sled script.";
this.version				= "2.31";
"use strict";

this.collided = function()
	{
	if(this.ship.energy < this.ship.maxEnergy * 0.95)
		{ this.ship.energy = this.ship.maxEnergy * 0.95; }
	}

this.collisionOn = function()
	{ delete this.collided; }

this.switchAI = function()
	{ this.ship.switchAI("dockingAI.plist"); }

this.shipDied = function(who,why)
	{
	if(!who)
		{ return; }

	this.carriedCargo = Math.ceil((Math.random() * 30) + 5);
	this.ship.spawn("cargopod", this.carriedCargo);

	this.smallCount = (Math.ceil(Math.random() * 5) - 1); // between 0-4 small pieces of wreckage
	this.fuelCount = (Math.ceil(Math.random() * 20) + 10); // between 11-30 burning fuel entities

	this.ship.spawn("aquatics_burningFuel", this.fuelCount); // spawn the burning fuel

	if(this.smallCount > 0) // if there is small wreckage
		{ this.ship.spawn("aquatics_smallWreckage", this.smallCount); } // spawn the small pieces of exploding wreckage
	}
Scripts/aquatics_jammer.js
this.name           = "aquatics_jammer";
this.author         = "Thargoid";
this.copyright      = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description    = "Ship script for Thargon jammer mine";
this.version        = "2.31";
"use strict";

this.performJamming = function() 
	{ 
	this.ship.commsMessage("Activated.");

	function isThargon(entity) 
		{ return (entity.isShip && (entity.primaryRole == "thargon" || entity.primaryRole == "tharglet" || entity.primaryRole == "CT_thargon")); }

	var thargonArray = system.filteredEntities(this, isThargon, this.ship, 25600)
	if(thargonArray.length > 0)

	var thargCounter = 0 ; // reset the counter
	for(thargCounter = 0; thargCounter < thargonArray.length; thargCounter++)
		{
		this.jamChance = (this.ship.position.distanceTo(thargonArray[thargCounter].position)) / 25600;
		this.diceRole = Math.random();
		if(this.diceRole > this.jamChance)
			{ thargonArray[thargCounter].switchAI("aquatics_uncontrolledThargonAI.plist"); }
		}
	}

Scripts/aquatics_kraken.js
this.name					= "aquatics_kraken";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Script for mission of stolen Kraken IV hauler.";
this.version				= "2.31";
"use strict";

function aquatics_localMines(entity) { 
	var isWeapon = entity.isWeapon, 
		isActive = entity.AI !== "aquatics_deactivatedAI.plist", 
		mines = ["EQ_QC_MINE", "EQ_RMB_CASCADE_MISSILE", "EQ_RMB_LAW_MISSILE", "EQ_RMB_OVERRIDE_MISSILE", 
			"energy-bomb", "EQ_NUKE2_MISSILE", "EQ_HARPOON_NUKE2_MISSILE", "EQ_PHOTON_MISSILE", "RANDOM_HITS_MINE"], 
		isLocalMine = mines.some(function (element, i, array) {return entity.hasRole(element);}); 
	return isWeapon && isActive && isLocalMine; 
	} 

this.shipSpawned = function()
	{
	if(this.scanTimer)
		{ this.scanTimer.start(); }
	else
		{ this.scanTimer = new Timer(this, this.mineScan, 0, 4.75); }
	}	
	
this.mineScan = function()
	{
	this.mineArray = system.filteredEntities(this, aquatics_localMines, this.ship, 25600);
	
	if(this.mineArray.length > 0)
		{
		player.consoleMessage("Cascade suppression field detected", 6);
		this.mineArray.forEach(function (mine) 
			{
			mine.setAI("aquatics_deactivatedAI.plist"); 
			mine.displayName = "Disarmed " + mine.displayName;
			mine.scannerDisplayColor1 = "whiteColor";
			mine.scannerDisplayColor2 = "whiteColor";
			}
			)
		}
	}
	
this.findTargets = function()
	{
	function isHostileTarget(entity) 
		{ 
		return (entity.isShip && (entity.isPlayer || entity.isPolice || entity.isThargoid));
		}
	var targets = system.filteredEntities(this, isHostileTarget, this.ship, 25600);
	if(targets.length > 0)
		{
		this.ship.target = targets[0];
		this.ship.reactToAIMessage("TARGET_FOUND");
		}
	else
		{
		this.ship.reactToAIMessage("NOTHING_FOUND");
		}
	}

this.shipExitedWitchspace = this.leftSystem = function()
	{
	missionVariables.aquatics_krakenMission = "FAILED";
	mission.setInstructionsKey("aquatics_krakenShortFailure", "aquatics_populator");
	this.ship.switchAI("sunSkimExitAI.plist");
	}

this.shipDied = function(who,why)
	{
	if(missionVariables.aquatics_krakenMission == "BEGUN") // if the ship was killed whilst the mission was active - success
		{
		missionVariables.aquatics_krakenMission = "SUCCEEDED";
		mission.setInstructionsKey("aquatics_krakenShortSuccess", "aquatics_populator");
		}

	this.largeCount = (Math.ceil(Math.random() * 6) - 1); // between 0-5 large pieces of wreckage
	this.smallCount = (Math.ceil(Math.random() * 16) - 1); // between 0-15 small pieces of wreckage
	this.fuelCount = (Math.ceil(Math.random() * 30) + 30); // between 31-60 burning fuel entities

	this.ship.spawn("aquatics_burningFuel", this.fuelCount); // spawn the burning fuel

	if(Math.random() > 0.33) //  2 in 3 chance of a piece of non-exploding wreckage
		{ this.ship.spawn("aquatics_survivingWreckage", 1); } // spawn the surviving piece of wreckage

	if(this.largeCount > 0) // if there is large wreckage
		{ this.ship.spawn("aquatics_largeWreckage", this.largeCount); } // spawn the large pieces of exploding wreckage

	if(this.smallCount > 0) // if there is small wreckage
		{ this.ship.spawn("aquatics_smallWreckage", this.smallCount); } // spawn the small pieces of exploding wreckage
	}
Scripts/aquatics_krakenPolice.js
this.name					= "aquatics_krakenPolice";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "AI-actions script for the police ships tasked to help kill the Kraken";
this.version				= "2.31";
"use strict";

this.locateKraken = function()
	{
	this.krakenArray = system.shipsWithPrimaryRole("aquatics_kraken");
	if(this.krakenArray.length > 0) // found it! Now let's go in for the kill
		{
		this.ship.target = this.krakenArray[0];
		this.ship.reactToAIMessage("KRAKEN_FOUND");
		}
	else // Kraken has been destroyer or left the system, so we can go back to the day job
		{
		this.ship.switchAI("route1patrolAI.plist");
		this.ship.displayName = this.ship.scriptInfo.ship_name;
		}
	}
Scripts/aquatics_lDoor.js
this.name = "planetFall_lDoor";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "FX script";
this.version = "2.31";
"use strict";

this.effectSpawned = function()
	{
	this.distance = 500;
	this.visualEffect.position = player.ship.position.add(player.ship.vectorForward.multiply(this.distance));
	this.visualEffect.orientation = player.ship.orientation;
	this.lifetime = 0;
	this.callback = addFrameCallback(this.doorPosition.bind(this));
	}

this.effectRemoved = function()
	{ removeFrameCallback(this.callback); }
	
this.doorPosition = function(delta)
	{
	if(!player.ship.isValid || this.lifetime > 2) 
		{ 
		this.visualEffect.remove();
		return;
		}
	if(!delta) { return; }
	this.visualEffect.position = player.ship.position.add(player.ship.vectorForward.multiply(this.distance));
	this.visualEffect.position = this.visualEffect.position.subtract(player.ship.vectorRight.multiply((lifetime / 2) * 250));
	this.visualEffect.orientation = player.ship.orientation;
	this.lifetime += delta;
	return;
	}
Scripts/aquatics_largeWreck.js
this.name 					= "aquatics_largeWreck"; 
this.author 				= "Thargoid"; 
this.copyright 				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt"; 
this.description 			= "Break-up of larger wreckage into small ones"; 
this.version 				= "2.31"; 
"use strict";

this.shipDied = function()
	{
	this.subSmallCount = (Math.ceil(Math.random() * 5) - 2); // between 0 (-1) and 3 pieces of small wreckage spawned from large one
	if(this.subSmallCount > 0) // if we have a positive amount of small wreckage
		{
		this.ship.spawn("aquatics_smallWreckage", this.subSmallCount); // spawn the small wreckage
		}
	this.fuelCount = (Math.ceil(Math.random() * 10)); // between 1-10 burning fuel entities
	this.ship.spawn("aquatics_burningFuel", this.fuelCount); // spawn the burning fuel
	delete this.shipDied;
	}
Scripts/aquatics_launchFX.js
this.name = "planetFall_launchFX";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "FX script";
this.version = "2.31";
"use strict";

this.effectSpawned = function()
	{
	this.distance = 250;
	this.visualEffect.position = player.ship.position.add(player.ship.vectorForward.multiply(this.distance));
	this.visualEffect.orientation = player.ship.orientation;
	this.visualEffect.orientation = this.visualEffect.orientation.rotate(this.visualEffect.vectorUp, Math.PI);
	this.lifetime = 0;
	this.firstFrame = true;
	this.callback = addFrameCallback(this.doorPosition.bind(this));
	}

this.effectRemoved = function()
	{ removeFrameCallback(this.callback); }
	
this.doorPosition = function(delta)
	{
	if(this.firstFrame)
		{
		this.firstFrame = false;
		return;
		}
		
	if(!player.ship.isValid || this.lifetime > 2.5) 
		{ 
		this.visualEffect.remove();
		return;
		}
	if(!delta) { return; }
	this.visualEffect.position = player.ship.position.add(player.ship.vectorForward.multiply(this.distance - ((this.lifetime / 2.5) * 250))); 
	this.visualEffect.orientation = player.ship.orientation;
	this.visualEffect.orientation = this.visualEffect.orientation.rotate(this.visualEffect.vectorUp, Math.PI);
	this.lifetime += delta;
	return;
	}
Scripts/aquatics_pirateDestroyer.js
this.name					= "aquatics_pirateDestroyer";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Ship script for Orca Destroyer(stolen)";
this.version				= "2.31";
"use strict";

this.findTarget = function()
	{
	function isBadGuy(entity) 
		{ return (entity && entity.isValid && entity.isShip && (entity.isPlayer || entity.isPolice || entity.isTrader || entity.isCloaked)); }

	var targets = system.filteredEntities(this, isBadGuy, this.ship, this.ship.scannerRange);
	if(targets.length > 0)
		{
		this.ship.target = targets[0];
		this.ship.reactToAIMessage("HOSTILE_FOUND");
		}
	}

this.collided = function()
	{
	if(this.ship.energy < this.ship.maxEnergy * 0.95)
		{ this.ship.energy = this.ship.maxEnergy * 0.95; }
	}

this.collisionOn = function()
	{ delete this.collided; }

this.switchAI = function()
	{ this.ship.switchAI("aquatics_destroyerAI.plist"); }

this.checkRange = function()
	{
	var kraken = system.shipsWithRole("aquatics_kraken");
	if(kraken.length == 0 || this.ship.position.distanceTo(kraken[0].position) > 100)
		{ this.ship.switchAI("aquatics_destroyerAI.plist"); }
	}

this.shipDied = function(who,why)
	{
	this.largeCount = (Math.ceil(Math.random() * 4) - 1); // between 0-3 large pieces of wreckage
	this.smallCount = (Math.ceil(Math.random() * 9) - 1); // between 0-8 small pieces of wreckage
	this.fuelCount = (Math.ceil(Math.random() * 30) + 30); // between 31-40 burning fuel entities

	this.ship.spawn("aquatics_burningFuel", this.fuelCount); // spawn the burning fuel

	if(Math.random() > 0.33) //  2 in 3 chance of a piece of non-exploding wreckage
		{ this.ship.spawn("aquatics_survivingWreckage", 1); } // spawn the surviving piece of wreckage

	if(this.largeCount > 0) // if there is large wreckage
		{ this.ship.spawn("aquatics_largeWreckage", this.largeCount); } // spawn the large pieces of exploding wreckage

	if(this.smallCount > 0) // if there is small wreckage
		{ this.ship.spawn("aquatics_smallWreckage", this.smallCount); } // spawn the small pieces of exploding wreckage
	}
Scripts/aquatics_piratePlatform.js
this.name					= "aquatics_piratePlatform";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Ship script for Leviathan Weapons Platform (stolen)";
this.version				= "2.31";
"use strict";

this.findTarget = function()
	{
	function isBadGuy(entity) 
		{ 
		return (entity && entity.isValid && entity.isShip && (entity.isPlayer || entity.isPolice || entity.isCloaked || entity.isThargoid || entity.isPirate || entity.bounty > 49));
		}

	var targets = system.filteredEntities(this, isBadGuy, this.ship, this.ship.scannerRange);
	if(targets.length > 0)
		{
		this.ship.target = targets[0];
		this.ship.reactToAIMessage("HOSTILE_FOUND");
		}
	}

this.shipDied = function(who,why)
	{
	if(this.ship.isSubEntity && this.ship.owner && this.ship.owner.hasRole("aquatics_kraken"))
		{
		this.krakenCleanUp();
		}

	this.largeCount = (Math.ceil(Math.random() * 4) - 1); // between 0-3 large pieces of wreckage
	this.smallCount = (Math.ceil(Math.random() * 9) - 1); // between 0-8 small pieces of wreckage
	this.fuelCount = (Math.ceil(Math.random() * 30) + 30); // between 31-40 burning fuel entities

	this.ship.spawn("aquatics_burningFuel", this.fuelCount); // spawn the burning fuel

	if(Math.random() > 0.33) //  2 in 3 chance of a piece of non-exploding wreckage
		{ this.ship.spawn("aquatics_survivingWreckage", 1); } // spawn the surviving piece of wreckage

	if(this.largeCount > 0) // if there is large wreckage
		{ this.ship.spawn("aquatics_largeWreckage", this.largeCount); } // spawn the large pieces of exploding wreckage

	if(this.smallCount > 0) // if there is small wreckage
		{ this.ship.spawn("aquatics_smallWreckage", this.smallCount); } // spawn the small pieces of exploding wreckage
	}

this.krakenCleanUp = function()
	{
	if(this.ship.position.x < 0)	// if this is the left hand platform
		{
		var subCounter = 0 ; 	// reset the counter
		var ownerSubCount = this.ship.owner.subEntities.length - 1;
		for(subCounter = ownerSubCount; subCounter >= 0; subCounter--)
			{
			if(this.ship.owner.subEntities[subCounter].hasRole("aquatics_krakenLeftSubEnt"))
				{
				this.ship.owner.subEntities[subCounter].remove();
				}
			}

		}
	else 					// if this was the right hand platform
		{
		var subCounter = 0 ; 	// reset the counter
		var ownerSubCount = this.ship.owner.subEntities.length - 1;
		for(subCounter = ownerSubCount; subCounter >= 0; subCounter--)
			{
			if(this.ship.owner.subEntities[subCounter].hasRole("aquatics_krakenRightSubEnt"))
				{
				this.ship.owner.subEntities[subCounter].remove();
				}
			}

		}

	}
Scripts/aquatics_platform.js
this.name					= "aquatics_platform";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Ship script for Leviathan Weapons Platform (normal)";
this.version				= "2.31";
"use strict";

this.findTarget = function()
	{
	function isBadGuy(entity) 
		{ 
		return (entity && entity.isValid && entity.isShip && (!entity.scanClass == "CLASS_CARGO") && (entity.isThargoid || entity.isPirate || entity.bounty > 49));
		}

	var targets = system.filteredEntities(this, isBadGuy, this.ship, this.ship.scannerRange);
	if(targets.length > 0)
		{
		this.ship.target = targets[0];
		this.ship.reactToAIMessage("HOSTILE_FOUND");
		}
	}

this.switchAI = function()
	{ this.ship.switchAI("aquatics_platformAI.plist"); }

this.shipDied = function(who,why)
	{
	this.largeCount = (Math.ceil(Math.random() * 4) - 1); // between 0-3 large pieces of wreckage
	this.smallCount = (Math.ceil(Math.random() * 9) - 1); // between 0-8 small pieces of wreckage
	this.fuelCount = (Math.ceil(Math.random() * 30) + 30); // between 31-40 burning fuel entities

	this.ship.spawn("aquatics_burningFuel", this.fuelCount); // spawn the burning fuel

	if(Math.random() > 0.33) //  2 in 3 chance of a piece of non-exploding wreckage
		{ this.ship.spawn("aquatics_survivingWreckage", 1); } // spawn the surviving piece of wreckage

	if(this.largeCount > 0) // if there is large wreckage
		{ this.ship.spawn("aquatics_largeWreckage", this.largeCount); } // spawn the large pieces of exploding wreckage

	if(this.smallCount > 0) // if there is small wreckage
		{ this.ship.spawn("aquatics_smallWreckage", this.smallCount); } // spawn the small pieces of exploding wreckage
	}
Scripts/aquatics_populator.js
this.name					= "aquatics_populator";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Adds the hammerhead haulers and platforms, plus runs the mission offering.";
this.version				= "2.31";
"use strict";

/*
External control of adding system platforms in systems and interstellar space is now available to other OXPs.
To use simply set worldScripts["aquatics_populator"].allowInterstellar = false or worldScripts["aquatics_populator"].allowPlatforms = false to suppress their appearance in the next system or IS entry. They will self reset to true upon arrival after the set-up is blocked.
*/

this.startUp = function()
	{
	this.allowInterstellar = true;
	this.allowPlatforms = true;
	this.platformMessage = null;
	this.launchFlag = null;
	missionVariables.aquatics_cloakRandom = Math.random();
	}

this.systemWillPopulate = function()	
	{
	this.addHauler();
	this.addPlatforms();
	this.addHQ();
	}
	
this.interstellarSpaceWillPopulate = function()
	{ this.addPlatforms(); }
	
this.shipWillEnterWitchspace = function()
	{
	this.platformMessage = null;
	this.launchFlag = null;

	if(galaxyNumber == 2 && system.ID == 99 && worldScripts.PlanetFall) // if Planetfall is loaded and we're leaving Aqualina
		{ worldScripts.PlanetFall.planetFallOverride = null; } // reset the override 

	if(missionVariables.aquatics_krakenMission == "BEGUN")
		{
		missionVariables.aquatics_krakenMission = "FAILED";
		mission.setInstructionsKey(null);
		}
	}

this.shipWillExitWitchspace = function()
	{
	if(missionVariables.aquatics_reputation < 0 && Math.random() > 0.5)
		{ missionVariables.aquatics_reputation++; }
	}

this.shipWillLaunchFromStation = function(station)
	{
	this.launchFlag = true;
	
	if(galaxyNumber == 2 && system.ID == 99 && station.hasRole("aquatics_HQ"))
		{
		if(missionVariables.aquatics_krakenMission == "BRIEFING_1" || missionVariables.aquatics_krakenMission == "BRIEFING_2" || missionVariables.aquatics_krakenMission == "IMMEDIATE_OFFER")
			{ missionVariables.aquatics_krakenMission = "MAKE_OFFER"; }

		if(missionVariables.aquatics_krakenMission == "NOT_WELCOME" || missionVariables.aquatics_krakenMission == "KICK_OUT")
			{ missionVariables.aquatics_krakenMission = "CASE_CLOSED"; }
			
		if(worldScripts.PlanetFall) // if Planetfall is loaded
			{ worldScripts.PlanetFall.planetFallOverride = true; } // set the override, so only external OXP locations appear 

		if(missionVariables.aquatics_krakenMission == "BEGUN" && system.countShipsWithRole("aquatics_kraken") == 0)
			{ this.addKraken(); } 
		}
	}

this.shipLaunchedFromStation = this.shipEnteredStationAegis = function()
	{
	if(this.platformMessage == true)
		{
		player.commsMessage("ALERT! This is " + system.name + " station. Communication has been lost with the system platform. We are investigating, please proceed with extreme caution!", 6);
		this.platformMessage = null;
		}
	}

this.addHQ = function()
	{
	if(galaxyNumber == 2 && system.ID == 99 && system.countShipsWithRole("aquatics_HQ") == 0)
		{
//		this.$spawnedHQ = system.addShipsToRoute("aquatics_HQ", 1, 0.9, "sp")

		var x_coord = system.scrambledPseudoRandomNumber(12) * 25E3;
		var y_coord = system.scrambledPseudoRandomNumber(13) * 25E3;
		var z_coord = system.mainPlanet.radius + (0.15 * (system.sun.position.distanceTo(system.mainPlanet.position) - system.sun.radius - system.mainPlanet.radius));
		var exactPosition = Vector3D(x_coord, y_coord, z_coord).fromCoordinateSystem("psm");

		system.setPopulator("aquatics_HQ", {
			callback: function(pos) {			
				this.$spawnedHQ = system.addShips("aquatics_HQ", 1, pos, 0);					
				system.addShips("aquatics_platform", 1, this.$spawnedHQ[0].position.add([20000,0,0]));
				system.addShips("aquatics_platform", 1, this.$spawnedHQ[0].position.add([-20000,0,0]));
			}.bind(this),
			location: "COORDINATES",
			coordinates: exactPosition,
			deterministic: true
			}
		)
		
		if(worldScripts.PlanetFall) // if Planetfall is loaded
			{ worldScripts.PlanetFall.planetFallOverride = true;} // set the override, so only external OXP locations appear
		
		system.addShips("aquatics_platform", 1, [0, 0, 0], 22000);
		}	
	}
	
this.addHauler = function()
	{
	if(this.launchFlag || worldScripts["bigShips_populator"]) // don't place anything if we've already tried to populate the system or bigShips can handle it
		{ return; }
	
	if(Math.random() < (0.1 + ((system.techLevel + system.government)/50)) )
		{
		this.zDistance = ((Math.random() * 0.75) + 0.15);
		system.addShipsToRoute("aquatics_hammerHead", 1, this.zDistance, "pw");
		log(this.name, "Hammerhead hauler added to the " + system.name + " system by populator script.");
		}
	}

this.addPlatforms = function()
	{
	if(system.isInterstellarSpace)
		{
		if(Math.random() < 0.05 && this.allowInterstellar)
			{
			system.addShips("aquatics_platform", 1, player.ship.position, 20000);
			log(this.name, "System platform added in interstellar space by populator script.");
			}
		this.allowInterstellar = true;	
		return;
		}

	if(this.launchFlag || this.allowPlatforms === false) 
		{ // don't place anything if the system is going nova, we've already tried to populate the system or the external block is set
		this.allowPlatforms = true;
		return;
		}
	
	if(galaxyNumber == 2 && system.ID == 99) // if we're at Aqualina 
		{ return; } // no need to go any further for this system
		

	if(Math.random() < ((system.techLevel + system.government)/100))
		{
		system.addShips("aquatics_platform", 1, [0, 0, 0], 22000);
		log(this.name, "System platform near the " + system.name + " witchpoint.");
		}


	if(Math.random() < ((system.techLevel + system.government)/50))
		{
		if(Math.random() < 0.05)
			{
			system.addShips("aquatics_piratePlatform", 1, system.mainStation.position, 22000);
			system.addShips("aquatics_destroyer", 1, system.mainStation.position, 22000);
			system.addShips("aquatics_defender", 2, system.mainStation.position, 22000);
			this.platformMessage = true;
			}
		else
			{
			system.addShips("aquatics_platform", 1, system.mainStation.position, 22000);
			this.platformMessage = null;
			}
		}
	}

this.addKraken = function()
	{
	this.zDistance = ((Math.random() * 0.4) + 0.4);
	system.addShipsToRoute("aquatics_kraken", 1, this.zDistance, "pw");

	var addCounter = 0 ; 	// reset the counter
	var policeCount = Math.ceil(Math.random() * 3) + 1; // random number between 2 and 4
	for(addCounter = 0; addCounter < policeCount; addCounter++) // add police tasked to pursue and destroy the Kraken
		{
		this.zDistance = ((Math.random() * 0.9) + 0.05);
		system.addShipsToRoute("aquatics_krakenPolice", 1, this.zDistance, "pw"); 
		}

	log(this.name, "AHC Kraken and " + policeCount + " pursuing security forces added to Aqualina system by populator script.");
	}

// mission-related events below here.

this.shipDockedWithStation = function(station)
	{
	if(galaxyNumber == 2 && system.ID == 99 && station.hasRole("aquatics_HQ")) // if we're docked at the HQ
		{
		missionVariables.aquatics_cloakRandom = Math.random();

		if(player.score > 511 && missionVariables.aquatics_krakenMission == "UNAVAILABLE")
			{
			missionVariables.aquatics_krakenMission = "MAKE_OFFER";
			}
		}
	}
	
this.notWelcome = function()
	{
	missionVariables.aquatics_krakenMission = "CASE_CLOSED";
	player.ship.launch();
	}
	
this.missionChoice = function(choice)
	{
	switch(choice)
		{
		case "AQUATICS_1_ACCEPTED":
			{
			missionVariables.aquatics_krakenMission = "BRIEFING_1";
			mission.runScreen({title: "Mission Briefing", messageKey:"aquatics_krakenBriefing1", background:"aquatics_logo.png"}, this.missionChoice);
			mission.setInstructionsKey("aquatics_krakenShortHunt");
			break;
			}

		case "AQUATICS_2_REJECTED":
			{
			mission.runScreen({title: "Are you sure?", messageKey:"aquatics_firstDecline", background:"aquatics_logo.png", choicesKey:"aquatics_secondChoice"}, this.missionChoice);
			break;
			}

		case "AQUATICS_3_REJECTED":
			{
			missionVariables.aquatics_krakenMission = "KICK_OUT"
			mission.runScreen({title: "Well if that's your attitude!", messageKey:"aquatics_secondDecline", background:"aquatics_logo.png"});
			break;
			}
		}
	}

this.missionScreenOpportunity = function()
	{
	if(galaxyNumber == 2 && system.ID == 99 && player.ship.docked && player.ship.dockedStation.hasRole("aquatics_HQ")) // if we're docked at the HQ
		{

		if(guiScreen == "GUI_SCREEN_MISSION") // if something else has taken the mission screen.
			{
			return;
			}

		if(missionVariables.aquatics_reputation < 0)
			{
			mission.runScreen({title: "Uh-oh!", messageKey:"aquatics_notWelcome", background:"aquatics_logo.png"}, this.notWelcome);
			return;
			}

		switch(missionVariables.aquatics_krakenMission)
			{
			case "KICK_OUT": // if we've rejected the mission
				{
				missionVariables.aquatics_reputation = -10;
				this.notWelcome();
				return;
				}
			
			case "MAKE_OFFER": // if we've been here before, but didn't have high enough rank
				{
				mission.runScreen({title: "Time for a beer, but...", messageKey:"aquatics_initialOffer", background:"aquatics_logo.png", choicesKey:"aquatics_firstChoice"}, this.missionChoice);
				break;
				}

			case "FAILED": // Kraken jumped out of system (failed)
				{
				mission.setInstructionsKey(null);
				missionVariables.aquatics_krakenMission = "CASE_CLOSED";
				mission.runScreen({title: "Mission Failed", messageKey:"aquatics_krakenFailure", background:"aquatics_logo.png"});
				break;
				}

			case "SUCCEEDED": // Kraken destroyed during mission (success)
				{
				mission.setInstructionsKey(null);
				missionVariables.aquatics_krakenMission = "CASE_CLOSED";
				missionVariables.aquatics_reputation = 1;
				mission.runScreen({title: "Mission Accomplished", messageKey:"aquatics_krakenSuccess", background:"aquatics_logo.png"});
				break;
				}
				
			case "BRIEFING_2":
				{
				missionVariables.aquatics_krakenMission = "BEGUN";
				mission.runScreen({title: "Mission Briefing", messageKey:"aquatics_krakenBriefing3", background:"aquatics_logo.png"});
				return;
				break;
				}

			case "BRIEFING_1":
				{
				missionVariables.aquatics_krakenMission = "BRIEFING_2";
				mission.runScreen({title: "Mission Briefing", messageKey:"aquatics_krakenBriefing2", model:"aquatics_kraken"});
				return;
				break;
				}

			case "IMMEDIATE_OFFER":
				{
				missionVariables.aquatics_krakenMission = "MAKE_OFFER";
				this.missionScreenOpportunity();
				return;
				break;
				}	

			default: // anything else, including first visit to HQ
				{
				if(!missionVariables.aquatics_krakenMission)
					{
					if(player.score > 511)
						{
						missionVariables.aquatics_krakenMission = "IMMEDIATE_OFFER";
						}
					else
						{ 
						missionVariables.aquatics_krakenMission = "UNAVAILABLE";
						}

					missionVariables.aquatics_timer = clock.days;  // start the equipment appearance clock
					mission.runScreen({title: "Welcome to Aqualina!", messageKey:"aquatics_firstVisit", background:"aquatics_logo.png"});
					}
				break;
				}
			}
		}
	}
Scripts/aquatics_rDoor.js
this.name = "planetFall_rDoor";
this.author = "Thargoid";
this.copyright = "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description = "FX script";
this.version = "2.31";
"use strict";

this.effectSpawned = function()
	{
	this.distance = 500;
	this.visualEffect.position = player.ship.position.add(player.ship.vectorForward.multiply(this.distance));
	this.visualEffect.orientation = player.ship.orientation;
	this.lifetime = 0;
	this.callback = addFrameCallback(this.doorPosition.bind(this));
	}

this.effectRemoved = function()
	{ removeFrameCallback(this.callback); }
	
this.doorPosition = function(delta)
	{
	if(!player.ship.isValid || this.lifetime > 2) 
		{ 
		this.visualEffect.remove();
		return;
		}
	if(!delta) { return; }
	this.visualEffect.position = player.ship.position.add(player.ship.vectorForward.multiply(this.distance));
	this.visualEffect.position = this.visualEffect.position.add(player.ship.vectorRight.multiply((lifetime / 2) * 250));
	this.visualEffect.orientation = player.ship.orientation;
	this.lifetime += delta;
	return;
	}
Scripts/aquatics_ringScript.js
this.name					= "aquatics_ringScript.js";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Script to add ring of blocks around system main planet in Aqualina system";
this.version				= "2.31";

this.systemWillPopulate = function()
	{
	if(galaxyNumber !== 2 || system.ID !== 99 || !system.mainPlanet || !system.mainStation) 
		{ return; }
	
	var spawnPosition = system.mainPlanet.position;

	this.ringGroup = new ShipGroup("ringGroup");
	
	system.setPopulator("aquatics_Ring", {
		callback: function(pos) {			
			this.$sectionLength = 6000; // orbit circumference divided by segment width (5000m) plus a 1000m gap on each side to prevent collisions (2000m total gap between segments)
			this.$gateCount = 4;
			this.$ringHeight = ((system.mainPlanet.position.distanceTo(system.mainStation.position) - system.mainPlanet.radius) / 2) + system.mainPlanet.radius; // ring orbits half-way between planet surface and main station
			this.$segmentNumber = (2 * Math.PI * this.$ringHeight) / this.$sectionLength; 
			this.$gateFrequency = Math.floor(this.$segmentNumber / this.$gateCount);
			this.$basePosition =  pos;

			this.$segmentNumber = Math.floor(this.$segmentNumber); // ensure we're not trying to spawn a fraction of a segment
			this.$ringHeight = (this.$sectionLength * this.$segmentNumber) /  (2 * Math.PI);
		
			let $vecForward = system.mainPlanet.orientation.vectorForward();
			let $vecUp = system.mainPlanet.orientation.vectorUp();
			let $vecRight = system.mainPlanet.orientation.vectorRight();
	
			let $segmentCounter = 0;
			for($segmentCounter = 0; $segmentCounter < this.$segmentNumber; $segmentCounter++)
				{
				this.$segmentAngle = 2 * Math.PI * ($segmentCounter / this.$segmentNumber);
				this.$jointAngle = 2 * Math.PI * (($segmentCounter + 0.5) / this.$segmentNumber);

				this.$segmentPosition = this.$basePosition.add($vecRight.multiply(this.$ringHeight * Math.sin(this.$segmentAngle))).add($vecForward.multiply(this.$ringHeight * Math.cos(this.$segmentAngle)));

				this.$jointPosition = this.$basePosition.add($vecRight.multiply(this.$ringHeight * Math.sin(this.$jointAngle))).add($vecForward.multiply(this.$ringHeight * Math.cos(this.$jointAngle)));
		
				this.$gateFlag = $segmentCounter / this.$gateFrequency;
				if($segmentCounter > 0 && (this.$gateFlag == Math.floor(this.$gateFlag)))
					{ 
					this.$nextSegment = system.addShips("aquatics_ringGateway", 1, this.$segmentPosition, 0); 
					this.$nextSegment[0].displayName = ("Ring Gateway " + (Math.floor(this.$gateFlag)) + "");	
					this.$nextSegment[0].group = this.ringGroup;
					}
				else
					{ 
					this.$nextSegment = system.addShips("aquatics_ringSegment", 1, this.$segmentPosition, 0); 
					this.$nextSegment[0].group = this.ringGroup;}

				this.$nextSegment[0].orientation = system.mainPlanet.orientation.rotate($vecUp, (this.$segmentAngle * -1));
				this.$nextSegment[0].script.$spawnOrientation = this.$nextSegment[0].orientation;
				this.$nextSegment[0].script.$spawnPosition = this.$nextSegment[0].position;

				// add the joints in between segments
				this.$nextJoint = system.addVisualEffect("aquatics_ringJoint", this.$jointPosition);
				this.$nextJoint.orientation = system.mainPlanet.orientation.rotate($vecUp, (this.$jointAngle * -1));
				this.$nextJoint.group = this.ringGroup;
				}
			}.bind(this),
		location: "COORDINATES",
		coordinates: spawnPosition,
		deterministic: true
		}
	)	
}
Scripts/aquatics_ringSegment.js
this.name					= "aquatics_ringSegment.js";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Positioning ship script for ring segments.";
this.version				= "2.31";
"use strict";

this.shipTakingDamage = function(amount, whom, type)
	{
	if(type && type === "scrape damage")
		{
		this.ship.position = this.$spawnPosition;
		this.ship.orientation = this.$spawnOrientation;
		if(amount) { this.ship.energy += amount; }
		}
	}
	
this.shipDied = function(whom, why)
	{
log(this.name, "whom - " + whom + ", why - " + why);	
	if(why && why !=="removed")
		{
		let newSegment = this.ship.spawnOne(this.ship.primaryRole);
		newSegment.position = this.ship.position;
		newSegment.orientation = this.ship.orientation;
		newSegment.name = this.ship.name;
		newSegment.displayName = this.ship.displayName;
		}
	}
Scripts/aquatics_trident.js
this.name					= "aquatics_trident";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike with clauses - see readme.txt";
this.description			= "Ship script for the trident missile";
this.version				= "2.31";
"use strict";

this.spawnTines = function() 
	{ 
	var loopCounter = 0 ; // reset the counter
	for(loopCounter = 0; loopCounter < 9; loopCounter++)
		{
		var Tine = this.ship.spawnOne("aquatics_tridentTine");
		this.xDistance = ((Math.random() * 40) - 20);
		this.yDistance = ((Math.random() * 40) - 20);
		Tine.setPosition(this.ship.position.add([this.xDistance, this.yDistance, 0]));
		Tine.setOrientation(this.ship.orientation); 
		Tine.target = this.ship.target;
		}
	}