Lone_Wolf wrote:script which runs it points exactly to the issue, it's determined at run-time and the code has no idea what this
will be, until it's run (except in cases where it's defined clearly by oolite gamecode).
No, not so. (Or, at least, it
is clearly defined by the oolite gamecode and the JS language syntax in almost all cases) - the behaviour of
this
in Javascript
is fairly complicated, but unless you define your own object types and constructors, in Oolite most of that complexity can be ignored.
The script which runs the code is the script that it belongs to. If you say
Code: Select all
{script}.method = function() { ... }
then however you call that function, unless you explicitly
bind()
or
apply()
it, its
this
will be the script you attached it to.
The reason this works is that scripts in Oolite are themselves JS objects. If you ask Oolite to load a JS script file, what you get is an object (of JS type Script and Oolite core type OOJSScript). This object then has a bunch of properties, some of which are functions, based on the definitions in the script file (and can also have them attached at run-time later).
When you then do
{script}.method()
you're asking that Script object to run the method - that's what I meant by "the script which runs it", which I could have been clearer about - and as with any JS object method call,
this
points to the owning object by default.
The exceptions - timers and other callbacks - happen because you don't want those to run in the context of their owning object (which is an internal implementation detail to Oolite) - so Oolite tries to be helpful by setting
this
to be what looks like the creating Script object. It doesn't always get it right, which is why you should explicitly
bind()
callbacks when you create them.
So if I define in my shipscript:
Code: Select all
this._help = function() { this.ship.broadcastDistressMessage(); }
then I can call that from a chain of method calls that begins in a ship script event (for that ship or a different ship), in a world script event, in a player equipment script and within the function
this.ship
will always refer to the ship that instance of the ship script is attached to.
So when you define this line in
new_std
Code: Select all
amount = worldScripts["Reduce Weapon Damage"]._rwd_ShipTakingDamage(this.ship, amount, whom, type);
you're defining it inside the worldscript. If, still in the world script, you did
new_std(a,w,t);
then
this
refers to the world script (and
this.ship
is undefined). Having assigned the function to the ship script with
ship.script.shipTakingDamage = new_std;
then
this
- when that copy of the function runs later on the ship script event - refers to the ship script, and
this.ship
is the script.
Both, however, will call
worldScripts["Reduce Weapon Damage"]._rwd_ShipTakingDamage
and when this function runs, it is run by the worldscript object regardless of where it gets called from. You don't currently use
this
in that function, but if it did, it would point to the worldscript object.
Lone_Wolf wrote:Do you know a better / cleaner way ?
No, that's about it for NPCs. (It fails in the case where the ship
changes its ship script after creation, but that's rare). Generally you should try to avoid making global changes by editing ship scripts, but sometimes it's the only option to get a particular effect.
Incidentally, out of politeness to other OXPers, you should probably check that the ship has
ship.autoWeapons == true
before modifying its script, or you might break things.