Scripting requests

An area for discussing new ideas and additions to Oolite.

Moderators: winston, another_commander

User avatar
szaumix
Deadly
Deadly
Posts: 171
Joined: Sun Apr 24, 2022 4:23 am

quick lines of code for parcel and passenger contracts?

Post by szaumix »

I do have a long desired scripting request that feels like it might be a breeze for the js hounds?

I have never been fully satisfied with the parcel and passenger contract prices, and they are my favorite kinds of missions. I've had a look through them a number of times, but my js skills are too poor to find and therefore change them how I think they should be priced.

Basically I want the price to increase much more the more "hot" a passenger or parcel is. That's it, that's the whole request. I have had a look and I don't know which line of code to edit, and even if I found it, I hate (do not really get) js math. Right now there is little (if any?) price bonus for the things that get you assassinated at every witch point. For some reason it only prioritizes based on the *final destination* system, which I find completely irrelevant to the mission.
In 2023 I returned to following Jesus Christ, after years lost and miserable. (Not to churches, any denomination, just praying and walking before God, reading and believing whatever the Bible says). I probably won't be back. Take care, gentlemen
User avatar
phkb
Impressively Grand Sub-Admiral
Impressively Grand Sub-Admiral
Posts: 5269
Joined: Tue Jan 21, 2014 10:37 pm
Location: Writing more OXPs, because the world needs more OXPs.

Re: Scripting requests

Post by phkb »

Line 370 of the oolite-contracts-passengers.js file reads:

Code: Select all

		passenger.payment += (passenger.risk * 200);
You could make it read something like

Code: Select all

		passenger.payment += (passenger.risk * (passenger.risk >= 2 ? 500 : 200));
Then increase the 500 to whatever suits.

The parcels system is almost identical, but it's on line 359 of oolite-contracts-parcels.js file:

Code: Select all

			parcel.payment += (parcel.risk * 200);
Change that to

Code: Select all

			parcel.payment += (parcel.risk * (parcel.risk >= 2 ? 500 : 200));
Alnivel
Dangerous
Dangerous
Posts: 100
Joined: Fri Jun 10, 2022 7:05 pm

Re: Scripting requests

Post by Alnivel »

Request 1: canAwardEquipment (and maybe even awardEquipment) for "purchase" context
Either new methods for PlayerShip or via a new optional second parameter of existing.

One of the use cases - for custom ship outfitting interfaces: a simple "Recommended for you" page with a list of equipment selected by a script based on the player’s progress/playstyle or a complete replacement of standard F3, with bells and whistles (read - with categories and sorting).


Request 2: Equipment condition script for stations
To allow the station itself to decide what to sell and prices.

