Access ACEs of the passed object type from runtime.js?

For developers using the Construct 2 Javascript SDK

Post » Thu Oct 23, 2014 5:35 pm

Message: Lunarovich can only post plain text URLS until they have 500 rep. 1 URLS modified. Why?
Hello, I've just started to "roll my own" plugin :)

However, I am confused about how to access ACEs (conditions, actions, expressions) inside the runtime.js. For example, I have this piece of code in my edittime.js:

Code: Select all
AddNumberParam("Size", "Enter a map size.");
AddObjectParam("Object", "An array to store the map in.");
AddAction(1, af_none, "Generate map", "Generator", "Generate map of size {0} and store it in {1}", "Generate a 2D map", "GenerateMap");


I send an Array object form inside the C2,

Image

Now, from inside my runtime.js, I would like to be able to call, for example, SetX() function of the Array object. I've managed to do something similar with

Code: Select all
       Acts.prototype.GenerateMap = function (size, array)
   {
            var arrayInstance =  array.getFirstPicked();             
            arrayInstance.set(0, 0, 0, 23);
   };


However, this one will not work

Code: Select all
       Acts.prototype.GenerateMap = function (size, array)
   {
            var arrayInstance =  array.getFirstPicked();             
            arrayInstance.SetX(0, 23);
            // this one neither: arrayInstance.acts.SetX(0, 23);
   };


I have understood that I can access all the functions which are attached on the instanceProto object. This makes sense, since I am accessing functions of the instance of the plugin. For example,

Code: Select all
   instanceProto.set = function (x, y, z, val)
   {
      x = Math.floor(x);
      y = Math.floor(y);
      z = Math.floor(z);
      
      if (isNaN(x) || x < 0 || x > this.cx - 1)
         return;
         
      if (isNaN(y) || y < 0 || y > this.cy - 1)
         return;
         
      if (isNaN(z) || z < 0 || z > this.cz - 1)
         return;
         
      this.arr[x][y][z] = val;
   };


However, I see that ACEs are neither attached to the pluginProto.Type.prototype nor to the pluginProto.Instance.prototype, but directly to the pluginProto. (The latter is the "shortcut" to the cr.plugins_.MyPlugin.prototype). So, if I access types and instances functions/properties via type. and inst., how do I access ACEs which are attached directly to the prototype of the plugin?

I want to do all this, in order to learn how to implement plugins/behaviors and to implement a C2 http://ondras.github.io/rot.js/hp/ interface. This will facilitate the job of making roguelikes in C2.
B
6
S
2
Posts: 64
Reputation: 576

Post » Thu Oct 23, 2014 6:57 pm

ACE's are function definitions tied to an object type. Instances of that object type may call those functions but must pass the "this" instance reference to the ACE function when calling it.

As en example many of the plugins i create for my self call the function plugin directly from plugin. To do that when I instantiate an instance of my plugin I perform a look up to find the global function plugin instance and store it.

Example: I search for runtime instances objects of Function type objects and store the instance reference to a local variable "this.Function.self "
Code: Select all
var objects = this.runtime.objectsByUid;
for (var i in objects) {
   if (objects[i] instanceof cr.plugins_.Function.prototype.Instance) {
      this.Function.self = objects[i];
   }
}


Then later on somewhere in my code when ever I want to call a function on the event sheet by its name I have to Call the ACE the Action of the function instance i stored.

Code: Select all
this.CallFunction = function(fnName, params) {
   cr.plugins_.Function.prototype.acts.CallFunction.call(this.Function.self, fnName, params)
}


so now any where in my plugin that I desire I can call a local function "this.CallFunction()" and pass it the name and paramters and it will call that function on the event sheet.

What you need to do is find the instance of the object you want to pass to the ACE function and call the function and pass that instance to it. The best way to learn about this would be to open up some of the other official plugins and review the code and how they work. You can see that many them of call there ACE's in the same manner. The hard part is figuring out how to get the correct instance object to pass to it.
B
20
S
7
G
1
Posts: 221
Reputation: 2,077

Post » Thu Oct 23, 2014 8:48 pm

Thanks for your thorough and informative answer. I don't know why such a useful functionality should be bogged down by this intricate function call chain / object reference chain.

Could you please point me to some plugins where I can see the examples of this going on?

P.S. Here are the first results of making a plugin out of rot.js library:

Image

Image
B
6
S
2
Posts: 64
Reputation: 576

Post » Fri Oct 24, 2014 12:01 am

Hey, I've got it :)))

This works and makes sense (although it's a bit convoluted for my taste...):

Code: Select all
var dictInst = dict.getFirstPicked();
dictInst.type.plugin.acts.AddKey.call(dictInst, "ace", 51);
console.log("SUCCESS: " + dictInst.dictionary["ace"]);


Hope to finish soon rot.js C2 integration and offer an intuitive way to make a roguelike for all those roguelike fans :)
B
6
S
2
Posts: 64
Reputation: 576

