[UPDATED RELEASE 03-07-2014] Superhub V1.6 OXZ

Discussion and information relevant to creating special missions, new ships, skins etc.

Moderators: winston, another_commander

Post Reply
User avatar
ZygoUgo
---- E L I T E ----
---- E L I T E ----
Posts: 406
Joined: Mon Nov 17, 2008 4:15 pm
Location: Blighty

Post by ZygoUgo »

Love this model, is it going to replace any main stations at any point?
What I wanted to quizz you about was how do I change the speed at which it rotates? I want to slow it down to emphasise it's scale.
(I'd also like to slow the planets *a bit* and salvageable wrecks *so they're more drifting than spinning*, Is this done in the same way?)
Thanks for any replies!
User avatar
pagroove
---- E L I T E ----
---- E L I T E ----
Posts: 3035
Joined: Wed Feb 21, 2007 11:52 pm
Location: On a famous planet

Post by pagroove »

ZygoUgo wrote:
Love this model, is it going to replace any main stations at any point?
What I wanted to quizz you about was how do I change the speed at which it rotates? I want to slow it down to emphasise it's scale.
(I'd also like to slow the planets *a bit* and salvageable wrecks *so they're more drifting than spinning*, Is this done in the same way?)
Thanks for any replies!
At level 12 or higer it sits at the edge of the original Station Aegis. So you get 2 stations. This is simulating that the busy systems get so busy that a second station with high tech facilities where needed.
For P.A. Groove's music check
https://soundcloud.com/p-a-groove
Famous Planets v 2.7. (for Povray)
Image
https://bb.oolite.space/viewtopic.php?f=4&t=13709
User avatar
pagroove
---- E L I T E ----
---- E L I T E ----
Posts: 3035
Joined: Wed Feb 21, 2007 11:52 pm
Location: On a famous planet

Post by pagroove »

A question to the more advanced sripters here. While traveling through the Ooniverse I noticed that the Superhubs are not added permanently to a planet. You may come back to a system to notice that it is not there and on the next jump it is added again. How can I change this?
For P.A. Groove's music check
https://soundcloud.com/p-a-groove
Famous Planets v 2.7. (for Povray)
Image
https://bb.oolite.space/viewtopic.php?f=4&t=13709
User avatar
Thargoid
Thargoid
Thargoid
Posts: 5528
Joined: Thu Jun 12, 2008 6:55 pm

Post by Thargoid »

At the moment they're spawned at least semi-randomly (alongside the minimum requirements there is a random chance).

What you would have to do is remove that random chance, but then they would appear in every system that meets the minimum criteria. To make it appear random but not be, you would need to use something that does not vary, for example the system ID number.

So for example if you set the usual minimum criteria (tech level, population, government type etc) but then also set (for example) that the system ID had to be exactly divisible by 4, then you would have superhubs appearing in 1 in 4 of the systems which meet the minimum criteria, but they would always appear there...
User avatar
pagroove
---- E L I T E ----
---- E L I T E ----
Posts: 3035
Joined: Wed Feb 21, 2007 11:52 pm
Location: On a famous planet

Post by pagroove »

Code: Select all

this.name           = "PAGroove_superhubPopulator";
this.author         = "Thargoid";
this.copyright      = "This script is hereby placed in the public domain.";
this.version        = "1.1";
this.description    = "Script to add a single superhub near the system main station";


this.shipWillLaunchFromStation = function(station)
   {
   if(station.isMainStation)
      {
      this.spawnHub();
      }
   }

this.shipWillExitWitchspace = function()
   {
   this.spawnHub();
   }

this.spawnHub = function()
   {
   if(Math.random() < 0.5 || system.techLevel < 11 || system.government < 4 || system.countShipsWithRole("pagroove_superhub") > 0 || this.superHubBlock)   
      {
      this.superHubBlock = true;
      return;
      }
   else
      {
      this.xOffset = (Math.random() * 10000) + 10000; // x offset between 10 and 20km
      this.yOffset = (Math.random() * 10000) + 10000; // y offset between 10 and 20km

      if(Math.random() > 0.5) // 50:50 chance of offsetting in the other direction
         {
         this.xOffset = -this.xOffset;
         }
      if(Math.random() > 0.5) // 50:50 chance of offsetting in the other direction
         {
         this.yOffset = -this.yOffset;
         }

      system.legacy_addShipsAtPrecisely("pagroove_superhub", 1, "abs", system.mainStation.position.add([this.xOffset, this.yOffset, 0]));
      }
   }

