Help: Button for randomly assigning Gifts not working

I have the latest version of Twine and using the story format SugarCube 2.36.1. My Twine projects open up in Chrome. I have a basic understanding of HTML/CSS and figured I would learn JavaScript as I work through my project which has been working decently so far.

However, I am running into an issue that I can’t figure out no matter how much I research. I even attempted to use ChatGPT to see if it could assist me with no such luck. I’m hoping someone here can help.

I have the following passages for my story; Gifts, Gift 1, Gift 2, Gift 3, and Character Home.

When I have my story starting at Gifts, the button that gives the character three random Gifts from an array and assigns them to variables which works flawlessly. It flows through Gifts 1-3 without an issue, but I wanted to add a passage before Gifts called Character Home. When my story starts from Character Home, the button in Gifts is no longer clickable unless I refresh the page manually. Changing the starting point back to Gifts allows the button to work. I just can’t have any passage leading into it.

Can someone help me understand why that is happening and how I can fix it? Here is all my coding:

:: StoryInit

<<set $gift1 to {}>>
<<set $gift2 to {}>>
<<set $gift3 to {}>>

<<set $gift1bg to "">>
<<set $gift2bg to "">>
<<set $gift3bg to "">>

<<set $home to "">>





:: Character Home

What does your home look like? What is it made out of? What does it look like inside? You will need a good strong fire, candles to make light after dark, and a decently sized workbench. You’ll also need a warm comfortable place to sleep and dream.

<<textarea "$home" "">>

<<button "Submit">><<goto "Gifts">><</button>>






:: Gifts

You get three different starting gifts that are chosen at random. 

`<button id="creategiftsButton" type="button">Gifts</button>`






:: Gift 1

You have received $gift1.name as a gift. How did you get this? What does it mean to you?

<<textarea "$gift1bg" "">>

<<button "Submit">><<goto "Gift 2">><</button>>






:: Gift 2

You has received $gift2.name as a gift. How did they get this? What does it mean to them?

<<textarea "$gift2bg" "">>

<<button "Submit">><<goto "Gift 3">><</button>>






:: Gift 3

You has received $gift3.name as a gift. How did they get this? What does it mean to them?

<<textarea "$gift3bg" "">>






:: Story JavaScript

// Define an array of possible origin gifts (only names)
var originGifts = [
  "Round Stone",
  "Spool of Gold Thread",
  "Delicate Chain",
  "Sprig of Red Ivy",
  "Dingy Cord",
  "Hollow Reed Flute",
  "Three Drops of Perfume",
  "Spider-Linen Gloves",
  "Talon Needle",
  "Carved Jade Pendant",
  "Cottle-Buttons",
  "Ghost Dust",
  "Sand",
  "Blue Paper",
  "Pouch of Moth Teeth",
  "Cracked Teapot",
  "Lingering Bead",
  "Wish Folio",
  "Sky-Blue Shard",
  "Bottle of Green Ink",
  "Moon Seed",
  "Bright",
  "Tangled Knot",
  "Old Tin Coin",
  "Blooming Scarf",
  "Burnished Fingerbone",
  "Wooden Key",
  "Winter Grease",
  "Bound Egg",
  "Lucky Thimble",
  "Lost Ember",
  "Bag of Seeds",
  "Silver Paw",
  "Twisted Root",
  "Fish Amber",
  "Hare-Hair Paintbrush",
];

// Function to shuffle an array (Fisher-Yates algorithm)
function shuffleArray(array) {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1));
    [array[i], array[j]] = [array[j], array[i]];
  }
  return array;
}

// Function to randomly select three origin gifts
function getRandomOriginGifts() {
  // Shuffle the array to randomize the order
  originGifts = shuffleArray(originGifts);

  // Select the first three gifts
  var gift1 = originGifts[0];
  var gift2 = originGifts[1];
  var gift3 = originGifts[2];

  // Return the selected gifts
  return [gift1, gift2, gift3];
}

// Function to handle character creation
function handleGiftCreation() {
  // Get random origin gifts
  var randomGifts = getRandomOriginGifts();

  // Assign the selected gifts to Twine variables
  State.variables.gift1 = { name: randomGifts[0] };
  State.variables.gift2 = { name: randomGifts[1] };
  State.variables.gift3 = { name: randomGifts[2] };

  // Display the selected gifts (optional)
  alert(
    "You received the following origin gifts:\n1. " +
      State.variables.gift1.name +
      "\n2. " +
      State.variables.gift2.name +
      "\n3. " +
      State.variables.gift3.name
  );

  // Move to the next passage (e.g., "Gift 1")
  State.display("Gift 1");
}

// Wait for the story to fully load before setting up the button click event
$(document).one(":passagedisplay", function (event) {
  // Find the button with the specific ID and attach the click event
  $("#creategiftsButton").on("click", handleGiftCreation);
});