"oolite-barred-equipment" requires equipment to have a condition script that supports it, plenty of equipment does not, and even if it would have, you need to enumerate them all.
Setting "equivalent_tech_level" to 0 removes most of the equipment, sure, but then you need to add pseudo-equipment to allow what you need back and it too has pitfalls (like there is no way to check if equipment's conditional script allows you to buy it in this system).


Request 3: Global property/method for accessing the currently running script (or its name)
Use cases - mostly shenanigans with overriding other's methods.


(Rather plist-ing than scripting) Request 4: "script_info_overrides" for ships and equipment
Overriding via "-overrides.plist" replaces value entirely, which is not always what you may want with dictionaries, especially with script_info. Example of use - having something like an Ship Configuration Equipment Weight Overrides.oxz as a managed add-on alongside with own tweaks of script_info in the AddOns folder for other matters.

Order of applying:
1. script_info from "-overrides.plist" replace script_info in shipdata/equipment.plist
2. script_info_overrides from "-overrides.plist" merge with script_info_overrides in shipdata/equipment.plist
3. Merged script_info_overrides applies to script_info
Switeck
---- E L I T E ----
---- E L I T E ----
Posts: 2411
Joined: Mon May 31, 2010 11:11 pm

Scripting request: How did I get here?

Post by Switeck »

Or rather: How did the player's ship get somewhere?

Fortunately, there's built-in scripting features to do this:

this.shipWillEnterWitchspace = function(jump)
{
// where the variable jump returning values of "standard jump", "wormhole", "galactic jump", and "carried"

So...one could code this if-else chain inside that function:

Code: Select all

	if(jump == "wormhole") var JumpType=1
	else if(jump == "galactic") var JumpType=2
	else if(jump == "carried") var JumpType=3
	else var JumpType=0;
...and JumpType would return a value of 0-3 depending on the type of jump.

This also seems to work with:

this.playerStartedJumpCountdown = function (jump)
this.playerWillEnterWitchspace = function(jump)

But there's a problem where I want to use it:

this.shipExitedWitchspace = function(jump)
or
this.playerExitedWitchspace = function(jump)
(jump doesn't work with these!)

...because it's really handy to know what kind of jump was just done, since in the example code above -- its value is LOST when you enter a new system!
Instead you'd need to use a global variable or mission variable (example: missionVariables.murphy_thargoid_drive_driveCounter == 0-5 ), which gets really messy code-wise and bloats savegames:
https://wiki.alioth.net/index.php/Varia ... avaScripts

Also it would be nice to know if the previous jump was a misjump and from what system it started from:

Placing this in the this.playerWillEnterWitchspace function:
PreviousJump = system.ID;

Would return a number from -1 to 255, where -1 is a misjump and 0-255 is the starting system number.
But PreviousJump also needs to be a global or mission variable to work.

So my request is for the core game's scripting:

this.shipExitedWitchspace = function(jump)
this.playerExitedWitchspace = function(jump)

...to return a jump value for type of jump, like these all do:

this.shipWillEnterWitchspace = function(jump)
this.playerStartedJumpCountdown = function (jump)
this.playerWillEnterWitchspace = function(jump)

And for PreviousJump (variable name-choice not being great) to also be something returned by the core game.
User avatar
szaumix
Deadly
Deadly
Posts: 171
Joined: Sun Apr 24, 2022 4:23 am

Re: Scripting requests

Post by szaumix »

*Daily minor request:

I'm running Ngalo's Configuration Populator and I'm looking for some places I can increase trader spawn. I think I just want more traders in my ooniverse, both a more frequent flow in/out if possible AND more spawning initially and overall, maybe 50%-100% more. I want my lanes fuller, I never see them any more since I buffed pirates so hard.

Before I started tinkering and blowing stuff up I figured I'd prod the experts for the line
In 2023 I returned to following Jesus Christ, after years lost and miserable. (Not to churches, any denomination, just praying and walking before God, reading and believing whatever the Bible says). I probably won't be back. Take care, gentlemen
User avatar
phkb
Impressively Grand Sub-Admiral
Impressively Grand Sub-Admiral
Posts: 5269
Joined: Tue Jan 21, 2014 10:37 pm
Location: Writing more OXPs, because the world needs more OXPs.

Re: Scripting requests

Post by phkb »

Try this:
In the "oolite-populator.js" inside the OXP, there are two different Library settings objects: "this.$LibraryConfig1" and "this.$LibraryConfig2".
Change "this.$LibraryConfig" to look like this:

Code: Select all

	this.$LibraryConfig2 = {
		Name: this.name,
		Display: "System Populator",
		Alive: "$LibraryConfig2",
		SInt: {
			S3: {
				Name: "$groupSizeFactor",
				Def: 1,
				Min: 0,
				Max: 3,
				Desc: "Group size factor for pirate, hunter and assassin packs.",
				Float: true
			},
			S4: {
				Name: "$pirateFrequencyFactor",
				Def: 1,
				Min: 0,
				Max: 2,
				Desc: "Pirate spawn frequency multiplier.",
				Float: true
			},
			S5: {
				Name: "$hunterFrequencyFactor",
				Def: 1,
				Min: 0,
				Max: 2,
				Desc: "Bounty hunter spawn frequency multiplier.",
				Float: true
			},
			S6: {
				Name: "$assassinFrequencyFactor",
				Def: 1,
				Min: 0,
				Max: 2,
				Desc: "Assassin spawn frequency multiplier.",
				Float: true
			},
			S7: {
				Name: "$traderFrequencyFactor",
				Def: 1,
				Min: 0.1,
				Max: 3,
				Desc: "Trader spawn frequency multiplier.",
				Float: true
			},
			Notify: "$saveSettings"
		}
	};
I used "3" for the max, but feel free to adjust as required.
Immediately below this is the point we read Populator settings from missionVariables. Change that code to be:

Code: Select all

	//Read saved settings, if any:
	if (missionVariables.PopulatorSettings)
	{
		var saved = JSON.parse(missionVariables.PopulatorSettings);
		this.$weaponLevelBias = saved.weaponBias;
		this.$skillLevelBias = saved.skillBias;
		this.$skillLevelMaximum = saved.maxSkill;
		this.$groupSizeFactor = saved.groupSize;
		this.$pirateFrequencyFactor = saved.pirateFreq;
		this.$hunterFrequencyFactor = saved.hunterFreq;
		this.$assassinFrequencyFactor = saved.assassinFreq;
		if (saved.hasOwnProperty("traderFreq")) this.$traderFrequencyFactor = saved.traderFreq;
	}
In the "this.$saveSettings" function, change the bit where settings are updated to be:

Code: Select all

		var settings = {
			weaponBias: pop.$weaponLevelBias,
			skillBias: pop.$skillLevelBias,
			maxSkill: pop.$skillLevelMaximum,
			groupSize: pop.$groupSizeFactor,
			pirateFreq: pop.$pirateFrequencyFactor,
			hunterFreq: pop.$hunterFrequencyFactor,
			assassinFreq: pop.$assassinFrequencyFactor,
			traderFreq: pop.$traderFrequencyFactor
		};
That covers all the settings, so you will be able to adjust things via Library.

Now for the actual changes.
In the "this.systemWillPopulate" function, find the part where it's looping through the "locals" array (should be about line 416ff)
Just after the "bad economic match" part add in the following lines (shown below in context)

Code: Select all

		// bad economic match: one every 2 hours if safe
		else
		{
			rate = 60/(120+(trdanger*2));
		}
		
		rate *= this.$traderFrequencyFactor; // MODIFICATION: multiplied by adjustable trader frequency factor

		this.$repopulatorFrequencyIncoming.traderFreighters += rate;
		this.$repopulatorFrequencyOutgoing.traderFreighters += rate;
		freighters += rate;

		second = seconds[i];
		// couriers are non-mirrored
		rate = (20/(10+((14-local.techlevel)*5)))/second.length;
		if (bottleneck)
		{
			couriers *= 1.5; // simulate long-range routes
		}
		rate *= this.$traderFrequencyFactor; // MODIFICATION: multiplied by adjustable trader frequency factor << ADD THIS LINE
		this.$repopulatorFrequencyIncoming.traderCouriers += rate;
		couriers += rate;
		// smugglers are non-mirrored
		rate = (20/(10+(local.techlevel*5)))/second.length;
		rate *= this.$traderFrequencyFactor; // MODIFICATION: multiplied by adjustable trader frequency factor << ADD THIS LINE
		this.$repopulatorFrequencyIncoming.traderSmugglers += rate;
		smugglers += rate;
	}
	// and outgoing rates for smugglers/couriers. Don't need to
	// specify destination since all rates are equal
	rate = 20/(10+((14-system.info.techlevel)*5));
	rate *= this.$traderFrequencyFactor; // MODIFICATION: multiplied by adjustable trader frequency factor << ADD THIS LINE
	this.$repopulatorFrequencyOutgoing.traderCouriers = rate;
	rate = (20/(10+(system.info.techlevel*5)))/locals.length;
	rate *= this.$traderFrequencyFactor; // MODIFICATION: multiplied by adjustable trader frequency factor << ADD THIS LINE
	this.$repopulatorFrequencyOutgoing.traderSmugglers = rate;
And that should give you adjustable trader spawning.
User avatar
szaumix
Deadly
Deadly
Posts: 171
Joined: Sun Apr 24, 2022 4:23 am

Re: Scripting requests

Post by szaumix »

phkb wrote: Thu Sep 21, 2023 2:23 am
Try this:
Tried these but the populator nuked the system, it was like flying through a ghost system. Checked the log and saw that nothing had spawned but asteroids, the buoy, and the galcop. Checked the in game config and got this:

Image
In 2023 I returned to following Jesus Christ, after years lost and miserable. (Not to churches, any denomination, just praying and walking before God, reading and believing whatever the Bible says). I probably won't be back. Take care, gentlemen
User avatar
phkb
Impressively Grand Sub-Admiral
Impressively Grand Sub-Admiral
Posts: 5269
Joined: Tue Jan 21, 2014 10:37 pm
Location: Writing more OXPs, because the world needs more OXPs.

Re: Scripting requests

Post by phkb »

Ah, yeah, I missed a spot.

On around line 88 you should see something like this:

Code: Select all

        this.$assassinFrequencyFactor = 1;
Add the following line directly underneath:

Code: Select all

        this.$traderFrequencyFactor = 1;
And see how that goes.
User avatar
szaumix
Deadly
Deadly
Posts: 171
Joined: Sun Apr 24, 2022 4:23 am

Re: Scripting requests

Post by szaumix »

phkb wrote: Fri Sep 22, 2023 11:59 am
Ah, yeah, I missed a spot.

On around line 88 you should see something like this:

Code: Select all

        this.$assassinFrequencyFactor = 1;
Add the following line directly underneath:

Code: Select all

        this.$traderFrequencyFactor = 1;
And see how that goes.
Works! Thanks amigo, as always.
In 2023 I returned to following Jesus Christ, after years lost and miserable. (Not to churches, any denomination, just praying and walking before God, reading and believing whatever the Bible says). I probably won't be back. Take care, gentlemen
User avatar
Cholmondely
Archivist
Archivist
Posts: 6220
Joined: Tue Jul 07, 2020 11:00 am
Location: The Delightful Domains of His Most Britannic Majesty (industrial? agricultural? mainly anything?)
Contact:

Re:

Post by Cholmondely »

Eric Walch wrote: Wed Nov 24, 2010 8:59 am
Switeck wrote:
Eric Walch wrote:
Hunters do have a state were they are looking for cargo,
...though they're extremely unlikely to be in such a state in Oolite v1.74.2 :lol:
A ship entering this state will be slightly more likely in 1.75. But this state was missing a "ACCEPT_DISTRESS_CALL" message, with the potency that it goes for the wrong target. In this case a found offender could be overwritten by the aggressor in distress call. A harmless bug though as both are potential targets in the first place.

When an AI scans in the UPDATE state, there is no problem as it will execute the FOUND_TARGET message during the same update. But when doing a search in any other state, including ENTER, the FOUND_TARGET message is generated and a found_target is transferred. However, execution of the message will only start on the next AI-update.
When Oolite itself now sets a new found_target before the AI-update and the AI has no corresponding message to react on, it will assume the found_target was a result of its previous search.

Until yesterday I never realised that even an ATTACKED has the same potential problem. When that message is generated, also a new found_target is set. So when doing a search in ENTER and there is no ATTACK message defined, (like the current INBOUND_CHECK of route1patrolAI.plist) there is a change it goes for the attacker, thinking it is the offender it was looking for. Not a serious mistake for a police though.

Doing the scan in UPDATE or making sure the state has an ATTACKED reaction defined, prevents these kind of rare conflicts.

JS has no such problems as the script has the logic for making decisions.
Yesterday I wrote an eventhandler:

distressMessageReceived(agressor, sender) in analogy to the just added handler commsMessageReceived(message, sender). It will go to all 16 nearby ships having a crew on board. When there are no problems, it might be added to trunk.
Two Questions!

1) Does the distress message only reach the 16 nearest ships?
2) Is there a range limit?