this.shipWillEnterWitchspace = function()
   {
   this.superHubBlock = null;
   }
Can you give an example with this code?
I want to have them appear always at the same systems
For P.A. Groove's music check
https://soundcloud.com/p-a-groove
Famous Planets v 2.7. (for Povray)
Image
https://bb.oolite.space/viewtopic.php?f=4&t=13709
User avatar
Commander McLane
---- E L I T E ----
---- E L I T E ----
Posts: 9520
Joined: Thu Dec 14, 2006 9:08 am
Location: a Hacker Outpost in a moderately remote area
Contact:

Post by Commander McLane »

Thargoid wrote:
To make it appear random but not be, you would need to use something that does not vary, for example the system ID number.
Errmmm... Actually this is exactly what the pseudoFixed random numbers in Oolite are meant for.

JS-syntax is system.pseudoRandom100 and system.pseudoRandom256 respectively, creating an integer between 0-99 or 0-255 which will always be the same in any given system. According to the progress thread there is system.pseudoRandomNumber as well, which creates a floating point.

pagroove: all you have to do is replace this

Code: Select all

   if(Math.random() < 0.5 || system.techLevel < 11 || system.government < 4 || system.countShipsWithRole("pagroove_superhub") > 0 || this.superHubBlock)
with this

Code: Select all

   if(system.pseudoRandomNumber < 0.5 || ...
This would make them reliably appear in half of the systems which qualify TL- and government-wise. Choosing another value would make the portion bigger or smaller.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

Thargoid’s suggestion can be implemented by replacing Math.random() < 0.5 with system.ID % 2 == 0. % is the remainder operation, and 2 can be seen as the denominator of a fraction: this gives a 1/2 chance of appearing. (As Commander McLane points out it’s theoretically cleaner to use system.pseudoRandomNumber instead, but it doesn’t really make a difference since the IDs are “geographically” distributed in a random-looking way. system.pseudoRandom256 could be used as a direct substitute for system.ID but has annoying side effects and should be avoided in new code.)

The problem with this approach is that if two OXPs use it, their objects will always appear in the same system. One workaround is to use a slightly different fraction and a bias, like (system.ID * 27 + 463) % 53 == 0. This gives a 27/53 (0.509) chance of appearing. Obviously you should write an explanatory comment so anyone copies your code uses different magic numbers.
User avatar
Thargoid
Thargoid
Thargoid
Posts: 5528
Joined: Thu Jun 12, 2008 6:55 pm

Post by Thargoid »

I was a little wary of pseudoRandomNumber, as I can't say exactly how it is generated (and hence whether there would be any conflict with the other minimum requirements) hence why I said system.ID.

But learned something with the % operator, didn't know that one :)
User avatar
pagroove
---- E L I T E ----
---- E L I T E ----
Posts: 3035
Joined: Wed Feb 21, 2007 11:52 pm
Location: On a famous planet

Post by pagroove »

Thanks for delving into this. So what is the definitive code to replace?
From what I read its better to avoid psuedorandom?
For P.A. Groove's music check
https://soundcloud.com/p-a-groove
Famous Planets v 2.7. (for Povray)
Image
https://bb.oolite.space/viewtopic.php?f=4&t=13709
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

pagroove wrote:
Thanks for delving into this. So what is the definitive code to replace?
From what I read its better to avoid psuedorandom?
The definitive code is, as always¹, the last thing I said. ;-)

To be clear, there is nothing wrong with pseudoRandomNumber as such, but pseudoRandom256 and pseudoRandom100 should be avoided. However, using pseudoRandomNumber to decide whether to spawn an object is a bad idea because if several OXPs do it, their objects will always appear in the same system.

Using system.ID with a not-quite-0.5 fraction and an offset will not have this problem, as long as each OXP doing it uses different magic numbers.

¹Except when it isn’t.
User avatar
Commander McLane
---- E L I T E ----
---- E L I T E ----
Posts: 9520
Joined: Thu Dec 14, 2006 9:08 am
Location: a Hacker Outpost in a moderately remote area
Contact:

Post by Commander McLane »

