| Scripts/stellarSerpents_bodySegment.js | this.name					= "stellarSerpents_bodySegment.js";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike.";
this.description			= "Script to control Serpent body segment";
this.version				= "1.00";
"use strict";
this.shipSpawned = function()
	{
	/*this.amplitudeX, this.amplitudeY, this.phase,
	this.offset and this.mother are all defined by the mother script which spawns this segment. */
	this.segCounter = 0;
	switch(this.phase) // so segments near the head don't oscillate as much
		{
		case(0):
			{
			this.amplitudeX *= 0.2;
			this.amplitudeY *= 0.2;
			break;
			}
		case(1):
			{
			this.amplitudeX *= 0.4;
			this.amplitudeY *= 0.4;
			break;
			}
		case(2):
			{
			this.amplitudeX *= 0.6;
			this.amplitudeY *= 0.6;
			break;
			}
		}
	this.callbackID = addFrameCallback(this.moveSegment.bind(this));
	}
	
this.moveSegment = function(delta)
	{
	if(this.mother && this.mother.isValid)
		{
		this.ship.scannerDisplayColor1 = [0,0,0,0];
		this.ship.position = this.mother.position.subtract(this.mother.vectorForward.multiply(275+(this.phase*140)));
		this.ship.orientation = this.mother.orientation;
		this.angleX = (Math.sin((clock.absoluteSeconds * Math.PI) + ((this.phase / 4) * Math.PI))); 
		this.angleY = (Math.sin((clock.absoluteSeconds * Math.PI) + (((this.phase + this.offset) / 4) * Math.PI))); 
		this.swingX = this.angleX * this.amplitudeX;
		this.swingY = this.angleY * this.amplitudeY;
		this.ship.position = this.ship.position.add(this.ship.vectorRight.multiply(this.swingX));
		this.ship.position = this.ship.position.add(this.ship.vectorUp.multiply(this.swingY));
		}
	else
		{
		this.freelance();
		}
	}
	
this.freelance = function()
	{
	this.ship.scannerDisplayColor1 = null;
	if(this.callbackID && isValidFrameCallback(this.callbackID))
		{ removeFrameCallback(this.callbackID); }
	this.ship.displayName = "Stellar Serpent Corpse Segment";
	}
	
this.shipBeingAttacked = function(whom)
	{
	if(this.mother && whom)
		{
		this.mother.target = whom;
		this.mother.reactToAIMessage("ATTACKED");
		}
	}
	
this.shipAttackedWithMissile = function(missile, whom)
	{
	if(this.mother && whom)
		{
		this.mother.target = whom;
		this.mother.reactToAIMessage("ATTACKED");
		}
	}
	
this.shipDied = this.shipRemoved = this.playerWillEnterWitchspace = function()
	{
	this.freelance();
	function siblings(entity) {return entity.isShip && entity.hasRole("stellarSerpents_body") && entity.script.mother && entity.script.mother == this.mother && entity.script.phase > this.phase}; 
	var segments = system.filteredEntities(this, siblings, this.ship, 5000); 
	
	if(segments.length > 0)
		{
		for(var looseCounter = 0;looseCounter<segments.length;looseCounter++)
			{ segments[looseCounter].script.freelance(); }
		}
	} | 
                
                    | Scripts/stellarSerpents_head.js | this.name					= "stellarSerpents_head.js";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike.";
this.description			= "Stellar Serpent set-up";
this.version				= "1.00";
"use strict";
this.shipSpawned = function()
	{
	this.phase = -1; // ensure the oscillation-stoppings of following segments when higher one is destroyed doesn't affect head.
	
	this.serpentType = Math.ceil(Math.random() * 4);
	switch(this.serpentType)
		{
		case 0:
		case 1: // horizontal oscillation only
			{
			this.amplitudeX = 50 + Math.ceil(Math.random() * 100);
			this.amplitudeY = 0;
			this.spawnBody();
			break;
			}
			
		case 2: // vertical oscillation only
			{
			this.amplitudeX = 0;
			this.amplitudeY = 50 + Math.ceil(Math.random() * 100);
			this.spawnBody();
			break;
			}
			
		case 3: // anti-clockwise rotation
			{
			this.amplitudeX = -50 - Math.ceil(Math.random() * 100);
			this.amplitudeY = 50 + Math.ceil(Math.random() * 100);
			this.spawnBody();
			break;
			}	
			
		case 4: //  clockwise rotation
			{
			this.amplitudeX = 50 + Math.ceil(Math.random() * 100);
			this.amplitudeY = 50 + Math.ceil(Math.random() * 100);
			this.spawnBody();
			break;
			}			
		}	
	}	
