# Creating macros/functions for non-linear stats changes (Twine 2, Sugarcube 2.18) using JavaScript

0 votes
asked Sep 10
edited Sep 10

I'm trying to change stats in a non-linear way, which would make it easy to surpass mediocrity, but difficult to master something. I'm hoping this will lead to a more dynamic user experience (note: this will have the opposite effect and make users 'average' if there isn't an internal game mechanic which encourages min/max playing).

This is how I envision it working - a percentage change (bound between 0 and 1) will increase the percent left to attain, or decrease the percent already attained.

For example, if a stat \$honor is currently 80 and we want to modify it by +30% for a heroic act, it would only increase 6 percentage points to 86/100 ((100-80)*0.3)+80=86. (an honorable person doing a heroic act? kind of expected).
Whereas if the \$honor stat was 20, and the user decided to perform an unexpected act of heroism with a +30% modifier, the stat would increase 24 percentage points to 44/100 ((100-20)*0.3)+20=44. (a coward performing a heroic act? How noble of you!)

A decreasing modifier will do the opposite. In the above case, an 80 stat modified with -30% will decrease 24 percentage points to 56/100, and be calculated as such: 80-(80*0.3)=56. (a heroic person act as a coward? that's a big tarnish on your record!)
Whereas a stat of 20 with a -30% modifier would only decrease 6 percentage points to 14/100, and be calculated: 20-(20*0.3)=14. (a coward taking the easy way out? That's as expected.)

As long as your starting value is between 1 and 100 (inclusive), this system will bound your results between 1 and 100 (inclusive), thus no need for clamping a stats value.

The only catch is, I have no idea how to write this in JavaScript.
This is the point I've been able to logic/google myself to...

set \$tempvariable = \$statvariable. [Call it a]
set b to percent modifier
try {
macros.add("percentincrease", {
handler: function(a, b) {
return (((100 - a) * b ) + a);
}
}
} catch(e) {
return this.error("percentincrease Error: " + e.message);
}

set \$statvariable = a

try {
macros.add("percentdecrease", {
handler: function(a, b) {
return (a - (a * b));
}
}
} catch(e) {
return this.error("percentincrease Error: " + e.message);
}

set \$statvariable = a

## 1 Answer

+1 vote
answered Sep 10 by (9,520 points)
selected Sep 10 by ShinySue

Best answer

In the future.  Please tag your questions with the name+major version of both the compiler you're using (e.g. twine1, twine2, tweego1) and story format (e.g. harlowe2, sugarcube2), because it can, and often does, make a difference to the answers you'll receive and how quickly you receive them.  I'd also suggest specifying the full version of each somewhere, either as additional tags (e.g. twine2-1-3, sugarcube2-1-8) or within the post, because that can matter too.

For now, based on your example "code" alone, I'm going to assume you're using SugarCube.

Your best bet would probably be to do these as functions, not macros, actually.  I'd probably suggest either using the built-in setup object or your own custom object to hold them, for the purposes of namespacing.  Further, if using your own custom object, placing it upon the window object, so that it becomes an auto-global (i.e. accessible everywhere).

For example, using a custom object on window: (goes in a script section; Twine 2: Story JavaScript, Twine 1: script-tagged passage)

``````window.MyLib = {
increase : function (value, percentage) {
return (100 - value) * percentage + value;
},
decrease : function (value, percentage) {
return value - value * percentage;
}
};
``````

Usage examples:

``````/* Specifying a number literal as the value. */
<<set \$someStat to MyLib.increase(80, 0.3)>>
<<set \$someStat to MyLib.decrease(80, 0.3)>>

/* Specifying a story variable as the value. */
<<set \$someStat to MyLib.increase(\$someStat, 0.3)>>
<<set \$someStat to MyLib.decrease(\$someStat, 0.3)>>

/* Specifying a temporary variable as the value. */
<<set \$someStat to MyLib.increase(_someTempVar, 0.3)>>
<<set \$someStat to MyLib.decrease(_someTempVar, 0.3)>>
``````

Though I only showed using a number literal for the percentage in the examples, you may instead pass it a variable just as easily.

PS:  Please disregard whatever examples you found that lead to your macro attempts above.  They're pointless and the fact that they've survived this long is a sad state of affairs.  They're cargo cult programming at its worst and their originator should feel bad for inflicting them upon the community.

commented Sep 10 by (160 points)
That's fantastic! Thank you for your help. And I edited the title to include Twine 2 and Sugarcube, as per your suggestions.
commented Sep 10 by (3,820 points)

@ShinySue: Wouldn't a simple "change" function (which does the right thing depending on whether the change value is positive or negative) be easier to use here?

``````window.MyLib = {
change: function(value, percentage) {
if(_.isFinite(percentage)) {
if(percentage > 0) {
return (100 - value) * percentage + value;
} else if(percentage < 0) {
return value + value * percentage;
}
}
return value;
},
};

...

<<set \$someStat to MyLib.change(\$someStat, 0.3)>>
<<set \$someStat to MyLib.change(\$someStat, -0.3)>>``````