pagroove wrote:
Thanks for delving into this. So what is the definitive code to replace?
From what I read its better to avoid psuedorandom?
No, it's not, as long as you stick to the JS-variant system.pseudoRandomNumber.

So just use the one I posted above.
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

No, don’t use the one Commander McLane posted above.

Cabbages at dawn!
User avatar
Kaks
Quite Grand Sub-Admiral
Quite Grand Sub-Admiral
Posts: 3009
Joined: Mon Jan 21, 2008 11:41 pm
Location: The Big Smoke

Post by Kaks »

May I suggest a compromise to save cabbages?

The thing is, the original problem can actually be split up into two closely connected problems pulling in two opposite directions.

The first half of it is of course the original question: how can we get a consistent 'random' result for each system?
Using plain pseudoRandomNumber solves it, but then the second half of the problem shows up: if every single oxp uses the same identical formula to determine where 'random' stuff should appear we'll end up with a very non-random looking universe.

The best solution, which should hopefully deliver the desired 'random-but-consistent' universe, is to mix up system.pseudoRandomNumber with some added semi-random factors, and use different 'mix up' formulas for different OXPs.

Using Ahruman's latest advice above, and combining it with Commander McLane's we should get something like this:

Code: Select all

// mix up pseudo random number up
if((system.pseudoRandomNumber * 27 * 463) % 53 == 0  || .
However, if this is acceptable to both parties above, what shall we do with the saved cabbages? :P
Hey, free OXPs: farsun v1.05 & tty v0.5! :0)
User avatar
JensAyton
Grand Admiral Emeritus
Grand Admiral Emeritus
Posts: 6657
Joined: Sat Apr 02, 2005 2:43 pm
Location: Sweden
Contact:

Post by JensAyton »

Wouldn’t work straight up… but thinking about it, mine is broken too. Thank heavens for disclamatory footnotes! Will think again this evening.
User avatar
Cmd. Cheyd
---- E L I T E ----
---- E L I T E ----
Posts: 934
Joined: Tue Dec 16, 2008 2:52 pm
Location: Deep Horizon Industries Manufacturing & Research Site somewhere in G8...

Post by Cmd. Cheyd »

I have a Proposed Solution (having run into this same problem). This was meant to come out when SR2 does, but letting this part out early won't matter.

I created (with a large contribution from PhantorGorth) a "common functions" script that will go in most of my OXP's. It's purpose is to provide some common things that I needed, and other OXP developers seem to use a lot. One of those (the one Phantor contributed) is a system-consistent psuedorandom number generator. Call the function and pass it the following:

Code: Select all

worldScripts["Cheyd_JS_Common_Functions_1"].generateUniquePseudoRandomArray({LowerLimit: <Integer Inclusive>, UpperLimit: <Integer Inclusive>, HowMany: <Integer>, Seed_1: <Prime Number>, Seed_2: <Prime Number>, Seed_3: <Prime Number>, MaxSeed:<Prime Number>});
Here's the actual script. If you'd like to use it, let me know and I'll write you a license statement.

Code: Select all

this.name = "Cheyd_JS_Common_Functions_1";
this.author	= "Blake Deakins - a.k.a. Cmd. Cheyd";
this.description = "Provide a set of commonly used JS functions for all OXPs.";
this.version = "0.0.2";
this.copyright = "© Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States";

// These variables are used in various functions within the system.
// ********************* DO NOT ALTER THESE  *********************
this.oxpArraySeeds = new Array();
this.oxpArrayArguments = new Array();
this.totalArrays = 0;
var arraysNeeded = 0;

// ***************************************************************

// ********************* this.dbg_Cmn_JS_Func_ScriptGING VARIABLES  *********************
// These variables are for this.dbg_Cmn_JS_Func_Scriptging purposes only.  Some of them
// can cause significant decrease in system speed while enabled.  
// Enabling of these options is not recommended unless you are
// familiar with scripting.
this.dbg_Cmn_JS_Func_Script = false;
this.dbg_oxpRegistration = false;
this.dbg_oxpArraySeeds = false;
this.dbg_events = false;
this.dbg_stringsearch = true;
// ***************************************************************

/*    ********  Internal Functions for this OXP  ********    */

