Page 1 of 1

Scripted 'new' event handlers.

Posted: Tue Apr 03, 2012 4:21 am
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.

Re: Scripted 'new' event handlers.

Posted: Tue Apr 03, 2012 5:02 am
by Kaks
Impressive stuff! :)

Re: Scripted 'new' event handlers.

Posted: Tue Apr 03, 2012 5:17 am
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);
}

Re: Scripted 'new' event handlers.

Posted: Tue Apr 03, 2012 12:42 pm
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.