The contents of the Story JavaScript area is processed during engine start-up, and only then.

The :passagedisplay event is triggered each time a Passage is visited (displayed), including after the first Passage is shown.

The jQuery <query>.one() function you are using to listen for the :passagedisplay event disables itself as soon as one instance of that event has occurred.

So the reason the button in the 2nd (Gift) Passage visited didn’t have an attached on click event handler was because the one(":passagedisplay", ...) listener disabled itself after the 1st Passage visited’s :passagedisplay event occurred.

Changing that JavaScript to use the <query>.on() function instead of one() would fix that issue.

However there should be no need to use a such an on click event handler at all, because you could call your handleGiftCreation() function using the <<run>> macro from within the body of a <<button>> macro, once the Scope of that function has been changed to be available in SugarCube’s own Private Scope.

And that can be achieve by simply defining that function on the special setup variable.

// Function to handle character creation
setup.handleGiftCreation = function handleGiftCreation() {
  // Get random origin gifts
  var randomGifts = getRandomOriginGifts();

  // Assign the selected gifts to Twine variables
  State.variables.gift1 = { name: randomGifts[0] };
  State.variables.gift2 = { name: randomGifts[1] };
  State.variables.gift3 = { name: randomGifts[2] };

  // Display the selected gifts (optional)
  alert(
    "You received the following origin gifts:\n1. " +
      State.variables.gift1.name +
      "\n2. " +
      State.variables.gift2.name +
      "\n3. " +
      State.variables.gift3.name
  );
};

And using a <<button>> macro instead of a <button> element would also allow you to specify the target Passage, and not have to hard-wire it into the handleGiftCreation() function itself.

:: Gift
You get three different starting gifts that are chosen at random. 

<<button "Gifts" "Gift 1">>
	<<run setup.handleGiftCreation()>>
<</button>>
2 Likes

Hi, Welcome to Intfiction!

As Greyelf has, at least to my eyes, pointed out the biggest oversights, I thought maybe you would find different solutions helpful for future reference, the magic behind Twine/programming is there are so many ways to do most things.

Twine already includes many nifty functions that can help speed up your Development speed if you take some time to look at the Docs.

  1. .randomMany()
<<set $gifts to [ "Round Stone", "Spool of Gold Thread", "Delicate Chain", "Sprig of Red Ivy", "Dingy Cord", "Hollow Reed Flute", "Three Drops of Perfume", "Spider-Linen Gloves", "Talon Needle", "Carved Jade Pendant", "Cottle-Buttons", "Ghost Dust", "Sand", "Blue Paper", "Pouch of Moth Teeth", "Cracked Teapot", "Lingering Bead", "Wish Folio", "Sky-Blue Shard", "Bottle of Green Ink", "Moon Seed", "Bright", "Tangled Knot", "Old Tin Coin", "Blooming Scarf", "Burnished Fingerbone", "Wooden Key", "Winter Grease", "Bound Egg", "Lucky Thimble", "Lost Ember", "Bag of Seeds", "Silver Paw", "Twisted Root", "Fish Amber", "Hare-Hair Paintbrush",]
<<set $userGifts to $gifts.randomMany(3)
<<unset $gifts>>

  1. .shuffle()
<<set $gifts to [ "Round Stone", "Spool of Gold Thread", "Delicate Chain", "Sprig of Red Ivy", "Dingy Cord", "Hollow Reed Flute", "Three Drops of Perfume", "Spider-Linen Gloves", "Talon Needle", "Carved Jade Pendant", "Cottle-Buttons", "Ghost Dust", "Sand", "Blue Paper", "Pouch of Moth Teeth", "Cracked Teapot", "Lingering Bead", "Wish Folio", "Sky-Blue Shard", "Bottle of Green Ink", "Moon Seed", "Bright", "Tangled Knot", "Old Tin Coin", "Blooming Scarf", "Burnished Fingerbone", "Wooden Key", "Winter Grease", "Bound Egg", "Lucky Thimble", "Lost Ember", "Bag of Seeds", "Silver Paw", "Twisted Root", "Fish Amber", "Hare-Hair Paintbrush",]
$gift.shuffle()
<<set $gift1 = $gifts[0], $gift2 = $gifts[1], $gift2 = $gifts[3]>>

  1. .deleteAt() With random(min, max) Removes elements from $Gifts
