How would you let users write a whole function as a parameter in the plugin manager?

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,787
Reaction score
939
First Language
Chinese
Primarily Uses
N/A
In my newly published DoubleX RMMV Formulae Edit, I tried something new to myself:

* @param makeEscapeRatio * @desc Sets the party escape ratio upon battle start as makeEscapeRatio, which * must return a function that takes no arguments * The function returned by makeEscapeRatio will be bound to BattleManager * upon use * @default function() { this._escapeRatio = 0.5 * $gameParty.agility() / $gameTroop.agility(); } * * @param speed * @desc Sets the action speed as speed, which must return a function that takes * the subject's agi as the argument and returns a Number * The function returned by speed will be bound to Game_Action upon use * @default function(agi) { return agi + Math.randomInt(Math.floor(5 + agi / 4)); } * * @param lukEffectRate * @desc Sets the luk effect rate multiplier applied to adding states and * debuffs to a target as lukEffectRate, which must return a function that * takes a target as the argument and returns a Number * The function returned by lukEffectRate will be bound to Game_Action * upon use * @default function(target) { return Math.max(1.0 + (this.subject().luk - target.luk) * 0.001, 0.0); } * * @param expForLevel * @desc Sets the required experience for levelling up to level as expForLevel, * which must return a function that takes the level, actor's class, * experience basis, extra, acceleration a and b as arguments and returns * a Number * The function returned by expForLevel will be bound to Game_Actor upon * use * @default function(level, c, basis, extra, acc_a, acc_ { return Math.round(basis * (Math.pow(level - 1, 0.9 + acc_a / 250)) * level * (level + 1) / (6 + Math.pow(level, 2) / 50 / acc_ + (level - 1) * extra); } * * @param distancePerFrame * @desc Sets the characters' moving distance per frame as distancePerFrame, * which must return a function that takes no arguments and returns a * Number * The function returned by expForLevel will be bound to * Game_CharacterBase upon use * @default function() { return Math.pow(2, this.realMoveSpeed()) / 256; } * * @param makeEncounterCount * @desc Sets the number of steps needed to trigger an encounter as * makeEncounterCount, which must return a function that takes the * encounter count as the argument * The function returned by makeEncounterCount will be bound to * Game_Player upon use * @default function(n) { this._encounterCount = Math.randomInt(n) + Math.randomInt(n) + 1; }
While I do know @param is supposed to work with simple values only, I'm thinking of letting users to set @param as a string storing a whole function.

Right now I use the ridiculously disastrous eval(even though I've added "use strict" for the whole plugin) to retrieve the function from the string stored in @param:

DoubleX_RMMV.Formulae_Edit = { // Stores all this plugin's parameters that are shown in the plugin manager params: PluginManager.parameters(DoubleX_RMMV.Formulae_Edit_File),}; // DoubleX_RMMV.Formulae_EditObject.keys(DoubleX_RMMV.Formulae_Edit.params).forEach(function(param) { DoubleX_RMMV.Formulae_Edit[param] = eval(DoubleX_RMMV.Formulae_Edit.params[param]);});
What do you think about this approach as both a plugin developer and user? What would be your approach instead if you're to let users write their own functions in @param?
 
Last edited by a moderator:

Mellye

Veteran
Veteran
Joined
Oct 24, 2015
Messages
347
Reaction score
279
First Language
Portuguese
Right now I use the ridiculously disastrous eval(even though I've added "use strict" for the whole plugin) to retrieve the function from the string stored in @param:
Well, don't feel that bad about doing something like that, I guess. Considering that the base RM also use eval() to parse the Formula field we type in the skill database (with all the accompanying issues that one would expect).

Though I wouldn't recommend you expect the the whole function as a parameter. Instead, do it like the skill Formula field. Where the user is supposed to type something that results in a number:

foo.MakeEscapeRatio = function(){    this._escapeRatio = Number(eval(theParameter));    if (isNaN(this._escapeRatio))    {        //Throw an error and/or swap it for a default value.    }}Of course, at the end of the day it depends on what exactly you want to achieve, and who will use your plugin.
 
Last edited by a moderator:

Iavra

Veteran
Veteran
Joined
Apr 9, 2015
Messages
1,797
Reaction score
863
First Language
German
Primarily Uses
Last edited by a moderator:

DarknessFalls

Rpg Maker Jesus - JS Dev.
Veteran
Joined
Jun 7, 2013
Messages
1,393
Reaction score
210
First Language
English
I wouldnt. I would only let them pass in variables and then make you write the function that handles it. Its better, less complicated and generally less prone to bugs as debugging eval is like debugging code after you cut out your eyes and ate them ... 
 

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,787
Reaction score
939
First Language
Chinese
Primarily Uses
N/A
You can create a new function from the given parameter, like this:

var callback = new Function(input1, input2, "return " + parameter + ";");See here: https://developer.mozilla.org/de/docs/Web/JavaScript/Reference/Global_Objects/FunctionThis way you don't need to run the costy eval() everytime you want to evaluate that formula, but have a more lightweight function you can execute.
I'll try that :)

Well, don't feel that bad about doing something like that, I guess. Considering that the base RM also use eval() to parse the Formula field we type in the skill database (with all the accompanying issues that one would expect).

Though I wouldn't recommend you expect the the whole function as a parameter. Instead, do it like the skill Formula field. Where the user is supposed to type something that results in a number:

foo.MakeEscapeRatio = function(){    this._escapeRatio = Number(eval(theParameter));    if (isNaN(this._escapeRatio))    {        //Throw an error and/or swap it for a default value.    }}Of course, at the end of the day it depends on what exactly you want to achieve, and who will use your plugin.
Sometimes some "advanced" users will, for whatever reasons, insert something completely unrelated to makeEscapeRatio, into the makeEscapeRatio formulae. I've seen that happening in RMVXA, so I'd expect similar requests in RMMV, as quite some RMMV users come from RMVXA.

I wouldnt. I would only let them pass in variables and then make you write the function that handles it. Its better, less complicated and generally less prone to bugs as debugging eval is like debugging code after you cut out your eyes and ate them ... 
Right now I can think of 3 ways to let users overwrite the default RMMV formulae into their own versions:

1. Let them edit the plugin js file directly, and in this manner:

/*---------------------------------------------------------------------------- * * Edit class: BattleManager *----------------------------------------------------------------------------*/BattleManager.makeEscapeRatio = function() { // Rewrite /* Write your own makeEscapeRatio formulae here Default: this._escapeRatio = 0.5 * $gameParty.agility() / $gameTroop.agility(); */ //}; // BattleManager.makeEscapeRatio
2. Let them edit the plugin jf file directly, but in this manner instead:

DoubleX_RMMV.Formulae_Edit = { // Sets your own makeEscapeRatio formulae makeEscapeRatio: function() { this._escapeRatio = 0.5 * $gameParty.agility() / $gameTroop.agility(); }, // etc}; // DoubleX_RMMV.Formulae_Edit/*----------------------------------------------------------------------------* * Edit class: BattleManager*----------------------------------------------------------------------------*/BattleManager.makeEscapeRatio = function() { // Rewrite DoubleX_RMMV.Formulae_Edit.makeEscapeRatio.call(this); // Rewritten}; // BattleManager.makeEscapeRatio
3. The current setup in the OP post.

As it seems to me that many RMMV plugin users prefer to set the configuration values via the plugin manager rather than editing the plugin js file directly, I wanted to try something new that, let users set a whole function as the configuration value via the plugin manager instead of editing the plugin js file directly. I also know that my implementation of the setup sucks like hell(eval alone is a sufficient reason), so I think there should be better ways to implement the setup, such as what larva suggested.
 
Last edited by a moderator:

DarknessFalls

Rpg Maker Jesus - JS Dev.
Veteran
Joined
Jun 7, 2013
Messages
1,393
Reaction score
210
First Language
English
if your editing formulas, then allow thenm to pass in something like: functionName() * 2/ otherFunctionName() + 3 ...
 

estriole

Veteran
Veteran
Joined
Jun 27, 2012
Messages
1,309
Reaction score
531
First Language
indonesian
also remember to create a shortcut variable before you eval the formula for easier usage:

var party = $gameParty

var party_size = $gameParty.members.length

etc... like tsukihime did in his ACE scripts.

so people can write using those shortened variable in plugin parameter than have to use long one like $gameParty.members.length
 

Mellye

Veteran
Veteran
Joined
Oct 24, 2015
Messages
347
Reaction score
279
First Language
Portuguese
also remember to create a shortcut variable before you eval the formula for easier usage:

var party = $gameParty

var party_size = $gameParty.members.length

etc... like tsukihime did in his ACE scripts.

so people can write using those shortened variable in plugin parameter than have to use long one like $gameParty.members.length
Yup, that's what the base game does with the Skill formula damage too (var a = this.subject; var b = target; var v = $gameVariables)
 

DarknessFalls

Rpg Maker Jesus - JS Dev.
Veteran
Joined
Jun 7, 2013
Messages
1,393
Reaction score
210
First Language
English
I hate the stupid $variableName things this game engine has, I like them for in scripts, I hate them for anything else, I would rather just instantiate a new Actor class ... 
 

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,787
Reaction score
939
First Language
Chinese
Primarily Uses
N/A
also remember to create a shortcut variable before you eval the formula for easier usage:

var party = $gameParty

var party_size = $gameParty.members.length

etc... like tsukihime did in his ACE scripts.

so people can write using those shortened variable in plugin parameter than have to use long one like $gameParty.members.length
Do you mean something like this?

* * @param makeEscapeRatio * @desc Sets the party escape ratio upon battle start as makeEscapeRatio, which * must return a function that takes partyAgi and troopAgi as the arguments * partyAgi and troopAgi is $gameParty.agility() and $gameTroop.agility() * respectively * The function returned by makeEscapeRatio will be bound to BattleManager * upon use * @default function(partyAgi, troopAgi) { this._escapeRatio = 0.5 * partyAgi / troopAgi; } *
While I can only provide shorthands for the defaults as I won't know what users will need/want(providing shorthands for everything will ironically defeat the purposes), maybe it's still better than providing nothing, so I'll think about that.

if your editing formulas, then allow thenm to pass in something like: functionName() * 2/ otherFunctionName() + 3 ...
If those functions already exist(from the default RMMV codebase or some other custom plugins), the setup in the OP already allows that.

If those functions are defined by users, then I don't find a way to do it yet:

- If those new functions are declared in the formulae directly, it'd defeat the purposes(and the setup in the OP already allows that as well).

- If those new functions are declared as new @param, then users would have to edit the plugin js file directly, which defeats the purposes for utilizing @param this way in the 1st place(to let users set configuration values via the plugin manager instead of editing the js files directly).

Also, it's impossible for me to define new functions to aid users write their own formulae, as I've absolutely no way to know what they'll need/want.

Of course, that's just because I haven't figured it out yet :)
 
Last edited by a moderator:

Ramiro

Now with an army of Mecha-Ralphs!
Veteran
Joined
Aug 5, 2015
Messages
858
Reaction score
364
First Language
Spanish
AT the moemnt I'm using the simple:

Help Window X | Graphics.boxWidth / 2 + 1And doing in code:

Function("return " + PluginManager.parameters("name")["Help Window X"]);Not *exactly* that, because I'm wrapping my code to do:

var params = D$E.fromSchema(PluginManager.parameters("HelpWindowTest"), {  "Help Window X": "function"});But, it's basically a fancy way to do the thing than is above...
 

Mouser

Veteran
Veteran
Joined
Aug 19, 2012
Messages
1,245
Reaction score
264
First Language
English
Primarily Uses
A couple questions: How advanced do you expect the users of your plugin to be and do you plan to do any debugging based on user reports/requests?

My personal 'gut' feeling is that any user capable of writing a function that can be passed and parsed whole is capable of rewriting the code in either the plugin or just adding their own (or overwriting the engine file).

If you plan on doing any debugging the first question I would ask on _any_ bug report is "Are you trying to pass a function to the plugin?" and make sure they remove that before doing anything else (no matter what the bug appears to be). Users are very creative and capable of introducing bugs that don't show up for quite some time after they're created. Yes, local variables and scope and all that, but I've seen weird things crop up that "shouldn't", or at least that you wouldn't think should on first or second (or third or fourth) glance.
 

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,787
Reaction score
939
First Language
Chinese
Primarily Uses
N/A
A couple questions: How advanced do you expect the users of your plugin to be and do you plan to do any debugging based on user reports/requests?

My personal 'gut' feeling is that any user capable of writing a function that can be passed and parsed whole is capable of rewriting the code in either the plugin or just adding their own (or overwriting the engine file).

If you plan on doing any debugging the first question I would ask on _any_ bug report is "Are you trying to pass a function to the plugin?" and make sure they remove that before doing anything else (no matter what the bug appears to be). Users are very creative and capable of introducing bugs that don't show up for quite some time after they're created. Yes, local variables and scope and all that, but I've seen weird things crop up that "shouldn't", or at least that you wouldn't think should on first or second (or third or fourth) glance.
Basically, when the configuration values are designed to be whole functions, I'll explicitly state(maybe with few exceptions) that users need some Javascript coding proficiency(inexperienced Javascript coders having written few easy, simple and small plugins and a basic knowledge to the default RMMV codebase) to fully utilize the plugin(but less capable users can still use it to some extents or just ask for supports). I can imagine there can be some cases where decent Javascript coding proficiency will be needed(experienced Javascript coders having written dozens of quality plugins and a solid understanding to the default RMMV codebase) to fully utilize the plugins.

In this case(DoubleX RMMV Formulae Edit), I've explicitly stated that users need some Javascript coding proficiency to fully utilize it.

These plugins exists for several reasons:

1. I want to practice Javascript by writing plugins and getting feedback by publishing them

2. I want to give plugin users some alternatives even though they're able to write what they need/want on their own

3. I want to ease some users' work by implementing most of the plugin and leave the rest as the configurable regions

For instance, in RMVXA(users are supposed to edit the scripts directly there), I've enhanced 1 custom script written by others to need decent RGSS3 scripting proficiency(experienced scripters having written dozens of quality plugins and a solid understanding to the default RMVXA codebase) to fully utilize it, but enhancing that script itself into my result will need advanced RGSS3 scripting proficiency(scripting veteran having written at least 1 advanced complex script and a thorough comprehension to the default RMVXA codebase) and a lot more work(I used 7 months to made mine whereas getting familiar with mine just needs several hours at most), so I think there's still some points to enhance that script that way. I think the same can be applied to RMMV plugins.

When users tell me they've faced bugs, I'll normally ask them to give me the error logs(if they haven't already), then I'll ask them to show all the related configuration values(sometimes the errors are so obvious that even this step can be skipped). With both information, it'll be my job to pinpoint the root causes and give those users my fixes. This also applies to "creative" users.
 
Last edited by a moderator:

DoubleX

Just a nameless weakling
Veteran
Joined
Jan 2, 2014
Messages
1,787
Reaction score
939
First Language
Chinese
Primarily Uses
N/A
I've changed my setup in DoubleX RMMV Formulae Edit a bit:

* @param makeEscapeRatio * @desc Sets the party escape ratio upon battle start as makeEscapeRatio, which * must return the content of a function that takes the party and troop's * agi, which can be referenced by partyAgi and troopAgi respectively, as * the arguments * The content of the function returned by makeEscapeRatio will be bound * to BattleManager upon use * @default this._escapeRatio = 0.5 * partyAgi / troopAgi; * * @param speed * @desc Sets the action speed as speed, which must return the content of a * function that takes the subject's agi, which can be referenced by agi, * as the argument and returns a Number * The content of the function returned by speed will be bound to * Game_Action upon use * @default return agi + Math.randomInt(Math.floor(5 + agi / 4)); * * @param lukEffectRate * @desc Sets the luk effect rate multiplier applied to adding states and * debuffs to a target as lukEffectRate, which must return the content of * a function that takes a subject and target, which can be referenced by * subject and target respectively, as the arguments and returns a Number * The content of the function returned by lukEffectRate will be bound to * Game_Action upon use * @default return Math.max(1.0 + (subject.luk - target.luk) * 0.001, 0.0); * * @param expForLevel * @desc Sets the required experience for levelling up to level as expForLevel, * which must return the content of a function that takes the level, * actor's class, experience basis, extra, acceleration a and b, which can * be referenced by level, c, basis, extra, acc_a and acc_b respectively, * as the arguments and returns a Number * The content of the function returned by expForLevel will be bound to * Game_Actor upon use * @default return Math.round(basis * (Math.pow(level - 1, 0.9 + acc_a / 250)) * level * (level + 1) / (6 + Math.pow(level, 2) / 50 / acc_ + (level - 1) * extra); * * @param distancePerFrame * @desc Sets the characters' moving distance per frame as distancePerFrame, * which must return the content of a function that takes the real move * speed, which can be referenced by speed, as the argument and returns a * Number * The content of the function returned by expForLevel will be bound to * Game_CharacterBase upon use * @default return Math.pow(2, speed) / 256; * * @param makeEncounterCount * @desc Sets the number of steps needed to trigger an encounter as * makeEncounterCount, which must return the content of a function that * takes the encounter count, which can be referenced by n, as the * argument * The content of the function returned by makeEncounterCount will be * bound to Game_Player upon use * @default this._encounterCount = Math.randomInt(n) + Math.randomInt(n) + 1;
Code:
DoubleX_RMMV.Formulae_Edit = {    // Stores all this plugin's parameters that are shown in the plugin manager    params: PluginManager.parameters(DoubleX_RMMV.Formulae_Edit_File),    // BattleManager    makeEscapeRatio: new Function("partyAgi", "troopAgi",                      DoubleX_RMMV.Formulae_Edit.params.makeEscapeRatio),    // Game_Action    speed: new Function("agi", DoubleX_RMMV.Formulae_Edit.params.speed),    lukEffectRate: new Function("subject", "target",                    DoubleX_RMMV.Formulae_Edit.params.lukEffectRate),    // Game_Actor    expForLevel: new Function("level", "c", "basis", "extra", "acc_a", "acc_b",                               DoubleX_RMMV.Formulae_Edit.expForLevel.speed),    // Game_CharacterBase    distancePerFrame: new Function("speed",                       DoubleX_RMMV.Formulae_Edit.distancePerFrame.speed),    // Game_Player    makeEncounterCount: new Function("n",                       DoubleX_RMMV.Formulae_Edit.makeEncounterCount.speed)}; // DoubleX_RMMV.Formulae_Edit
The rationale is that, instead of asking users to write the whole function, just asking them to write its content will suffice, as the rest should never be changed anyway.

This should make the setup at least slightly more user-friendly, as users now can write less, meaning they're less likely to make mistakes, as long as the descriptions are clear enough.

I also provided some shorthands used by the default function contents, like partyAgi for $gameParty.agility(). I hope doing so can make this plugin even easier and simpler to use.

What do you think about this setup? How would you compare that with the one in the OP?
 

Users Who Are Viewing This Thread (Users: 0, Guests: 1)

Latest Threads

Latest Posts

Latest Profile Posts

Couple hours of work. Might use in my game as a secret find or something. Not sure. Fancy though no? :D
Holy stink, where have I been? Well, I started my temporary job this week. So less time to spend on game design... :(
Cartoonier cloud cover that better fits the art style, as well as (slightly) improved blending/fading... fading clouds when there are larger patterns is still somewhat abrupt for some reason.
Do you Find Tilesetting or Looking for Tilesets/Plugins more fun? Personally I like making my tileset for my Game (Cretaceous Park TM) xD
How many parameters is 'too many'??

Forum statistics

Threads
105,862
Messages
1,017,049
Members
137,569
Latest member
Shtelsky
Top