this.spawnBody = function()
	{
	this.offset = Math.ceil(2 * Math.random() * 16) - 8;	
	this.segmentCount = 8 + Math.ceil(Math.random() * 16); // between 9 and 24 segments to the serpent
	for(var spawnCounter = 0;spawnCounter<this.segmentCount;spawnCounter++)
		{
		if(spawnCounter == this.segmentCount - 1)
			{
			var section = this.ship.spawnOne("stellarSerpents_tail");
			}
		else
			{
			var section = this.ship.spawnOne("stellarSerpents_bodySegment");
			}
		section.script.phase = spawnCounter;	
		section.script.mother = this.ship;
		section.script.amplitudeX = this.amplitudeX;
		section.script.amplitudeY = this.amplitudeY;
		section.script.offset = this.offset;
		}
	}
this.shipDied = function(whom, why)
	{
	if(why && why == "removed") //  if the serpent was removed rather than killed
		{
		return;
		}
	
	missionVariables.stellarSerpents_status = "NO_SERPENT";
	mission.setInstructionsKey("stellarSerpents_shortNoSerpent", "stellarSerpents_masterScript.js");
	
	if(whom && (whom.isPlayer || (whom.owner && whom.owner.isPlayer) || whom.hasRole("hiredGuns_escort") || whom.hasRole("aquatics_guardianPlayer"))) // killed by player or their henchthings
		{
		missionVariables.stellarSerpents_notification = "PLAYER_DESTROYED";
		player.consoleMessage("500₢ awarded for Serpent termination.", 6)
		player.credits += 500;
		player.score += 1;
		}
	else
		{
		missionVariables.stellarSerpents_notification = "NPC_DESTROYED";
		}
	} | 
                
                    | Scripts/stellarSerpents_masterScript.js | this.name					= "stellarSerpents_masterScript.js";
this.author					= "Thargoid";
this.copyright				= "Creative Commons: attribution, non-commercial, sharealike.";
this.description			= "Wandering and spawning of Stellar Serpent ";
this.version				= "1.21";
"use strict";
this.startUp = function()
	{
	if(!missionVariables.stellarSerpents_status)
		{ missionVariables.stellarSerpents_status = "NO_SERPENT"; } //  Whether the serpent is in the galaxy or not
	if(!missionVariables.stellarSerpents_serpentLife)
		{ missionVariables.stellarSerpents_serpentLife = 0; } //  how long the serpent has been in the galaxy
	if(!missionVariables.stellarSerpents_notification)
		{ missionVariables.stellarSerpents_notification = "INITIAL_BROADCAST"; } // OXP's opening mission screen broadcast
	}
	
this.shipWillEnterWitchspace = function()
	{
	this.actionChance = Math.random();		
	if(missionVariables.stellarSerpents_status == "SERPENT" && this.actionChance > 0.45)
		{
		if(this.actionChance < 0.9) // serpent moves system 45% of time
			{
			missionVariables.stellarSerpents_serpentLife += 1;
			var currentSystem = System.infoForSystem(galaxyNumber, missionVariables.stellarSerpents_currentLocation);
			var systemArray = currentSystem.systemsInRange(7);
			var destinationSystem = systemArray[Math.floor((Math.random()) * systemArray.length)];
			missionVariables.stellarSerpents_currentLocation = destinationSystem.systemID;
			missionVariables.stellarSerpents_locationName = destinationSystem.name;
			mission.setInstructionsKey("stellarSerpents_shortMoved", "stellarSerpents_masterScript.js");
			}
		else // serpent is destroyed by NPC 10% of time, if it has lived at least 3 jumps 
			{
			if(missionVariables.stellarSerpents_serpentLife > 3)
				{ // killed by NPC
				missionVariables.stellarSerpents_status = "NO_SERPENT";
				missionVariables.stellarSerpents_notification = "NPC_DESTROYED";
				mission.setInstructionsKey("stellarSerpents_shortNoSerpent", "stellarSerpents_masterScript.js");
				missionVariables.stellarSerpents_serpentLife = 0;
				}
			else
				{ // short-lived serpent, so keep alive and stay put
				missionVariables.stellarSerpents_serpentLife += 1;
				mission.setInstructionsKey("stellarSerpents_shortStayed", "stellarSerpents_masterScript.js");
				}			
			}
		}	
	else
		{
		if(missionVariables.stellarSerpents_status == "SERPENT")
			{ // serpent exists and stays where it was
			missionVariables.stellarSerpents_serpentLife += 1;
			mission.setInstructionsKey("stellarSerpents_shortStayed", "stellarSerpents_masterScript.js");
			}
		else
			{ // no serpents in the galaxy
			mission.setInstructionsKey("stellarSerpents_shortNoSerpent", "stellarSerpents_masterScript.js");
			}
		}
	}	