<<set $gifts to [ "Round Stone", "Spool of Gold Thread", "Delicate Chain", "Sprig of Red Ivy", "Dingy Cord", "Hollow Reed Flute", "Three Drops of Perfume", "Spider-Linen Gloves", "Talon Needle", "Carved Jade Pendant", "Cottle-Buttons", "Ghost Dust", "Sand", "Blue Paper", "Pouch of Moth Teeth", "Cracked Teapot", "Lingering Bead", "Wish Folio", "Sky-Blue Shard", "Bottle of Green Ink", "Moon Seed", "Bright", "Tangled Knot", "Old Tin Coin", "Blooming Scarf", "Burnished Fingerbone", "Wooden Key", "Winter Grease", "Bound Egg", "Lucky Thimble", "Lost Ember", "Bag of Seeds", "Silver Paw", "Twisted Root", "Fish Amber", "Hare-Hair Paintbrush",]
<<set $gift1 = $gifts.deleteAt(random(0, $gifts.length -1 )),
      $gift2 = $gifts.deleteAt(random(0, $gifts.length -1 )),
      $gift2 $gifts.deleteAt(random(0, $gifts.length -1 ))>>

Hope you have fun learning, but try and keep things simple if possible or complicated tasks will sooner or later become a big headaches.

Thank you so much Greyelf! I really appreciate you taking your time to explain what was going on and providing a solution to fix my issue. I was able to apply your changes and now it works flawlessly. I've struggled over this for multiple days so I can't say thank you enough.

Hi, Oktu! Thank you for the welcome to Intfiction. I was wondering if there was a more simple way of going about what I was doing. I felt what I had attempted to figure out was more complicated than it should have been. I do have some follow up questions for your method, if you don't mind answering them. I apologize in advance for the long post.

Just so that I understand it correctly, using the codes you provided would mean that I wouldn't need all the stuff I have in my Story JavaScript? I could just use the code examples you've provided within my passages?

If that is the case, with the randomMany() choosing the gifts randomly, is there a benefit to shuffling them?

With the randomMany() code you provided, you have it set to apply the gifts to $userGifts variable. Does that mean my use of the $gift1, $gift2, $gift3 variables would not be usable? Having the three gifts as separate variables is important for my project. Perhaps an explanation of my project goal would be helpful to determine if this is the best method for me to utilize.

My project is going to be based on a writing game. Basically, the player is a solitary forest dweller that goes out and explores the forest daily. After they create their character with the gifts, they go through their day making journal entries about what happens. I already have figured out the coding for the journals, so I'm good there. 

When they wake up for the day, they are given three Dream Fragments which I was going to use the same coding for the way I did the Gifts. After they are assigned the random words for their dream, the player is given a textarea to write about their dream for their journal.

They will then leave their house to explore the forest in which five different events can happen. They could find a new plant, see a rare sight, make a discovery, encounter a new creature, or a forest spirit will reveal itself to them.

Each of those events has some random words applied to them. Like for finding a new plant, the player will receive two colors and three plant features. The player will then write about the plant in their journal.

The player will then have a choice to continue their exploration in which I will use the random events again, or they can go home.

Once home, the player can then have the option to write more about the reflections of the day in their journal before going to bed and starting the process over again of dreaming, exploring, and then going home.

If I'm understanding your method correctly, I think I would do the following . . . please correct me if I'm wrong.

Let's say the random event (which I've figured out how to do, but I've most likely overcomplicated it, lol) is finding a new plant. That means my Events passage goes to the Plant passage.

Within my Plant passage, I would use the randomMany() for the color and then another randomMany() for the features. I then provide my textarea for the journal entry. Then when the player uses the submit button to apply the text to the journal and send the player to a passage that allows them to choose if they want to continue or go home, I would also include the deleteAt() to remove the variables so it can be used again.

This brings up another question about randomMany(). Does it automatically display what is chosen? Or will I still need to include something like $plantcolor1 to display it?

Seems like you wrapped your post in `, no worries xD

  1. Yes as you can read up, they are all in the sugercube documentation and should work fine without any external setup.

I haven’t looked too deep into Sugarcube, so I do not know how it works behind the scenes, but shuffling and then picking out a random gift, shouldn’t amount to a much different output.

Well you see RandomMany() outputs a Array with the elements Array = [GiftName1, GiftName2, GiftName3]

You could just go and do

<<set $gift1 to $usergifts[0]>>
<<set $gift2 to $usergift[1]>>
<<set $gift3 to $usergift[2]>>
<<unset $usergift>> <- will remove it so it doesn't take up space in Passage History. 

So yes if you want to display it you would have to set it to single variables and then $gift1 $gift2 $gift3 to show it in your passage,

sorry for the late response I had written this a few hours ago, just not posted it by accident…

Sorry about that, but every time I attempted to post it, the system told me there was coding that it recommended doing it for and when I went through and did the single line thing to everything I’d consider code, it still warned me so I decided to just change the entire post.

No worries for the late response. I appreciate you taking your time to answer my questions. This has definitely helped me out a lot in my project. Thank you so much!

Because of you, I feel confident that I’ll be able to finish my project sometime today and I’m already thinking of starting a new project that I can expand my knowledge even more by adding features such as character stats and health and such.

Thank you, again!

1 Like