this.setUpArrays = function()
{
	for (var i=this.oxpArraySeeds.length; i < this.oxpArrayArguments.length; i++) {
		this.oxpArraySeeds[i] = this.generateUniquePseudoRandomArray({LowerLimit: 1, UpperLimit: 1048575, HowMany: 2048, Seed_1: this.oxpArrayArguments[i][0], Seed_2: this.oxpArrayArguments[i][1], Seed_3: this.oxpArrayArguments[i][2], MaxSeed: 16777215});
	}
	if(this.dbg_Cmn_JS_Func_Script == true && this.dbg_oxpArraySeeds == true) {
		for (var i=0; i < this.oxpArraySeeds.length; i++) {
			for (var j=0; j < this.oxpArraySeeds[i].length; j++) {
				log (this.name,"this.oxpArraySeeds["+i+"]["+j+"] = " + this.oxpArraySeeds[i][j]);
			}
		}
	}

}

this.registerOXPArrayRequirements = function (Seed, Prime1, Prime2) {
	if (this.dbg_Cmn_JS_Func_Script == true && this.dbg_oxpRegistration == true) {
		log (this.name, "this.registerOXPArrayRequirements called by: " + this.registerOXPArrayRequirements.caller.toString());
		log (this.name, "Arguments passed were Seed: " + Seed + ".  Prime1: " + Prime1 + ".  Prime2: " + Prime2 + ".");
	}
	return this.oxpArrayArguments.push([Seed, Prime1, Prime2]) - 1;
}

this.fetchOXPArraySeeds = function (SeedSet, element){
	if (this.dbg_Cmn_JS_Func_Script == true && this.dbg_oxpRegistration == true) {
		log (this.name, "this.fetchOXPArraySeeds called by: " + this.fetchOXPArraySeeds.caller.toString());
		log (this.name, "Arguments passed were: SeedSet = " + SeedSet + ".  Element requested was: " + element + ".");
		log (this.name, "this.fetchOXPArraySeeds.length = " + this.fetchOXPArraySeeds.length);
	}
	return this.oxpArraySeeds[SeedSet][element];
}

/*    ********  String Manipulation Functions  ********    */
this.Left = function(str, n) {
	if (n <= 0)
	    return "";
	else if (n > String(str).length)
	    return str;
	else
	    return String(str).substring(0,n);
}
this.Right = function (str, n) {
    if (n <= 0)
       return "";
    else if (n > String(str).length)
       return str;
    else {
       var iLen = String(str).length;
       return String(str).substring(iLen, iLen - n);
    }
}

this.searchStringAForStringB = function (stringA,stringB) {
	if (this.dbg_Cmn_JS_Func_Script == true && this.dbg_stringsearch == true) log(this.name, "String A = " + stringA + ".  String B = " + stringB + ".");
	if(stringA.indexOf(stringB) != -1 ) return true;
	return false;
}


/*    ********  Orientation / Vector Functions  ********    */
this.turnEntityToPlanet = function(entity) {
	var targetVector = system.mainPlanet.position.subtract(entity.position).direction();
	var angle = entity.heading.angleTo(targetVector);
	var cross = entity.heading.cross(targetVector).direction();
	entity.orientation = entity.orientation.rotate(cross, -angle);
	return;		
}

this.turnEntity1ToEntity2 = function(entity1,entity2) {
	var targetVector = entity2.position.subtract(entity1.position).direction();
	var angle = entity1.heading.angleTo(targetVector);
	var cross = entity1.heading.cross(targetVector).direction();
	entity1.orientation = entity1.orientation.rotate(cross, -angle);
	return;		
}