Post » Fri Oct 24, 2014 12:08 am

You could access action, condition, and expression.
http://c2plugins.blogspot.tw/2014/02/reuse-ace.html
B
110
S
28
G
280
Posts: 4,487
Reputation: 156,566

Post » Fri Oct 24, 2014 1:01 am

glad you figured it out. ... I'm not a fan of protoType usage (i extend my class objects using a different method) but to wrap your head around it, its just and extension of the object.

At the top of each plugin you will something like this

var pluginProto = cr.plugins_.MyPlugin.prototype;

"pluginProto" is now a refrence to the MyPlugin plugin prototype extension (not any instance of it but the object definition it self)

further down in a plugin you will see something like this

function Acts() {};

Acts.prototype.DoSomething = function(param1){
//Code in here that does stuff to the instance that called it.
}


This is building a set of actions to add to this object.

Lastly those actions are added to the object by something like this

pluginProto.acts = new Acts();

"pluginProto.acts" is the same as typing "cr.plugins_.MyPlugin.protype.acts"

So the final location of the DoSomething() function is "cr.plugins_.MyPlugin.protype.acts.DoSomething"

Now in Javascript if you were to just call the function "cr.plugins_.MyPlugin.protype.acts.DoSomething(param1)" the function would have no refence to the instance it is supposed to do work on.

To pass the instance you need to add a .call so it would be "cr.plugins_.MyPlugin.protype.acts.DoSomething.call(instanceObject,param1)"

And thats plugin ACE function calling in a nut shell :)
B
20
S
7
G
1
Posts: 221
Reputation: 2,077

Post » Fri Oct 24, 2014 7:52 am

Hey, @troublesum, thanks for the great explanation!

I already quite figured it out on my own (thanks to your help from the previous post), but I appriciate always a clear and systematic explanation. Stays here for the benefit of the C2 mankind :) Plus, I think it should be in the official manual. It would have saved my time, anyway...

The only critique goes to the sequence of your emails... The last one should have been the firt one ;)
B
6
S
2
Posts: 64
Reputation: 576

Post » Fri Oct 24, 2014 12:14 pm

I would *strongly* recommend you do not write plugins that directly access other plugins. It breaks encapsulation and is brittle. For example we may go ahead in future and change how the Array object works to fix some bug or change some feature, and this will break everything that depends on it like this.

Also, it's less useful for users. You force them in to a specific workflow. For example if you at some point run an action in another object, then the user is locked in to that dependency and can never alter it. If on the other hand you run a trigger, then the user can place an action in that event to do that task, or they can do something else, perhaps using a different third party plugin or some alternative feature. It's also easier to understand since you can see what's happening, instead of there being under-the-hood invisible magic causing actions to run unexpectedly. Consider that in the entire C2 engine, actions never run except where they have been added visibly in the event sheet, and a plugin that uniquely breaks that rule will be uniquely surprising. Users can also more easily test by adding logging to triggers, temporarily disabling the action, etc.

Yes it's possible, but you will design inflexible, breakable plugins - please think of other ways to design your plugin.
Scirra Founder
B
402
S
238
G
89
Posts: 24,628
Reputation: 196,023

Post » Fri Oct 24, 2014 1:11 pm

Thank you for your response. I should have supposed that there is a good reason behind the absence of a straightforward way to access other plugins' ACEs. I think that everyone, including me in the first place, would appreciate very much a manual page or a blog post on plugin development best practices.

While we are at it, I just wanted to share with you a few bumps i ran into while I was reading the SDK manual. For example, the Runtime reference explains how to access the runtime. However, I had to figure out on my own how to access layouts, layers, types and instances. Maybe a small adition to the first paragraph of the corresponding pages would facilitate the access.

Be that as it may, I am thankful for this wonderful piece of software. Less frustration, more fun, elegant solutions :)
B
6
S
2
Posts: 64
Reputation: 576

Post » Fri Oct 24, 2014 4:34 pm

@Ashley I understand. That's why I have not released any of my plugins that directly access other plugins to the general public. I don't want to have to maintain them so i get where you're coming from.

PS. But my plugin (unreleased) that ties the Multiplayer and Function plugin together to allow for calling functions on all peers at the same time with a single call is pretty sweet though :) ... Taking two powerful plugins and creating a wrapper to combine their functionality adds an amazing amount of power to my projects. I wouldn't be so harsh on others (or myself) that want to do this so long as we respect the C2 eco system. I can actually have a game engine now that is synced across all peers controlled by the host because the host has functional control over everything with super simple control.
B
20
S
7
G
1
Posts: 221
Reputation: 2,077

Next

Return to Javascript SDK

Who is online

Users browsing this forum: No registered users and 0 guests