References:
1) ShipEntityAI.m

Code: Select all

- (void) broadcastDistressMessage
{
	/*-- Locates all the stations, bounty hunters and police ships in range and tells them that you are under attack --*/
	[self broadcastDistressMessageWithDumping:YES];
}	

- (void) broadcastDistressMessageWithDumping:(BOOL)dumpCargo
{
	[self checkScannerIgnoringUnpowered];
	DESTROY(_foundTarget);
	
	ShipEntity	*aggressor_ship = (ShipEntity*)[self primaryAggressor];
	if (aggressor_ship == nil)  return;
	
	// don't send too many distress messages at once, space them out semi-randomly
	if (messageTime > 2.0 * randf())  return;
	
	NSString	*distress_message = nil;
	BOOL		is_buoy = (scanClass == CLASS_BUOY);
	if (is_buoy)  distress_message = @"[buoy-distress-call]";
	else  distress_message = @"[distress-call]";
	
	unsigned i;
	for (i = 0; i < n_scanned_ships; i++)
	{
		ShipEntity*	ship = scanned_ships[i];

    // dump cargo if energy is low
		if (dumpCargo && !is_buoy && [self primaryAggressor] == ship && energy < 0.375 * maxEnergy)
		{
			[self ejectCargo];
			[self performFlee];
		}
		
		// tell it! (only plist AIs send comms here; JS AIs are
		// expected to handle their own)
		if (ship->isPlayer && ![self hasNewAI])
		{
			[ship doScriptEvent:OOJSID("distressMessageReceived") withArgument:aggressor_ship andArgument:self];

			if (!is_buoy && [self primaryAggressor] == ship && energy < 0.375 * maxEnergy)
			{
				[self sendExpandedMessage:@"[beg-for-mercy]" toShip:ship];
			}
			else if ([self bounty] == 0)
			{
				// only send distress message to player if plausibly sending
				// one more generally
				[self sendExpandedMessage:distress_message toShip:ship];
			}
			
			// reset the thanked_ship_id
			DESTROY(_thankedShip);
		}
		else if ([self bounty] == 0 && [ship crew]) // Only clean ships can have their distress calls accepted
		{
			[ship doScriptEvent:OOJSID("distressMessageReceived") withArgument:aggressor_ship andArgument:self];
			
			// we only can send distressMessages to ships that are known to have a "ACCEPT_DISTRESS_CALL" reaction
			// in their AI, or they might react wrong on the added found_target.

			// ship must have a plist AI for this next bit. JS AIs
			// should already have done something sensible on
			// distressMessageReceived
			if (![self hasNewAI])
			{
				// FIXME: this test only works with core AIs
				if (ship->isStation || [ship hasPrimaryRole:@"police"] || [ship hasPrimaryRole:@"hunter"])
				{
					[ship acceptDistressMessageFrom:self];
				}
			}
		}
	}
}
2) oolite-priorityai.js

