+2 votes
asked by (190 points)
Okay so i am trying to make a large game, not some 5 min story, thats why i decided to track some variables in javascript as i heard that its better to use JS for that. Basically i have the code that tracks affection points with the character Aeronda and some choices give it while others detract it. Heres the JS code:

macros.aeronda_affection = {
    "handler" : function(place, macroName, params, parsers) {
        var player = params(o);
        new wikifies(place, "player aeronda_affection" +

Here is the code im currently using to give a point:

<<Aeronda_affection $player + 1>>

<<goto "Sit up">>

and here is the code im using to attempt to pull the info so i can see if its working(obvously its not, and i have no idea why since im a total rookie who just spent 10 hours on youtube watching tutotials about it)

<<print $aeronda_affection>>

So the errors it gives me

It gives me absolutely no values. just white space. So what am i doing wrong?

2 Answers

0 votes
answered by (58.9k points)
selected by
Best answer

Attempting to bypass the normal mechanism for handling persistent state is your first mistake.  If you want to properly track some statistic, then you should be storing it in a story variable.  If someone actually argued against doing so (i.e. you didn't misunderstand what they were saying), then they're wrong.  Beyond that, the macro you tried to write isn't using SugarCube's Macro API and is broken in multiple ways besides.  I don't know where you've been finding information, but its veracity seems questionable.


Just initialize $aeronda_affection in your StoryInit special passage to whatever its initial value should be:

<<set $aeronda_affection to 0>> /* 0 or whatever */

Here are a few examples of how to modify it as the game progresses:

<<set $aeronda_affection += 1>> /* add 1 */
<<set $aeronda_affection -= 1>> /* sub 1 */
<<set $aeronda_affection += 5>> /* add 5 */
<<set $aeronda_affection -= 5>> /* sub 5 */


I'd also suggest reading SugarCube's documentation.  It's not sexy, and there are gaps, but at least it's accurate.

commented by (190 points)
Thank you for your response. I thought about doing it like this, but the tutorial i followed (not sure if im allowed to post it?) uses the justification that by using JS for certain variables it saves you alot of work and time by saving you from repeating alot of code. I also wanted to basically make myself learn JS by just kinda doing it, but i guess im still too far away from that. As for the documentation, i have been going through it, but i dont understand alot of terms in there yet (yeah im watching more tutorials as i write this :P)
commented by (125k points)

Please post a link to the tutorial that advised you to use JavaScript for certain variables so that we can read their justification ourselves, and so we can possibly contact the author if any clarification is required.
commented by (190 points)

Is pretty much where it starts. For him it works fine in the video, and as youll see is that i have pretty much copied the code and then altered it for my variables. I have done this before with some of his codes and altered it, though it usually gave some errors i managed to fix them through trial and error and actually learned a bit of the coding stuff that way, however after messing with it for 2 hours, i kinda gave up and went to ask you guys :P (off topic, i know. just wanted to give some jbackground info, since like i said, i am a first time user and complete rookie at coding :P)
commented by (125k points)


Unfortunately those tutorial videos by VegetarianZombie target the old SugarCube 1.x series story format, and you are (correctly) using the new SugarCube 2.x series. This means that some of the information within those videos is either invalid or out-of-date with the current version of SugarCube you are using, in particular how you write a Custom Macro.

BTW: You introduced two syntax errors into your version of VegetarianZombie's custom SugarCube 1.x macro, both errors are in the line accessing the first element of the params parameter.
1. You have incorrectly used parenthesis ( ) when you should of been using square brackets [ ] 
2. You typed the letter 'o' when you should of used the number 0 zero.

/* you wrote.. */
var player = params(o);

/* it should be.. */
var player = params[0];


commented by (190 points)

I see, that explains alot. Thank you!

Now after i did some more researching, i went to the wiki and eventually found this:http://twinery.org/forum/discussion/1516

Would i be able to for example use this:

<<set $aeronda = {
  name: "Jane",
  age: 21,
  height: 63,
  weight: 100,
  hair: "Blonde",
  eyes: "Blue",
  affection: "23"  

and increase/decrease

<<set $aeronda.affection +=>>

and pull it up using

<<print $aeronda.affection>>

Would that be possible, is that for an older version as well?

I do want to use multiple variable on some characters, including player and aeronda(in this case). Note that the affection variable will only be set to the player, there will not be other characters who have it(just yet, but then ill figure it out)

commented by (125k points)

There are two syntax errors in your latest examples:

1. You initially assigned the affection property a String value of "23" instead of a numerical value of 23, and String values can't be increased (or decreased) the same way that numerical values can. The following is a corrected version.

<<set $aeronda = {
  name: "Jane",
  age: 21,
  height: 63,
  weight: 100,
  hair: "Blonde",
  eyes: "Blue",
  affection: 23

2. You have left out the numerical value required to indicate how much you want to increase the affection property by. The following example increases the property by 1.

<<set $aeronda.affection += 1>>


commented by (190 points)
Thank you! The twine community is so helpful! i appreciate it.
0 votes
answered by (8.5k points)

If all you want to keep track of is one character's affection to one other (the player) TheMadExile's answer is perfectly fine and you won't need much else. If you want the full N:M mapping between multiple characters, read on.

Let's first define what we're trying to have available.

Modify affection from one character to another
As the second argument, we can always use someone's ID (as string) instead of their object
<<affection $aeronda $player +1>>

Print the affection of someone to someone else
<<affection $aeronda $player>>

Check if the affection is at or above 10
<<if affection.between($aeronda, $player) >= 10>>

For this, let's define the data structures: All characters are JavaScript objects and have an "aff" property, which itself is an object, and an "id" property which is unique to them and used to identify them, like this:

    "id": "aeronda",
    "aff": {
        "player": -2,
        "bob": 3,
        "sauron": -1000

Obviously, there are other attributes (like the name!) for each character, but we don't care much about those at the moment. The default affection is 0. In the JavaScript part, let's define a global "affection" object which will hold all our helper JavaScript methods, including an "init" one which initialises the structure and methods for modifying and querying the affection values. At the beginning, it looks like this.

window.affection = {
	init: function(ch) {
		// Make sure we're dealing with actual characters
		if(typeof ch !== 'object' || Array.isArray(ch) || typeof ch.id !== "string") {
			throw "Not an valid character: " + JSON.stringify(ch);
		// Initialise affinity attribute
		if(typeof ch.aff !== 'object' || Array.isArray(ch.aff)) {
			ch.aff = {};
		// for call chaining
		return ch;
	between: function(ch, target) {
		// target can be a character object or an id string here
		if(typeof target === "string") {
			return affection.init(ch).aff[target] || 0;
		if(typeof target !== 'object' || Array.isArray(target) || typeof target.id !== "string") {
			throw "Not an valid character: " + JSON.stringify(target);
		return affection.init(ch).aff[target.id] || 0;
	mod: function(ch, target, amount) {
		var current = affection.between(ch, target);
		// ch's affection attribute is initialized at this point,
		// so we can use it safely
		ch.aff[typeof target === "string" ? target : target.id] = current + amount;

Now all we need is a small widget to make the usage a bit neater.

<<widget "affection">>
	<<if $args.length === 2>>
		<<= affection.between($args[0], $args[1])>>
	<<elseif $args.length === 3 && Number.isFinite($args[2])>>
		<<= affection.mod($args[0], $args[1], $args[2])>>
		<<run console.log("Unknown arguments to <<affection>>:"); console.log($args);>>

Usage examples:

<<set $aeronda = {id: "aeronda", aff: {player: -3, bob: 1, sauron: -1000}}>>
<<set $player = {id: "player"}>>
<<affection $aeronda $player +1>>
Current affection: <<affection $aeronda $player>>
<<if affection.between($aeronda, $player) > 0>>Aeronda likes player!<<else>>Aeronda doesn't care much for player ...<</if>>
Affection to Sauron: <<affection $aeronda "sauron">>


commented by (190 points)
reshown by
Thank you for your answer! I am going to use TheMadExiles code since its simpler, and I guess im not good enough to be using JS. I do have a qeustion for you though, specifically your widget. How do i use widgets? I tried using the one on the documentation(the one that changes " he" to " she" if pcsex is male/female) but i couldnt get it to work. i put it in a seperate passage with the tag " widget"  and then i followed the example by putting a " he" in <<he>> but it said that it doesnt exist. I then tried putting it in a story passage with the widget text but that kind of removed it from the story. I want to use your widget but i don't know how to use it.
commented by (125k points)

 i put it in a seperate passage with the tag " widget"  and then

In the above statement you have a single space character before the word widget, make sure that the actual tag assigned to your passage doesn't include that (or any) space characters and that it is in all lower-case letters.

commented by (190 points)
Yes, there were no spaces in there and all lowercase.
commented by (8.5k points)
A separate passage tagged "widget" is the correct way. If that somehow doesn't work, check that you have the newest Twine version, and if that doesn't help either - post a link to a minimal test story where you try it and it doesn't work so that we can check your actual code in its "native" environment.
Welcome to Twine Q&A, where you can ask questions and receive answers from other members of the community.

You can also find hints and information on Twine on the official wiki and the old forums archive.

See a spam question? Flag it instead of downvoting. A question flagged enough times will automatically be hidden while moderators review it.