Looking for a smarter way to deal with a random element

Twine Version: 2.3.8
Story Format: SugarCube v2.31.1

I have a few passages that make use of either(list...) to randomly select a character to interact with in that passage.

Initially I did this in a pretty basic fashion because all of the characters would have identical dialogue, the only difference was the name. So by using the following code I could have a passage with one of six characters randomly.

In StoryInit:

<<set $tutorRandomSet to ["Holly", "Kayley", "Cheryl", "Frank", "Rob", "Griffon"]>>

In link to passage:

<<set $tutorRandom to either($tutorRandomSet)>>

In passage:

"Hi $tutorRandom, how are you?"
"I'm good!"

So the player gets some variation with very little reproduction of dialogue and code.

But now I’ve gotten to the point where I would like to make some slight changes to their dialogue based on other variables they’ve picked up. (They all have identical variables initialized, the only difference being the value.) For example, if the character Holly has texted you:

"Hi $tutorRandom, how are you?"
"I'm good! <<if $holly.text.1>>Did you get my text?"<</if>>

Now, this would work when $tutorRandom is “Holly” but would give the wrong response whenever $tutorRandom is anyone else. I could wrap that whole conversation in <<if $tutorRandom is "Holly">>, but then, instead of having one piece of dialogue for 6 characters, I’d have to write out all of the dialogue for every one of the random characters.

Is there a way that I could do something like this:

"Hi $tutorRandom, how are you?"
"I'm good!<<if $tutorRandom.text.1>>Did you get my text?"<</if>>

Where the $tutorRandom.text.1 above gets it’s value from $holly.text.1

Or maybe I’m going about this all wrong. Any advice would be appreciated.

First, any data you have which won’t change during your game should be stored on the SugarCube setup object. That way no space will be wasted storing duplicate data in your game’s history, which would slow down saves, loads, and passage transitions. For example, you could do something like this in your StoryInit passage:

<<set setup.tutors = ["Holly", "Kayley", "Cheryl", "Frank", "Rob", "Griffon"]>>

You could then access that in your passages like this:

<<set $tutorRandom to either(setup.tutors)>>

Now, to make things simpler for accessing their comments, you could put something like this in your StoryInit passage:

<<set setup.tutorTxt = {}>>
<<set setup.tutorTxt.Holly = "Did you get my text?">>
<<set setup.tutorTxt.Kayley = "(more text)">>
<<set setup.tutorTxt.Cheryl = "(more text)">>
<<set setup.tutorTxt.Frank = "(more text)">>
<<set setup.tutorTxt.Rob = "(more text)">>
<<set setup.tutorTxt.Griffon = "(more text)">>

Then your passage code would look like this:

<<set _tutorRandom to either(setup.tutors)>>
"Hi _tutorRandom, how are you?"
"I'm good! <<= setup.tutorTxt[_tutorRandom]>>"

Note that I switched from using a story variable (one which starts with a “$”) to using a temporary variable (which start with “_”), assuming that you were only using that variable within that one passage.

Also, <<=>> is an alias for the <<print>> macro.

Instead of setting the setup.tutorTxt.(name) variables to a string, you could set them to an array, like this:

<<set setup.tutorTxt.Holly = ["Did you get my text?", "(more text)", "(more text)", "(more text)"]>>

If you do that, then you could access it like this:

"I'm good! <<= setup.tutorTxt[_tutorRandom][0]>>"

since the first element in an array is the zeroth element.

Please let me know if you have any questions on any of that.

Hope that helps. :smiley:

Thanks for the response! But I’m not sure how much of that I can use.

So, that list of random people:

<<set $tutorRandomSet to ["Holly", "Kayley", "Cheryl", "Frank", "Rob", "Griffon"]>>

does change throughout the game, based on other events; some characters are removed, others added.

Also, $tutorRandom needs to persist through several passages, only getting re-randomized when the player returns to the originating passage.

And each of the characters already has their own full set of variables:

<<set $holly ={
	main: {
		name: "Holly",
		age: "22",
		influence: 0,
		storyStage: 0,
		playerTitle: "none",
		follower: false,
	},
	misc: {
		hasNumber: false,
		payments: false,
		goPlayerHome: false,
		waitTime: 0,
		TotalScore: 0,
	},
	text: {
		one: 0,
		two: 0,
		three: 0,
		four: 0,
		five: 0,
		six: 0,
		seven: 0,
		eight: 0,
		nine: 0,
		ten: 0,
	}
}>>

So what I’m really looking for is a shorter/easier way to poll the value of their variables. Like, if $tutorRandom is currently “Frank”, is there any code I could use that will use that to print the value of $frank.main.name?. And if I then $tutorRandom changes to “Cheryl”, that same passage will now print the value of $cheryl.main.name?

I guess I’m trying to use one variable to access multiple other variables. And that’s probably a bad practice, but it seems like it would be the easiest way for me to move forward with this, otherwise I’ll need to have that passage be just full of if-statements:

"Hey $tutorRandom"

"Hi, <<if $tutorRandom is "Frank" and $frank.text.one is 1>>did you get my text?<<elseif $tutorRandom is "Cheryl" and $cheryl.text.one is 1>> did you get my text?<<elseif $tutorRandom is "Rob" and $rob.text.one is 1>> did you get my text?<<elseif $tutorRandom is "Holly" and $holly.text.one is 1>> did you get my text?<<elseif $tutorRandom is "Kayley" and $keyley.text.one is 1>> did you get my text?<</if>>"

"Yeah, $tutorRandom, I got your text. By the way: how old are you?"

"I'm <<if $tutorRandom is "Frank">>$frank.main.age<<elseif $tutorRandom is "Cheryl">>$cheryl.main.age<<elseif $tutorRandom is "Rob">>$rob.main.age<<elseif $tutorRandom is "Holly">>$holly.main.age<<elseif $tutorRandom is "Kayley">>$keyley.main.age<</if>>."

etc.etc.etc.

It would probably be best to make each character a property on a single object. For example:

<<set $tutor = {}>>
<<set $tutor.Holly = {
	main: {
(etc.)
	text: new Array(11).fill(0)
}>>
<<set $tutor.Kayley = {
(etc.)
}>>
(etc...)

That would make $tutor.Holly.text into an array of eleven items (indexes from 0 to 10), and each element in the array would be set to 0 by the .fill() method. (Note: The capitalization of the names, like “Holly”, have to match the capitalization used in $tutorRandomSet.)

Then you could do things like this:

"Hi, <<if $tutor[$tutorRandom].text[1] is 1>>did you get my text?<<else>>how are you?<</if>>"

"<<if $tutor[$tutorRandom].text[1] is 1>>Yeah, I got your text.<<else>>Fine.<</if>> By the way, $tutorRandom, how old are you?"

"I'm <<= $tutor[$tutorRandom].main.age>>."

You can use the bracket notation like that to use a variable as the name of a property on a variable. In other words, if $tutorRandom is set to “Holly”, then $tutor[$tutorRandom].main.age acts the same as if you’d done $tutor.Holly.main.age.

That should make things a lot simpler.

Basically, anytime you find yourself repeating essentially the same code more than two or three times, there’s almost always a more efficient way to do that code.

Hopefully that makes sense now. :smiley:

2 Likes

Perfect, thank you!