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

Expansion Stellar Serpents

Content

Manifest

from Expansion Manager's OXP list from Expansion Manifest
Description A little ongoing mission/flavour OXP on the theme of space monsters. A little ongoing mission/flavour OXP on the theme of space monsters.
Identifier oolite.oxp.Thargoid.StellarSerpents oolite.oxp.Thargoid.StellarSerpents
Title Stellar Serpents Stellar Serpents
Category Missions Missions
Author Thargoid Thargoid
Version 1.21 1.21
Tags ships, missions ships, missions
Required Oolite Version
Maximum Oolite Version
Required Expansions
Optional Expansions
Conflict Expansions
Information URL http://wiki.alioth.net/index.php/Stellar_Serpents_OXP n/a
Download URL https://wiki.alioth.net/img_auth.php/c/c0/Stellar_Serpents_1.21.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 1610873458

Documentation

Also read http://wiki.alioth.net/index.php/Stellar%20Serpents

Stellar Serpents 1.21 Readme & License.txt

Stellar Serpents OXP by Thargoid.

A little OXP as I was bored and saw an old thread musing about space monsters in the game.

So this OXP was born. It randomly adds a large stellar serpent to the game (it affects all 8 galaxies), which threatens ships and needs to be destroyed. The creatures wanders from system to system using wormholes from other ships transit to witchspace, causing a navigational hazard and generally making a nuisance of themselves. Hence each one carries a 500 credit bounty on its head.

Official notification is given on docking with the main station when a serpent is sighted within the current galaxy, and when it is killed by someone else (you're not the only snake-hunter in the Ooniverse!). You can also keep track of where any serpent currently in the galaxy is via the manifest (F5-F5) screen.

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

Requirements -

This OXP is scripted for Oolite v1.75 or later, and will not run on any earlier versions of the game. 

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

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. Basically if it's unique or would identify or overwrite anything in the original OXP, then you may not re-use it (for obvious compatibility reasons).
* 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.
* The license information (either as this file or merged into a larger one) must be included in the OXP.
* 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 "Stellar Serpents 1.21.oxp" to the AddOns directory of your Oolite installation. Then start the game up and the ships should be added. 

Version history:

25/06/2010 - Version 1.00, Initial release - snakes in the game!
27/06/2010 - Version 1.01, minor tweak as tail segment wasn't stopping if body segment destroyed. Also randomised snake length
14/07/2010 - Version 1.02, minor bug fix to add weapon_energy line to turret eyes in shipdata, so they do damage...
13/02/2011 - Version 1.10, update for 1.75 including system. > System. where appropriate
19/02/2011 - Version 1.11, small script tweak to make the serpents spawn after save/reload if in-system (thanks Screet!).
27/02/2011 - Version 1.20, updated to use the frame callback for animation rather than timers (making it 1.75+ only). Also used some new shipdata keys to give a kill only for the head of the serpent, not the segments.
20/02/2012 - Version 1.21, script fix to correct the randomness of the spawning locations.

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

Acknowledgements:

Thanks to SandJ, cim and Eric Walch for the identification of the randomness bug in v1.20

Equipment

This expansion declares no equipment.

Ships

Name
Stellar Serpent
Stellar Serpent
Laser Mount
Turret Mount
stellarSerpents_tail

Models

This expansion declares no models.

Scripts

Path
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();
		}
	}