[Plugin] JavaScript (C2 and C3)

  • Amazing plugin! I ran into a little problem I was hoping you could help me with:

    I'm trying to use this script to eval a string. This string could contain any number of global variables and even call functions

    A test string, called var1 is this: "shotCount % 3 == 0"

    The event is JS Call Function "Compare" with parameter 0 being var1

    The .js file has this:

    function Compare(a) {
    	return eval(a);
    }[/code:3ij8w5km]
    
    But it doesn't work, it says "shotCount is not defined" in the log. If I change var1 to something like "2+2" it evals correctly.
    I don't want to pass the global variable because I need this to be flexible and allow multiples and any variable. Do I need to somehow put the variables into the scope of the JS plugin?
  • Did I get this right? You have a global Construct variable called ShotCount. You have a js function called Compare (Sic!) that takes a string argument, evals it and returns the result. Then you call js function "Compare" from Construct with the action "Call js function" and pass the parameter which is basically "shotCount % 3 == 0".

    If I got this right, you get a fair response "shotCount is not defined". Because there's no such variable in global scope of your js-script. Construct variables are not real. They don't exist in your js-script. Construct variables are virtual and only make sense to the engine itself and to the js-plugin ACEs. So if you want your js script to operate with construct variables (which is totally upside-down), you should either pass them as arguments to js functions, or create js variables in your script, set their values to Construct variables values (use aliases) and then operate with those js variables.

    If you're just playing around with the plugin, it's alright. But if you're really trying to achieve something here, this something is not clear to me. Maybe if you tell me what you're trying to do, we'll find a suitable solution for that.

    Also can you send me a project file please?

    Amazing plugin! I ran into a little problem I was hoping you could help me with:

    I'm trying to use this script to eval a string. This string could contain any number of global variables and even call functions

    A test string, called var1 is this: "shotCount % 3 == 0"

    The event is JS Call Function "Compare" with parameter 0 being var1

    The .js file has this:

    function Compare(a) {
    	return eval(a);
    }[/code:2t75r8km]
    
    But it doesn't work, it says "shotCount is not defined" in the log. If I change var1 to something like "2+2" it evals correctly.
    I don't want to pass the global variable because I need this to be flexible and allow multiples and any variable. Do I need to somehow put the variables into the scope of the JS plugin?
    
  • EDIT: I think I found a way to do this... ignore this post for now

    Ok I got around this by passing all the variables as a single string separated by commas into the function. Then in the JS script I do a split() and this is working just fine

    The JS script looks like this:

    function Compare(a,attacker,target) {
    	var stat = {armor:0, crit:1, critDmg:2, dmg:3, mhp:4, hp:5, shotcount:6};
    	
    	var aArmor = attacker.split(",")[stat.armor];
    	var aCrit = attacker.split(",")[stat.crit];
    	var aCritDmg = attacker.split(",")[stat.critDmg];
    	var aDmg = attacker.split(",")[stat.dmg];
    	var aMHp = attacker.split(",")[stat.mhp];
    	var aHp = attacker.split(",")[stat.hp];
    	var aShotCount = attacker.split(",")[stat.shotcount];
    	
    	var tArmor = target.split(",")[stat.armor];
    	var tCrit = target.split(",")[stat.crit];
    	var tCritDmg = target.split(",")[stat.critDmg];
    	var tDmg = target.split(",")[stat.dmg];
    	var tMHp = target.split(",")[stat.mhp];
    	var tHp = target.split(",")[stat.hp];
    	var tShotCount = target.split(",")[stat.shotcount];
    
    	return eval(a);
    }[/code:o1ddw9dm]
    
    
    

    create js variables in your script, set their values to Construct variables values (use aliases) and then operate with those js variables.

    That's what I thought I had to do, but I don't think I understand how to do that properly

    I don't think I can send you the file because it's a huge mess, but what I'm trying to do is very simple.

    In my game there are many items, and they can have conditions to activate (like based on a random chance, or when the enemy has a certain % hp, or based on the enemy armor, etc) and I want to write this condition in an external file, as a string, so I don't need to add an event for each and every item to do the comparison.

    In this engine, both enemies and the player are in the same family, and they use a dictionary to store their stats. Every time someone attacks someone else, a "calculate damage" function is run, and it sets local variables to do all the calculations regarding the attacker and the target

    So if I have an item that increases the damage only if the target's health is below 50%, i'd add a condition later in this function that would compare if tHP is lower than 50%. But I plan on having around 80 items, so that's a lot of hard-coded conditions

    So I made it so all the items have their conditions written somewhere else, in a text file. So the condition would read "tHp < 0.5" and I'd use the JS plugin to eval that expression. But like I said, I can't pass the variable as a parameter because the expression could be anything.

    So if I got this right, I could Alias the variables right after I set them (in the image above) and then the JS would be able to use the variable, right?

  • Try Construct 3

    Develop games in your browser. Powerful, performant & highly capable.

    Try Now Construct 3 users don't see these ads
  • I think i kinda see what you're trying to do. You can do this, yes:

    EDIT: I think I found a way to do this... ignore this post for now

    function Compare(a,attacker,target) {
    	var stat = {armor:0, crit:1, critDmg:2, dmg:3, mhp:4, hp:5, shotcount:6};
    	
    	var aArmor = attacker.split(",")[stat.armor];
    	var aCrit = attacker.split(",")[stat.crit];
    	var aCritDmg = attacker.split(",")[stat.critDmg];
    	var aDmg = attacker.split(",")[stat.dmg];
    	var aMHp = attacker.split(",")[stat.mhp];
    	var aHp = attacker.split(",")[stat.hp];
    	var aShotCount = attacker.split(",")[stat.shotcount];
    	
    	var tArmor = target.split(",")[stat.armor];
    	var tCrit = target.split(",")[stat.crit];
    	var tCritDmg = target.split(",")[stat.critDmg];
    	var tDmg = target.split(",")[stat.dmg];
    	var tMHp = target.split(",")[stat.mhp];
    	var tHp = target.split(",")[stat.hp];
    	var tShotCount = target.split(",")[stat.shotcount];
    
    	return eval(a);
    }[/code:10uk0rlf]
    
    

    It's just you wouldn't have to do that if you had Player and Enemy objects in javascript from the very beginning. The plugin allows you to keep the whole model part in javascript and never store object data in Construct. So you never have to pass data to javascript. You just call certain javascript functions with certain parameters.

    Imagine that you only have player's and multiple enemie's sprites in Construct. And these sprites only represent the objects. They are not the objects. They don't store any data except their own x and y sprite coordinates. These sprites have only to have one instance variable — id. This is how you know that this certain sprite represents this certain object in javascript.

    Also imagine that you have "Player_sprite on collision with Enemy_sprite condition". And you do "Call JS function" with parameters:

    Name: CalculateDamage

    Param 0: Player_sprite.id

    Param 1: Enemy_sprite.id

    And also imagine that you have something like this in javascript:

    var players = [];
    
    var Player = 
    {
    	armor: 100, 
    	crit: 50, 
    	critDmg: 20, 
    	dmg: 10, 
    	mhp: 10, 
    	hp: 75, 
    	shotcount: 0
    }
    
    var Enemy = 
    {
    	armor: 100, 
    	crit: 50, 
    	critDmg: 20, 
    	dmg: 10, 
    	mhp: 10, 
    	hp: 75, 
    	shotcount: 0
    }
    
    function InitGame()
    {
    	players.push( Player );
    
    	return players.length-1;
    }
    
    function CreateEnemy()
    {
    	players.push( Object.assign({}, Enemy) );	
    
    	return players.length-1;
    }
    
    function CalculateDamage( attacker_id, attacked_id )
    {
    	var attacker = players[attacker_id];
    	var attacked = players[attacked_id];
    
    	attacked.health -= attacker.damage;
    	...
    }
    [/code:10uk0rlf]
    
    This is how you're really supposed to use the plugin.
  • Hello, super usefull plugin, thanks very much.

    1-would it be possible to add trigger support ?

    What I mean is, the JS plugin would have a trigger called "on trigger" or "on event" with a tag. And then in my js script I can make that trigger fire with a tag.

    Could be usefull for any asynchronous operations in my script.

    Right now, I can just use compare + trigger once. But arent triggers more performant ?

    2-In your landing page you said that "Javascript Plugin is slower than built-in Construct instructions and functions"

    Do you mean that a chunk of code written in JS is slower that code written with construct's ACEs ?

    3-Does your plugin uses any ecmascript 6 features that might not be supported in older browsers or mobile ?

    Thanks.

  • Thank you!

    1-would it be possible to add trigger support ?

    I didn't add trigger support because:

    — You can come up with your own triggers (boolean variables) and use Compare

    — If you're operating with Promises in your js functions, triggers won't help you. Like: ok, the function is executed, here we are, shooting the trigger, but what the function really does is not finished yet. So you will have to manually do some «this.ShootMe()» in your js code when the Promise resolved. Sounds like API to me. I want any js code just work without it knowing that it's being executed in some special environment.

    — I believe, this is a broken architecture if you want to make Construct do something right after asynchronous js-algorithm is finished. If you need something to be done right after something else, do it in javascript. Don't bother Construct with that. If you really need Construct to react on a result of your js-algorithm, use states. This is a more consistent way of doing that. Pseudocode:

    js:
    Promise resolved ? Player.state = "walking".
    
    construct:
    Compare alias [Player.state] == "walking" ? player_sprite.SetAnimation("walking_anim")
    [/code:5yui0vlz]
    What do you think of it? Don't hesitate to share your ideas and tell me more about your architecture. 
    
    
    

    2-In your landing page you said that "Javascript Plugin is slower than built-in Construct instructions and functions"

    Do you mean that a chunk of code written in JS is slower that code written with construct's ACEs ?

    No. A chunk of code written in JS is always faster that code written with construct's ACEs. Because pure js is always faster.

    What I meant is that the whole process of calling the action "Call JS function" that executes js function "Foo" that does "i=0" is slower than Construct instruction "Set variable i to 0". The js code itself is faster. What is slower is calling a plugin action. The more your JS does (not just "i=0"), the smaller the difference. Iterating through large arrays are much faster in js than in Construct, for example.

    3-Does your plugin uses any ecmascript 6 features that might not be supported in older browsers or mobile ?

    Thanks.

    Oh! Good question, thanks! I forgot to mention it in a FAQ. No, it doesn't use any ecmascript 6 features.

  • Thanks for your answer.

    1-Yeah states works too, but I still think that trigger is handy lol, like the official Function plugin exposes a trigger to any external JS script (window["c2_callFunction"]), and it causes no problem;

    2- It would be really handy if the expression JS.AliasValue(alias) returns a json string if the value is an object or an array.

  • Thanks for your answer.

    It would be really handy if the expression JS.AliasValue(alias) returns a json string if the value is an object or an array.

    Imagine AliasValue returns a json string of a js-object. How would you use this string in Construct?

  • [quote:2s6qbtf9]Imagine AliasValue returns a json string of a js-object. How would you use this string in Construct?

    Another plugin would, that expect a json value.

    Anyway, I can always add a utility function (toJSON) to my script and use it.

    Thanks.

  • [quote:babvkc1m]Imagine AliasValue returns a json string of a js-object. How would you use this string in Construct?

    Another plugin would, that expect a json value.

    That is very interesting. I'm just trying to figure out what is it you're trying to do here. So you have the object in js. Then you get it's JSON string in Construct. Then you pass this JSON string to a plugin that reads JSON. And then what? Access object's properties?

    If so, and you just need to access js object's properties, just do this:

  • Yeah, the other plugin could be some web API, like playfab, some portal API, etc.. and the json is the save data;

    Ideally, a web API call should also be done in JS and not in Construct

    1-Do you think looping over an array (defined in js script) in construct (to build a leaderboard for example) is a bad idea, since everytime the JavaScript plugin need to parse an alias expression ?

    2-It would be nice if we can get the return value of a function with an expression, currently I have to call the alias and use the StoredReturnValue expression, it would be nice if we can do this in one step.

    3-difference between "call alias" and "call function" ? the preferred one of the 2 ?

  • Hey valerypopoff, I'm excited to use your plugin but I'm having trouble integrating a few files into a project that I've got working in the command line otherwise. What I'm essentially trying to do is call a function from the jstat library to convert a z score to a percentage value. In my most recent attempt I'm only using jstat and trying as many combinations of things as I can think of bit my return value is always 0.

    This is a small project I'm tinkering in to try and get a percentage value:

    https://www.dropbox.com/s/qwy5nyslwp157 ... .capx?dl=0

    I don't share the file to have anything done *for* me, just to get info on how to get things moving. I'm not sure what I could be doing wrong here. Any help learning to effectively use this plugin is appreciated!

  • If you look in the browser console (always look in the console, you're working with javascript for Christ's sake), you'll see error message:

    JS code: jstat.normal.cdf
    jstat is not defined
    [/code:iy76sa4y]
    
    And this is true. Thre's no "jstat" variable or object in your js script. No wonder, the result is "undefined" which is interpreted by Constrauct as zero. 
    
    But there's "jStat". So what you wanted to do is call "jStat.normal.cdf" function, right? You should use "Call JS function" action then. Like this:
    
    [img="http://valerypopoff.ru/temp/scirra-forum/jstat.png"]
    
    Don't use "Execute JS code" action unless it's absolutely necessary, because it uses "eval", which is slow.
    
    
    

    Hey valerypopoff, I'm excited to use your plugin but I'm having trouble integrating a few files into a project that I've got working in the command line otherwise. What I'm essentially trying to do is call a function from the jstat library to convert a z score to a percentage value. In my most recent attempt I'm only using jstat and trying as many combinations of things as I can think of bit my return value is always 0.

    This is a small project I'm tinkering in to try and get a percentage value:

    https://www.dropbox.com/s/qwy5nyslwp157 ... .capx?dl=0

    I don't share the file to have anything done *for* me, just to get info on how to get things moving. I'm not sure what I could be doing wrong here. Any help learning to effectively use this plugin is appreciated!

  • 1-Do you think looping over an array (defined in js script) in construct (to build a leaderboard for example) is a bad idea, since everytime the JavaScript plugin need to parse an alias expression ?

    This is a bad idea and wrong on so many levels. The whole point of the plugin is that you don't have to loop over arrays in Construct anymore. Construct is designed neither for modeling data, nor for programming algorithms with loops, arrays and stuff. But you can still do that if you want to. It's just a little bit slower.

    2-It would be nice if we can get the return value of a function with an expression, currently I have to call the alias and use the StoredReturnValue expression, it would be nice if we can do this in one step.

    Word. Gonna do that soon.

    3-difference between "call alias" and "call function" ? the preferred one of the 2 ?

    There's no preferred one. It all depends on what you have in your code. If there's just a global js-function, it's easier to do "Call JS function". If there's a js-object that has multiple properties and methods you would want to access later from Construct, it's easier to come up with the alias for this object and do "SetAlias", "Get Alias" and "CallAlias".

    Cheers.

  • If you look in the browser console (always look in the console, you're working with javascript for Christ's sake), you'll see error message:

    > JS code: jstat.normal.cdf
    jstat is not defined
    [/code:w57miijg]
    
    And this is true. Thre's no "jstat" variable or object in your js script. No wonder, the result is "undefined" which is interpreted by Constrauct as zero. 
    
    But there's "jStat". So what you wanted to do is call "jStat.normal.cdf" function, right? You should use "Call JS function" action then. Like this:
    
    [img="http://valerypopoff.ru/temp/scirra-forum/jstat.png"]
    
    Don't use "Execute JS code" action unless it's absolutely necessary, because it uses "eval", which is slow.
    
    
    > Hey 

    valerypopoff, I'm excited to use your plugin but I'm having trouble integrating a few files into a project that I've got working in the command line otherwise. What I'm essentially trying to do is call a function from the jstat library to convert a z score to a percentage value. In my most recent attempt I'm only using jstat and trying as many combinations of things as I can think of bit my return value is always 0. [/p] > [/p] > This is a small project I'm tinkering in to try and get a percentage value: [/p] > [url=https://www.dropbox.com/s/qwy5nyslwp157ra/integrating%20js.capx?dl=0]https://www.dropbox.com/s/qwy5nyslwp157 ... .capx?dl=0[/url][/p] > [/p] > I don't share the file to have anything done *for* me, just to get info on how to get things moving. I'm not sure what I could be doing wrong here. Any help learning to effectively use this plugin is appreciated![/p] > [/p]

    Hey valerypopoff , thanks for getting back to me so quickly! argh lol, thwarted by case. Thank you for letting me know about the console; I was trying to see what was going on with the debugger - which I learned was not helpful lol. I'm only just learning about js and how to do these things so I appreciate the direction (total noob here).

    The function still isn't working (normal also does not appear to be defined) but now at least I can investigate what's going on there in the js file <img src="{SMILIES_PATH}/icon_e_smile.gif" alt=":)" title="Smile">. Thanks again!

Jump to:
Active Users
There are 1 visitors browsing this topic (0 users and 1 guests)