Using delegates
The Pixie
10 Jul 2013, 07:18How do you use delegates? I had a go, assuming they work kind of like a hybrid between a script and a function, as in the code below, but Quest complains:
Error running script: Error evaluating expression '"The " + this.name + " has " + RunDelegateFunction (this, "getpc") + "% of its hits."': Object 'goblin' has no delegate implementation 'getpc'
I am not getting what is wrong here.
Error running script: Error evaluating expression '"The " + this.name + " has " + RunDelegateFunction (this, "getpc") + "% of its hits."': Object 'goblin' has no delegate implementation 'getpc'
I am not getting what is wrong here.
<!--Saved by Quest 5.4.4873.16527-->
<asl version="540">
<include ref="English.aslx" />
<include ref="Core.aslx" />
<game name="test">
<gameid>cb4455e4-6e7c-45da-bf39-f2126e817fb1</gameid>
<version>1.0</version>
<firstpublished>2013</firstpublished>
</game>
<object name="room">
<inherit name="editor_room" />
<object name="player">
<inherit name="editor_object" />
<inherit name="editor_player" />
</object>
<object name="goblin">
<inherit name="editor_object" />
<fullhits type="int">10</fullhits>
<hitslost type="int">0</hitslost>
<delegate name="getpc" type="int">
return (100 * (this.fullhits - this.hitslost) / this.fullhits)
</delegate>
<hit type="script">
msg (HasDelegateImplementation (this, "getpc"))
msg ("You hit the " + this.name + " and it loses 7 hits!")
this.hitslost = this.hitslost + 7
msg ("The " + this.name + " has " + RunDelegateFunction (this, "getpc") + "% of its hits.")
</hit>
</object>
</object>
</asl>

jaynabonne
10 Jul 2013, 08:54You use the "delegate" element to define the delegate function template:
(I added a "scale" parameter just to show how parameters are used.)
Then in your object, add an attribute of that type:
Finally, to invoke it, use either rundelegate (if no return value needed) or RunDelegateFunction (if a return value is needed). Parameters come after the attribute name:
Hope that helps!
<delegate name="GetPC_Delegate" parameters="scale" type="int"/>
(I added a "scale" parameter just to show how parameters are used.)
Then in your object, add an attribute of that type:
<getpc type="GetPC_Delegate">
return (scale* (this.fullhits - this.hitslost) / this.fullhits)
</getpc>
Finally, to invoke it, use either rundelegate (if no return value needed) or RunDelegateFunction (if a return value is needed). Parameters come after the attribute name:
msg ("The " + this.name + " has " + RunDelegateFunction (this, "getpc", 100) + "% of its hits.")
Hope that helps!
The Pixie
10 Jul 2013, 09:58Thanks, I was not about to guess that. So in fact a delegate is just defining a signature that you can then use for an object's method (in OO speak), and there is (effectively) one built-in delegate already:
<delegate name="script" />
And you can set up your own signatures:
<delegate name="script_returns_int" type="int" />
<delegate name="script_returns_int_with_3_params" type="int" parameters="a, b, c" />
<delegate name="script_returns_object" type="object" />
<delegate name="script" />
And you can set up your own signatures:
<delegate name="script_returns_int" type="int" />
<delegate name="script_returns_int_with_3_params" type="int" parameters="a, b, c" />
<delegate name="script_returns_object" type="object" />

jaynabonne
10 Jul 2013, 11:12Actually, scripts and delegates are different things. For example, scripts have a different parameter passing scheme. More importantly, you can't run a script with "rundelegate" (I just tried).
But, yes, you are creating your own signatures. It's a bit like an indirect (attribute-based) function call.
But, yes, you are creating your own signatures. It's a bit like an indirect (attribute-based) function call.
The Pixie
10 Jul 2013, 13:38Thanks. I have updated the Wiki with my new knowledge; you might like to check it over.
http://quest5.net/wiki/Using_Delegates
http://quest5.net/wiki/Using_Delegates

jaynabonne
11 Jul 2013, 08:53That looks good. You made an interesting choice for your delegate naming strategy, basing the names on the signature as opposed to the delegate semantics (in other words, what it means for the object). I had gone that way in my own code originally and soon got tired of generic parameters. It *does* probably allow fewer delegate definitions in the long run if many delegates are used!
(I might reconsider that choice in my own code.)
You could probably come up with a nice concise convention, like "Delegate_N_type", where "N" is the number of arguments and "type" is the return type (if there is one). So a delegate taking two params and returning a string could be "Delegate_2_string". Something like that... That's basically what you did in a more verbose way.
Edit: one downside to using the signature (param/type) naming is if you later need to (say) add a parameter to an existing delegate that you're using. Let's say you have a delegate in use across many objects (e.g. every room in your game) and you decide you need to add a parameter for a special case that has come up. If you have named your delegate based on functional use, where you have a unique name for that *behavior*, then you can simply go to the delegate definition for that usage, add the new parameter, and you're done. If you have based the delegate name on the number of parameters used, then you must go to each object and change each type usage individually.

You could probably come up with a nice concise convention, like "Delegate_N_type", where "N" is the number of arguments and "type" is the return type (if there is one). So a delegate taking two params and returning a string could be "Delegate_2_string". Something like that... That's basically what you did in a more verbose way.
Edit: one downside to using the signature (param/type) naming is if you later need to (say) add a parameter to an existing delegate that you're using. Let's say you have a delegate in use across many objects (e.g. every room in your game) and you decide you need to add a parameter for a special case that has come up. If you have named your delegate based on functional use, where you have a unique name for that *behavior*, then you can simply go to the delegate definition for that usage, add the new parameter, and you're done. If you have based the delegate name on the number of parameters used, then you must go to each object and change each type usage individually.