Page 5 of 14

Posted: Fri Nov 06, 2009 7:25 pm
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!

Posted: Fri Nov 06, 2009 9:10 pm
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.

Posted: Sat Apr 10, 2010 8:43 pm
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?

Posted: Sat Apr 10, 2010 9:09 pm
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...

Posted: Sat Apr 10, 2010 9:29 pm
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

Posted: Sat Apr 10, 2010 9:40 pm
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.

Posted: Sat Apr 10, 2010 9:59 pm
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.

Posted: Sun Apr 11, 2010 9:12 am
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 :)

Posted: Mon Apr 12, 2010 10:04 pm
by pagroove
Thanks for delving into this. So what is the definitive code to replace?
From what I read its better to avoid psuedorandom?

Posted: Mon Apr 12, 2010 10:19 pm
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.

Posted: Mon Apr 12, 2010 10:20 pm
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.

Posted: Mon Apr 12, 2010 10:21 pm
by JensAyton
No, don’t use the one Commander McLane posted above.

Cabbages at dawn!

Posted: Tue Apr 13, 2010 5:19 am
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

Posted: Tue Apr 13, 2010 6:45 am
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.

Posted: Tue Apr 13, 2010 2:45 pm
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