this.playerEnteredNewGalaxy = function(galaxyNumber) // reset things if we galaxy hop
	{
	missionVariables.stellarSerpents_status = "NO_SERPENT";
	mission.setInstructionsKey("stellarSerpents_shortNoSerpent", "stellarSerpents_masterScript.js");
	}
	
this.shipLaunchedFromStation = function()
	{
	this.shipExitedWitchspace();
	delete this.shipLaunchedFromStation;
	}
	
this.shipExitedWitchspace = function()
	{
	if(system.isInterstellarSpace) 
		{ // don't place serpent if we're in interstellar space
		return;
		}
	
	if(missionVariables.stellarSerpents_status == "SERPENT" && system.ID == missionVariables.stellarSerpents_currentLocation && system.countShipsWithRole("stellarSerpents_head") == 0)
		{
		system.addShipsToRoute("stellarSerpents_head", 1);
		}
		
	if(system.countShipsWithRole("stellarSerpents_head") > 0)
		{
		this.delayedMessage("Stellar Serpent's presence is confirmed in-system!",6,5);
		}
	
	this.appearanceChance = Math.random();
	if(missionVariables.stellarSerpents_status == "NO_SERPENT" && this.appearanceChance < 0.1) // add a serpent 10% of time if none present in galaxy
		{
		missionVariables.stellarSerpents_status = "SERPENT";
		missionVariables.stellarSerpents_serpentLife = 0;
		missionVariables.stellarSerpents_currentLocation = Math.floor(Math.random() * 256);
		if(galaxyNumber == 2 && (missionVariables.stellarSerpents_currentLocation == 133 || missionVariables.stellarSerpents_currentLocation == 206))
			{ // if the spawning location is one of the two unreachable systems, change it to somewhere else.
			missionVariables.stellarSerpents_currentLocation += Math.ceil(Math.random() * 40);
			}
		missionVariables.stellarSerpents_locationName = System.infoForSystem(galaxyNumber, missionVariables.stellarSerpents_currentLocation).name;
		mission.setInstructionsKey("stellarSerpents_shortAppeared", "stellarSerpents_masterScript.js");
		missionVariables.stellarSerpents_notification = "SERPENT_APPEARED";
		}
	}
	
this.delayedMessage = function(messageString, duration, delay)
	{
	this.messageTimer = new Timer(this, function() {this.displayMessage(messageString, duration);}, delay);
	}
	
this.displayMessage = function(messageString, duration)
	{
	if(messageString)
		{
		if(!duration || duration < 1)
			{
			duration = 6;
			}
		player.commsMessage(messageString, duration);
		}
	}
this.missionScreenOpportunity = function()
	{
	if(!player.ship.docked || !player.ship.dockedStation.isMainStation) 
		{
		return;
		}
	switch(missionVariables.stellarSerpents_notification)
		{
		case "INITIAL_BROADCAST":
			{
			mission.runScreen({title: "Notice to All Commanders", messageKey:"stellarSerpents_initial", background:"stellarSerpent_snakeBackground.png"});
			mission.setInstructionsKey("stellarSerpents_shortNoSerpent", "stellarSerpents_masterScript.js");
			missionVariables.stellarSerpents_notification = "NO_NOTIFICATION";
			break;
			}
		
		case "NPC_DESTROYED":
			{
			mission.runScreen({title: "Serpent Terminated", messageKey:"stellarSerpents_NPCTerminated", background:"stellarSerpent_starBackground.png"});
			missionVariables.stellarSerpents_notification = "NO_NOTIFICATION";
			break;
			}	
		case "PLAYER_DESTROYED":
			{
			mission.runScreen({title: "Serpent Terminated", messageKey:"stellarSerpents_playerTerminated", background:"stellarSerpent_starBackground.png"});
			missionVariables.stellarSerpents_notification = "NO_NOTIFICATION";
			break;
			}	
			
		case "SERPENT_APPEARED":
			{
			mission.runScreen({title: "Serpent Sighted", messageKey:"stellarSerpents_appeared", background:"stellarSerpent_snakeBackground.png"});
			missionVariables.stellarSerpents_notification = "NO_NOTIFICATION";
			break;
			}				
		}
	
	}
	
this.shipDied = function()
	{
	if(this.messageTimer && this.messageTimer.isRunning)
		{
		this.messageTimer.stop();
		}
	} |