Join us at the Oolite Anniversary Party -- London, 7th July 2024, 1pm
More details in this thread.

Scripted 'new' event handlers.

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

Moderators: winston, another_commander

Post Reply
User avatar
Capt. Murphy
Commodore
Commodore
Posts: 1127
Joined: Fri Feb 25, 2011 8:46 am
Location: UK South Coast.

Scripted 'new' event handlers.

Post by Capt. Murphy »

In post https://bb.oolite.space/viewtopic.ph ... 10#p167807 in the Scripting Requests thread in response to
Commander McLane wrote:
Phasted wrote:
An event-handler (well, two actually) that triggers when the player buys or sells cargo (passing commoditiy name, quantity, and price).
Seconded, as it seems quite useful. :)

I said.
In the meantime you could do something clever with the this.guiScreenChanged handler triggering a framecallback including code to keep an eye on the players manifest and credit level whilst on the market screen and react appropriately. In effect using framecallbacks to make your own event handlers.
Well here is some sample code that does just that

Code: Select all

// script needed to set up custom event handlers.
this.cargoType = ["food", "textiles", "radioactives", "slaves", "liquorWines", "luxuries", "narcotics", "computers", "machinery", "alloys", "firearms", "furs", "minerals", "gold", "platinum", "gemStones", "alienItems"];

this.guiScreenChanged = function(to,from)
{
	if (to !== "GUI_SCREEN_MARKET" || guiScreen !== "GUI_SCREEN_MARKET"){return;}
	this.originalManifest = this.getManifest();
	this.originalCredits = player.credits;
	this.monitorManifestCallBack = addFrameCallback(this.monitorManifest.bind(this));
}

this.monitorManifest = function(delta)
{
	if (guiScreen !== "GUI_SCREEN_MARKET")
	{
		removeFrameCallback(this.monitorManifestCallBack);
		delete this.monitorManifestCallBack;
		return;
	}
	this.currentManifest = this.getManifest();
	var counter;
	var cargoDiff;
	var creditDiff;
	for (counter = 0; counter < 17;counter++)
	{
	cargoDiff = this.currentManifest[counter] - this.originalManifest[counter];
	creditDiff = this.originalCredits - player.credits;
	if (cargoDiff < 0) {this.playerSoldCargo(this.cargoType[counter],Math.abs(cargoDiff),Math.abs(creditDiff));}
	if (cargoDiff > 0) {this.playerBoughtCargo(this.cargoType[counter],Math.abs(cargoDiff),Math.abs(creditDiff));}
	if (cargoDiff !== 0) {this.originalManifest = this.currentManifest; this.originalCredits = player.credits;break;}
	}
}

this.getManifest = function()
{
	var counter;
	var tempArray = new Array(17);
	for (counter = 0; counter < 17;counter++)
	{
		tempArray[counter] = manifest[this.cargoType[counter]];
	}
	return tempArray;
}


// and here we have the custom event handlers...
this.playerBoughtCargo = function(cargoName,quantity,price)
{
	log(this.name,"Player bought "+quantity+" of "+cargoName+" for "+price+" credits.");
}

this.playerSoldCargo = function(cargoName,quantity,price)
{
	log(this.name,"Player sold "+quantity+" of "+cargoName+" for "+price+" credits.");
}
I think there is probably scope for more of this kind of thing. However it would be inefficient for this to be replicated through many scripts, and have multiple identical framecallbacks running. So I'm thinking there may be scope for Cabal_Common_eventHandlers?

A script that wanted to use Cabal_Common_eventHandlers would call a function in Cabal_Common_eventHandlers to register it's this.name. Cabal_Common_eventHandlers would contain an adapted version of the code above which would look up it's register of worldScripts, check that the eventHandler existed in a aprticular worldScript and if so call the eventHandler in that worldScript.

Svengali, what do you think? It's doable, but would it be a resource killer? Have I missed anything?

I'll have a trawl through scripting requests to see if there is anything else outstanding that could be done in a similar way, in the meantime anyone with any bright ideas, or code snippets like that above please post.
Last edited by Capt. Murphy on Tue Apr 03, 2012 6:01 am, edited 1 time in total.
[EliteWiki] Capt. Murphy's OXPs
External JavaScript resources - W3Schools & Mozilla Developer Network
Win 7 64bit, Intel Core i5 with HD3000 (driver rev. 8.15.10.2696 - March 2012), Oolite 1.76.1
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

Re: Scripted 'new' event handlers.

Post by Kaks »

Impressive stuff! :)
Hey, free OXPs: farsun v1.05 & tty v0.5! :0)
User avatar
Capt. Murphy
Commodore
Commodore
Posts: 1127
Joined: Fri Feb 25, 2011 8:46 am
Location: UK South Coast.

Re: Scripted 'new' event handlers.

Post by Capt. Murphy »

Kaks wrote:
Impressive stuff! :)
Thanks....worth doing to let the devs concentrate on more fun stuff?

here is another - this.equipmentRepaired(equipment)

Code: Select all

this.damagedEquipment = new Array;