Code: Select all


/* ****************** Response definition functions ************** */

/* Standard state-machine responses. These set up a set of standard
 * state machine responses where incoming events will cause reasonable
 * default behaviour and often force a reconsideration of
 * priorities. Many behaviours will need to supplement the standard
 * responses with additional definitions. */

PriorityAIController.prototype.responsesAddStandard = function(handlers) 
{
...
	handlers.distressMessageReceived = this.responseComponent_standard_distressMessageReceived;
...
PriorityAIController.prototype.responseComponent_station_distressMessageReceived = function(aggressor, sender)
{
	if (this.getParameter("oolite_flag_listenForDistressCall") != true)
	{
		return;
	}
	if (aggressor.scanClass == "CLASS_POLICE" || (aggressor.isStation && aggressor.allegiance == "galcop"))
	{
		return;
	}
	if (this.ship.scanClass == "CLASS_POLICE" || (this.ship.isStation && this.ship.allegiance == "galcop"))
	{
		if (this.distance(aggressor) < this.scannerRange)
		{
			aggressor.setBounty(aggressor.bounty | 8,"attacked innocent");
		}
	}
	this.setParameter("oolite_distressAggressor",aggressor);
	this.setParameter("oolite_distressSender",sender);
	this.setParameter("oolite_distressTimestamp",clock.adjustedSeconds);
	this.reconsiderNow();
}
...
/* ******************* Response components *********************** */

/* Response components. These are standard response component
 * functions which can be passed by reference to save on variable
 * destruction/creation */
...
PriorityAIController.prototype.responseComponent_standard_distressMessageReceived = function(aggressor, sender)
{
	if (this.getParameter("oolite_flag_listenForDistressCall") != true)
	{
		return;
	}
	if (aggressor.scanClass == "CLASS_POLICE" || (aggressor.isStation && aggressor.allegiance == "galcop"))
	{
		return;
	}
	if (this.ship.scanClass == "CLASS_POLICE" || (this.ship.isStation && this.ship.allegiance == "galcop"))
	{
		if (this.distance(aggressor) < this.scannerRange)
		{
			aggressor.bounty |= 8;
		}
	}
	this.setParameter("oolite_distressAggressor",aggressor);
	this.setParameter("oolite_distressSender",sender);
	this.setParameter("oolite_distressTimestamp",clock.adjustedSeconds);
	this.reconsiderNow();
}
...
left out gubbins about the station's response
Last edited by Cholmondely on Tue Jul 15, 2025 12:02 pm, edited 1 time in total.
Comments wanted:
Missing OXPs? What do you think is missing?
Lore: The economics of ship building How many built for Aronar?
Lore: The Space Traders Flight Training Manual: Cowell & MgRath Do you agree with Redspear?
User avatar
phkb
Impressively Grand Sub-Admiral
Impressively Grand Sub-Admiral
Posts: 5269
Joined: Tue Jan 21, 2014 10:37 pm
Location: Writing more OXPs, because the world needs more OXPs.

Re: Scripting requests

Post by phkb »

Cholmondely wrote: Tue Jul 15, 2025 9:28 am
1) Does the distress message only reach the 16 nearest ships?
My initial scan through the code seems to indicate the number is 32, not 16. But not 100% on that.
Cholmondely wrote: Tue Jul 15, 2025 9:28 am
2) Is there a range limit?
It's certainly limited in some way, and my reading of the code seems to indicate scannerRange * scannerRange (so, scannerRange squared, where scannerRange is the value set via "scanner_range" in shipdata), but again, definitely not 100% on that.
Post Reply