/*    ********  Numerical Manipulation Functions  ********    */
this.generateUniquePseudoRandomArray = function(parameters) {
	/*
	This function was provided by PhantorGorth (Paul Cooper) and is
	licensed under Creative Commons Attribution-Non-Commercial 2.5 UK: Scotland Licence.
	Full license and acknowledgement details are available in the ReadMe.
	*/
	
	
	// Break out passed parameters into individual components
	var intLower = parameters.LowerLimit;	// Minimum Value for the random numbers selected
	var intUpper = parameters.UpperLimit;	// Maximum Value for the random numbers selected
	var intSize = parameters.HowMany;		// How many results do you want returned?
	var intSeed = parameters.Seed_1;			// Seed value from Global array
	var intMaxInternalSeed = parameters.MaxSeed
	var SeedVal1 = parameters.Seed_2;	// Prime Number
	var SeedVal2 = parameters.Seed_3;	// Second Prime Number

	
	//Initialise List
	var List = new Array();
	var Tree = new Array();
	if (SeedVal1 == undefined) SeedVal1 = 3631;
	if (SeedVal2 == undefined) SeedVal2 = 761;

	Tree[0] = {"Value" : undefined, "Lower" : -1, "Higher": -1, "CountBelow" : 0};

	function AddSeedToTree(inValue, NodeRef)
	{
		if ((NodeRef.Lower == -1) && (NodeRef.Higher == -1) && (NodeRef.Value == undefined))
		{
			NodeRef.Value = inValue;
			return;
		}
		if (inValue < NodeRef.Value)
		{
			NodeRef.CountBelow++;
			if (NodeRef.Lower == -1)
			{
				//Create and Intialise New NodeRef
				var obj = {"Value" : undefined, "Lower" : -1, "Higher": -1, "CountBelow" : 0};
				NodeRef.Lower = Tree.push(obj) - 1;
			}
			AddSeedToTree(inValue, Tree[NodeRef.Lower]);
		}
		else if (inValue > NodeRef.Value)
		{
			if (NodeRef.Higher == -1)
			{
				//Create and Intialise New NodeRef
				var obj = {"Value" : undefined, "Lower" : -1, "Higher": -1, "CountBelow" : 0};
				NodeRef.Higher = Tree.push(obj) - 1;
			}
			AddSeedToTree(inValue, Tree[NodeRef.Higher]);
		}
		else
		{
			return //Shouldn't happen as no two values in tree should be the same
		}
		return; 
	}

	function CountSeedsBelow(inValue, NodeRef)
	{
		if (NodeRef.Value == inValue)
		{
			return NodeRef.CountBelow + 1;
		}
		else if (inValue < NodeRef.Value)
		{
			if (NodeRef.Lower == -1)
			{
				return 0;
			}
			else
			{
				return CountSeedsBelow(inValue, Tree[NodeRef.Lower]);
			}
		}
		if (inValue > NodeRef.Value)
		{
			if (NodeRef.Higher == -1)
			{
				return NodeRef.CountBelow + 1;
			}
			else
			{
				return NodeRef.CountBelow + 1 + CountSeedsBelow(inValue, Tree[NodeRef.Higher]);
			}
		}
		else
		{
			return 0; // shouldn't get here
		}
	} 

	var newSeed = intSeed;
	var count = 0;
	var skipbase = 0;
	var lastvalue = 0;
	
	while (count < intSize)
	{	
		var c2 = 1;
		skipbase = lastvalue;
		newSeed = ((SeedVal1 * newSeed + SeedVal2) % (intMaxInternalSeed + 1));
		var trynewvalue = lastvalue + newSeed;
		if (trynewvalue > intUpper)
		{
			trynewvalue = (trynewvalue - intUpper) % (intUpper-intLower+1-count) + intUpper + 1;
		}
		if (count > 0)
		{
			var tryoldvalue = trynewvalue +1; // Just to make it different for the while test.
			while (trynewvalue != tryoldvalue)
			{
				if (tryoldvalue < trynewvalue) skipbase = tryoldvalue;
				var tryoldvalue = trynewvalue;
				var trynewvalue = trynewvalue + CountSeedsBelow(trynewvalue, Tree[0]) - CountSeedsBelow(skipbase, Tree[0]);
				if (trynewvalue > intUpper)  // To deal with wrapping
				{
					trynewvalue = (trynewvalue - (intUpper-intLower+1));
					skipbase = intLower - 1;
				}
				c2++;
			}
		}
		else
		{
			trynewvalue = ((newSeed) % (intUpper-intLower+1)) + intLower;
		}
		//Add newSeed to List
		List.push(trynewvalue);
		lastvalue = trynewvalue
		AddSeedToTree(trynewvalue, Tree[0]);
		count++;
	} 
	return List;
}

/*    ********  Miscellaneous Functions  ********    */
Alternatively, the function itself was licensed by PhantorGorth as follows"
PhantorGorth (Paul Cooper)
Licensed under Creative Commons Attribution-Non-Commercial 2.5 UK: Scotland Licence
Date 10/01/2010
Post Reply