this.equipmentDamaged = function(equipment)
{
 this.damagedEquipment.push(equipment)
 if (!this.monitorDamagedEquipmentCallback){this.monitorDamagedEquipmentCallback = addFrameCallback(this.monitorDamagedEquipment.bind(this));}
}

this.monitorDamagedEquipment = function()
{
 if (this.damagedEquipment.length === 0)
 {
	removeFrameCallback(this.monitorDamagedEquipmentCallback);
	delete this.monitorDamagedEquipmentCallback;
	return;
 }
 var counter;
 var arrayLength = damagedEquipment.length;
 for (counter = 0; counter < arrayLength;counter++)
	{
		if (player.ship.equipmentStatus(this.damagedEquipment[counter]) === "EQUIPMENT_OK")
	    {
			var equipment = this.damagedEquipment[counter];
			this.damagedEquipment.splice(counter,1);
			arrayLength = this.damagedEquipment.length;
			this.equipmentRepaired(equipment);
		}
	}
}

this.equipmentRepaired = function(equipment)
{
  log(this.name,"Equipment Item repaired: "+equipment);
}
and the last one for this morning - this.shieldsTakingDamage(forward,aft)

edit to add - I'll think about incorporating a whom, and type into this one using some different original event handlers to trigger the callback.....(scratches head) - but that's for tomorrow...

Code: Select all

this.shipWillLaunchFromStation = function()
{
	this.currentForwardShield = player.ship.forwardShield;
	this.currentAftShield = player.ship.aftShield;
	this.monitorShieldsCallback = addFrameCallback(this.monitorShields.bind(this));
}

this.monitorShields = function()
{
 if (player.ship.docked)
 {
	removeFrameCallback(this.monitorShieldsCallback);
	delete this.monitorShieldsCallback;
	return;
 }
 var forwardShieldDiff = this.currentForwardShield - player.ship.forwardShield;
 this.currentForwardShield = player.ship.forwardShield;
 var aftShieldDiff = this.currentAftShield - player.ship.aftShield;
 this.currentAftShield = player.ship.aftShield;
 if (forwardShieldDiff < 0){forwardShieldDiff = 0;}
 if (aftShieldDiff < 0){aftShieldDiff = 0;}
 if (forwardShieldDiff > 0 || aftShieldDiff > 0){this.shieldsTakingDamage(forwardShieldDiff,aftShieldDiff);}
}

this.shieldsTakingDamage = function(forward,aft)
{
	log(this.name,"Shields taking damage. Forward:"+forward+" Aft:"+aft);
}
[EliteWiki] Capt. Murphy's OXPs
External JavaScript resources - W3Schools & Mozilla Developer Network
Win 7 64bit, Intel Core i5 with HD3000 (driver rev. 8.15.10.2696 - March 2012), Oolite 1.76.1
User avatar
Svengali
Commander
Commander
Posts: 2370
Joined: Sat Oct 20, 2007 2:52 pm

Re: Scripted 'new' event handlers.

Post by Svengali »

Capt. Murphy wrote:
Svengali, what do you think? It's doable, but would it be a resource killer? Have I missed anything?
I think there are some really good ideas. I'd say go for it, Capt. Murphy!

A few things might be worth to think about. The following stuff may not apply in all cases and I'm pretty sure the list is incomplete, but this is what is driving my decisions for the OXPs I am involved / working on.

Part A - Callbacks
- Methods to remove and/or change registered callback functions
If you want to give OXPs a dynamic thing that can be changed while running then OXPs would need these methods. E.g. CCL_Comms is working this way, opening the possibility to change the hierarchy of the 'talk' on the fly.

- Conditions Part I
Is it enough to check if the worldScript and the function exists? E.g. CCL_Music uses a different approach that checks a specified property to create the list of active OXPs before any other action happens. This keeps the todo list for the OXP short and gives OXPs a dynamic range.

- Conditions Part II
Does the registering need a way to declare conditions under which a callback should happen? E.g. CCL_MissionHandling uses a few properties to define conditions to be checked. Only if the checks are passed a callback happens. CCL_Music goes a step further - the registered stuff can have optional a property to be checked for every single event. This way OXPs can 'create' groups of 'events'.

- Conditions Part III
Does it need mutal exclusion, priority handling, etc? What happens if colliding OXPs are registering functions. E.g. CCL_MissionHandling uses muteGroups, so it will call only one function for a specified group.

- Context
Is the called function always running in the same JS-context/scope? Sure it's in most cases more a question for the OXPs which intend to use the registering, but it still needs open eyes.

Part B - Simple method
Is it necessary to invoke any callbacks at all? Sometimes it may be easier to push the underlaying methods in CCL_Functions.js (or any similiar working lib), so any OXP can instantiate and use the methods. This leaves the handling and condition checking to the OXPs.

CCL is for sure open for contributions. This was important for us (Cmd.Cheyd, PhantorGorth and myself) when we've started this project and Eric and CMcL have done it already. We are currently thinking about using a public repository (probably on GitHub) to make it even easier to join the team and to make access and sync easier.
Post Reply